xref: /petsc/src/dm/impls/plex/plex.c (revision 4c0734734d58d4ca034ce314f11017fb06cebca7)
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 
131   /* the vcdof and globalvcdof are sized to allow every polytope type and simple vertex at DM_NUM_POLYTOPES */
132   PetscCall(PetscArrayzero(vcdof, DM_NUM_POLYTOPES + 1));
133   PetscCall(DMGetCoordinateDim(dm, &cdim));
134   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
135   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
136   if (field >= 0) {
137     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, vStart, field, &vcdof[DM_NUM_POLYTOPES]));
138   } else {
139     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetDof(section, vStart, &vcdof[DM_NUM_POLYTOPES]));
140   }
141 
142   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
143   PetscCall(DMPlexGetDepth(dm, &depth));
144   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
145   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
146   for (c = 0; c < DM_NUM_POLYTOPES; ++c) {
147     const DMPolytopeType ict = (DMPolytopeType)c;
148     PetscInt             dep;
149 
150     if (ict == DM_POLYTOPE_FV_GHOST) continue;
151     PetscCall(DMLabelGetStratumBounds(ctLabel, ict, &cStart, &cEnd));
152     if (pStart >= 0) {
153       PetscCall(DMLabelGetValue(depthLabel, cStart, &dep));
154       if (dep != depth - cellHeight) continue;
155     }
156     if (field >= 0) {
157       if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, cStart, field, &vcdof[c]));
158     } else {
159       if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetDof(section, cStart, &vcdof[c]));
160     }
161     PetscCall(MPIU_Allreduce(vcdof, globalvcdof, 2, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
162   }
163 
164   PetscCall(MPIU_Allreduce(vcdof, globalvcdof, DM_NUM_POLYTOPES + 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
165   *types = 0;
166 
167   for (c = 0; c < DM_NUM_POLYTOPES + 1; ++c) {
168     if (globalvcdof[c]) ++(*types);
169   }
170 
171   PetscCall(PetscMalloc3(*types, &sStart, *types, &sEnd, *types, &ft));
172   t = 0;
173   if (globalvcdof[DM_NUM_POLYTOPES]) {
174     sStart[t] = vStart;
175     sEnd[t]   = vEnd;
176     ft[t]     = (globalvcdof[t] == cdim) ? PETSC_VTK_POINT_VECTOR_FIELD : PETSC_VTK_POINT_FIELD;
177     ++t;
178   }
179 
180   for (c = 0; c < DM_NUM_POLYTOPES; ++c) {
181     if (globalvcdof[c]) {
182       const DMPolytopeType ict = (DMPolytopeType)c;
183 
184       PetscCall(DMLabelGetStratumBounds(ctLabel, ict, &cStart, &cEnd));
185       sStart[t] = cStart;
186       sEnd[t]   = cEnd;
187       ft[t]     = (globalvcdof[c] == cdim) ? PETSC_VTK_CELL_VECTOR_FIELD : PETSC_VTK_CELL_FIELD;
188       ++t;
189     }
190   }
191 
192   if (!(*types)) {
193     if (field >= 0) {
194       const char *fieldname;
195 
196       PetscCall(PetscSectionGetFieldName(section, field, &fieldname));
197       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section field %" PetscInt_FMT " \"%s\"\n", field, fieldname));
198     } else {
199       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section\n"));
200     }
201   }
202 
203   *ssStart = sStart;
204   *ssEnd   = sEnd;
205   *sft     = ft;
206   PetscFunctionReturn(PETSC_SUCCESS);
207 }
208 
209 PetscErrorCode DMPlexRestoreFieldTypes_Internal(DM dm, PetscSection section, PetscInt field, PetscInt *types, PetscInt **sStart, PetscInt **sEnd, PetscViewerVTKFieldType **ft)
210 {
211   PetscFunctionBegin;
212   PetscCall(PetscFree3(*sStart, *sEnd, *ft));
213   PetscFunctionReturn(PETSC_SUCCESS);
214 }
215 
216 PetscErrorCode DMPlexGetFieldType_Internal(DM dm, PetscSection section, PetscInt field, PetscInt *sStart, PetscInt *sEnd, PetscViewerVTKFieldType *ft)
217 {
218   PetscInt cdim, pStart, pEnd, vStart, vEnd, cStart, cEnd;
219   PetscInt vcdof[2] = {0, 0}, globalvcdof[2];
220 
221   PetscFunctionBegin;
222   *ft = PETSC_VTK_INVALID;
223   PetscCall(DMGetCoordinateDim(dm, &cdim));
224   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
225   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
226   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
227   if (field >= 0) {
228     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, vStart, field, &vcdof[0]));
229     if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, cStart, field, &vcdof[1]));
230   } else {
231     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetDof(section, vStart, &vcdof[0]));
232     if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetDof(section, cStart, &vcdof[1]));
233   }
234   PetscCall(MPIU_Allreduce(vcdof, globalvcdof, 2, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
235   if (globalvcdof[0]) {
236     *sStart = vStart;
237     *sEnd   = vEnd;
238     if (globalvcdof[0] == cdim) *ft = PETSC_VTK_POINT_VECTOR_FIELD;
239     else *ft = PETSC_VTK_POINT_FIELD;
240   } else if (globalvcdof[1]) {
241     *sStart = cStart;
242     *sEnd   = cEnd;
243     if (globalvcdof[1] == cdim) *ft = PETSC_VTK_CELL_VECTOR_FIELD;
244     else *ft = PETSC_VTK_CELL_FIELD;
245   } else {
246     if (field >= 0) {
247       const char *fieldname;
248 
249       PetscCall(PetscSectionGetFieldName(section, field, &fieldname));
250       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section field %" PetscInt_FMT " \"%s\"\n", field, fieldname));
251     } else {
252       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section\n"));
253     }
254   }
255   PetscFunctionReturn(PETSC_SUCCESS);
256 }
257 
258 /*@
259   DMPlexVecView1D - Plot many 1D solutions on the same line graph
260 
261   Collective
262 
263   Input Parameters:
264 + dm     - The `DMPLEX` object
265 . n      - The number of vectors
266 . u      - The array of local vectors
267 - viewer - The `PetscViewer`
268 
269   Level: advanced
270 
271 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `VecViewFromOptions()`, `VecView()`
272 @*/
273 PetscErrorCode DMPlexVecView1D(DM dm, PetscInt n, Vec u[], PetscViewer viewer)
274 {
275   PetscDS            ds;
276   PetscDraw          draw = NULL;
277   PetscDrawLG        lg;
278   Vec                coordinates;
279   const PetscScalar *coords, **sol;
280   PetscReal         *vals;
281   PetscInt          *Nc;
282   PetscInt           Nf, f, c, Nl, l, i, vStart, vEnd, v;
283   char             **names;
284 
285   PetscFunctionBegin;
286   PetscCall(DMGetDS(dm, &ds));
287   PetscCall(PetscDSGetNumFields(ds, &Nf));
288   PetscCall(PetscDSGetTotalComponents(ds, &Nl));
289   PetscCall(PetscDSGetComponents(ds, &Nc));
290 
291   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
292   if (!draw) PetscFunctionReturn(PETSC_SUCCESS);
293   PetscCall(PetscDrawLGCreate(draw, n * Nl, &lg));
294 
295   PetscCall(PetscMalloc3(n, &sol, n * Nl, &names, n * Nl, &vals));
296   for (i = 0, l = 0; i < n; ++i) {
297     const char *vname;
298 
299     PetscCall(PetscObjectGetName((PetscObject)u[i], &vname));
300     for (f = 0; f < Nf; ++f) {
301       PetscObject disc;
302       const char *fname;
303       char        tmpname[PETSC_MAX_PATH_LEN];
304 
305       PetscCall(PetscDSGetDiscretization(ds, f, &disc));
306       /* TODO Create names for components */
307       for (c = 0; c < Nc[f]; ++c, ++l) {
308         PetscCall(PetscObjectGetName(disc, &fname));
309         PetscCall(PetscStrncpy(tmpname, vname, sizeof(tmpname)));
310         PetscCall(PetscStrlcat(tmpname, ":", sizeof(tmpname)));
311         PetscCall(PetscStrlcat(tmpname, fname, sizeof(tmpname)));
312         PetscCall(PetscStrallocpy(tmpname, &names[l]));
313       }
314     }
315   }
316   PetscCall(PetscDrawLGSetLegend(lg, (const char *const *)names));
317   /* Just add P_1 support for now */
318   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
319   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
320   PetscCall(VecGetArrayRead(coordinates, &coords));
321   for (i = 0; i < n; ++i) PetscCall(VecGetArrayRead(u[i], &sol[i]));
322   for (v = vStart; v < vEnd; ++v) {
323     PetscScalar *x, *svals;
324 
325     PetscCall(DMPlexPointLocalRead(dm, v, coords, &x));
326     for (i = 0; i < n; ++i) {
327       PetscCall(DMPlexPointLocalRead(dm, v, sol[i], &svals));
328       for (l = 0; l < Nl; ++l) vals[i * Nl + l] = PetscRealPart(svals[l]);
329     }
330     PetscCall(PetscDrawLGAddCommonPoint(lg, PetscRealPart(x[0]), vals));
331   }
332   PetscCall(VecRestoreArrayRead(coordinates, &coords));
333   for (i = 0; i < n; ++i) PetscCall(VecRestoreArrayRead(u[i], &sol[i]));
334   for (l = 0; l < n * Nl; ++l) PetscCall(PetscFree(names[l]));
335   PetscCall(PetscFree3(sol, names, vals));
336 
337   PetscCall(PetscDrawLGDraw(lg));
338   PetscCall(PetscDrawLGDestroy(&lg));
339   PetscFunctionReturn(PETSC_SUCCESS);
340 }
341 
342 static PetscErrorCode VecView_Plex_Local_Draw_1D(Vec u, PetscViewer viewer)
343 {
344   DM dm;
345 
346   PetscFunctionBegin;
347   PetscCall(VecGetDM(u, &dm));
348   PetscCall(DMPlexVecView1D(dm, 1, &u, viewer));
349   PetscFunctionReturn(PETSC_SUCCESS);
350 }
351 
352 static PetscErrorCode VecView_Plex_Local_Draw_2D(Vec v, PetscViewer viewer)
353 {
354   DM                 dm;
355   PetscSection       s;
356   PetscDraw          draw, popup;
357   DM                 cdm;
358   PetscSection       coordSection;
359   Vec                coordinates;
360   const PetscScalar *array;
361   PetscReal          lbound[3], ubound[3];
362   PetscReal          vbound[2], time;
363   PetscBool          flg;
364   PetscInt           dim, Nf, f, Nc, comp, vStart, vEnd, cStart, cEnd, c, N, level, step, w = 0;
365   const char        *name;
366   char               title[PETSC_MAX_PATH_LEN];
367 
368   PetscFunctionBegin;
369   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
370   PetscCall(VecGetDM(v, &dm));
371   PetscCall(DMGetCoordinateDim(dm, &dim));
372   PetscCall(DMGetLocalSection(dm, &s));
373   PetscCall(PetscSectionGetNumFields(s, &Nf));
374   PetscCall(DMGetCoarsenLevel(dm, &level));
375   PetscCall(DMGetCoordinateDM(dm, &cdm));
376   PetscCall(DMGetLocalSection(cdm, &coordSection));
377   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
378   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
379   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
380 
381   PetscCall(PetscObjectGetName((PetscObject)v, &name));
382   PetscCall(DMGetOutputSequenceNumber(dm, &step, &time));
383 
384   PetscCall(VecGetLocalSize(coordinates, &N));
385   PetscCall(DMGetBoundingBox(dm, lbound, ubound));
386   PetscCall(PetscDrawClear(draw));
387 
388   /* Could implement something like DMDASelectFields() */
389   for (f = 0; f < Nf; ++f) {
390     DM          fdm = dm;
391     Vec         fv  = v;
392     IS          fis;
393     char        prefix[PETSC_MAX_PATH_LEN];
394     const char *fname;
395 
396     PetscCall(PetscSectionGetFieldComponents(s, f, &Nc));
397     PetscCall(PetscSectionGetFieldName(s, f, &fname));
398 
399     if (v->hdr.prefix) PetscCall(PetscStrncpy(prefix, v->hdr.prefix, sizeof(prefix)));
400     else prefix[0] = '\0';
401     if (Nf > 1) {
402       PetscCall(DMCreateSubDM(dm, 1, &f, &fis, &fdm));
403       PetscCall(VecGetSubVector(v, fis, &fv));
404       PetscCall(PetscStrlcat(prefix, fname, sizeof(prefix)));
405       PetscCall(PetscStrlcat(prefix, "_", sizeof(prefix)));
406     }
407     for (comp = 0; comp < Nc; ++comp, ++w) {
408       PetscInt nmax = 2;
409 
410       PetscCall(PetscViewerDrawGetDraw(viewer, w, &draw));
411       if (Nc > 1) PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s_%" PetscInt_FMT " Step: %" PetscInt_FMT " Time: %.4g", name, fname, comp, step, (double)time));
412       else PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s Step: %" PetscInt_FMT " Time: %.4g", name, fname, step, (double)time));
413       PetscCall(PetscDrawSetTitle(draw, title));
414 
415       /* TODO Get max and min only for this component */
416       PetscCall(PetscOptionsGetRealArray(NULL, prefix, "-vec_view_bounds", vbound, &nmax, &flg));
417       if (!flg) {
418         PetscCall(VecMin(fv, NULL, &vbound[0]));
419         PetscCall(VecMax(fv, NULL, &vbound[1]));
420         if (vbound[1] <= vbound[0]) vbound[1] = vbound[0] + 1.0;
421       }
422 
423       PetscCall(PetscDrawGetPopup(draw, &popup));
424       PetscCall(PetscDrawScalePopup(popup, vbound[0], vbound[1]));
425       PetscCall(PetscDrawSetCoordinates(draw, lbound[0], lbound[1], ubound[0], ubound[1]));
426       PetscCall(VecGetArrayRead(fv, &array));
427       for (c = cStart; c < cEnd; ++c) {
428         PetscScalar       *coords = NULL, *a = NULL;
429         const PetscScalar *coords_arr;
430         PetscBool          isDG;
431         PetscInt           numCoords, color[4] = {-1, -1, -1, -1};
432 
433         PetscCall(DMPlexPointLocalRead(fdm, c, array, &a));
434         if (a) {
435           color[0] = PetscDrawRealToColor(PetscRealPart(a[comp]), vbound[0], vbound[1]);
436           color[1] = color[2] = color[3] = color[0];
437         } else {
438           PetscScalar *vals = NULL;
439           PetscInt     numVals, va;
440 
441           PetscCall(DMPlexVecGetClosure(fdm, NULL, fv, c, &numVals, &vals));
442           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);
443           switch (numVals / Nc) {
444           case 3: /* P1 Triangle */
445           case 4: /* P1 Quadrangle */
446             for (va = 0; va < numVals / Nc; ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp]), vbound[0], vbound[1]);
447             break;
448           case 6: /* P2 Triangle */
449           case 8: /* P2 Quadrangle */
450             for (va = 0; va < numVals / (Nc * 2); ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp + numVals / (Nc * 2)]), vbound[0], vbound[1]);
451             break;
452           default:
453             SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of values for cell closure %" PetscInt_FMT " cannot be handled", numVals / Nc);
454           }
455           PetscCall(DMPlexVecRestoreClosure(fdm, NULL, fv, c, &numVals, &vals));
456         }
457         PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
458         switch (numCoords) {
459         case 6:
460         case 12: /* Localized triangle */
461           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]));
462           break;
463         case 8:
464         case 16: /* Localized quadrilateral */
465           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]));
466           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]));
467           break;
468         default:
469           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells with %" PetscInt_FMT " coordinates", numCoords);
470         }
471         PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
472       }
473       PetscCall(VecRestoreArrayRead(fv, &array));
474       PetscCall(PetscDrawFlush(draw));
475       PetscCall(PetscDrawPause(draw));
476       PetscCall(PetscDrawSave(draw));
477     }
478     if (Nf > 1) {
479       PetscCall(VecRestoreSubVector(v, fis, &fv));
480       PetscCall(ISDestroy(&fis));
481       PetscCall(DMDestroy(&fdm));
482     }
483   }
484   PetscFunctionReturn(PETSC_SUCCESS);
485 }
486 
487 static PetscErrorCode VecView_Plex_Local_Draw(Vec v, PetscViewer viewer)
488 {
489   DM        dm;
490   PetscDraw draw;
491   PetscInt  dim;
492   PetscBool isnull;
493 
494   PetscFunctionBegin;
495   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
496   PetscCall(PetscDrawIsNull(draw, &isnull));
497   if (isnull) PetscFunctionReturn(PETSC_SUCCESS);
498 
499   PetscCall(VecGetDM(v, &dm));
500   PetscCall(DMGetCoordinateDim(dm, &dim));
501   switch (dim) {
502   case 1:
503     PetscCall(VecView_Plex_Local_Draw_1D(v, viewer));
504     break;
505   case 2:
506     PetscCall(VecView_Plex_Local_Draw_2D(v, viewer));
507     break;
508   default:
509     SETERRQ(PetscObjectComm((PetscObject)v), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT ". Try PETSCVIEWERGLVIS", dim);
510   }
511   PetscFunctionReturn(PETSC_SUCCESS);
512 }
513 
514 static PetscErrorCode VecView_Plex_Local_VTK(Vec v, PetscViewer viewer)
515 {
516   DM                      dm;
517   Vec                     locv;
518   const char             *name;
519   PetscSection            section;
520   PetscInt                pStart, pEnd;
521   PetscInt                numFields;
522   PetscViewerVTKFieldType ft;
523 
524   PetscFunctionBegin;
525   PetscCall(VecGetDM(v, &dm));
526   PetscCall(DMCreateLocalVector(dm, &locv)); /* VTK viewer requires exclusive ownership of the vector */
527   PetscCall(PetscObjectGetName((PetscObject)v, &name));
528   PetscCall(PetscObjectSetName((PetscObject)locv, name));
529   PetscCall(VecCopy(v, locv));
530   PetscCall(DMGetLocalSection(dm, &section));
531   PetscCall(PetscSectionGetNumFields(section, &numFields));
532   if (!numFields) {
533     PetscCall(DMPlexGetFieldType_Internal(dm, section, PETSC_DETERMINE, &pStart, &pEnd, &ft));
534     PetscCall(PetscViewerVTKAddField(viewer, (PetscObject)dm, DMPlexVTKWriteAll, PETSC_DEFAULT, ft, PETSC_TRUE, (PetscObject)locv));
535   } else {
536     PetscInt f;
537 
538     for (f = 0; f < numFields; f++) {
539       PetscCall(DMPlexGetFieldType_Internal(dm, section, f, &pStart, &pEnd, &ft));
540       if (ft == PETSC_VTK_INVALID) continue;
541       PetscCall(PetscObjectReference((PetscObject)locv));
542       PetscCall(PetscViewerVTKAddField(viewer, (PetscObject)dm, DMPlexVTKWriteAll, f, ft, PETSC_TRUE, (PetscObject)locv));
543     }
544     PetscCall(VecDestroy(&locv));
545   }
546   PetscFunctionReturn(PETSC_SUCCESS);
547 }
548 
549 PetscErrorCode VecView_Plex_Local(Vec v, PetscViewer viewer)
550 {
551   DM        dm;
552   PetscBool isvtk, ishdf5, isdraw, isglvis, iscgns;
553 
554   PetscFunctionBegin;
555   PetscCall(VecGetDM(v, &dm));
556   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
557   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
558   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
559   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
560   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
561   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
562   if (isvtk || ishdf5 || isdraw || isglvis || iscgns) {
563     PetscInt    i, numFields;
564     PetscObject fe;
565     PetscBool   fem  = PETSC_FALSE;
566     Vec         locv = v;
567     const char *name;
568     PetscInt    step;
569     PetscReal   time;
570 
571     PetscCall(DMGetNumFields(dm, &numFields));
572     for (i = 0; i < numFields; i++) {
573       PetscCall(DMGetField(dm, i, NULL, &fe));
574       if (fe->classid == PETSCFE_CLASSID) {
575         fem = PETSC_TRUE;
576         break;
577       }
578     }
579     if (fem) {
580       PetscObject isZero;
581 
582       PetscCall(DMGetLocalVector(dm, &locv));
583       PetscCall(PetscObjectGetName((PetscObject)v, &name));
584       PetscCall(PetscObjectSetName((PetscObject)locv, name));
585       PetscCall(PetscObjectQuery((PetscObject)v, "__Vec_bc_zero__", &isZero));
586       PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", isZero));
587       PetscCall(VecCopy(v, locv));
588       PetscCall(DMGetOutputSequenceNumber(dm, NULL, &time));
589       PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_TRUE, locv, time, NULL, NULL, NULL));
590     }
591     if (isvtk) {
592       PetscCall(VecView_Plex_Local_VTK(locv, viewer));
593     } else if (ishdf5) {
594 #if defined(PETSC_HAVE_HDF5)
595       PetscCall(VecView_Plex_Local_HDF5_Internal(locv, viewer));
596 #else
597       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
598 #endif
599     } else if (isdraw) {
600       PetscCall(VecView_Plex_Local_Draw(locv, viewer));
601     } else if (isglvis) {
602       PetscCall(DMGetOutputSequenceNumber(dm, &step, NULL));
603       PetscCall(PetscViewerGLVisSetSnapId(viewer, step));
604       PetscCall(VecView_GLVis(locv, viewer));
605     } else if (iscgns) {
606 #if defined(PETSC_HAVE_CGNS)
607       PetscCall(VecView_Plex_Local_CGNS(locv, viewer));
608 #else
609       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "CGNS not supported in this build.\nPlease reconfigure using --download-cgns");
610 #endif
611     }
612     if (fem) {
613       PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", NULL));
614       PetscCall(DMRestoreLocalVector(dm, &locv));
615     }
616   } else {
617     PetscBool isseq;
618 
619     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
620     if (isseq) PetscCall(VecView_Seq(v, viewer));
621     else PetscCall(VecView_MPI(v, viewer));
622   }
623   PetscFunctionReturn(PETSC_SUCCESS);
624 }
625 
626 PetscErrorCode VecView_Plex(Vec v, PetscViewer viewer)
627 {
628   DM        dm;
629   PetscBool isvtk, ishdf5, isdraw, isglvis, isexodusii, iscgns;
630 
631   PetscFunctionBegin;
632   PetscCall(VecGetDM(v, &dm));
633   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
634   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
635   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
636   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
637   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
638   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
639   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodusii));
640   if (isvtk || isdraw || isglvis || iscgns) {
641     Vec         locv;
642     PetscObject isZero;
643     const char *name;
644 
645     PetscCall(DMGetLocalVector(dm, &locv));
646     PetscCall(PetscObjectGetName((PetscObject)v, &name));
647     PetscCall(PetscObjectSetName((PetscObject)locv, name));
648     PetscCall(DMGlobalToLocalBegin(dm, v, INSERT_VALUES, locv));
649     PetscCall(DMGlobalToLocalEnd(dm, v, INSERT_VALUES, locv));
650     PetscCall(PetscObjectQuery((PetscObject)v, "__Vec_bc_zero__", &isZero));
651     PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", isZero));
652     PetscCall(VecView_Plex_Local(locv, viewer));
653     PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", NULL));
654     PetscCall(DMRestoreLocalVector(dm, &locv));
655   } else if (ishdf5) {
656 #if defined(PETSC_HAVE_HDF5)
657     PetscCall(VecView_Plex_HDF5_Internal(v, viewer));
658 #else
659     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
660 #endif
661   } else if (isexodusii) {
662 #if defined(PETSC_HAVE_EXODUSII)
663     PetscCall(VecView_PlexExodusII_Internal(v, viewer));
664 #else
665     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
666 #endif
667   } else {
668     PetscBool isseq;
669 
670     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
671     if (isseq) PetscCall(VecView_Seq(v, viewer));
672     else PetscCall(VecView_MPI(v, viewer));
673   }
674   PetscFunctionReturn(PETSC_SUCCESS);
675 }
676 
677 PetscErrorCode VecView_Plex_Native(Vec originalv, PetscViewer viewer)
678 {
679   DM                dm;
680   MPI_Comm          comm;
681   PetscViewerFormat format;
682   Vec               v;
683   PetscBool         isvtk, ishdf5;
684 
685   PetscFunctionBegin;
686   PetscCall(VecGetDM(originalv, &dm));
687   PetscCall(PetscObjectGetComm((PetscObject)originalv, &comm));
688   PetscCheck(dm, comm, PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
689   PetscCall(PetscViewerGetFormat(viewer, &format));
690   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
691   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
692   if (format == PETSC_VIEWER_NATIVE) {
693     /* Natural ordering is the common case for DMDA, NATIVE means plain vector, for PLEX is the opposite */
694     /* this need a better fix */
695     if (dm->useNatural) {
696       if (dm->sfNatural) {
697         const char *vecname;
698         PetscInt    n, nroots;
699 
700         PetscCall(VecGetLocalSize(originalv, &n));
701         PetscCall(PetscSFGetGraph(dm->sfNatural, &nroots, NULL, NULL, NULL));
702         if (n == nroots) {
703           PetscCall(DMPlexCreateNaturalVector(dm, &v));
704           PetscCall(DMPlexGlobalToNaturalBegin(dm, originalv, v));
705           PetscCall(DMPlexGlobalToNaturalEnd(dm, originalv, v));
706           PetscCall(PetscObjectGetName((PetscObject)originalv, &vecname));
707           PetscCall(PetscObjectSetName((PetscObject)v, vecname));
708         } else SETERRQ(comm, PETSC_ERR_ARG_WRONG, "DM global to natural SF only handles global vectors");
709       } else SETERRQ(comm, PETSC_ERR_ARG_WRONGSTATE, "DM global to natural SF was not created");
710     } else v = originalv;
711   } else v = originalv;
712 
713   if (ishdf5) {
714 #if defined(PETSC_HAVE_HDF5)
715     PetscCall(VecView_Plex_HDF5_Native_Internal(v, viewer));
716 #else
717     SETERRQ(comm, PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
718 #endif
719   } else if (isvtk) {
720     SETERRQ(comm, PETSC_ERR_SUP, "VTK format does not support viewing in natural order. Please switch to HDF5.");
721   } else {
722     PetscBool isseq;
723 
724     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
725     if (isseq) PetscCall(VecView_Seq(v, viewer));
726     else PetscCall(VecView_MPI(v, viewer));
727   }
728   if (v != originalv) PetscCall(VecDestroy(&v));
729   PetscFunctionReturn(PETSC_SUCCESS);
730 }
731 
732 PetscErrorCode VecLoad_Plex_Local(Vec v, PetscViewer viewer)
733 {
734   DM        dm;
735   PetscBool ishdf5;
736 
737   PetscFunctionBegin;
738   PetscCall(VecGetDM(v, &dm));
739   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
740   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
741   if (ishdf5) {
742     DM          dmBC;
743     Vec         gv;
744     const char *name;
745 
746     PetscCall(DMGetOutputDM(dm, &dmBC));
747     PetscCall(DMGetGlobalVector(dmBC, &gv));
748     PetscCall(PetscObjectGetName((PetscObject)v, &name));
749     PetscCall(PetscObjectSetName((PetscObject)gv, name));
750     PetscCall(VecLoad_Default(gv, viewer));
751     PetscCall(DMGlobalToLocalBegin(dmBC, gv, INSERT_VALUES, v));
752     PetscCall(DMGlobalToLocalEnd(dmBC, gv, INSERT_VALUES, v));
753     PetscCall(DMRestoreGlobalVector(dmBC, &gv));
754   } else PetscCall(VecLoad_Default(v, viewer));
755   PetscFunctionReturn(PETSC_SUCCESS);
756 }
757 
758 PetscErrorCode VecLoad_Plex(Vec v, PetscViewer viewer)
759 {
760   DM        dm;
761   PetscBool ishdf5, isexodusii;
762 
763   PetscFunctionBegin;
764   PetscCall(VecGetDM(v, &dm));
765   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
766   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
767   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodusii));
768   if (ishdf5) {
769 #if defined(PETSC_HAVE_HDF5)
770     PetscCall(VecLoad_Plex_HDF5_Internal(v, viewer));
771 #else
772     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
773 #endif
774   } else if (isexodusii) {
775 #if defined(PETSC_HAVE_EXODUSII)
776     PetscCall(VecLoad_PlexExodusII_Internal(v, viewer));
777 #else
778     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
779 #endif
780   } else PetscCall(VecLoad_Default(v, viewer));
781   PetscFunctionReturn(PETSC_SUCCESS);
782 }
783 
784 PetscErrorCode VecLoad_Plex_Native(Vec originalv, PetscViewer viewer)
785 {
786   DM                dm;
787   PetscViewerFormat format;
788   PetscBool         ishdf5;
789 
790   PetscFunctionBegin;
791   PetscCall(VecGetDM(originalv, &dm));
792   PetscCheck(dm, PetscObjectComm((PetscObject)originalv), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
793   PetscCall(PetscViewerGetFormat(viewer, &format));
794   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
795   if (format == PETSC_VIEWER_NATIVE) {
796     if (dm->useNatural) {
797       if (dm->sfNatural) {
798         if (ishdf5) {
799 #if defined(PETSC_HAVE_HDF5)
800           Vec         v;
801           const char *vecname;
802 
803           PetscCall(DMPlexCreateNaturalVector(dm, &v));
804           PetscCall(PetscObjectGetName((PetscObject)originalv, &vecname));
805           PetscCall(PetscObjectSetName((PetscObject)v, vecname));
806           PetscCall(VecLoad_Plex_HDF5_Native_Internal(v, viewer));
807           PetscCall(DMPlexNaturalToGlobalBegin(dm, v, originalv));
808           PetscCall(DMPlexNaturalToGlobalEnd(dm, v, originalv));
809           PetscCall(VecDestroy(&v));
810 #else
811           SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
812 #endif
813         } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Reading in natural order is not supported for anything but HDF5.");
814       }
815     } else PetscCall(VecLoad_Default(originalv, viewer));
816   }
817   PetscFunctionReturn(PETSC_SUCCESS);
818 }
819 
820 PETSC_UNUSED static PetscErrorCode DMPlexView_Ascii_Geometry(DM dm, PetscViewer viewer)
821 {
822   PetscSection       coordSection;
823   Vec                coordinates;
824   DMLabel            depthLabel, celltypeLabel;
825   const char        *name[4];
826   const PetscScalar *a;
827   PetscInt           dim, pStart, pEnd, cStart, cEnd, c;
828 
829   PetscFunctionBegin;
830   PetscCall(DMGetDimension(dm, &dim));
831   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
832   PetscCall(DMGetCoordinateSection(dm, &coordSection));
833   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
834   PetscCall(DMPlexGetCellTypeLabel(dm, &celltypeLabel));
835   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
836   PetscCall(PetscSectionGetChart(coordSection, &pStart, &pEnd));
837   PetscCall(VecGetArrayRead(coordinates, &a));
838   name[0]       = "vertex";
839   name[1]       = "edge";
840   name[dim - 1] = "face";
841   name[dim]     = "cell";
842   for (c = cStart; c < cEnd; ++c) {
843     PetscInt *closure = NULL;
844     PetscInt  closureSize, cl, ct;
845 
846     PetscCall(DMLabelGetValue(celltypeLabel, c, &ct));
847     PetscCall(PetscViewerASCIIPrintf(viewer, "Geometry for cell %" PetscInt_FMT " polytope type %s:\n", c, DMPolytopeTypes[ct]));
848     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
849     PetscCall(PetscViewerASCIIPushTab(viewer));
850     for (cl = 0; cl < closureSize * 2; cl += 2) {
851       PetscInt point = closure[cl], depth, dof, off, d, p;
852 
853       if ((point < pStart) || (point >= pEnd)) continue;
854       PetscCall(PetscSectionGetDof(coordSection, point, &dof));
855       if (!dof) continue;
856       PetscCall(DMLabelGetValue(depthLabel, point, &depth));
857       PetscCall(PetscSectionGetOffset(coordSection, point, &off));
858       PetscCall(PetscViewerASCIIPrintf(viewer, "%s %" PetscInt_FMT " coords:", name[depth], point));
859       for (p = 0; p < dof / dim; ++p) {
860         PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
861         for (d = 0; d < dim; ++d) {
862           if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
863           PetscCall(PetscViewerASCIIPrintf(viewer, "%g", (double)PetscRealPart(a[off + p * dim + d])));
864         }
865         PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
866       }
867       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
868     }
869     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
870     PetscCall(PetscViewerASCIIPopTab(viewer));
871   }
872   PetscCall(VecRestoreArrayRead(coordinates, &a));
873   PetscFunctionReturn(PETSC_SUCCESS);
874 }
875 
876 typedef enum {
877   CS_CARTESIAN,
878   CS_POLAR,
879   CS_CYLINDRICAL,
880   CS_SPHERICAL
881 } CoordSystem;
882 const char *CoordSystems[] = {"cartesian", "polar", "cylindrical", "spherical", "CoordSystem", "CS_", NULL};
883 
884 static PetscErrorCode DMPlexView_Ascii_Coordinates(PetscViewer viewer, CoordSystem cs, PetscInt dim, const PetscScalar x[])
885 {
886   PetscInt i;
887 
888   PetscFunctionBegin;
889   if (dim > 3) {
890     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double)PetscRealPart(x[i])));
891   } else {
892     PetscReal coords[3], trcoords[3] = {0., 0., 0.};
893 
894     for (i = 0; i < dim; ++i) coords[i] = PetscRealPart(x[i]);
895     switch (cs) {
896     case CS_CARTESIAN:
897       for (i = 0; i < dim; ++i) trcoords[i] = coords[i];
898       break;
899     case CS_POLAR:
900       PetscCheck(dim == 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Polar coordinates are for 2 dimension, not %" PetscInt_FMT, dim);
901       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
902       trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
903       break;
904     case CS_CYLINDRICAL:
905       PetscCheck(dim == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cylindrical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
906       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
907       trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
908       trcoords[2] = coords[2];
909       break;
910     case CS_SPHERICAL:
911       PetscCheck(dim == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Spherical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
912       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]) + PetscSqr(coords[2]));
913       trcoords[1] = PetscAtan2Real(PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1])), coords[2]);
914       trcoords[2] = PetscAtan2Real(coords[1], coords[0]);
915       break;
916     }
917     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double)trcoords[i]));
918   }
919   PetscFunctionReturn(PETSC_SUCCESS);
920 }
921 
922 static PetscErrorCode DMPlexView_Ascii(DM dm, PetscViewer viewer)
923 {
924   DM_Plex          *mesh = (DM_Plex *)dm->data;
925   DM                cdm, cdmCell;
926   PetscSection      coordSection, coordSectionCell;
927   Vec               coordinates, coordinatesCell;
928   PetscViewerFormat format;
929 
930   PetscFunctionBegin;
931   PetscCall(PetscViewerGetFormat(viewer, &format));
932   if (format == PETSC_VIEWER_ASCII_INFO_DETAIL) {
933     const char *name;
934     PetscInt    dim, cellHeight, maxConeSize, maxSupportSize;
935     PetscInt    pStart, pEnd, p, numLabels, l;
936     PetscMPIInt rank, size;
937 
938     PetscCall(DMGetCoordinateDM(dm, &cdm));
939     PetscCall(DMGetCoordinateSection(dm, &coordSection));
940     PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
941     PetscCall(DMGetCellCoordinateDM(dm, &cdmCell));
942     PetscCall(DMGetCellCoordinateSection(dm, &coordSectionCell));
943     PetscCall(DMGetCellCoordinatesLocal(dm, &coordinatesCell));
944     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
945     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
946     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
947     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
948     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
949     PetscCall(DMGetDimension(dm, &dim));
950     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
951     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
952     else PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
953     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
954     PetscCall(PetscViewerASCIIPrintf(viewer, "Supports:\n"));
955     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
956     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max support size: %" PetscInt_FMT "\n", rank, maxSupportSize));
957     for (p = pStart; p < pEnd; ++p) {
958       PetscInt dof, off, s;
959 
960       PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
961       PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
962       for (s = off; s < off + dof; ++s) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d]: %" PetscInt_FMT " ----> %" PetscInt_FMT "\n", rank, p, mesh->supports[s]));
963     }
964     PetscCall(PetscViewerFlush(viewer));
965     PetscCall(PetscViewerASCIIPrintf(viewer, "Cones:\n"));
966     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max cone size: %" PetscInt_FMT "\n", rank, maxConeSize));
967     for (p = pStart; p < pEnd; ++p) {
968       PetscInt dof, off, c;
969 
970       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
971       PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
972       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]));
973     }
974     PetscCall(PetscViewerFlush(viewer));
975     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
976     if (coordSection && coordinates) {
977       CoordSystem        cs = CS_CARTESIAN;
978       const PetscScalar *array, *arrayCell = NULL;
979       PetscInt           Nf, Nc, pvStart, pvEnd, pcStart = PETSC_MAX_INT, pcEnd = PETSC_MIN_INT, pStart, pEnd, p;
980       PetscMPIInt        rank;
981       const char        *name;
982 
983       PetscCall(PetscOptionsGetEnum(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_coord_system", CoordSystems, (PetscEnum *)&cs, NULL));
984       PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)viewer), &rank));
985       PetscCall(PetscSectionGetNumFields(coordSection, &Nf));
986       PetscCheck(Nf == 1, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Coordinate section should have 1 field, not %" PetscInt_FMT, Nf);
987       PetscCall(PetscSectionGetFieldComponents(coordSection, 0, &Nc));
988       PetscCall(PetscSectionGetChart(coordSection, &pvStart, &pvEnd));
989       if (coordSectionCell) PetscCall(PetscSectionGetChart(coordSectionCell, &pcStart, &pcEnd));
990       pStart = PetscMin(pvStart, pcStart);
991       pEnd   = PetscMax(pvEnd, pcEnd);
992       PetscCall(PetscObjectGetName((PetscObject)coordinates, &name));
993       PetscCall(PetscViewerASCIIPrintf(viewer, "%s with %" PetscInt_FMT " fields\n", name, Nf));
994       PetscCall(PetscViewerASCIIPrintf(viewer, "  field 0 with %" PetscInt_FMT " components\n", Nc));
995       if (cs != CS_CARTESIAN) PetscCall(PetscViewerASCIIPrintf(viewer, "  output coordinate system: %s\n", CoordSystems[cs]));
996 
997       PetscCall(VecGetArrayRead(coordinates, &array));
998       if (coordinatesCell) PetscCall(VecGetArrayRead(coordinatesCell, &arrayCell));
999       PetscCall(PetscViewerASCIIPushSynchronized(viewer));
1000       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "Process %d:\n", rank));
1001       for (p = pStart; p < pEnd; ++p) {
1002         PetscInt dof, off;
1003 
1004         if (p >= pvStart && p < pvEnd) {
1005           PetscCall(PetscSectionGetDof(coordSection, p, &dof));
1006           PetscCall(PetscSectionGetOffset(coordSection, p, &off));
1007           if (dof) {
1008             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dim %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
1009             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &array[off]));
1010             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
1011           }
1012         }
1013         if (cdmCell && p >= pcStart && p < pcEnd) {
1014           PetscCall(PetscSectionGetDof(coordSectionCell, p, &dof));
1015           PetscCall(PetscSectionGetOffset(coordSectionCell, p, &off));
1016           if (dof) {
1017             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dim %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
1018             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &arrayCell[off]));
1019             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
1020           }
1021         }
1022       }
1023       PetscCall(PetscViewerFlush(viewer));
1024       PetscCall(PetscViewerASCIIPopSynchronized(viewer));
1025       PetscCall(VecRestoreArrayRead(coordinates, &array));
1026       if (coordinatesCell) PetscCall(VecRestoreArrayRead(coordinatesCell, &arrayCell));
1027     }
1028     PetscCall(DMGetNumLabels(dm, &numLabels));
1029     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
1030     for (l = 0; l < numLabels; ++l) {
1031       DMLabel     label;
1032       PetscBool   isdepth;
1033       const char *name;
1034 
1035       PetscCall(DMGetLabelName(dm, l, &name));
1036       PetscCall(PetscStrcmp(name, "depth", &isdepth));
1037       if (isdepth) continue;
1038       PetscCall(DMGetLabel(dm, name, &label));
1039       PetscCall(DMLabelView(label, viewer));
1040     }
1041     if (size > 1) {
1042       PetscSF sf;
1043 
1044       PetscCall(DMGetPointSF(dm, &sf));
1045       PetscCall(PetscSFView(sf, viewer));
1046     }
1047     if (mesh->periodic.face_sf) PetscCall(PetscSFView(mesh->periodic.face_sf, viewer));
1048     PetscCall(PetscViewerFlush(viewer));
1049   } else if (format == PETSC_VIEWER_ASCII_LATEX) {
1050     const char  *name, *color;
1051     const char  *defcolors[3]  = {"gray", "orange", "green"};
1052     const char  *deflcolors[4] = {"blue", "cyan", "red", "magenta"};
1053     char         lname[PETSC_MAX_PATH_LEN];
1054     PetscReal    scale      = 2.0;
1055     PetscReal    tikzscale  = 1.0;
1056     PetscBool    useNumbers = PETSC_TRUE, drawNumbers[4], drawColors[4], useLabels, useColors, plotEdges, drawHasse = PETSC_FALSE;
1057     double       tcoords[3];
1058     PetscScalar *coords;
1059     PetscInt     numLabels, l, numColors, numLColors, dim, d, depth, cStart, cEnd, c, vStart, vEnd, v, eStart = 0, eEnd = 0, e, p, n;
1060     PetscMPIInt  rank, size;
1061     char       **names, **colors, **lcolors;
1062     PetscBool    flg, lflg;
1063     PetscBT      wp = NULL;
1064     PetscInt     pEnd, pStart;
1065 
1066     PetscCall(DMGetCoordinateDM(dm, &cdm));
1067     PetscCall(DMGetCoordinateSection(dm, &coordSection));
1068     PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1069     PetscCall(DMGetCellCoordinateDM(dm, &cdmCell));
1070     PetscCall(DMGetCellCoordinateSection(dm, &coordSectionCell));
1071     PetscCall(DMGetCellCoordinatesLocal(dm, &coordinatesCell));
1072     PetscCall(DMGetDimension(dm, &dim));
1073     PetscCall(DMPlexGetDepth(dm, &depth));
1074     PetscCall(DMGetNumLabels(dm, &numLabels));
1075     numLabels  = PetscMax(numLabels, 10);
1076     numColors  = 10;
1077     numLColors = 10;
1078     PetscCall(PetscCalloc3(numLabels, &names, numColors, &colors, numLColors, &lcolors));
1079     PetscCall(PetscOptionsGetReal(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_scale", &scale, NULL));
1080     PetscCall(PetscOptionsGetReal(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_tikzscale", &tikzscale, NULL));
1081     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_numbers", &useNumbers, NULL));
1082     for (d = 0; d < 4; ++d) drawNumbers[d] = useNumbers;
1083     for (d = 0; d < 4; ++d) drawColors[d] = PETSC_TRUE;
1084     n = 4;
1085     PetscCall(PetscOptionsGetBoolArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_numbers_depth", drawNumbers, &n, &flg));
1086     PetscCheck(!flg || n == dim + 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim + 1);
1087     n = 4;
1088     PetscCall(PetscOptionsGetBoolArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_colors_depth", drawColors, &n, &flg));
1089     PetscCheck(!flg || n == dim + 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim + 1);
1090     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_labels", names, &numLabels, &useLabels));
1091     if (!useLabels) numLabels = 0;
1092     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_colors", colors, &numColors, &useColors));
1093     if (!useColors) {
1094       numColors = 3;
1095       for (c = 0; c < numColors; ++c) PetscCall(PetscStrallocpy(defcolors[c], &colors[c]));
1096     }
1097     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_lcolors", lcolors, &numLColors, &useColors));
1098     if (!useColors) {
1099       numLColors = 4;
1100       for (c = 0; c < numLColors; ++c) PetscCall(PetscStrallocpy(deflcolors[c], &lcolors[c]));
1101     }
1102     PetscCall(PetscOptionsGetString(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_label_filter", lname, sizeof(lname), &lflg));
1103     plotEdges = (PetscBool)(depth > 1 && drawNumbers[1] && dim < 3);
1104     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_edges", &plotEdges, &flg));
1105     PetscCheck(!flg || !plotEdges || depth >= dim, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Mesh must be interpolated");
1106     if (depth < dim) plotEdges = PETSC_FALSE;
1107     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_hasse", &drawHasse, NULL));
1108 
1109     /* filter points with labelvalue != labeldefaultvalue */
1110     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
1111     PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1112     PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
1113     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1114     if (lflg) {
1115       DMLabel lbl;
1116 
1117       PetscCall(DMGetLabel(dm, lname, &lbl));
1118       if (lbl) {
1119         PetscInt val, defval;
1120 
1121         PetscCall(DMLabelGetDefaultValue(lbl, &defval));
1122         PetscCall(PetscBTCreate(pEnd - pStart, &wp));
1123         for (c = pStart; c < pEnd; c++) {
1124           PetscInt *closure = NULL;
1125           PetscInt  closureSize;
1126 
1127           PetscCall(DMLabelGetValue(lbl, c, &val));
1128           if (val == defval) continue;
1129 
1130           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1131           for (p = 0; p < closureSize * 2; p += 2) PetscCall(PetscBTSet(wp, closure[p] - pStart));
1132           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1133         }
1134       }
1135     }
1136 
1137     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1138     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
1139     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1140     PetscCall(PetscViewerASCIIPrintf(viewer, "\
1141 \\documentclass[tikz]{standalone}\n\n\
1142 \\usepackage{pgflibraryshapes}\n\
1143 \\usetikzlibrary{backgrounds}\n\
1144 \\usetikzlibrary{arrows}\n\
1145 \\begin{document}\n"));
1146     if (size > 1) {
1147       PetscCall(PetscViewerASCIIPrintf(viewer, "%s for process ", name));
1148       for (p = 0; p < size; ++p) {
1149         if (p) PetscCall(PetscViewerASCIIPrintf(viewer, (p == size - 1) ? ", and " : ", "));
1150         PetscCall(PetscViewerASCIIPrintf(viewer, "{\\textcolor{%s}%" PetscInt_FMT "}", colors[p % numColors], p));
1151       }
1152       PetscCall(PetscViewerASCIIPrintf(viewer, ".\n\n\n"));
1153     }
1154     if (drawHasse) {
1155       PetscInt maxStratum = PetscMax(vEnd - vStart, PetscMax(eEnd - eStart, cEnd - cStart));
1156 
1157       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vStart}{%" PetscInt_FMT "}\n", vStart));
1158       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vEnd}{%" PetscInt_FMT "}\n", vEnd - 1));
1159       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numVertices}{%" PetscInt_FMT "}\n", vEnd - vStart));
1160       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vShift}{%.2f}\n", 3 + (maxStratum - (vEnd - vStart)) / 2.));
1161       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eStart}{%" PetscInt_FMT "}\n", eStart));
1162       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eEnd}{%" PetscInt_FMT "}\n", eEnd - 1));
1163       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eShift}{%.2f}\n", 3 + (maxStratum - (eEnd - eStart)) / 2.));
1164       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numEdges}{%" PetscInt_FMT "}\n", eEnd - eStart));
1165       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cStart}{%" PetscInt_FMT "}\n", cStart));
1166       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cEnd}{%" PetscInt_FMT "}\n", cEnd - 1));
1167       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numCells}{%" PetscInt_FMT "}\n", cEnd - cStart));
1168       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cShift}{%.2f}\n", 3 + (maxStratum - (cEnd - cStart)) / 2.));
1169     }
1170     PetscCall(PetscViewerASCIIPrintf(viewer, "\\begin{tikzpicture}[scale = %g,font=\\fontsize{8}{8}\\selectfont]\n", (double)tikzscale));
1171 
1172     /* Plot vertices */
1173     PetscCall(VecGetArray(coordinates, &coords));
1174     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
1175     for (v = vStart; v < vEnd; ++v) {
1176       PetscInt  off, dof, d;
1177       PetscBool isLabeled = PETSC_FALSE;
1178 
1179       if (wp && !PetscBTLookup(wp, v - pStart)) continue;
1180       PetscCall(PetscSectionGetDof(coordSection, v, &dof));
1181       PetscCall(PetscSectionGetOffset(coordSection, v, &off));
1182       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1183       PetscCheck(dof <= 3, PETSC_COMM_SELF, PETSC_ERR_PLIB, "coordSection vertex %" PetscInt_FMT " has dof %" PetscInt_FMT " > 3", v, dof);
1184       for (d = 0; d < dof; ++d) {
1185         tcoords[d] = (double)(scale * PetscRealPart(coords[off + d]));
1186         tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1187       }
1188       /* Rotate coordinates since PGF makes z point out of the page instead of up */
1189       if (dim == 3) {
1190         PetscReal tmp = tcoords[1];
1191         tcoords[1]    = tcoords[2];
1192         tcoords[2]    = -tmp;
1193       }
1194       for (d = 0; d < dof; ++d) {
1195         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1196         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)tcoords[d]));
1197       }
1198       if (drawHasse) color = colors[0 % numColors];
1199       else color = colors[rank % numColors];
1200       for (l = 0; l < numLabels; ++l) {
1201         PetscInt val;
1202         PetscCall(DMGetLabelValue(dm, names[l], v, &val));
1203         if (val >= 0) {
1204           color     = lcolors[l % numLColors];
1205           isLabeled = PETSC_TRUE;
1206           break;
1207         }
1208       }
1209       if (drawNumbers[0]) {
1210         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", v, rank, color, v));
1211       } else if (drawColors[0]) {
1212         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", v, rank, !isLabeled ? 1 : 2, color));
1213       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", v, rank));
1214     }
1215     PetscCall(VecRestoreArray(coordinates, &coords));
1216     PetscCall(PetscViewerFlush(viewer));
1217     /* Plot edges */
1218     if (plotEdges) {
1219       PetscCall(VecGetArray(coordinates, &coords));
1220       PetscCall(PetscViewerASCIIPrintf(viewer, "\\path\n"));
1221       for (e = eStart; e < eEnd; ++e) {
1222         const PetscInt *cone;
1223         PetscInt        coneSize, offA, offB, dof, d;
1224 
1225         if (wp && !PetscBTLookup(wp, e - pStart)) continue;
1226         PetscCall(DMPlexGetConeSize(dm, e, &coneSize));
1227         PetscCheck(coneSize == 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Edge %" PetscInt_FMT " cone should have two vertices, not %" PetscInt_FMT, e, coneSize);
1228         PetscCall(DMPlexGetCone(dm, e, &cone));
1229         PetscCall(PetscSectionGetDof(coordSection, cone[0], &dof));
1230         PetscCall(PetscSectionGetOffset(coordSection, cone[0], &offA));
1231         PetscCall(PetscSectionGetOffset(coordSection, cone[1], &offB));
1232         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "("));
1233         for (d = 0; d < dof; ++d) {
1234           tcoords[d] = (double)(0.5 * scale * PetscRealPart(coords[offA + d] + coords[offB + d]));
1235           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1236         }
1237         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1238         if (dim == 3) {
1239           PetscReal tmp = tcoords[1];
1240           tcoords[1]    = tcoords[2];
1241           tcoords[2]    = -tmp;
1242         }
1243         for (d = 0; d < dof; ++d) {
1244           if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1245           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)tcoords[d]));
1246         }
1247         if (drawHasse) color = colors[1 % numColors];
1248         else color = colors[rank % numColors];
1249         for (l = 0; l < numLabels; ++l) {
1250           PetscInt val;
1251           PetscCall(DMGetLabelValue(dm, names[l], e, &val));
1252           if (val >= 0) {
1253             color = lcolors[l % numLColors];
1254             break;
1255           }
1256         }
1257         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "} --\n", e, rank, color, e));
1258       }
1259       PetscCall(VecRestoreArray(coordinates, &coords));
1260       PetscCall(PetscViewerFlush(viewer));
1261       PetscCall(PetscViewerASCIIPrintf(viewer, "(0,0);\n"));
1262     }
1263     /* Plot cells */
1264     if (dim == 3 || !drawNumbers[1]) {
1265       for (e = eStart; e < eEnd; ++e) {
1266         const PetscInt *cone;
1267 
1268         if (wp && !PetscBTLookup(wp, e - pStart)) continue;
1269         color = colors[rank % numColors];
1270         for (l = 0; l < numLabels; ++l) {
1271           PetscInt val;
1272           PetscCall(DMGetLabelValue(dm, names[l], e, &val));
1273           if (val >= 0) {
1274             color = lcolors[l % numLColors];
1275             break;
1276           }
1277         }
1278         PetscCall(DMPlexGetCone(dm, e, &cone));
1279         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", color, cone[0], rank, cone[1], rank));
1280       }
1281     } else {
1282       DMPolytopeType ct;
1283 
1284       /* Drawing a 2D polygon */
1285       for (c = cStart; c < cEnd; ++c) {
1286         if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1287         PetscCall(DMPlexGetCellType(dm, c, &ct));
1288         if (DMPolytopeTypeIsHybrid(ct)) {
1289           const PetscInt *cone;
1290           PetscInt        coneSize, e;
1291 
1292           PetscCall(DMPlexGetCone(dm, c, &cone));
1293           PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
1294           for (e = 0; e < coneSize; ++e) {
1295             const PetscInt *econe;
1296 
1297             PetscCall(DMPlexGetCone(dm, cone[e], &econe));
1298             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));
1299           }
1300         } else {
1301           PetscInt *closure = NULL;
1302           PetscInt  closureSize, Nv = 0, v;
1303 
1304           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1305           for (p = 0; p < closureSize * 2; p += 2) {
1306             const PetscInt point = closure[p];
1307 
1308             if ((point >= vStart) && (point < vEnd)) closure[Nv++] = point;
1309           }
1310           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] ", colors[rank % numColors]));
1311           for (v = 0; v <= Nv; ++v) {
1312             const PetscInt vertex = closure[v % Nv];
1313 
1314             if (v > 0) {
1315               if (plotEdges) {
1316                 const PetscInt *edge;
1317                 PetscInt        endpoints[2], ne;
1318 
1319                 endpoints[0] = closure[v - 1];
1320                 endpoints[1] = vertex;
1321                 PetscCall(DMPlexGetJoin(dm, 2, endpoints, &ne, &edge));
1322                 PetscCheck(ne == 1, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Could not find edge for vertices %" PetscInt_FMT ", %" PetscInt_FMT, endpoints[0], endpoints[1]);
1323                 PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- (%" PetscInt_FMT "_%d) -- ", edge[0], rank));
1324                 PetscCall(DMPlexRestoreJoin(dm, 2, endpoints, &ne, &edge));
1325               } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- "));
1326             }
1327             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "(%" PetscInt_FMT "_%d)", vertex, rank));
1328           }
1329           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ";\n"));
1330           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1331         }
1332       }
1333     }
1334     for (c = cStart; c < cEnd; ++c) {
1335       double             ccoords[3] = {0.0, 0.0, 0.0};
1336       PetscBool          isLabeled  = PETSC_FALSE;
1337       PetscScalar       *cellCoords = NULL;
1338       const PetscScalar *array;
1339       PetscInt           numCoords, cdim, d;
1340       PetscBool          isDG;
1341 
1342       if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1343       PetscCall(DMGetCoordinateDim(dm, &cdim));
1344       PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1345       PetscCheck(!(numCoords % cdim), PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "coordinate dim %" PetscInt_FMT " does not divide numCoords %" PetscInt_FMT, cdim, numCoords);
1346       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1347       for (p = 0; p < numCoords / cdim; ++p) {
1348         for (d = 0; d < cdim; ++d) {
1349           tcoords[d] = (double)(scale * PetscRealPart(cellCoords[p * cdim + d]));
1350           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1351         }
1352         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1353         if (cdim == 3) {
1354           PetscReal tmp = tcoords[1];
1355           tcoords[1]    = tcoords[2];
1356           tcoords[2]    = -tmp;
1357         }
1358         for (d = 0; d < dim; ++d) ccoords[d] += tcoords[d];
1359       }
1360       for (d = 0; d < cdim; ++d) ccoords[d] /= (numCoords / cdim);
1361       PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1362       for (d = 0; d < cdim; ++d) {
1363         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1364         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)ccoords[d]));
1365       }
1366       if (drawHasse) color = colors[depth % numColors];
1367       else color = colors[rank % numColors];
1368       for (l = 0; l < numLabels; ++l) {
1369         PetscInt val;
1370         PetscCall(DMGetLabelValue(dm, names[l], c, &val));
1371         if (val >= 0) {
1372           color     = lcolors[l % numLColors];
1373           isLabeled = PETSC_TRUE;
1374           break;
1375         }
1376       }
1377       if (drawNumbers[dim]) {
1378         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", c, rank, color, c));
1379       } else if (drawColors[dim]) {
1380         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", c, rank, !isLabeled ? 1 : 2, color));
1381       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", c, rank));
1382     }
1383     if (drawHasse) {
1384       color = colors[depth % numColors];
1385       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Cells\n"));
1386       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\c in {\\cStart,...,\\cEnd}\n"));
1387       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1388       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\c_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\cShift+\\c-\\cStart,0) {\\c};\n", rank, color));
1389       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1390 
1391       color = colors[1 % numColors];
1392       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Edges\n"));
1393       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\e in {\\eStart,...,\\eEnd}\n"));
1394       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1395       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\e_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\eShift+\\e-\\eStart,1) {\\e};\n", rank, color));
1396       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1397 
1398       color = colors[0 % numColors];
1399       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Vertices\n"));
1400       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\v in {\\vStart,...,\\vEnd}\n"));
1401       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1402       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\v_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\vShift+\\v-\\vStart,2) {\\v};\n", rank, color));
1403       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1404 
1405       for (p = pStart; p < pEnd; ++p) {
1406         const PetscInt *cone;
1407         PetscInt        coneSize, cp;
1408 
1409         PetscCall(DMPlexGetCone(dm, p, &cone));
1410         PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
1411         for (cp = 0; cp < coneSize; ++cp) PetscCall(PetscViewerASCIIPrintf(viewer, "\\draw[->, shorten >=1pt] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", cone[cp], rank, p, rank));
1412       }
1413     }
1414     PetscCall(PetscViewerFlush(viewer));
1415     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
1416     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{tikzpicture}\n"));
1417     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{document}\n"));
1418     for (l = 0; l < numLabels; ++l) PetscCall(PetscFree(names[l]));
1419     for (c = 0; c < numColors; ++c) PetscCall(PetscFree(colors[c]));
1420     for (c = 0; c < numLColors; ++c) PetscCall(PetscFree(lcolors[c]));
1421     PetscCall(PetscFree3(names, colors, lcolors));
1422     PetscCall(PetscBTDestroy(&wp));
1423   } else if (format == PETSC_VIEWER_LOAD_BALANCE) {
1424     Vec                    cown, acown;
1425     VecScatter             sct;
1426     ISLocalToGlobalMapping g2l;
1427     IS                     gid, acis;
1428     MPI_Comm               comm, ncomm = MPI_COMM_NULL;
1429     MPI_Group              ggroup, ngroup;
1430     PetscScalar           *array, nid;
1431     const PetscInt        *idxs;
1432     PetscInt              *idxs2, *start, *adjacency, *work;
1433     PetscInt64             lm[3], gm[3];
1434     PetscInt               i, c, cStart, cEnd, cum, numVertices, ect, ectn, cellHeight;
1435     PetscMPIInt            d1, d2, rank;
1436 
1437     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1438     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1439 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1440     PetscCallMPI(MPI_Comm_split_type(comm, MPI_COMM_TYPE_SHARED, rank, MPI_INFO_NULL, &ncomm));
1441 #endif
1442     if (ncomm != MPI_COMM_NULL) {
1443       PetscCallMPI(MPI_Comm_group(comm, &ggroup));
1444       PetscCallMPI(MPI_Comm_group(ncomm, &ngroup));
1445       d1 = 0;
1446       PetscCallMPI(MPI_Group_translate_ranks(ngroup, 1, &d1, ggroup, &d2));
1447       nid = d2;
1448       PetscCallMPI(MPI_Group_free(&ggroup));
1449       PetscCallMPI(MPI_Group_free(&ngroup));
1450       PetscCallMPI(MPI_Comm_free(&ncomm));
1451     } else nid = 0.0;
1452 
1453     /* Get connectivity */
1454     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1455     PetscCall(DMPlexCreatePartitionerGraph(dm, cellHeight, &numVertices, &start, &adjacency, &gid));
1456 
1457     /* filter overlapped local cells */
1458     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
1459     PetscCall(ISGetIndices(gid, &idxs));
1460     PetscCall(ISGetLocalSize(gid, &cum));
1461     PetscCall(PetscMalloc1(cum, &idxs2));
1462     for (c = cStart, cum = 0; c < cEnd; c++) {
1463       if (idxs[c - cStart] < 0) continue;
1464       idxs2[cum++] = idxs[c - cStart];
1465     }
1466     PetscCall(ISRestoreIndices(gid, &idxs));
1467     PetscCheck(numVertices == cum, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Unexpected %" PetscInt_FMT " != %" PetscInt_FMT, numVertices, cum);
1468     PetscCall(ISDestroy(&gid));
1469     PetscCall(ISCreateGeneral(comm, numVertices, idxs2, PETSC_OWN_POINTER, &gid));
1470 
1471     /* support for node-aware cell locality */
1472     PetscCall(ISCreateGeneral(comm, start[numVertices], adjacency, PETSC_USE_POINTER, &acis));
1473     PetscCall(VecCreateSeq(PETSC_COMM_SELF, start[numVertices], &acown));
1474     PetscCall(VecCreateMPI(comm, numVertices, PETSC_DECIDE, &cown));
1475     PetscCall(VecGetArray(cown, &array));
1476     for (c = 0; c < numVertices; c++) array[c] = nid;
1477     PetscCall(VecRestoreArray(cown, &array));
1478     PetscCall(VecScatterCreate(cown, acis, acown, NULL, &sct));
1479     PetscCall(VecScatterBegin(sct, cown, acown, INSERT_VALUES, SCATTER_FORWARD));
1480     PetscCall(VecScatterEnd(sct, cown, acown, INSERT_VALUES, SCATTER_FORWARD));
1481     PetscCall(ISDestroy(&acis));
1482     PetscCall(VecScatterDestroy(&sct));
1483     PetscCall(VecDestroy(&cown));
1484 
1485     /* compute edgeCut */
1486     for (c = 0, cum = 0; c < numVertices; c++) cum = PetscMax(cum, start[c + 1] - start[c]);
1487     PetscCall(PetscMalloc1(cum, &work));
1488     PetscCall(ISLocalToGlobalMappingCreateIS(gid, &g2l));
1489     PetscCall(ISLocalToGlobalMappingSetType(g2l, ISLOCALTOGLOBALMAPPINGHASH));
1490     PetscCall(ISDestroy(&gid));
1491     PetscCall(VecGetArray(acown, &array));
1492     for (c = 0, ect = 0, ectn = 0; c < numVertices; c++) {
1493       PetscInt totl;
1494 
1495       totl = start[c + 1] - start[c];
1496       PetscCall(ISGlobalToLocalMappingApply(g2l, IS_GTOLM_MASK, totl, adjacency + start[c], NULL, work));
1497       for (i = 0; i < totl; i++) {
1498         if (work[i] < 0) {
1499           ect += 1;
1500           ectn += (array[i + start[c]] != nid) ? 0 : 1;
1501         }
1502       }
1503     }
1504     PetscCall(PetscFree(work));
1505     PetscCall(VecRestoreArray(acown, &array));
1506     lm[0] = numVertices > 0 ? numVertices : PETSC_MAX_INT;
1507     lm[1] = -numVertices;
1508     PetscCall(MPIU_Allreduce(lm, gm, 2, MPIU_INT64, MPI_MIN, comm));
1509     PetscCall(PetscViewerASCIIPrintf(viewer, "  Cell balance: %.2f (max %" PetscInt_FMT ", min %" PetscInt_FMT, -((double)gm[1]) / ((double)gm[0]), -(PetscInt)gm[1], (PetscInt)gm[0]));
1510     lm[0] = ect;                     /* edgeCut */
1511     lm[1] = ectn;                    /* node-aware edgeCut */
1512     lm[2] = numVertices > 0 ? 0 : 1; /* empty processes */
1513     PetscCall(MPIU_Allreduce(lm, gm, 3, MPIU_INT64, MPI_SUM, comm));
1514     PetscCall(PetscViewerASCIIPrintf(viewer, ", empty %" PetscInt_FMT ")\n", (PetscInt)gm[2]));
1515 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1516     PetscCall(PetscViewerASCIIPrintf(viewer, "  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n", (PetscInt)(gm[0] / 2), gm[0] ? ((double)(gm[1])) / ((double)gm[0]) : 1.));
1517 #else
1518     PetscCall(PetscViewerASCIIPrintf(viewer, "  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n", (PetscInt)(gm[0] / 2), 0.0));
1519 #endif
1520     PetscCall(ISLocalToGlobalMappingDestroy(&g2l));
1521     PetscCall(PetscFree(start));
1522     PetscCall(PetscFree(adjacency));
1523     PetscCall(VecDestroy(&acown));
1524   } else {
1525     const char    *name;
1526     PetscInt      *sizes, *hybsizes, *ghostsizes;
1527     PetscInt       locDepth, depth, cellHeight, dim, d;
1528     PetscInt       pStart, pEnd, p, gcStart, gcEnd, gcNum;
1529     PetscInt       numLabels, l, maxSize = 17;
1530     DMPolytopeType ct0 = DM_POLYTOPE_UNKNOWN;
1531     MPI_Comm       comm;
1532     PetscMPIInt    size, rank;
1533 
1534     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1535     PetscCallMPI(MPI_Comm_size(comm, &size));
1536     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1537     PetscCall(DMGetDimension(dm, &dim));
1538     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1539     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1540     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
1541     else PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
1542     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
1543     PetscCall(DMPlexGetDepth(dm, &locDepth));
1544     PetscCall(MPIU_Allreduce(&locDepth, &depth, 1, MPIU_INT, MPI_MAX, comm));
1545     PetscCall(DMPlexGetCellTypeStratum(dm, DM_POLYTOPE_FV_GHOST, &gcStart, &gcEnd));
1546     gcNum = gcEnd - gcStart;
1547     if (size < maxSize) PetscCall(PetscCalloc3(size, &sizes, size, &hybsizes, size, &ghostsizes));
1548     else PetscCall(PetscCalloc3(3, &sizes, 3, &hybsizes, 3, &ghostsizes));
1549     for (d = 0; d <= depth; d++) {
1550       PetscInt Nc[2] = {0, 0}, ict;
1551 
1552       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
1553       if (pStart < pEnd) PetscCall(DMPlexGetCellType(dm, pStart, &ct0));
1554       ict = ct0;
1555       PetscCallMPI(MPI_Bcast(&ict, 1, MPIU_INT, 0, comm));
1556       ct0 = (DMPolytopeType)ict;
1557       for (p = pStart; p < pEnd; ++p) {
1558         DMPolytopeType ct;
1559 
1560         PetscCall(DMPlexGetCellType(dm, p, &ct));
1561         if (ct == ct0) ++Nc[0];
1562         else ++Nc[1];
1563       }
1564       if (size < maxSize) {
1565         PetscCallMPI(MPI_Gather(&Nc[0], 1, MPIU_INT, sizes, 1, MPIU_INT, 0, comm));
1566         PetscCallMPI(MPI_Gather(&Nc[1], 1, MPIU_INT, hybsizes, 1, MPIU_INT, 0, comm));
1567         if (d == depth) PetscCallMPI(MPI_Gather(&gcNum, 1, MPIU_INT, ghostsizes, 1, MPIU_INT, 0, comm));
1568         PetscCall(PetscViewerASCIIPrintf(viewer, "  Number of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1569         for (p = 0; p < size; ++p) {
1570           if (rank == 0) {
1571             PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT, sizes[p] + hybsizes[p]));
1572             if (hybsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT ")", hybsizes[p]));
1573             if (ghostsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "]", ghostsizes[p]));
1574           }
1575         }
1576       } else {
1577         PetscInt locMinMax[2];
1578 
1579         locMinMax[0] = Nc[0] + Nc[1];
1580         locMinMax[1] = Nc[0] + Nc[1];
1581         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, sizes));
1582         locMinMax[0] = Nc[1];
1583         locMinMax[1] = Nc[1];
1584         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, hybsizes));
1585         if (d == depth) {
1586           locMinMax[0] = gcNum;
1587           locMinMax[1] = gcNum;
1588           PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, ghostsizes));
1589         }
1590         PetscCall(PetscViewerASCIIPrintf(viewer, "  Min/Max of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1591         PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT "/%" PetscInt_FMT, sizes[0], sizes[1]));
1592         if (hybsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT "/%" PetscInt_FMT ")", hybsizes[0], hybsizes[1]));
1593         if (ghostsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "/%" PetscInt_FMT "]", ghostsizes[0], ghostsizes[1]));
1594       }
1595       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
1596     }
1597     PetscCall(PetscFree3(sizes, hybsizes, ghostsizes));
1598     {
1599       const PetscReal *maxCell;
1600       const PetscReal *L;
1601       PetscBool        localized;
1602 
1603       PetscCall(DMGetPeriodicity(dm, &maxCell, NULL, &L));
1604       PetscCall(DMGetCoordinatesLocalized(dm, &localized));
1605       if (L || localized) {
1606         PetscCall(PetscViewerASCIIPrintf(viewer, "Periodic mesh"));
1607         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1608         if (L) {
1609           PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
1610           for (d = 0; d < dim; ++d) {
1611             if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1612             PetscCall(PetscViewerASCIIPrintf(viewer, "%s", L[d] > 0.0 ? "PERIODIC" : "NONE"));
1613           }
1614           PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
1615         }
1616         PetscCall(PetscViewerASCIIPrintf(viewer, " coordinates %s\n", localized ? "localized" : "not localized"));
1617         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1618       }
1619     }
1620     PetscCall(DMGetNumLabels(dm, &numLabels));
1621     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
1622     for (l = 0; l < numLabels; ++l) {
1623       DMLabel         label;
1624       const char     *name;
1625       IS              valueIS;
1626       const PetscInt *values;
1627       PetscInt        numValues, v;
1628 
1629       PetscCall(DMGetLabelName(dm, l, &name));
1630       PetscCall(DMGetLabel(dm, name, &label));
1631       PetscCall(DMLabelGetNumValues(label, &numValues));
1632       PetscCall(PetscViewerASCIIPrintf(viewer, "  %s: %" PetscInt_FMT " strata with value/size (", name, numValues));
1633       PetscCall(DMLabelGetValueIS(label, &valueIS));
1634       PetscCall(ISGetIndices(valueIS, &values));
1635       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1636       for (v = 0; v < numValues; ++v) {
1637         PetscInt size;
1638 
1639         PetscCall(DMLabelGetStratumSize(label, values[v], &size));
1640         if (v > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1641         PetscCall(PetscViewerASCIIPrintf(viewer, "%" PetscInt_FMT " (%" PetscInt_FMT ")", values[v], size));
1642       }
1643       PetscCall(PetscViewerASCIIPrintf(viewer, ")\n"));
1644       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1645       PetscCall(ISRestoreIndices(valueIS, &values));
1646       PetscCall(ISDestroy(&valueIS));
1647     }
1648     {
1649       char    **labelNames;
1650       PetscInt  Nl = numLabels;
1651       PetscBool flg;
1652 
1653       PetscCall(PetscMalloc1(Nl, &labelNames));
1654       PetscCall(PetscOptionsGetStringArray(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_plex_view_labels", labelNames, &Nl, &flg));
1655       for (l = 0; l < Nl; ++l) {
1656         DMLabel label;
1657 
1658         PetscCall(DMHasLabel(dm, labelNames[l], &flg));
1659         if (flg) {
1660           PetscCall(DMGetLabel(dm, labelNames[l], &label));
1661           PetscCall(DMLabelView(label, viewer));
1662         }
1663         PetscCall(PetscFree(labelNames[l]));
1664       }
1665       PetscCall(PetscFree(labelNames));
1666     }
1667     /* If no fields are specified, people do not want to see adjacency */
1668     if (dm->Nf) {
1669       PetscInt f;
1670 
1671       for (f = 0; f < dm->Nf; ++f) {
1672         const char *name;
1673 
1674         PetscCall(PetscObjectGetName(dm->fields[f].disc, &name));
1675         if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Field %s:\n", name));
1676         PetscCall(PetscViewerASCIIPushTab(viewer));
1677         if (dm->fields[f].label) PetscCall(DMLabelView(dm->fields[f].label, viewer));
1678         if (dm->fields[f].adjacency[0]) {
1679           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM++\n"));
1680           else PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM\n"));
1681         } else {
1682           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FEM\n"));
1683           else PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FUNKY\n"));
1684         }
1685         PetscCall(PetscViewerASCIIPopTab(viewer));
1686       }
1687     }
1688     PetscCall(DMGetCoarseDM(dm, &cdm));
1689     if (cdm) {
1690       PetscCall(PetscViewerASCIIPushTab(viewer));
1691       PetscCall(PetscViewerASCIIPrintf(viewer, "Defined by transform from:\n"));
1692       PetscCall(DMPlexView_Ascii(cdm, viewer));
1693       PetscCall(PetscViewerASCIIPopTab(viewer));
1694     }
1695   }
1696   PetscFunctionReturn(PETSC_SUCCESS);
1697 }
1698 
1699 static PetscErrorCode DMPlexDrawCell(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[])
1700 {
1701   DMPolytopeType ct;
1702   PetscMPIInt    rank;
1703   PetscInt       cdim;
1704 
1705   PetscFunctionBegin;
1706   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1707   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1708   PetscCall(DMGetCoordinateDim(dm, &cdim));
1709   switch (ct) {
1710   case DM_POLYTOPE_SEGMENT:
1711   case DM_POLYTOPE_POINT_PRISM_TENSOR:
1712     switch (cdim) {
1713     case 1: {
1714       const PetscReal y  = 0.5;  /* TODO Put it in the middle of the viewport */
1715       const PetscReal dy = 0.05; /* TODO Make it a fraction of the total length */
1716 
1717       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y, PetscRealPart(coords[1]), y, PETSC_DRAW_BLACK));
1718       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y + dy, PetscRealPart(coords[0]), y - dy, PETSC_DRAW_BLACK));
1719       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[1]), y + dy, PetscRealPart(coords[1]), y - dy, PETSC_DRAW_BLACK));
1720     } break;
1721     case 2: {
1722       const PetscReal dx = (PetscRealPart(coords[3]) - PetscRealPart(coords[1]));
1723       const PetscReal dy = (PetscRealPart(coords[2]) - PetscRealPart(coords[0]));
1724       const PetscReal l  = 0.1 / PetscSqrtReal(dx * dx + dy * dy);
1725 
1726       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1727       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));
1728       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));
1729     } break;
1730     default:
1731       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of dimension %" PetscInt_FMT, cdim);
1732     }
1733     break;
1734   case DM_POLYTOPE_TRIANGLE:
1735     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));
1736     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1737     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1738     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1739     break;
1740   case DM_POLYTOPE_QUADRILATERAL:
1741     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));
1742     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));
1743     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1744     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1745     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_BLACK));
1746     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1747     break;
1748   case DM_POLYTOPE_SEG_PRISM_TENSOR:
1749     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));
1750     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));
1751     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1752     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_BLACK));
1753     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1754     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1755     break;
1756   case DM_POLYTOPE_FV_GHOST:
1757     break;
1758   default:
1759     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1760   }
1761   PetscFunctionReturn(PETSC_SUCCESS);
1762 }
1763 
1764 static PetscErrorCode DMPlexDrawCellHighOrder(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[], PetscInt edgeDiv, PetscReal refCoords[], PetscReal edgeCoords[])
1765 {
1766   DMPolytopeType ct;
1767   PetscReal      centroid[2] = {0., 0.};
1768   PetscMPIInt    rank;
1769   PetscInt       fillColor, v, e, d;
1770 
1771   PetscFunctionBegin;
1772   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1773   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1774   fillColor = PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2;
1775   switch (ct) {
1776   case DM_POLYTOPE_TRIANGLE: {
1777     PetscReal refVertices[6] = {-1., -1., 1., -1., -1., 1.};
1778 
1779     for (v = 0; v < 3; ++v) {
1780       centroid[0] += PetscRealPart(coords[v * 2 + 0]) / 3.;
1781       centroid[1] += PetscRealPart(coords[v * 2 + 1]) / 3.;
1782     }
1783     for (e = 0; e < 3; ++e) {
1784       refCoords[0] = refVertices[e * 2 + 0];
1785       refCoords[1] = refVertices[e * 2 + 1];
1786       for (d = 1; d <= edgeDiv; ++d) {
1787         refCoords[d * 2 + 0] = refCoords[0] + (refVertices[(e + 1) % 3 * 2 + 0] - refCoords[0]) * d / edgeDiv;
1788         refCoords[d * 2 + 1] = refCoords[1] + (refVertices[(e + 1) % 3 * 2 + 1] - refCoords[1]) * d / edgeDiv;
1789       }
1790       PetscCall(DMPlexReferenceToCoordinates(dm, cell, edgeDiv + 1, refCoords, edgeCoords));
1791       for (d = 0; d < edgeDiv; ++d) {
1792         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));
1793         PetscCall(PetscDrawLine(draw, edgeCoords[d * 2 + 0], edgeCoords[d * 2 + 1], edgeCoords[(d + 1) * 2 + 0], edgeCoords[(d + 1) * 2 + 1], PETSC_DRAW_BLACK));
1794       }
1795     }
1796   } break;
1797   default:
1798     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1799   }
1800   PetscFunctionReturn(PETSC_SUCCESS);
1801 }
1802 
1803 static PetscErrorCode DMPlexView_Draw(DM dm, PetscViewer viewer)
1804 {
1805   PetscDraw    draw;
1806   DM           cdm;
1807   PetscSection coordSection;
1808   Vec          coordinates;
1809   PetscReal    xyl[3], xyr[3];
1810   PetscReal   *refCoords, *edgeCoords;
1811   PetscBool    isnull, drawAffine = PETSC_TRUE;
1812   PetscInt     dim, vStart, vEnd, cStart, cEnd, c, edgeDiv = 4;
1813 
1814   PetscFunctionBegin;
1815   PetscCall(DMGetCoordinateDim(dm, &dim));
1816   PetscCheck(dim <= 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT, dim);
1817   PetscCall(PetscOptionsGetBool(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_view_draw_affine", &drawAffine, NULL));
1818   if (!drawAffine) PetscCall(PetscMalloc2((edgeDiv + 1) * dim, &refCoords, (edgeDiv + 1) * dim, &edgeCoords));
1819   PetscCall(DMGetCoordinateDM(dm, &cdm));
1820   PetscCall(DMGetLocalSection(cdm, &coordSection));
1821   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1822   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1823   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1824 
1825   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
1826   PetscCall(PetscDrawIsNull(draw, &isnull));
1827   if (isnull) PetscFunctionReturn(PETSC_SUCCESS);
1828   PetscCall(PetscDrawSetTitle(draw, "Mesh"));
1829 
1830   PetscCall(DMGetBoundingBox(dm, xyl, xyr));
1831   PetscCall(PetscDrawSetCoordinates(draw, xyl[0], xyl[1], xyr[0], xyr[1]));
1832   PetscCall(PetscDrawClear(draw));
1833 
1834   for (c = cStart; c < cEnd; ++c) {
1835     PetscScalar       *coords = NULL;
1836     const PetscScalar *coords_arr;
1837     PetscInt           numCoords;
1838     PetscBool          isDG;
1839 
1840     PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
1841     if (drawAffine) PetscCall(DMPlexDrawCell(dm, draw, c, coords));
1842     else PetscCall(DMPlexDrawCellHighOrder(dm, draw, c, coords, edgeDiv, refCoords, edgeCoords));
1843     PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
1844   }
1845   if (!drawAffine) PetscCall(PetscFree2(refCoords, edgeCoords));
1846   PetscCall(PetscDrawFlush(draw));
1847   PetscCall(PetscDrawPause(draw));
1848   PetscCall(PetscDrawSave(draw));
1849   PetscFunctionReturn(PETSC_SUCCESS);
1850 }
1851 
1852 static PetscErrorCode DMPlexCreateHighOrderSurrogate_Internal(DM dm, DM *hdm)
1853 {
1854   DM           odm = dm, rdm = dm, cdm;
1855   PetscFE      fe;
1856   PetscSpace   sp;
1857   PetscClassId id;
1858   PetscInt     degree;
1859   PetscBool    hoView = PETSC_TRUE;
1860 
1861   PetscFunctionBegin;
1862   PetscObjectOptionsBegin((PetscObject)dm);
1863   PetscCall(PetscOptionsBool("-dm_plex_high_order_view", "Subsample to view meshes with high order coordinates", "DMPlexCreateHighOrderSurrogate_Internal", hoView, &hoView, NULL));
1864   PetscOptionsEnd();
1865   PetscCall(PetscObjectReference((PetscObject)dm));
1866   *hdm = dm;
1867   if (!hoView) PetscFunctionReturn(PETSC_SUCCESS);
1868   PetscCall(DMGetCoordinateDM(dm, &cdm));
1869   PetscCall(DMGetField(cdm, 0, NULL, (PetscObject *)&fe));
1870   PetscCall(PetscObjectGetClassId((PetscObject)fe, &id));
1871   if (id != PETSCFE_CLASSID) PetscFunctionReturn(PETSC_SUCCESS);
1872   PetscCall(PetscFEGetBasisSpace(fe, &sp));
1873   PetscCall(PetscSpaceGetDegree(sp, &degree, NULL));
1874   for (PetscInt r = 0, rd = PetscCeilReal(((PetscReal)degree) / 2.); r < (PetscInt)PetscCeilReal(PetscLog2Real(degree)); ++r, rd = PetscCeilReal(((PetscReal)rd) / 2.)) {
1875     DM  cdm, rcdm;
1876     Mat In;
1877     Vec cl, rcl;
1878 
1879     PetscCall(DMRefine(odm, PetscObjectComm((PetscObject)odm), &rdm));
1880     if (rd > 1) PetscCall(DMPlexCreateCoordinateSpace(rdm, rd, PETSC_FALSE, NULL));
1881     PetscCall(PetscObjectSetName((PetscObject)rdm, "Refined Mesh with Linear Coordinates"));
1882     PetscCall(DMGetCoordinateDM(odm, &cdm));
1883     PetscCall(DMGetCoordinateDM(rdm, &rcdm));
1884     PetscCall(DMGetCoordinatesLocal(odm, &cl));
1885     PetscCall(DMGetCoordinatesLocal(rdm, &rcl));
1886     PetscCall(DMSetCoarseDM(rcdm, cdm));
1887     PetscCall(DMCreateInterpolation(cdm, rcdm, &In, NULL));
1888     PetscCall(MatMult(In, cl, rcl));
1889     PetscCall(MatDestroy(&In));
1890     PetscCall(DMSetCoordinatesLocal(rdm, rcl));
1891     PetscCall(DMDestroy(&odm));
1892     odm = rdm;
1893   }
1894   *hdm = rdm;
1895   PetscFunctionReturn(PETSC_SUCCESS);
1896 }
1897 
1898 #if defined(PETSC_HAVE_EXODUSII)
1899   #include <exodusII.h>
1900   #include <petscviewerexodusii.h>
1901 #endif
1902 
1903 PetscErrorCode DMView_Plex(DM dm, PetscViewer viewer)
1904 {
1905   PetscBool iascii, ishdf5, isvtk, isdraw, flg, isglvis, isexodus, iscgns;
1906   char      name[PETSC_MAX_PATH_LEN];
1907 
1908   PetscFunctionBegin;
1909   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1910   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1911   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &iascii));
1912   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
1913   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1914   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
1915   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
1916   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodus));
1917   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
1918   if (iascii) {
1919     PetscViewerFormat format;
1920     PetscCall(PetscViewerGetFormat(viewer, &format));
1921     if (format == PETSC_VIEWER_ASCII_GLVIS) PetscCall(DMPlexView_GLVis(dm, viewer));
1922     else PetscCall(DMPlexView_Ascii(dm, viewer));
1923   } else if (ishdf5) {
1924 #if defined(PETSC_HAVE_HDF5)
1925     PetscCall(DMPlexView_HDF5_Internal(dm, viewer));
1926 #else
1927     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1928 #endif
1929   } else if (isvtk) {
1930     PetscCall(DMPlexVTKWriteAll((PetscObject)dm, viewer));
1931   } else if (isdraw) {
1932     DM hdm;
1933 
1934     PetscCall(DMPlexCreateHighOrderSurrogate_Internal(dm, &hdm));
1935     PetscCall(DMPlexView_Draw(hdm, viewer));
1936     PetscCall(DMDestroy(&hdm));
1937   } else if (isglvis) {
1938     PetscCall(DMPlexView_GLVis(dm, viewer));
1939 #if defined(PETSC_HAVE_EXODUSII)
1940   } else if (isexodus) {
1941     /*
1942       exodusII requires that all sets be part of exactly one cell set.
1943       If the dm does not have a "Cell Sets" label defined, we create one
1944       with ID 1, containing all cells.
1945       Note that if the Cell Sets label is defined but does not cover all cells,
1946       we may still have a problem. This should probably be checked here or in the viewer;
1947     */
1948     PetscInt numCS;
1949     PetscCall(DMGetLabelSize(dm, "Cell Sets", &numCS));
1950     if (!numCS) {
1951       PetscInt cStart, cEnd, c;
1952       PetscCall(DMCreateLabel(dm, "Cell Sets"));
1953       PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1954       for (c = cStart; c < cEnd; ++c) PetscCall(DMSetLabelValue(dm, "Cell Sets", c, 1));
1955     }
1956     PetscCall(DMView_PlexExodusII(dm, viewer));
1957 #endif
1958 #if defined(PETSC_HAVE_CGNS)
1959   } else if (iscgns) {
1960     PetscCall(DMView_PlexCGNS(dm, viewer));
1961 #endif
1962   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex writing", ((PetscObject)viewer)->type_name);
1963   /* Optionally view the partition */
1964   PetscCall(PetscOptionsHasName(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_partition_view", &flg));
1965   if (flg) {
1966     Vec ranks;
1967     PetscCall(DMPlexCreateRankField(dm, &ranks));
1968     PetscCall(VecView(ranks, viewer));
1969     PetscCall(VecDestroy(&ranks));
1970   }
1971   /* Optionally view a label */
1972   PetscCall(PetscOptionsGetString(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_label_view", name, sizeof(name), &flg));
1973   if (flg) {
1974     DMLabel label;
1975     Vec     val;
1976 
1977     PetscCall(DMGetLabel(dm, name, &label));
1978     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Label %s provided to -dm_label_view does not exist in this DM", name);
1979     PetscCall(DMPlexCreateLabelField(dm, label, &val));
1980     PetscCall(VecView(val, viewer));
1981     PetscCall(VecDestroy(&val));
1982   }
1983   PetscFunctionReturn(PETSC_SUCCESS);
1984 }
1985 
1986 /*@
1987   DMPlexTopologyView - Saves a `DMPLEX` topology into a file
1988 
1989   Collective
1990 
1991   Input Parameters:
1992 + dm     - The `DM` whose topology is to be saved
1993 - viewer - The `PetscViewer` to save it in
1994 
1995   Level: advanced
1996 
1997 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexTopologyLoad()`, `PetscViewer`
1998 @*/
1999 PetscErrorCode DMPlexTopologyView(DM dm, PetscViewer viewer)
2000 {
2001   PetscBool ishdf5;
2002 
2003   PetscFunctionBegin;
2004   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2005   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2006   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2007   PetscCall(PetscLogEventBegin(DMPLEX_TopologyView, viewer, 0, 0, 0));
2008   if (ishdf5) {
2009 #if defined(PETSC_HAVE_HDF5)
2010     PetscViewerFormat format;
2011     PetscCall(PetscViewerGetFormat(viewer, &format));
2012     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2013       IS globalPointNumbering;
2014 
2015       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
2016       PetscCall(DMPlexTopologyView_HDF5_Internal(dm, globalPointNumbering, viewer));
2017       PetscCall(ISDestroy(&globalPointNumbering));
2018     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
2019 #else
2020     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2021 #endif
2022   }
2023   PetscCall(PetscLogEventEnd(DMPLEX_TopologyView, viewer, 0, 0, 0));
2024   PetscFunctionReturn(PETSC_SUCCESS);
2025 }
2026 
2027 /*@
2028   DMPlexCoordinatesView - Saves `DMPLEX` coordinates into a file
2029 
2030   Collective
2031 
2032   Input Parameters:
2033 + dm     - The `DM` whose coordinates are to be saved
2034 - viewer - The `PetscViewer` for saving
2035 
2036   Level: advanced
2037 
2038 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexLabelsView()`, `DMPlexCoordinatesLoad()`, `PetscViewer`
2039 @*/
2040 PetscErrorCode DMPlexCoordinatesView(DM dm, PetscViewer viewer)
2041 {
2042   PetscBool ishdf5;
2043 
2044   PetscFunctionBegin;
2045   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2046   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2047   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2048   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
2049   if (ishdf5) {
2050 #if defined(PETSC_HAVE_HDF5)
2051     PetscViewerFormat format;
2052     PetscCall(PetscViewerGetFormat(viewer, &format));
2053     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2054       PetscCall(DMPlexCoordinatesView_HDF5_Internal(dm, viewer));
2055     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
2056 #else
2057     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2058 #endif
2059   }
2060   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
2061   PetscFunctionReturn(PETSC_SUCCESS);
2062 }
2063 
2064 /*@
2065   DMPlexLabelsView - Saves `DMPLEX` labels into a file
2066 
2067   Collective
2068 
2069   Input Parameters:
2070 + dm     - The `DM` whose labels are to be saved
2071 - viewer - The `PetscViewer` for saving
2072 
2073   Level: advanced
2074 
2075 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsLoad()`, `PetscViewer`
2076 @*/
2077 PetscErrorCode DMPlexLabelsView(DM dm, PetscViewer viewer)
2078 {
2079   PetscBool ishdf5;
2080 
2081   PetscFunctionBegin;
2082   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2083   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2084   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2085   PetscCall(PetscLogEventBegin(DMPLEX_LabelsView, viewer, 0, 0, 0));
2086   if (ishdf5) {
2087 #if defined(PETSC_HAVE_HDF5)
2088     IS                globalPointNumbering;
2089     PetscViewerFormat format;
2090 
2091     PetscCall(PetscViewerGetFormat(viewer, &format));
2092     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2093       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
2094       PetscCall(DMPlexLabelsView_HDF5_Internal(dm, globalPointNumbering, viewer));
2095       PetscCall(ISDestroy(&globalPointNumbering));
2096     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2097 #else
2098     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2099 #endif
2100   }
2101   PetscCall(PetscLogEventEnd(DMPLEX_LabelsView, viewer, 0, 0, 0));
2102   PetscFunctionReturn(PETSC_SUCCESS);
2103 }
2104 
2105 /*@
2106   DMPlexSectionView - Saves a section associated with a `DMPLEX`
2107 
2108   Collective
2109 
2110   Input Parameters:
2111 + dm        - The `DM` that contains the topology on which the section to be saved is defined
2112 . viewer    - The `PetscViewer` for saving
2113 - sectiondm - The `DM` that contains the section to be saved
2114 
2115   Level: advanced
2116 
2117   Notes:
2118   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.
2119 
2120   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 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.
2121 
2122 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`, `PetscSectionView()`, `DMPlexSectionLoad()`, `PetscViewer`
2123 @*/
2124 PetscErrorCode DMPlexSectionView(DM dm, PetscViewer viewer, DM sectiondm)
2125 {
2126   PetscBool ishdf5;
2127 
2128   PetscFunctionBegin;
2129   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2130   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2131   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2132   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2133   PetscCall(PetscLogEventBegin(DMPLEX_SectionView, viewer, 0, 0, 0));
2134   if (ishdf5) {
2135 #if defined(PETSC_HAVE_HDF5)
2136     PetscCall(DMPlexSectionView_HDF5_Internal(dm, viewer, sectiondm));
2137 #else
2138     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2139 #endif
2140   }
2141   PetscCall(PetscLogEventEnd(DMPLEX_SectionView, viewer, 0, 0, 0));
2142   PetscFunctionReturn(PETSC_SUCCESS);
2143 }
2144 
2145 /*@
2146   DMPlexGlobalVectorView - Saves a global vector
2147 
2148   Collective
2149 
2150   Input Parameters:
2151 + dm        - The `DM` that represents the topology
2152 . viewer    - The `PetscViewer` to save data with
2153 . sectiondm - The `DM` that contains the global section on which vec is defined
2154 - vec       - The global vector to be saved
2155 
2156   Level: advanced
2157 
2158   Notes:
2159   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 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.
2160 
2161   Calling sequence:
2162 .vb
2163        DMCreate(PETSC_COMM_WORLD, &dm);
2164        DMSetType(dm, DMPLEX);
2165        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2166        DMClone(dm, &sectiondm);
2167        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2168        PetscSectionCreate(PETSC_COMM_WORLD, &section);
2169        DMPlexGetChart(sectiondm, &pStart, &pEnd);
2170        PetscSectionSetChart(section, pStart, pEnd);
2171        PetscSectionSetUp(section);
2172        DMSetLocalSection(sectiondm, section);
2173        PetscSectionDestroy(&section);
2174        DMGetGlobalVector(sectiondm, &vec);
2175        PetscObjectSetName((PetscObject)vec, "vec_name");
2176        DMPlexTopologyView(dm, viewer);
2177        DMPlexSectionView(dm, viewer, sectiondm);
2178        DMPlexGlobalVectorView(dm, viewer, sectiondm, vec);
2179        DMRestoreGlobalVector(sectiondm, &vec);
2180        DMDestroy(&sectiondm);
2181        DMDestroy(&dm);
2182 .ve
2183 
2184 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexLocalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2185 @*/
2186 PetscErrorCode DMPlexGlobalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2187 {
2188   PetscBool ishdf5;
2189 
2190   PetscFunctionBegin;
2191   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2192   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2193   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2194   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2195   /* Check consistency */
2196   {
2197     PetscSection section;
2198     PetscBool    includesConstraints;
2199     PetscInt     m, m1;
2200 
2201     PetscCall(VecGetLocalSize(vec, &m1));
2202     PetscCall(DMGetGlobalSection(sectiondm, &section));
2203     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2204     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2205     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2206     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2207   }
2208   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2209   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorView, viewer, 0, 0, 0));
2210   if (ishdf5) {
2211 #if defined(PETSC_HAVE_HDF5)
2212     PetscCall(DMPlexGlobalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2213 #else
2214     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2215 #endif
2216   }
2217   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorView, viewer, 0, 0, 0));
2218   PetscFunctionReturn(PETSC_SUCCESS);
2219 }
2220 
2221 /*@
2222   DMPlexLocalVectorView - Saves a local vector
2223 
2224   Collective
2225 
2226   Input Parameters:
2227 + dm        - The `DM` that represents the topology
2228 . viewer    - The `PetscViewer` to save data with
2229 . sectiondm - The `DM` that contains the local section on which `vec` is defined; may be the same as `dm`
2230 - vec       - The local vector to be saved
2231 
2232   Level: advanced
2233 
2234   Note:
2235   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 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.
2236 
2237   Calling sequence:
2238 .vb
2239        DMCreate(PETSC_COMM_WORLD, &dm);
2240        DMSetType(dm, DMPLEX);
2241        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2242        DMClone(dm, &sectiondm);
2243        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2244        PetscSectionCreate(PETSC_COMM_WORLD, &section);
2245        DMPlexGetChart(sectiondm, &pStart, &pEnd);
2246        PetscSectionSetChart(section, pStart, pEnd);
2247        PetscSectionSetUp(section);
2248        DMSetLocalSection(sectiondm, section);
2249        DMGetLocalVector(sectiondm, &vec);
2250        PetscObjectSetName((PetscObject)vec, "vec_name");
2251        DMPlexTopologyView(dm, viewer);
2252        DMPlexSectionView(dm, viewer, sectiondm);
2253        DMPlexLocalVectorView(dm, viewer, sectiondm, vec);
2254        DMRestoreLocalVector(sectiondm, &vec);
2255        DMDestroy(&sectiondm);
2256        DMDestroy(&dm);
2257 .ve
2258 
2259 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexGlobalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2260 @*/
2261 PetscErrorCode DMPlexLocalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2262 {
2263   PetscBool ishdf5;
2264 
2265   PetscFunctionBegin;
2266   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2267   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2268   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2269   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2270   /* Check consistency */
2271   {
2272     PetscSection section;
2273     PetscBool    includesConstraints;
2274     PetscInt     m, m1;
2275 
2276     PetscCall(VecGetLocalSize(vec, &m1));
2277     PetscCall(DMGetLocalSection(sectiondm, &section));
2278     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2279     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2280     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2281     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2282   }
2283   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2284   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorView, viewer, 0, 0, 0));
2285   if (ishdf5) {
2286 #if defined(PETSC_HAVE_HDF5)
2287     PetscCall(DMPlexLocalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2288 #else
2289     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2290 #endif
2291   }
2292   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorView, viewer, 0, 0, 0));
2293   PetscFunctionReturn(PETSC_SUCCESS);
2294 }
2295 
2296 PetscErrorCode DMLoad_Plex(DM dm, PetscViewer viewer)
2297 {
2298   PetscBool ishdf5;
2299 
2300   PetscFunctionBegin;
2301   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2302   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2303   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2304   if (ishdf5) {
2305 #if defined(PETSC_HAVE_HDF5)
2306     PetscViewerFormat format;
2307     PetscCall(PetscViewerGetFormat(viewer, &format));
2308     if (format == PETSC_VIEWER_HDF5_XDMF || format == PETSC_VIEWER_HDF5_VIZ) {
2309       PetscCall(DMPlexLoad_HDF5_Xdmf_Internal(dm, viewer));
2310     } else if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2311       PetscCall(DMPlexLoad_HDF5_Internal(dm, viewer));
2312     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2313     PetscFunctionReturn(PETSC_SUCCESS);
2314 #else
2315     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2316 #endif
2317   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex loading", ((PetscObject)viewer)->type_name);
2318 }
2319 
2320 /*@
2321   DMPlexTopologyLoad - Loads a topology into a `DMPLEX`
2322 
2323   Collective
2324 
2325   Input Parameters:
2326 + dm     - The `DM` into which the topology is loaded
2327 - viewer - The `PetscViewer` for the saved topology
2328 
2329   Output Parameter:
2330 . 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
2331 
2332   Level: advanced
2333 
2334 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2335           `PetscViewer`, `PetscSF`
2336 @*/
2337 PetscErrorCode DMPlexTopologyLoad(DM dm, PetscViewer viewer, PetscSF *globalToLocalPointSF)
2338 {
2339   PetscBool ishdf5;
2340 
2341   PetscFunctionBegin;
2342   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2343   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2344   if (globalToLocalPointSF) PetscAssertPointer(globalToLocalPointSF, 3);
2345   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2346   PetscCall(PetscLogEventBegin(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2347   if (ishdf5) {
2348 #if defined(PETSC_HAVE_HDF5)
2349     PetscViewerFormat format;
2350     PetscCall(PetscViewerGetFormat(viewer, &format));
2351     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2352       PetscCall(DMPlexTopologyLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2353     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2354 #else
2355     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2356 #endif
2357   }
2358   PetscCall(PetscLogEventEnd(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2359   PetscFunctionReturn(PETSC_SUCCESS);
2360 }
2361 
2362 /*@
2363   DMPlexCoordinatesLoad - Loads coordinates into a `DMPLEX`
2364 
2365   Collective
2366 
2367   Input Parameters:
2368 + dm                   - The `DM` into which the coordinates are loaded
2369 . viewer               - The `PetscViewer` for the saved coordinates
2370 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad()` when loading dm from viewer
2371 
2372   Level: advanced
2373 
2374 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2375           `PetscSF`, `PetscViewer`
2376 @*/
2377 PetscErrorCode DMPlexCoordinatesLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2378 {
2379   PetscBool ishdf5;
2380 
2381   PetscFunctionBegin;
2382   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2383   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2384   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2385   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2386   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesLoad, viewer, 0, 0, 0));
2387   if (ishdf5) {
2388 #if defined(PETSC_HAVE_HDF5)
2389     PetscViewerFormat format;
2390     PetscCall(PetscViewerGetFormat(viewer, &format));
2391     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2392       PetscCall(DMPlexCoordinatesLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2393     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2394 #else
2395     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2396 #endif
2397   }
2398   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesLoad, viewer, 0, 0, 0));
2399   PetscFunctionReturn(PETSC_SUCCESS);
2400 }
2401 
2402 /*@
2403   DMPlexLabelsLoad - Loads labels into a `DMPLEX`
2404 
2405   Collective
2406 
2407   Input Parameters:
2408 + dm                   - The `DM` into which the labels are loaded
2409 . viewer               - The `PetscViewer` for the saved labels
2410 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad()` when loading `dm` from viewer
2411 
2412   Level: advanced
2413 
2414   Note:
2415   The `PetscSF` argument must not be NULL if the `DM` is distributed, otherwise an error occurs.
2416 
2417 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2418           `PetscSF`, `PetscViewer`
2419 @*/
2420 PetscErrorCode DMPlexLabelsLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2421 {
2422   PetscBool ishdf5;
2423 
2424   PetscFunctionBegin;
2425   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2426   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2427   if (globalToLocalPointSF) PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2428   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2429   PetscCall(PetscLogEventBegin(DMPLEX_LabelsLoad, viewer, 0, 0, 0));
2430   if (ishdf5) {
2431 #if defined(PETSC_HAVE_HDF5)
2432     PetscViewerFormat format;
2433 
2434     PetscCall(PetscViewerGetFormat(viewer, &format));
2435     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2436       PetscCall(DMPlexLabelsLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2437     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2438 #else
2439     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2440 #endif
2441   }
2442   PetscCall(PetscLogEventEnd(DMPLEX_LabelsLoad, viewer, 0, 0, 0));
2443   PetscFunctionReturn(PETSC_SUCCESS);
2444 }
2445 
2446 /*@
2447   DMPlexSectionLoad - Loads section into a `DMPLEX`
2448 
2449   Collective
2450 
2451   Input Parameters:
2452 + dm                   - The `DM` that represents the topology
2453 . viewer               - The `PetscViewer` that represents the on-disk section (sectionA)
2454 . sectiondm            - The `DM` into which the on-disk section (sectionA) is migrated
2455 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad(`) when loading dm from viewer
2456 
2457   Output Parameters:
2458 + 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)
2459 - 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)
2460 
2461   Level: advanced
2462 
2463   Notes:
2464   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.
2465 
2466   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 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.
2467 
2468   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.
2469 
2470   Example using 2 processes:
2471 .vb
2472   NX (number of points on dm): 4
2473   sectionA                   : the on-disk section
2474   vecA                       : a vector associated with sectionA
2475   sectionB                   : sectiondm's local section constructed in this function
2476   vecB (local)               : a vector associated with sectiondm's local section
2477   vecB (global)              : a vector associated with sectiondm's global section
2478 
2479                                      rank 0    rank 1
2480   vecA (global)                  : [.0 .4 .1 | .2 .3]        <- to be loaded in DMPlexGlobalVectorLoad() or DMPlexLocalVectorLoad()
2481   sectionA->atlasOff             :       0 2 | 1             <- loaded in PetscSectionLoad()
2482   sectionA->atlasDof             :       1 3 | 1             <- loaded in PetscSectionLoad()
2483   sectionA's global point numbers:       0 2 | 3             <- loaded in DMPlexSectionLoad()
2484   [0, NX)                        :       0 1 | 2 3           <- conceptual partition used in globalToLocalPointSF
2485   sectionB's global point numbers:     0 1 3 | 3 2           <- associated with [0, NX) by globalToLocalPointSF
2486   sectionB->atlasDof             :     1 0 1 | 1 3
2487   sectionB->atlasOff (no perm)   :     0 1 1 | 0 1
2488   vecB (local)                   :   [.0 .4] | [.4 .1 .2 .3] <- to be constructed by calling DMPlexLocalVectorLoad() with localDofSF
2489   vecB (global)                  :    [.0 .4 | .1 .2 .3]     <- to be constructed by calling DMPlexGlobalVectorLoad() with globalDofSF
2490 .ve
2491   where "|" represents a partition of loaded data, and global point 3 is assumed to be owned by rank 0.
2492 
2493 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`, `PetscSectionLoad()`, `DMPlexSectionView()`, `PetscSF`, `PetscViewer`
2494 @*/
2495 PetscErrorCode DMPlexSectionLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF globalToLocalPointSF, PetscSF *globalDofSF, PetscSF *localDofSF)
2496 {
2497   PetscBool ishdf5;
2498 
2499   PetscFunctionBegin;
2500   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2501   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2502   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2503   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 4);
2504   if (globalDofSF) PetscAssertPointer(globalDofSF, 5);
2505   if (localDofSF) PetscAssertPointer(localDofSF, 6);
2506   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2507   PetscCall(PetscLogEventBegin(DMPLEX_SectionLoad, viewer, 0, 0, 0));
2508   if (ishdf5) {
2509 #if defined(PETSC_HAVE_HDF5)
2510     PetscCall(DMPlexSectionLoad_HDF5_Internal(dm, viewer, sectiondm, globalToLocalPointSF, globalDofSF, localDofSF));
2511 #else
2512     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2513 #endif
2514   }
2515   PetscCall(PetscLogEventEnd(DMPLEX_SectionLoad, viewer, 0, 0, 0));
2516   PetscFunctionReturn(PETSC_SUCCESS);
2517 }
2518 
2519 /*@
2520   DMPlexGlobalVectorLoad - Loads on-disk vector data into a global vector
2521 
2522   Collective
2523 
2524   Input Parameters:
2525 + dm        - The `DM` that represents the topology
2526 . viewer    - The `PetscViewer` that represents the on-disk vector data
2527 . sectiondm - The `DM` that contains the global section on which vec is defined
2528 . sf        - The `PetscSF` that migrates the on-disk vector data into vec
2529 - vec       - The global vector to set values of
2530 
2531   Level: advanced
2532 
2533   Notes:
2534   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 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.
2535 
2536   Calling sequence:
2537 .vb
2538        DMCreate(PETSC_COMM_WORLD, &dm);
2539        DMSetType(dm, DMPLEX);
2540        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2541        DMPlexTopologyLoad(dm, viewer, &sfX);
2542        DMClone(dm, &sectiondm);
2543        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2544        DMPlexSectionLoad(dm, viewer, sectiondm, sfX, &gsf, NULL);
2545        DMGetGlobalVector(sectiondm, &vec);
2546        PetscObjectSetName((PetscObject)vec, "vec_name");
2547        DMPlexGlobalVectorLoad(dm, viewer, sectiondm, gsf, vec);
2548        DMRestoreGlobalVector(sectiondm, &vec);
2549        PetscSFDestroy(&gsf);
2550        PetscSFDestroy(&sfX);
2551        DMDestroy(&sectiondm);
2552        DMDestroy(&dm);
2553 .ve
2554 
2555 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexLocalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`,
2556           `PetscSF`, `PetscViewer`
2557 @*/
2558 PetscErrorCode DMPlexGlobalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2559 {
2560   PetscBool ishdf5;
2561 
2562   PetscFunctionBegin;
2563   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2564   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2565   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2566   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2567   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2568   /* Check consistency */
2569   {
2570     PetscSection section;
2571     PetscBool    includesConstraints;
2572     PetscInt     m, m1;
2573 
2574     PetscCall(VecGetLocalSize(vec, &m1));
2575     PetscCall(DMGetGlobalSection(sectiondm, &section));
2576     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2577     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2578     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2579     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2580   }
2581   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2582   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorLoad, viewer, 0, 0, 0));
2583   if (ishdf5) {
2584 #if defined(PETSC_HAVE_HDF5)
2585     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2586 #else
2587     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2588 #endif
2589   }
2590   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorLoad, viewer, 0, 0, 0));
2591   PetscFunctionReturn(PETSC_SUCCESS);
2592 }
2593 
2594 /*@
2595   DMPlexLocalVectorLoad - Loads on-disk vector data into a local vector
2596 
2597   Collective
2598 
2599   Input Parameters:
2600 + dm        - The `DM` that represents the topology
2601 . viewer    - The `PetscViewer` that represents the on-disk vector data
2602 . sectiondm - The `DM` that contains the local section on which vec is defined
2603 . sf        - The `PetscSF` that migrates the on-disk vector data into vec
2604 - vec       - The local vector to set values of
2605 
2606   Level: advanced
2607 
2608   Notes:
2609   In general `dm` and `sectiondm` are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2610 
2611   Calling sequence:
2612 .vb
2613        DMCreate(PETSC_COMM_WORLD, &dm);
2614        DMSetType(dm, DMPLEX);
2615        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2616        DMPlexTopologyLoad(dm, viewer, &sfX);
2617        DMClone(dm, &sectiondm);
2618        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2619        DMPlexSectionLoad(dm, viewer, sectiondm, sfX, NULL, &lsf);
2620        DMGetLocalVector(sectiondm, &vec);
2621        PetscObjectSetName((PetscObject)vec, "vec_name");
2622        DMPlexLocalVectorLoad(dm, viewer, sectiondm, lsf, vec);
2623        DMRestoreLocalVector(sectiondm, &vec);
2624        PetscSFDestroy(&lsf);
2625        PetscSFDestroy(&sfX);
2626        DMDestroy(&sectiondm);
2627        DMDestroy(&dm);
2628 .ve
2629 
2630 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`,
2631           `PetscSF`, `PetscViewer`
2632 @*/
2633 PetscErrorCode DMPlexLocalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2634 {
2635   PetscBool ishdf5;
2636 
2637   PetscFunctionBegin;
2638   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2639   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2640   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2641   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2642   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2643   /* Check consistency */
2644   {
2645     PetscSection section;
2646     PetscBool    includesConstraints;
2647     PetscInt     m, m1;
2648 
2649     PetscCall(VecGetLocalSize(vec, &m1));
2650     PetscCall(DMGetLocalSection(sectiondm, &section));
2651     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2652     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2653     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2654     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2655   }
2656   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2657   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorLoad, viewer, 0, 0, 0));
2658   if (ishdf5) {
2659 #if defined(PETSC_HAVE_HDF5)
2660     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2661 #else
2662     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2663 #endif
2664   }
2665   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorLoad, viewer, 0, 0, 0));
2666   PetscFunctionReturn(PETSC_SUCCESS);
2667 }
2668 
2669 PetscErrorCode DMDestroy_Plex(DM dm)
2670 {
2671   DM_Plex *mesh = (DM_Plex *)dm->data;
2672 
2673   PetscFunctionBegin;
2674   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMSetUpGLVisViewer_C", NULL));
2675   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertBoundaryValues_C", NULL));
2676   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMCreateNeumannOverlap_C", NULL));
2677   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMInterpolateSolution_C", NULL));
2678   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertTimeDerivativeBoundaryValues_C", NULL));
2679   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", NULL));
2680   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeGetDefault_C", NULL));
2681   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeSetDefault_C", NULL));
2682   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "MatComputeNeumannOverlap_C", NULL));
2683   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderGetDefault_C", NULL));
2684   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderSetDefault_C", NULL));
2685   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", NULL));
2686   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexSetOverlap_C", NULL));
2687   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetUseCeed_C", NULL));
2688   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexSetUseCeed_C", NULL));
2689   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMGetIsoperiodicPointSF_C", NULL));
2690   if (--mesh->refct > 0) PetscFunctionReturn(PETSC_SUCCESS);
2691   PetscCall(PetscSectionDestroy(&mesh->coneSection));
2692   PetscCall(PetscFree(mesh->cones));
2693   PetscCall(PetscFree(mesh->coneOrientations));
2694   PetscCall(PetscSectionDestroy(&mesh->supportSection));
2695   PetscCall(PetscSectionDestroy(&mesh->subdomainSection));
2696   PetscCall(PetscFree(mesh->supports));
2697   PetscCall(PetscFree(mesh->cellTypes));
2698   PetscCall(DMPlexTransformDestroy(&mesh->tr));
2699   PetscCall(PetscFree(mesh->tetgenOpts));
2700   PetscCall(PetscFree(mesh->triangleOpts));
2701   PetscCall(PetscFree(mesh->transformType));
2702   PetscCall(PetscFree(mesh->distributionName));
2703   PetscCall(PetscPartitionerDestroy(&mesh->partitioner));
2704   PetscCall(DMLabelDestroy(&mesh->subpointMap));
2705   PetscCall(ISDestroy(&mesh->subpointIS));
2706   PetscCall(ISDestroy(&mesh->globalVertexNumbers));
2707   PetscCall(ISDestroy(&mesh->globalCellNumbers));
2708   PetscCall(PetscSFDestroy(&mesh->periodic.face_sf));
2709   PetscCall(PetscSFDestroy(&mesh->periodic.composed_sf));
2710   PetscCall(ISDestroy(&mesh->periodic.periodic_points));
2711   PetscCall(PetscSectionDestroy(&mesh->anchorSection));
2712   PetscCall(ISDestroy(&mesh->anchorIS));
2713   PetscCall(PetscSectionDestroy(&mesh->parentSection));
2714   PetscCall(PetscFree(mesh->parents));
2715   PetscCall(PetscFree(mesh->childIDs));
2716   PetscCall(PetscSectionDestroy(&mesh->childSection));
2717   PetscCall(PetscFree(mesh->children));
2718   PetscCall(DMDestroy(&mesh->referenceTree));
2719   PetscCall(PetscGridHashDestroy(&mesh->lbox));
2720   PetscCall(PetscFree(mesh->neighbors));
2721   if (mesh->metricCtx) PetscCall(PetscFree(mesh->metricCtx));
2722   /* This was originally freed in DMDestroy(), but that prevents reference counting of backend objects */
2723   PetscCall(PetscFree(mesh));
2724   PetscFunctionReturn(PETSC_SUCCESS);
2725 }
2726 
2727 PetscErrorCode DMCreateMatrix_Plex(DM dm, Mat *J)
2728 {
2729   PetscSection           sectionGlobal;
2730   PetscInt               bs = -1, mbs;
2731   PetscInt               localSize, localStart = 0;
2732   PetscBool              isShell, isBlock, isSeqBlock, isMPIBlock, isSymBlock, isSymSeqBlock, isSymMPIBlock, isMatIS;
2733   MatType                mtype;
2734   ISLocalToGlobalMapping ltog;
2735 
2736   PetscFunctionBegin;
2737   PetscCall(MatInitializePackage());
2738   mtype = dm->mattype;
2739   PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
2740   /* PetscCall(PetscSectionGetStorageSize(sectionGlobal, &localSize)); */
2741   PetscCall(PetscSectionGetConstrainedStorageSize(sectionGlobal, &localSize));
2742   PetscCallMPI(MPI_Exscan(&localSize, &localStart, 1, MPIU_INT, MPI_SUM, PetscObjectComm((PetscObject)dm)));
2743   PetscCall(MatCreate(PetscObjectComm((PetscObject)dm), J));
2744   PetscCall(MatSetSizes(*J, localSize, localSize, PETSC_DETERMINE, PETSC_DETERMINE));
2745   PetscCall(MatSetType(*J, mtype));
2746   PetscCall(MatSetFromOptions(*J));
2747   PetscCall(MatGetBlockSize(*J, &mbs));
2748   if (mbs > 1) bs = mbs;
2749   PetscCall(PetscStrcmp(mtype, MATSHELL, &isShell));
2750   PetscCall(PetscStrcmp(mtype, MATBAIJ, &isBlock));
2751   PetscCall(PetscStrcmp(mtype, MATSEQBAIJ, &isSeqBlock));
2752   PetscCall(PetscStrcmp(mtype, MATMPIBAIJ, &isMPIBlock));
2753   PetscCall(PetscStrcmp(mtype, MATSBAIJ, &isSymBlock));
2754   PetscCall(PetscStrcmp(mtype, MATSEQSBAIJ, &isSymSeqBlock));
2755   PetscCall(PetscStrcmp(mtype, MATMPISBAIJ, &isSymMPIBlock));
2756   PetscCall(PetscStrcmp(mtype, MATIS, &isMatIS));
2757   if (!isShell) {
2758     PetscBool fillMatrix = (PetscBool)(!dm->prealloc_only && !isMatIS);
2759     PetscInt *dnz, *onz, *dnzu, *onzu, bsLocal[2], bsMinMax[2], *pblocks;
2760     PetscInt  pStart, pEnd, p, dof, cdof, num_fields;
2761 
2762     PetscCall(DMGetLocalToGlobalMapping(dm, &ltog));
2763 
2764     PetscCall(PetscCalloc1(localSize, &pblocks));
2765     PetscCall(PetscSectionGetChart(sectionGlobal, &pStart, &pEnd));
2766     PetscCall(PetscSectionGetNumFields(sectionGlobal, &num_fields));
2767     for (p = pStart; p < pEnd; ++p) {
2768       switch (dm->blocking_type) {
2769       case DM_BLOCKING_TOPOLOGICAL_POINT: { // One block per topological point
2770         PetscInt bdof, offset;
2771 
2772         PetscCall(PetscSectionGetDof(sectionGlobal, p, &dof));
2773         PetscCall(PetscSectionGetOffset(sectionGlobal, p, &offset));
2774         PetscCall(PetscSectionGetConstraintDof(sectionGlobal, p, &cdof));
2775         for (PetscInt i = 0; i < dof - cdof; i++) pblocks[offset - localStart + i] = dof - cdof;
2776         dof  = dof < 0 ? -(dof + 1) : dof;
2777         bdof = cdof && (dof - cdof) ? 1 : dof;
2778         if (dof) {
2779           if (bs < 0) {
2780             bs = bdof;
2781           } else if (bs != bdof) {
2782             bs = 1;
2783           }
2784         }
2785       } break;
2786       case DM_BLOCKING_FIELD_NODE: {
2787         for (PetscInt field = 0; field < num_fields; field++) {
2788           PetscInt num_comp, bdof, offset;
2789           PetscCall(PetscSectionGetFieldComponents(sectionGlobal, field, &num_comp));
2790           PetscCall(PetscSectionGetFieldDof(sectionGlobal, p, field, &dof));
2791           if (dof < 0) continue;
2792           PetscCall(PetscSectionGetFieldOffset(sectionGlobal, p, field, &offset));
2793           PetscCall(PetscSectionGetFieldConstraintDof(sectionGlobal, p, field, &cdof));
2794           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);
2795           PetscInt num_nodes = dof / num_comp;
2796           for (PetscInt i = 0; i < dof - cdof; i++) pblocks[offset - localStart + i] = (dof - cdof) / num_nodes;
2797           // Handle possibly constant block size (unlikely)
2798           bdof = cdof && (dof - cdof) ? 1 : dof;
2799           if (dof) {
2800             if (bs < 0) {
2801               bs = bdof;
2802             } else if (bs != bdof) {
2803               bs = 1;
2804             }
2805           }
2806         }
2807       } break;
2808       }
2809     }
2810     /* Must have same blocksize on all procs (some might have no points) */
2811     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs;
2812     bsLocal[1] = bs;
2813     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
2814     if (bsMinMax[0] != bsMinMax[1]) bs = 1;
2815     else bs = bsMinMax[0];
2816     bs = PetscMax(1, bs);
2817     PetscCall(MatSetLocalToGlobalMapping(*J, ltog, ltog));
2818     if (dm->prealloc_skip) { // User will likely use MatSetPreallocationCOO(), but still set structural parameters
2819       PetscCall(MatSetBlockSize(*J, bs));
2820       PetscCall(MatSetUp(*J));
2821     } else {
2822       PetscCall(PetscCalloc4(localSize / bs, &dnz, localSize / bs, &onz, localSize / bs, &dnzu, localSize / bs, &onzu));
2823       PetscCall(DMPlexPreallocateOperator(dm, bs, dnz, onz, dnzu, onzu, *J, fillMatrix));
2824       PetscCall(PetscFree4(dnz, onz, dnzu, onzu));
2825     }
2826     { // Consolidate blocks
2827       PetscInt nblocks = 0;
2828       for (PetscInt i = 0; i < localSize; i += PetscMax(1, pblocks[i])) {
2829         if (pblocks[i] == 0) continue;
2830         pblocks[nblocks++] = pblocks[i]; // nblocks always <= i
2831         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]);
2832       }
2833       PetscCall(MatSetVariableBlockSizes(*J, nblocks, pblocks));
2834     }
2835     PetscCall(PetscFree(pblocks));
2836   }
2837   PetscCall(MatSetDM(*J, dm));
2838   PetscFunctionReturn(PETSC_SUCCESS);
2839 }
2840 
2841 /*@
2842   DMPlexGetSubdomainSection - Returns the section associated with the subdomain
2843 
2844   Not Collective
2845 
2846   Input Parameter:
2847 . dm - The `DMPLEX`
2848 
2849   Output Parameter:
2850 . subsection - The subdomain section
2851 
2852   Level: developer
2853 
2854 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `PetscSection`
2855 @*/
2856 PetscErrorCode DMPlexGetSubdomainSection(DM dm, PetscSection *subsection)
2857 {
2858   DM_Plex *mesh = (DM_Plex *)dm->data;
2859 
2860   PetscFunctionBegin;
2861   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2862   if (!mesh->subdomainSection) {
2863     PetscSection section;
2864     PetscSF      sf;
2865 
2866     PetscCall(PetscSFCreate(PETSC_COMM_SELF, &sf));
2867     PetscCall(DMGetLocalSection(dm, &section));
2868     PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_FALSE, PETSC_TRUE, &mesh->subdomainSection));
2869     PetscCall(PetscSFDestroy(&sf));
2870   }
2871   *subsection = mesh->subdomainSection;
2872   PetscFunctionReturn(PETSC_SUCCESS);
2873 }
2874 
2875 /*@
2876   DMPlexGetChart - Return the interval for all mesh points [`pStart`, `pEnd`)
2877 
2878   Not Collective
2879 
2880   Input Parameter:
2881 . dm - The `DMPLEX`
2882 
2883   Output Parameters:
2884 + pStart - The first mesh point
2885 - pEnd   - The upper bound for mesh points
2886 
2887   Level: beginner
2888 
2889 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetChart()`
2890 @*/
2891 PetscErrorCode DMPlexGetChart(DM dm, PetscInt *pStart, PetscInt *pEnd)
2892 {
2893   DM_Plex *mesh = (DM_Plex *)dm->data;
2894 
2895   PetscFunctionBegin;
2896   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2897   if (mesh->tr) PetscCall(DMPlexTransformGetChart(mesh->tr, pStart, pEnd));
2898   else PetscCall(PetscSectionGetChart(mesh->coneSection, pStart, pEnd));
2899   PetscFunctionReturn(PETSC_SUCCESS);
2900 }
2901 
2902 /*@
2903   DMPlexSetChart - Set the interval for all mesh points [`pStart`, `pEnd`)
2904 
2905   Not Collective
2906 
2907   Input Parameters:
2908 + dm     - The `DMPLEX`
2909 . pStart - The first mesh point
2910 - pEnd   - The upper bound for mesh points
2911 
2912   Level: beginner
2913 
2914 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetChart()`
2915 @*/
2916 PetscErrorCode DMPlexSetChart(DM dm, PetscInt pStart, PetscInt pEnd)
2917 {
2918   DM_Plex *mesh = (DM_Plex *)dm->data;
2919 
2920   PetscFunctionBegin;
2921   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2922   PetscCall(PetscSectionSetChart(mesh->coneSection, pStart, pEnd));
2923   PetscCall(PetscSectionSetChart(mesh->supportSection, pStart, pEnd));
2924   PetscCall(PetscFree(mesh->cellTypes));
2925   PetscFunctionReturn(PETSC_SUCCESS);
2926 }
2927 
2928 /*@
2929   DMPlexGetConeSize - Return the number of in-edges for this point in the DAG
2930 
2931   Not Collective
2932 
2933   Input Parameters:
2934 + dm - The `DMPLEX`
2935 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
2936 
2937   Output Parameter:
2938 . size - The cone size for point `p`
2939 
2940   Level: beginner
2941 
2942 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
2943 @*/
2944 PetscErrorCode DMPlexGetConeSize(DM dm, PetscInt p, PetscInt *size)
2945 {
2946   DM_Plex *mesh = (DM_Plex *)dm->data;
2947 
2948   PetscFunctionBegin;
2949   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2950   PetscAssertPointer(size, 3);
2951   if (mesh->tr) PetscCall(DMPlexTransformGetConeSize(mesh->tr, p, size));
2952   else PetscCall(PetscSectionGetDof(mesh->coneSection, p, size));
2953   PetscFunctionReturn(PETSC_SUCCESS);
2954 }
2955 
2956 /*@
2957   DMPlexSetConeSize - Set the number of in-edges for this point in the DAG
2958 
2959   Not Collective
2960 
2961   Input Parameters:
2962 + dm   - The `DMPLEX`
2963 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
2964 - size - The cone size for point `p`
2965 
2966   Level: beginner
2967 
2968   Note:
2969   This should be called after `DMPlexSetChart()`.
2970 
2971 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetConeSize()`, `DMPlexSetChart()`
2972 @*/
2973 PetscErrorCode DMPlexSetConeSize(DM dm, PetscInt p, PetscInt size)
2974 {
2975   DM_Plex *mesh = (DM_Plex *)dm->data;
2976 
2977   PetscFunctionBegin;
2978   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2979   PetscCheck(!mesh->tr, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Cannot call DMPlexSetConeSize() on a mesh with a transform defined.");
2980   PetscCall(PetscSectionSetDof(mesh->coneSection, p, size));
2981   PetscFunctionReturn(PETSC_SUCCESS);
2982 }
2983 
2984 /*@C
2985   DMPlexGetCone - Return the points on the in-edges for this point in the DAG
2986 
2987   Not Collective
2988 
2989   Input Parameters:
2990 + dm - The `DMPLEX`
2991 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
2992 
2993   Output Parameter:
2994 . cone - An array of points which are on the in-edges for point `p`
2995 
2996   Level: beginner
2997 
2998   Fortran Notes:
2999   You must also call `DMPlexRestoreCone()` after you finish using the returned array.
3000   `DMPlexRestoreCone()` is not needed/available in C.
3001 
3002 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSize()`, `DMPlexSetCone()`, `DMPlexGetConeTuple()`, `DMPlexSetChart()`, `DMPlexRestoreCone()`
3003 @*/
3004 PetscErrorCode DMPlexGetCone(DM dm, PetscInt p, const PetscInt *cone[])
3005 {
3006   DM_Plex *mesh = (DM_Plex *)dm->data;
3007   PetscInt off;
3008 
3009   PetscFunctionBegin;
3010   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3011   PetscAssertPointer(cone, 3);
3012   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3013   *cone = PetscSafePointerPlusOffset(mesh->cones, off);
3014   PetscFunctionReturn(PETSC_SUCCESS);
3015 }
3016 
3017 /*@C
3018   DMPlexGetConeTuple - Return the points on the in-edges of several points in the DAG
3019 
3020   Not Collective
3021 
3022   Input Parameters:
3023 + dm - The `DMPLEX`
3024 - p  - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3025 
3026   Output Parameters:
3027 + pConesSection - `PetscSection` describing the layout of `pCones`
3028 - pCones        - An array of points which are on the in-edges for the point set `p`
3029 
3030   Level: intermediate
3031 
3032 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeRecursive()`, `DMPlexSetChart()`, `PetscSection`, `IS`
3033 @*/
3034 PetscErrorCode DMPlexGetConeTuple(DM dm, IS p, PetscSection *pConesSection, IS *pCones)
3035 {
3036   PetscSection cs, newcs;
3037   PetscInt    *cones;
3038   PetscInt    *newarr = NULL;
3039   PetscInt     n;
3040 
3041   PetscFunctionBegin;
3042   PetscCall(DMPlexGetCones(dm, &cones));
3043   PetscCall(DMPlexGetConeSection(dm, &cs));
3044   PetscCall(PetscSectionExtractDofsFromArray(cs, MPIU_INT, cones, p, &newcs, pCones ? ((void **)&newarr) : NULL));
3045   if (pConesSection) *pConesSection = newcs;
3046   if (pCones) {
3047     PetscCall(PetscSectionGetStorageSize(newcs, &n));
3048     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)p), n, newarr, PETSC_OWN_POINTER, pCones));
3049   }
3050   PetscFunctionReturn(PETSC_SUCCESS);
3051 }
3052 
3053 /*@
3054   DMPlexGetConeRecursiveVertices - Expand each given point into its cone points and do that recursively until we end up just with vertices.
3055 
3056   Not Collective
3057 
3058   Input Parameters:
3059 + dm     - The `DMPLEX`
3060 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3061 
3062   Output Parameter:
3063 . expandedPoints - An array of vertices recursively expanded from input points
3064 
3065   Level: advanced
3066 
3067   Notes:
3068   Like `DMPlexGetConeRecursive()` but returns only the 0-depth `IS` (i.e. vertices only) and no sections.
3069 
3070   There is no corresponding Restore function, just call `ISDestroy()` on the returned `IS` to deallocate.
3071 
3072 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexRestoreConeRecursive()`,
3073           `DMPlexGetDepth()`, `IS`
3074 @*/
3075 PetscErrorCode DMPlexGetConeRecursiveVertices(DM dm, IS points, IS *expandedPoints)
3076 {
3077   IS      *expandedPointsAll;
3078   PetscInt depth;
3079 
3080   PetscFunctionBegin;
3081   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3082   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
3083   PetscAssertPointer(expandedPoints, 3);
3084   PetscCall(DMPlexGetConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
3085   *expandedPoints = expandedPointsAll[0];
3086   PetscCall(PetscObjectReference((PetscObject)expandedPointsAll[0]));
3087   PetscCall(DMPlexRestoreConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
3088   PetscFunctionReturn(PETSC_SUCCESS);
3089 }
3090 
3091 /*@
3092   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).
3093 
3094   Not Collective
3095 
3096   Input Parameters:
3097 + dm     - The `DMPLEX`
3098 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3099 
3100   Output Parameters:
3101 + depth          - (optional) Size of the output arrays, equal to `DMPLEX` depth, returned by `DMPlexGetDepth()`
3102 . expandedPoints - (optional) An array of index sets with recursively expanded cones
3103 - sections       - (optional) An array of sections which describe mappings from points to their cone points
3104 
3105   Level: advanced
3106 
3107   Notes:
3108   Like `DMPlexGetConeTuple()` but recursive.
3109 
3110   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.
3111   For example, for d=0 it contains only vertices, for d=1 it can contain vertices and edges, etc.
3112 
3113   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\:
3114   (1) DAG points in `expandedPoints`[d+1] with `depth` d+1 to their cone points in `expandedPoints`[d];
3115   (2) DAG points in `expandedPoints`[d+1] with `depth` in [0,d] to the same points in `expandedPoints`[d].
3116 
3117 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexRestoreConeRecursive()`, `DMPlexGetConeRecursiveVertices()`,
3118           `DMPlexGetDepth()`, `PetscSection`, `IS`
3119 @*/
3120 PetscErrorCode DMPlexGetConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
3121 {
3122   const PetscInt *arr0 = NULL, *cone = NULL;
3123   PetscInt       *arr = NULL, *newarr = NULL;
3124   PetscInt        d, depth_, i, n, newn, cn, co, start, end;
3125   IS             *expandedPoints_;
3126   PetscSection   *sections_;
3127 
3128   PetscFunctionBegin;
3129   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3130   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
3131   if (depth) PetscAssertPointer(depth, 3);
3132   if (expandedPoints) PetscAssertPointer(expandedPoints, 4);
3133   if (sections) PetscAssertPointer(sections, 5);
3134   PetscCall(ISGetLocalSize(points, &n));
3135   PetscCall(ISGetIndices(points, &arr0));
3136   PetscCall(DMPlexGetDepth(dm, &depth_));
3137   PetscCall(PetscCalloc1(depth_, &expandedPoints_));
3138   PetscCall(PetscCalloc1(depth_, &sections_));
3139   arr = (PetscInt *)arr0; /* this is ok because first generation of arr is not modified */
3140   for (d = depth_ - 1; d >= 0; d--) {
3141     PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &sections_[d]));
3142     PetscCall(PetscSectionSetChart(sections_[d], 0, n));
3143     for (i = 0; i < n; i++) {
3144       PetscCall(DMPlexGetDepthStratum(dm, d + 1, &start, &end));
3145       if (arr[i] >= start && arr[i] < end) {
3146         PetscCall(DMPlexGetConeSize(dm, arr[i], &cn));
3147         PetscCall(PetscSectionSetDof(sections_[d], i, cn));
3148       } else {
3149         PetscCall(PetscSectionSetDof(sections_[d], i, 1));
3150       }
3151     }
3152     PetscCall(PetscSectionSetUp(sections_[d]));
3153     PetscCall(PetscSectionGetStorageSize(sections_[d], &newn));
3154     PetscCall(PetscMalloc1(newn, &newarr));
3155     for (i = 0; i < n; i++) {
3156       PetscCall(PetscSectionGetDof(sections_[d], i, &cn));
3157       PetscCall(PetscSectionGetOffset(sections_[d], i, &co));
3158       if (cn > 1) {
3159         PetscCall(DMPlexGetCone(dm, arr[i], &cone));
3160         PetscCall(PetscMemcpy(&newarr[co], cone, cn * sizeof(PetscInt)));
3161       } else {
3162         newarr[co] = arr[i];
3163       }
3164     }
3165     PetscCall(ISCreateGeneral(PETSC_COMM_SELF, newn, newarr, PETSC_OWN_POINTER, &expandedPoints_[d]));
3166     arr = newarr;
3167     n   = newn;
3168   }
3169   PetscCall(ISRestoreIndices(points, &arr0));
3170   *depth = depth_;
3171   if (expandedPoints) *expandedPoints = expandedPoints_;
3172   else {
3173     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&expandedPoints_[d]));
3174     PetscCall(PetscFree(expandedPoints_));
3175   }
3176   if (sections) *sections = sections_;
3177   else {
3178     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&sections_[d]));
3179     PetscCall(PetscFree(sections_));
3180   }
3181   PetscFunctionReturn(PETSC_SUCCESS);
3182 }
3183 
3184 /*@
3185   DMPlexRestoreConeRecursive - Deallocates arrays created by `DMPlexGetConeRecursive()`
3186 
3187   Not Collective
3188 
3189   Input Parameters:
3190 + dm     - The `DMPLEX`
3191 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3192 
3193   Output Parameters:
3194 + depth          - (optional) Size of the output arrays, equal to `DMPLEX` depth, returned by `DMPlexGetDepth()`
3195 . expandedPoints - (optional) An array of recursively expanded cones
3196 - sections       - (optional) An array of sections which describe mappings from points to their cone points
3197 
3198   Level: advanced
3199 
3200   Note:
3201   See `DMPlexGetConeRecursive()`
3202 
3203 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexGetConeRecursiveVertices()`,
3204           `DMPlexGetDepth()`, `IS`, `PetscSection`
3205 @*/
3206 PetscErrorCode DMPlexRestoreConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
3207 {
3208   PetscInt d, depth_;
3209 
3210   PetscFunctionBegin;
3211   PetscCall(DMPlexGetDepth(dm, &depth_));
3212   PetscCheck(!depth || *depth == depth_, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "depth changed since last call to DMPlexGetConeRecursive");
3213   if (depth) *depth = 0;
3214   if (expandedPoints) {
3215     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&((*expandedPoints)[d])));
3216     PetscCall(PetscFree(*expandedPoints));
3217   }
3218   if (sections) {
3219     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&((*sections)[d])));
3220     PetscCall(PetscFree(*sections));
3221   }
3222   PetscFunctionReturn(PETSC_SUCCESS);
3223 }
3224 
3225 /*@
3226   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
3227 
3228   Not Collective
3229 
3230   Input Parameters:
3231 + dm   - The `DMPLEX`
3232 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3233 - cone - An array of points which are on the in-edges for point `p`
3234 
3235   Level: beginner
3236 
3237   Note:
3238   This should be called after all calls to `DMPlexSetConeSize()` and `DMSetUp()`.
3239 
3240 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`, `DMPlexSetSupport()`, `DMPlexSetSupportSize()`
3241 @*/
3242 PetscErrorCode DMPlexSetCone(DM dm, PetscInt p, const PetscInt cone[])
3243 {
3244   DM_Plex *mesh = (DM_Plex *)dm->data;
3245   PetscInt dof, off, c;
3246 
3247   PetscFunctionBegin;
3248   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3249   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3250   if (dof) PetscAssertPointer(cone, 3);
3251   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3252   if (PetscDefined(USE_DEBUG)) {
3253     PetscInt pStart, pEnd;
3254     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3255     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);
3256     for (c = 0; c < dof; ++c) {
3257       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);
3258       mesh->cones[off + c] = cone[c];
3259     }
3260   } else {
3261     for (c = 0; c < dof; ++c) mesh->cones[off + c] = cone[c];
3262   }
3263   PetscFunctionReturn(PETSC_SUCCESS);
3264 }
3265 
3266 /*@C
3267   DMPlexGetConeOrientation - Return the orientations on the in-edges for this point in the DAG
3268 
3269   Not Collective
3270 
3271   Input Parameters:
3272 + dm - The `DMPLEX`
3273 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3274 
3275   Output Parameter:
3276 . coneOrientation - An array of orientations which are on the in-edges for point `p`. An orientation is an
3277                     integer giving the prescription for cone traversal.
3278 
3279   Level: beginner
3280 
3281   Note:
3282   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3283   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3284   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3285   with the identity.
3286 
3287   Fortran Notes:
3288   You must also call `DMPlexRestoreConeOrientation()` after you finish using the returned array.
3289   `DMPlexRestoreConeOrientation()` is not needed/available in C.
3290 
3291 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPolytopeTypeComposeOrientation()`, `DMPolytopeTypeComposeOrientationInv()`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetCone()`, `DMPlexSetChart()`
3292 @*/
3293 PetscErrorCode DMPlexGetConeOrientation(DM dm, PetscInt p, const PetscInt *coneOrientation[])
3294 {
3295   DM_Plex *mesh = (DM_Plex *)dm->data;
3296   PetscInt off;
3297 
3298   PetscFunctionBegin;
3299   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3300   if (PetscDefined(USE_DEBUG)) {
3301     PetscInt dof;
3302     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3303     if (dof) PetscAssertPointer(coneOrientation, 3);
3304   }
3305   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3306 
3307   *coneOrientation = &mesh->coneOrientations[off];
3308   PetscFunctionReturn(PETSC_SUCCESS);
3309 }
3310 
3311 /*@
3312   DMPlexSetConeOrientation - Set the orientations on the in-edges for this point in the DAG
3313 
3314   Not Collective
3315 
3316   Input Parameters:
3317 + dm              - The `DMPLEX`
3318 . p               - The point, which must lie in the chart set with `DMPlexSetChart()`
3319 - coneOrientation - An array of orientations
3320 
3321   Level: beginner
3322 
3323   Notes:
3324   This should be called after all calls to `DMPlexSetConeSize()` and `DMSetUp()`.
3325 
3326   The meaning of coneOrientation is detailed in `DMPlexGetConeOrientation()`.
3327 
3328 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetConeOrientation()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3329 @*/
3330 PetscErrorCode DMPlexSetConeOrientation(DM dm, PetscInt p, const PetscInt coneOrientation[])
3331 {
3332   DM_Plex *mesh = (DM_Plex *)dm->data;
3333   PetscInt pStart, pEnd;
3334   PetscInt dof, off, c;
3335 
3336   PetscFunctionBegin;
3337   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3338   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3339   if (dof) PetscAssertPointer(coneOrientation, 3);
3340   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3341   if (PetscDefined(USE_DEBUG)) {
3342     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3343     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);
3344     for (c = 0; c < dof; ++c) {
3345       PetscInt cdof, o = coneOrientation[c];
3346 
3347       PetscCall(PetscSectionGetDof(mesh->coneSection, mesh->cones[off + c], &cdof));
3348       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);
3349       mesh->coneOrientations[off + c] = o;
3350     }
3351   } else {
3352     for (c = 0; c < dof; ++c) mesh->coneOrientations[off + c] = coneOrientation[c];
3353   }
3354   PetscFunctionReturn(PETSC_SUCCESS);
3355 }
3356 
3357 /*@
3358   DMPlexInsertCone - Insert a point into the in-edges for the point p in the DAG
3359 
3360   Not Collective
3361 
3362   Input Parameters:
3363 + dm        - The `DMPLEX`
3364 . p         - The point, which must lie in the chart set with `DMPlexSetChart()`
3365 . conePos   - The local index in the cone where the point should be put
3366 - conePoint - The mesh point to insert
3367 
3368   Level: beginner
3369 
3370 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3371 @*/
3372 PetscErrorCode DMPlexInsertCone(DM dm, PetscInt p, PetscInt conePos, PetscInt conePoint)
3373 {
3374   DM_Plex *mesh = (DM_Plex *)dm->data;
3375   PetscInt pStart, pEnd;
3376   PetscInt dof, off;
3377 
3378   PetscFunctionBegin;
3379   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3380   if (PetscDefined(USE_DEBUG)) {
3381     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3382     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);
3383     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);
3384     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3385     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);
3386   }
3387   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3388   mesh->cones[off + conePos] = conePoint;
3389   PetscFunctionReturn(PETSC_SUCCESS);
3390 }
3391 
3392 /*@
3393   DMPlexInsertConeOrientation - Insert a point orientation for the in-edge for the point p in the DAG
3394 
3395   Not Collective
3396 
3397   Input Parameters:
3398 + dm              - The `DMPLEX`
3399 . p               - The point, which must lie in the chart set with `DMPlexSetChart()`
3400 . conePos         - The local index in the cone where the point should be put
3401 - coneOrientation - The point orientation to insert
3402 
3403   Level: beginner
3404 
3405   Note:
3406   The meaning of coneOrientation values is detailed in `DMPlexGetConeOrientation()`.
3407 
3408 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3409 @*/
3410 PetscErrorCode DMPlexInsertConeOrientation(DM dm, PetscInt p, PetscInt conePos, PetscInt coneOrientation)
3411 {
3412   DM_Plex *mesh = (DM_Plex *)dm->data;
3413   PetscInt pStart, pEnd;
3414   PetscInt dof, off;
3415 
3416   PetscFunctionBegin;
3417   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3418   if (PetscDefined(USE_DEBUG)) {
3419     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3420     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);
3421     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3422     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);
3423   }
3424   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3425   mesh->coneOrientations[off + conePos] = coneOrientation;
3426   PetscFunctionReturn(PETSC_SUCCESS);
3427 }
3428 
3429 /*@C
3430   DMPlexGetOrientedCone - Return the points and orientations on the in-edges for this point in the DAG
3431 
3432   Not collective
3433 
3434   Input Parameters:
3435 + dm - The DMPlex
3436 - p  - The point, which must lie in the chart set with DMPlexSetChart()
3437 
3438   Output Parameters:
3439 + cone - An array of points which are on the in-edges for point `p`
3440 - ornt - An array of orientations which are on the in-edges for point `p`. An orientation is an
3441         integer giving the prescription for cone traversal.
3442 
3443   Level: beginner
3444 
3445   Notes:
3446   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3447   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3448   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3449   with the identity.
3450 
3451   Fortran Notes:
3452   You must also call `DMPlexRestoreCone()` after you finish using the returned array.
3453   `DMPlexRestoreCone()` is not needed/available in C.
3454 
3455 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreOrientedCone()`, `DMPlexGetConeSize()`, `DMPlexGetCone()`, `DMPlexGetChart()`
3456 @*/
3457 PetscErrorCode DMPlexGetOrientedCone(DM dm, PetscInt p, const PetscInt *cone[], const PetscInt *ornt[])
3458 {
3459   DM_Plex *mesh = (DM_Plex *)dm->data;
3460 
3461   PetscFunctionBegin;
3462   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3463   if (mesh->tr) {
3464     PetscCall(DMPlexTransformGetCone(mesh->tr, p, cone, ornt));
3465   } else {
3466     PetscInt off;
3467     if (PetscDefined(USE_DEBUG)) {
3468       PetscInt dof;
3469       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3470       if (dof) {
3471         if (cone) PetscAssertPointer(cone, 3);
3472         if (ornt) PetscAssertPointer(ornt, 4);
3473       }
3474     }
3475     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3476     if (cone) *cone = PetscSafePointerPlusOffset(mesh->cones, off);
3477     if (ornt) *ornt = PetscSafePointerPlusOffset(mesh->coneOrientations, off);
3478   }
3479   PetscFunctionReturn(PETSC_SUCCESS);
3480 }
3481 
3482 /*@C
3483   DMPlexRestoreOrientedCone - Restore the points and orientations on the in-edges for this point in the DAG
3484 
3485   Not Collective
3486 
3487   Input Parameters:
3488 + dm   - The DMPlex
3489 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3490 . cone - An array of points which are on the in-edges for point p
3491 - ornt - An array of orientations which are on the in-edges for point `p`. An orientation is an
3492         integer giving the prescription for cone traversal.
3493 
3494   Level: beginner
3495 
3496   Notes:
3497   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3498   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3499   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3500   with the identity.
3501 
3502   Fortran Notes:
3503   You must also call `DMPlexRestoreCone()` after you finish using the returned array.
3504   `DMPlexRestoreCone()` is not needed/available in C.
3505 
3506 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetOrientedCone()`, `DMPlexGetConeSize()`, `DMPlexGetCone()`, `DMPlexGetChart()`
3507 @*/
3508 PetscErrorCode DMPlexRestoreOrientedCone(DM dm, PetscInt p, const PetscInt *cone[], const PetscInt *ornt[])
3509 {
3510   DM_Plex *mesh = (DM_Plex *)dm->data;
3511 
3512   PetscFunctionBegin;
3513   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3514   if (mesh->tr) PetscCall(DMPlexTransformRestoreCone(mesh->tr, p, cone, ornt));
3515   PetscFunctionReturn(PETSC_SUCCESS);
3516 }
3517 
3518 /*@
3519   DMPlexGetSupportSize - Return the number of out-edges for this point in the DAG
3520 
3521   Not Collective
3522 
3523   Input Parameters:
3524 + dm - The `DMPLEX`
3525 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3526 
3527   Output Parameter:
3528 . size - The support size for point `p`
3529 
3530   Level: beginner
3531 
3532 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`, `DMPlexGetConeSize()`
3533 @*/
3534 PetscErrorCode DMPlexGetSupportSize(DM dm, PetscInt p, PetscInt *size)
3535 {
3536   DM_Plex *mesh = (DM_Plex *)dm->data;
3537 
3538   PetscFunctionBegin;
3539   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3540   PetscAssertPointer(size, 3);
3541   PetscCall(PetscSectionGetDof(mesh->supportSection, p, size));
3542   PetscFunctionReturn(PETSC_SUCCESS);
3543 }
3544 
3545 /*@
3546   DMPlexSetSupportSize - Set the number of out-edges for this point in the DAG
3547 
3548   Not Collective
3549 
3550   Input Parameters:
3551 + dm   - The `DMPLEX`
3552 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3553 - size - The support size for point `p`
3554 
3555   Level: beginner
3556 
3557   Note:
3558   This should be called after `DMPlexSetChart()`.
3559 
3560 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetSupportSize()`, `DMPlexSetChart()`
3561 @*/
3562 PetscErrorCode DMPlexSetSupportSize(DM dm, PetscInt p, PetscInt size)
3563 {
3564   DM_Plex *mesh = (DM_Plex *)dm->data;
3565 
3566   PetscFunctionBegin;
3567   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3568   PetscCall(PetscSectionSetDof(mesh->supportSection, p, size));
3569   PetscFunctionReturn(PETSC_SUCCESS);
3570 }
3571 
3572 /*@C
3573   DMPlexGetSupport - Return the points on the out-edges for this point in the DAG
3574 
3575   Not Collective
3576 
3577   Input Parameters:
3578 + dm - The `DMPLEX`
3579 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3580 
3581   Output Parameter:
3582 . support - An array of points which are on the out-edges for point `p`
3583 
3584   Level: beginner
3585 
3586   Fortran Notes:
3587   You must also call `DMPlexRestoreSupport()` after you finish using the returned array.
3588   `DMPlexRestoreSupport()` is not needed/available in C.
3589 
3590 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSupportSize()`, `DMPlexSetSupport()`, `DMPlexGetCone()`, `DMPlexSetChart()`
3591 @*/
3592 PetscErrorCode DMPlexGetSupport(DM dm, PetscInt p, const PetscInt *support[])
3593 {
3594   DM_Plex *mesh = (DM_Plex *)dm->data;
3595   PetscInt off;
3596 
3597   PetscFunctionBegin;
3598   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3599   PetscAssertPointer(support, 3);
3600   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3601   *support = PetscSafePointerPlusOffset(mesh->supports, off);
3602   PetscFunctionReturn(PETSC_SUCCESS);
3603 }
3604 
3605 /*@
3606   DMPlexSetSupport - Set the points on the out-edges for this point in the DAG, that is the list of points that this point covers
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 - support - An array of points which are on the out-edges for point `p`
3614 
3615   Level: beginner
3616 
3617   Note:
3618   This should be called after all calls to `DMPlexSetSupportSize()` and `DMSetUp()`.
3619 
3620 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetCone()`, `DMPlexSetConeSize()`, `DMPlexCreate()`, `DMPlexGetSupport()`, `DMPlexSetChart()`, `DMPlexSetSupportSize()`, `DMSetUp()`
3621 @*/
3622 PetscErrorCode DMPlexSetSupport(DM dm, PetscInt p, const PetscInt support[])
3623 {
3624   DM_Plex *mesh = (DM_Plex *)dm->data;
3625   PetscInt pStart, pEnd;
3626   PetscInt dof, off, c;
3627 
3628   PetscFunctionBegin;
3629   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3630   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3631   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3632   if (dof) PetscAssertPointer(support, 3);
3633   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3634   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);
3635   for (c = 0; c < dof; ++c) {
3636     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);
3637     mesh->supports[off + c] = support[c];
3638   }
3639   PetscFunctionReturn(PETSC_SUCCESS);
3640 }
3641 
3642 /*@
3643   DMPlexInsertSupport - Insert a point into the out-edges for the point p in the DAG
3644 
3645   Not Collective
3646 
3647   Input Parameters:
3648 + dm           - The `DMPLEX`
3649 . p            - The point, which must lie in the chart set with `DMPlexSetChart()`
3650 . supportPos   - The local index in the cone where the point should be put
3651 - supportPoint - The mesh point to insert
3652 
3653   Level: beginner
3654 
3655 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3656 @*/
3657 PetscErrorCode DMPlexInsertSupport(DM dm, PetscInt p, PetscInt supportPos, PetscInt supportPoint)
3658 {
3659   DM_Plex *mesh = (DM_Plex *)dm->data;
3660   PetscInt pStart, pEnd;
3661   PetscInt dof, off;
3662 
3663   PetscFunctionBegin;
3664   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3665   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3666   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3667   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3668   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);
3669   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);
3670   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);
3671   mesh->supports[off + supportPos] = supportPoint;
3672   PetscFunctionReturn(PETSC_SUCCESS);
3673 }
3674 
3675 /* Converts an orientation o in the current numbering to the previous scheme used in Plex */
3676 PetscInt DMPolytopeConvertNewOrientation_Internal(DMPolytopeType ct, PetscInt o)
3677 {
3678   switch (ct) {
3679   case DM_POLYTOPE_SEGMENT:
3680     if (o == -1) return -2;
3681     break;
3682   case DM_POLYTOPE_TRIANGLE:
3683     if (o == -3) return -1;
3684     if (o == -2) return -3;
3685     if (o == -1) return -2;
3686     break;
3687   case DM_POLYTOPE_QUADRILATERAL:
3688     if (o == -4) return -2;
3689     if (o == -3) return -1;
3690     if (o == -2) return -4;
3691     if (o == -1) return -3;
3692     break;
3693   default:
3694     return o;
3695   }
3696   return o;
3697 }
3698 
3699 /* Converts an orientation o in the previous scheme used in Plex to the current numbering */
3700 PetscInt DMPolytopeConvertOldOrientation_Internal(DMPolytopeType ct, PetscInt o)
3701 {
3702   switch (ct) {
3703   case DM_POLYTOPE_SEGMENT:
3704     if ((o == -2) || (o == 1)) return -1;
3705     if (o == -1) return 0;
3706     break;
3707   case DM_POLYTOPE_TRIANGLE:
3708     if (o == -3) return -2;
3709     if (o == -2) return -1;
3710     if (o == -1) return -3;
3711     break;
3712   case DM_POLYTOPE_QUADRILATERAL:
3713     if (o == -4) return -2;
3714     if (o == -3) return -1;
3715     if (o == -2) return -4;
3716     if (o == -1) return -3;
3717     break;
3718   default:
3719     return o;
3720   }
3721   return o;
3722 }
3723 
3724 /* Takes in a mesh whose orientations are in the previous scheme and converts them all to the current numbering */
3725 PetscErrorCode DMPlexConvertOldOrientations_Internal(DM dm)
3726 {
3727   PetscInt pStart, pEnd, p;
3728 
3729   PetscFunctionBegin;
3730   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3731   for (p = pStart; p < pEnd; ++p) {
3732     const PetscInt *cone, *ornt;
3733     PetscInt        coneSize, c;
3734 
3735     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
3736     PetscCall(DMPlexGetCone(dm, p, &cone));
3737     PetscCall(DMPlexGetConeOrientation(dm, p, &ornt));
3738     for (c = 0; c < coneSize; ++c) {
3739       DMPolytopeType ct;
3740       const PetscInt o = ornt[c];
3741 
3742       PetscCall(DMPlexGetCellType(dm, cone[c], &ct));
3743       switch (ct) {
3744       case DM_POLYTOPE_SEGMENT:
3745         if ((o == -2) || (o == 1)) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3746         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, 0));
3747         break;
3748       case DM_POLYTOPE_TRIANGLE:
3749         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3750         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3751         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3752         break;
3753       case DM_POLYTOPE_QUADRILATERAL:
3754         if (o == -4) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3755         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3756         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -4));
3757         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3758         break;
3759       default:
3760         break;
3761       }
3762     }
3763   }
3764   PetscFunctionReturn(PETSC_SUCCESS);
3765 }
3766 
3767 static inline PetscErrorCode DMPlexGetTransitiveClosure_Hot_Private(DM dm, PetscInt p, PetscBool useCone, PetscInt *size, const PetscInt *arr[], const PetscInt *ornt[])
3768 {
3769   DM_Plex *mesh = (DM_Plex *)dm->data;
3770 
3771   PetscFunctionBeginHot;
3772   if (PetscDefined(USE_DEBUG) || mesh->tr) {
3773     if (useCone) {
3774       PetscCall(DMPlexGetConeSize(dm, p, size));
3775       PetscCall(DMPlexGetOrientedCone(dm, p, arr, ornt));
3776     } else {
3777       PetscCall(DMPlexGetSupportSize(dm, p, size));
3778       PetscCall(DMPlexGetSupport(dm, p, arr));
3779     }
3780   } else {
3781     if (useCone) {
3782       const PetscSection s   = mesh->coneSection;
3783       const PetscInt     ps  = p - s->pStart;
3784       const PetscInt     off = s->atlasOff[ps];
3785 
3786       *size = s->atlasDof[ps];
3787       *arr  = mesh->cones + off;
3788       *ornt = mesh->coneOrientations + off;
3789     } else {
3790       const PetscSection s   = mesh->supportSection;
3791       const PetscInt     ps  = p - s->pStart;
3792       const PetscInt     off = s->atlasOff[ps];
3793 
3794       *size = s->atlasDof[ps];
3795       *arr  = mesh->supports + off;
3796     }
3797   }
3798   PetscFunctionReturn(PETSC_SUCCESS);
3799 }
3800 
3801 static inline PetscErrorCode DMPlexRestoreTransitiveClosure_Hot_Private(DM dm, PetscInt p, PetscBool useCone, PetscInt *size, const PetscInt *arr[], const PetscInt *ornt[])
3802 {
3803   DM_Plex *mesh = (DM_Plex *)dm->data;
3804 
3805   PetscFunctionBeginHot;
3806   if (PetscDefined(USE_DEBUG) || mesh->tr) {
3807     if (useCone) PetscCall(DMPlexRestoreOrientedCone(dm, p, arr, ornt));
3808   }
3809   PetscFunctionReturn(PETSC_SUCCESS);
3810 }
3811 
3812 static PetscErrorCode DMPlexGetTransitiveClosure_Depth1_Private(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3813 {
3814   DMPolytopeType  ct = DM_POLYTOPE_UNKNOWN;
3815   PetscInt       *closure;
3816   const PetscInt *tmp = NULL, *tmpO = NULL;
3817   PetscInt        off = 0, tmpSize, t;
3818 
3819   PetscFunctionBeginHot;
3820   if (ornt) {
3821     PetscCall(DMPlexGetCellType(dm, p, &ct));
3822     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;
3823   }
3824   if (*points) {
3825     closure = *points;
3826   } else {
3827     PetscInt maxConeSize, maxSupportSize;
3828     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3829     PetscCall(DMGetWorkArray(dm, 2 * (PetscMax(maxConeSize, maxSupportSize) + 1), MPIU_INT, &closure));
3830   }
3831   PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, p, useCone, &tmpSize, &tmp, &tmpO));
3832   if (ct == DM_POLYTOPE_UNKNOWN) {
3833     closure[off++] = p;
3834     closure[off++] = 0;
3835     for (t = 0; t < tmpSize; ++t) {
3836       closure[off++] = tmp[t];
3837       closure[off++] = tmpO ? tmpO[t] : 0;
3838     }
3839   } else {
3840     const PetscInt *arr = DMPolytopeTypeGetArrangement(ct, ornt);
3841 
3842     /* We assume that cells with a valid type have faces with a valid type */
3843     closure[off++] = p;
3844     closure[off++] = ornt;
3845     for (t = 0; t < tmpSize; ++t) {
3846       DMPolytopeType ft;
3847 
3848       PetscCall(DMPlexGetCellType(dm, tmp[t], &ft));
3849       closure[off++] = tmp[arr[t]];
3850       closure[off++] = tmpO ? DMPolytopeTypeComposeOrientation(ft, ornt, tmpO[t]) : 0;
3851     }
3852   }
3853   PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, p, useCone, &tmpSize, &tmp, &tmpO));
3854   if (numPoints) *numPoints = tmpSize + 1;
3855   if (points) *points = closure;
3856   PetscFunctionReturn(PETSC_SUCCESS);
3857 }
3858 
3859 /* We need a special tensor version because we want to allow duplicate points in the endcaps for hybrid cells */
3860 static PetscErrorCode DMPlexTransitiveClosure_Tensor_Internal(DM dm, PetscInt point, DMPolytopeType ct, PetscInt o, PetscBool useCone, PetscInt *numPoints, PetscInt **points)
3861 {
3862   const PetscInt *arr = DMPolytopeTypeGetArrangement(ct, o);
3863   const PetscInt *cone, *ornt;
3864   PetscInt       *pts, *closure = NULL;
3865   DMPolytopeType  ft;
3866   PetscInt        maxConeSize, maxSupportSize, coneSeries, supportSeries, maxSize;
3867   PetscInt        dim, coneSize, c, d, clSize, cl;
3868 
3869   PetscFunctionBeginHot;
3870   PetscCall(DMGetDimension(dm, &dim));
3871   PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, point, PETSC_TRUE, &coneSize, &cone, &ornt));
3872   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3873   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, dim + 1) - 1) / (maxConeSize - 1)) : dim + 1;
3874   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, dim + 1) - 1) / (maxSupportSize - 1)) : dim + 1;
3875   maxSize       = PetscMax(coneSeries, supportSeries);
3876   if (*points) {
3877     pts = *points;
3878   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &pts));
3879   c        = 0;
3880   pts[c++] = point;
3881   pts[c++] = o;
3882   PetscCall(DMPlexGetCellType(dm, cone[arr[0 * 2 + 0]], &ft));
3883   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[0 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[0 * 2 + 1], ornt[0]), useCone, &clSize, &closure));
3884   for (cl = 0; cl < clSize * 2; cl += 2) {
3885     pts[c++] = closure[cl];
3886     pts[c++] = closure[cl + 1];
3887   }
3888   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[1 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[1 * 2 + 1], ornt[1]), useCone, &clSize, &closure));
3889   for (cl = 0; cl < clSize * 2; cl += 2) {
3890     pts[c++] = closure[cl];
3891     pts[c++] = closure[cl + 1];
3892   }
3893   PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[0], useCone, &clSize, &closure));
3894   for (d = 2; d < coneSize; ++d) {
3895     PetscCall(DMPlexGetCellType(dm, cone[arr[d * 2 + 0]], &ft));
3896     pts[c++] = cone[arr[d * 2 + 0]];
3897     pts[c++] = DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]);
3898   }
3899   PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, point, PETSC_TRUE, &coneSize, &cone, &ornt));
3900   if (dim >= 3) {
3901     for (d = 2; d < coneSize; ++d) {
3902       const PetscInt  fpoint = cone[arr[d * 2 + 0]];
3903       const PetscInt *fcone, *fornt;
3904       PetscInt        fconeSize, fc, i;
3905 
3906       PetscCall(DMPlexGetCellType(dm, fpoint, &ft));
3907       const PetscInt *farr = DMPolytopeTypeGetArrangement(ft, DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]));
3908       PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, fpoint, PETSC_TRUE, &fconeSize, &fcone, &fornt));
3909       for (fc = 0; fc < fconeSize; ++fc) {
3910         const PetscInt cp = fcone[farr[fc * 2 + 0]];
3911         const PetscInt co = farr[fc * 2 + 1];
3912 
3913         for (i = 0; i < c; i += 2)
3914           if (pts[i] == cp) break;
3915         if (i == c) {
3916           PetscCall(DMPlexGetCellType(dm, cp, &ft));
3917           pts[c++] = cp;
3918           pts[c++] = DMPolytopeTypeComposeOrientation(ft, co, fornt[farr[fc * 2 + 0]]);
3919         }
3920       }
3921       PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, fpoint, PETSC_TRUE, &fconeSize, &fcone, &fornt));
3922     }
3923   }
3924   *numPoints = c / 2;
3925   *points    = pts;
3926   PetscFunctionReturn(PETSC_SUCCESS);
3927 }
3928 
3929 PetscErrorCode DMPlexGetTransitiveClosure_Internal(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3930 {
3931   DMPolytopeType ct;
3932   PetscInt      *closure, *fifo;
3933   PetscInt       closureSize = 0, fifoStart = 0, fifoSize = 0;
3934   PetscInt       maxConeSize, maxSupportSize, coneSeries, supportSeries;
3935   PetscInt       depth, maxSize;
3936 
3937   PetscFunctionBeginHot;
3938   PetscCall(DMPlexGetDepth(dm, &depth));
3939   if (depth == 1) {
3940     PetscCall(DMPlexGetTransitiveClosure_Depth1_Private(dm, p, ornt, useCone, numPoints, points));
3941     PetscFunctionReturn(PETSC_SUCCESS);
3942   }
3943   PetscCall(DMPlexGetCellType(dm, p, &ct));
3944   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;
3945   if (DMPolytopeTypeIsHybrid(ct) && ct != DM_POLYTOPE_POINT_PRISM_TENSOR) {
3946     PetscCall(DMPlexTransitiveClosure_Tensor_Internal(dm, p, ct, ornt, useCone, numPoints, points));
3947     PetscFunctionReturn(PETSC_SUCCESS);
3948   }
3949   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3950   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, depth + 1) - 1) / (maxConeSize - 1)) : depth + 1;
3951   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, depth + 1) - 1) / (maxSupportSize - 1)) : depth + 1;
3952   maxSize       = PetscMax(coneSeries, supportSeries);
3953   PetscCall(DMGetWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
3954   if (*points) {
3955     closure = *points;
3956   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &closure));
3957   closure[closureSize++] = p;
3958   closure[closureSize++] = ornt;
3959   fifo[fifoSize++]       = p;
3960   fifo[fifoSize++]       = ornt;
3961   fifo[fifoSize++]       = ct;
3962   /* Should kick out early when depth is reached, rather than checking all vertices for empty cones */
3963   while (fifoSize - fifoStart) {
3964     const PetscInt       q    = fifo[fifoStart++];
3965     const PetscInt       o    = fifo[fifoStart++];
3966     const DMPolytopeType qt   = (DMPolytopeType)fifo[fifoStart++];
3967     const PetscInt      *qarr = DMPolytopeTypeGetArrangement(qt, o);
3968     const PetscInt      *tmp, *tmpO = NULL;
3969     PetscInt             tmpSize, t;
3970 
3971     if (PetscDefined(USE_DEBUG)) {
3972       PetscInt nO = DMPolytopeTypeGetNumArrangements(qt) / 2;
3973       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);
3974     }
3975     PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, q, useCone, &tmpSize, &tmp, &tmpO));
3976     for (t = 0; t < tmpSize; ++t) {
3977       const PetscInt ip = useCone && qarr ? qarr[t * 2] : t;
3978       const PetscInt io = useCone && qarr ? qarr[t * 2 + 1] : 0;
3979       const PetscInt cp = tmp[ip];
3980       PetscCall(DMPlexGetCellType(dm, cp, &ct));
3981       const PetscInt co = tmpO ? DMPolytopeTypeComposeOrientation(ct, io, tmpO[ip]) : 0;
3982       PetscInt       c;
3983 
3984       /* Check for duplicate */
3985       for (c = 0; c < closureSize; c += 2) {
3986         if (closure[c] == cp) break;
3987       }
3988       if (c == closureSize) {
3989         closure[closureSize++] = cp;
3990         closure[closureSize++] = co;
3991         fifo[fifoSize++]       = cp;
3992         fifo[fifoSize++]       = co;
3993         fifo[fifoSize++]       = ct;
3994       }
3995     }
3996     PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, q, useCone, &tmpSize, &tmp, &tmpO));
3997   }
3998   PetscCall(DMRestoreWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
3999   if (numPoints) *numPoints = closureSize / 2;
4000   if (points) *points = closure;
4001   PetscFunctionReturn(PETSC_SUCCESS);
4002 }
4003 
4004 /*@C
4005   DMPlexGetTransitiveClosure - Return the points on the transitive closure of the in-edges or out-edges for this point in the DAG
4006 
4007   Not Collective
4008 
4009   Input Parameters:
4010 + dm      - The `DMPLEX`
4011 . p       - The mesh point
4012 - useCone - `PETSC_TRUE` for the closure, otherwise return the star
4013 
4014   Input/Output Parameter:
4015 . points - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...];
4016            if `NULL` on input, internal storage will be returned, otherwise the provided array is used
4017 
4018   Output Parameter:
4019 . numPoints - The number of points in the closure, so points[] is of size 2*`numPoints`
4020 
4021   Level: beginner
4022 
4023   Note:
4024   If using internal storage (points is `NULL` on input), each call overwrites the last output.
4025 
4026   Fortran Notes:
4027   The `numPoints` argument is not present in the Fortran binding since it is internal to the array.
4028 
4029 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
4030 @*/
4031 PetscErrorCode DMPlexGetTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
4032 {
4033   PetscFunctionBeginHot;
4034   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4035   if (numPoints) PetscAssertPointer(numPoints, 4);
4036   if (points) PetscAssertPointer(points, 5);
4037   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, p, 0, useCone, numPoints, points));
4038   PetscFunctionReturn(PETSC_SUCCESS);
4039 }
4040 
4041 /*@C
4042   DMPlexRestoreTransitiveClosure - Restore the array of points on the transitive closure of the in-edges or out-edges for this point in the DAG
4043 
4044   Not Collective
4045 
4046   Input Parameters:
4047 + dm        - The `DMPLEX`
4048 . p         - The mesh point
4049 . useCone   - `PETSC_TRUE` for the closure, otherwise return the star
4050 . numPoints - The number of points in the closure, so points[] is of size 2*`numPoints`
4051 - points    - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...]
4052 
4053   Level: beginner
4054 
4055   Note:
4056   If not using internal storage (points is not `NULL` on input), this call is unnecessary
4057 
4058 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
4059 @*/
4060 PetscErrorCode DMPlexRestoreTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
4061 {
4062   PetscFunctionBeginHot;
4063   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4064   if (numPoints) *numPoints = 0;
4065   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, points));
4066   PetscFunctionReturn(PETSC_SUCCESS);
4067 }
4068 
4069 /*@
4070   DMPlexGetMaxSizes - Return the maximum number of in-edges (cone) and out-edges (support) for any point in the DAG
4071 
4072   Not Collective
4073 
4074   Input Parameter:
4075 . dm - The `DMPLEX`
4076 
4077   Output Parameters:
4078 + maxConeSize    - The maximum number of in-edges
4079 - maxSupportSize - The maximum number of out-edges
4080 
4081   Level: beginner
4082 
4083 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
4084 @*/
4085 PetscErrorCode DMPlexGetMaxSizes(DM dm, PetscInt *maxConeSize, PetscInt *maxSupportSize)
4086 {
4087   DM_Plex *mesh = (DM_Plex *)dm->data;
4088 
4089   PetscFunctionBegin;
4090   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4091   if (maxConeSize) PetscCall(PetscSectionGetMaxDof(mesh->coneSection, maxConeSize));
4092   if (maxSupportSize) PetscCall(PetscSectionGetMaxDof(mesh->supportSection, maxSupportSize));
4093   PetscFunctionReturn(PETSC_SUCCESS);
4094 }
4095 
4096 PetscErrorCode DMSetUp_Plex(DM dm)
4097 {
4098   DM_Plex *mesh = (DM_Plex *)dm->data;
4099   PetscInt size, maxSupportSize;
4100 
4101   PetscFunctionBegin;
4102   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4103   PetscCall(PetscSectionSetUp(mesh->coneSection));
4104   PetscCall(PetscSectionGetStorageSize(mesh->coneSection, &size));
4105   PetscCall(PetscMalloc1(size, &mesh->cones));
4106   PetscCall(PetscCalloc1(size, &mesh->coneOrientations));
4107   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
4108   if (maxSupportSize) {
4109     PetscCall(PetscSectionSetUp(mesh->supportSection));
4110     PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &size));
4111     PetscCall(PetscMalloc1(size, &mesh->supports));
4112   }
4113   PetscFunctionReturn(PETSC_SUCCESS);
4114 }
4115 
4116 PetscErrorCode DMCreateSubDM_Plex(DM dm, PetscInt numFields, const PetscInt fields[], IS *is, DM *subdm)
4117 {
4118   PetscFunctionBegin;
4119   if (subdm) PetscCall(DMClone(dm, subdm));
4120   PetscCall(DMCreateSectionSubDM(dm, numFields, fields, is, subdm));
4121   if (subdm) (*subdm)->useNatural = dm->useNatural;
4122   if (dm->useNatural && dm->sfMigration) {
4123     PetscSF sfNatural;
4124 
4125     (*subdm)->sfMigration = dm->sfMigration;
4126     PetscCall(PetscObjectReference((PetscObject)dm->sfMigration));
4127     PetscCall(DMPlexCreateGlobalToNaturalSF(*subdm, NULL, (*subdm)->sfMigration, &sfNatural));
4128     (*subdm)->sfNatural = sfNatural;
4129   }
4130   PetscFunctionReturn(PETSC_SUCCESS);
4131 }
4132 
4133 PetscErrorCode DMCreateSuperDM_Plex(DM dms[], PetscInt len, IS **is, DM *superdm)
4134 {
4135   PetscInt i = 0;
4136 
4137   PetscFunctionBegin;
4138   PetscCall(DMClone(dms[0], superdm));
4139   PetscCall(DMCreateSectionSuperDM(dms, len, is, superdm));
4140   (*superdm)->useNatural = PETSC_FALSE;
4141   for (i = 0; i < len; i++) {
4142     if (dms[i]->useNatural && dms[i]->sfMigration) {
4143       PetscSF sfNatural;
4144 
4145       (*superdm)->sfMigration = dms[i]->sfMigration;
4146       PetscCall(PetscObjectReference((PetscObject)dms[i]->sfMigration));
4147       (*superdm)->useNatural = PETSC_TRUE;
4148       PetscCall(DMPlexCreateGlobalToNaturalSF(*superdm, NULL, (*superdm)->sfMigration, &sfNatural));
4149       (*superdm)->sfNatural = sfNatural;
4150       break;
4151     }
4152   }
4153   PetscFunctionReturn(PETSC_SUCCESS);
4154 }
4155 
4156 /*@
4157   DMPlexSymmetrize - Create support (out-edge) information from cone (in-edge) information
4158 
4159   Not Collective
4160 
4161   Input Parameter:
4162 . dm - The `DMPLEX`
4163 
4164   Level: beginner
4165 
4166   Note:
4167   This should be called after all calls to `DMPlexSetCone()`
4168 
4169 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMPlexSetCone()`
4170 @*/
4171 PetscErrorCode DMPlexSymmetrize(DM dm)
4172 {
4173   DM_Plex  *mesh = (DM_Plex *)dm->data;
4174   PetscInt *offsets;
4175   PetscInt  supportSize;
4176   PetscInt  pStart, pEnd, p;
4177 
4178   PetscFunctionBegin;
4179   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4180   PetscCheck(!mesh->supports, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONGSTATE, "Supports were already setup in this DMPlex");
4181   PetscCall(PetscLogEventBegin(DMPLEX_Symmetrize, dm, 0, 0, 0));
4182   /* Calculate support sizes */
4183   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4184   for (p = pStart; p < pEnd; ++p) {
4185     PetscInt dof, off, c;
4186 
4187     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
4188     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
4189     for (c = off; c < off + dof; ++c) PetscCall(PetscSectionAddDof(mesh->supportSection, mesh->cones[c], 1));
4190   }
4191   PetscCall(PetscSectionSetUp(mesh->supportSection));
4192   /* Calculate supports */
4193   PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &supportSize));
4194   PetscCall(PetscMalloc1(supportSize, &mesh->supports));
4195   PetscCall(PetscCalloc1(pEnd - pStart, &offsets));
4196   for (p = pStart; p < pEnd; ++p) {
4197     PetscInt dof, off, c;
4198 
4199     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
4200     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
4201     for (c = off; c < off + dof; ++c) {
4202       const PetscInt q = mesh->cones[c];
4203       PetscInt       offS;
4204 
4205       PetscCall(PetscSectionGetOffset(mesh->supportSection, q, &offS));
4206 
4207       mesh->supports[offS + offsets[q]] = p;
4208       ++offsets[q];
4209     }
4210   }
4211   PetscCall(PetscFree(offsets));
4212   PetscCall(PetscLogEventEnd(DMPLEX_Symmetrize, dm, 0, 0, 0));
4213   PetscFunctionReturn(PETSC_SUCCESS);
4214 }
4215 
4216 static PetscErrorCode DMPlexCreateDepthStratum(DM dm, DMLabel label, PetscInt depth, PetscInt pStart, PetscInt pEnd)
4217 {
4218   IS stratumIS;
4219 
4220   PetscFunctionBegin;
4221   if (pStart >= pEnd) PetscFunctionReturn(PETSC_SUCCESS);
4222   if (PetscDefined(USE_DEBUG)) {
4223     PetscInt  qStart, qEnd, numLevels, level;
4224     PetscBool overlap = PETSC_FALSE;
4225     PetscCall(DMLabelGetNumValues(label, &numLevels));
4226     for (level = 0; level < numLevels; level++) {
4227       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4228       if ((pStart >= qStart && pStart < qEnd) || (pEnd > qStart && pEnd <= qEnd)) {
4229         overlap = PETSC_TRUE;
4230         break;
4231       }
4232     }
4233     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);
4234   }
4235   PetscCall(ISCreateStride(PETSC_COMM_SELF, pEnd - pStart, pStart, 1, &stratumIS));
4236   PetscCall(DMLabelSetStratumIS(label, depth, stratumIS));
4237   PetscCall(ISDestroy(&stratumIS));
4238   PetscFunctionReturn(PETSC_SUCCESS);
4239 }
4240 
4241 static PetscErrorCode DMPlexStratify_CellType_Private(DM dm, DMLabel label)
4242 {
4243   PetscInt *pMin, *pMax;
4244   PetscInt  pStart, pEnd;
4245   PetscInt  dmin = PETSC_MAX_INT, dmax = PETSC_MIN_INT;
4246 
4247   PetscFunctionBegin;
4248   {
4249     DMLabel label2;
4250 
4251     PetscCall(DMPlexGetCellTypeLabel(dm, &label2));
4252     PetscCall(PetscObjectViewFromOptions((PetscObject)label2, NULL, "-ct_view"));
4253   }
4254   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4255   for (PetscInt p = pStart; p < pEnd; ++p) {
4256     DMPolytopeType ct;
4257 
4258     PetscCall(DMPlexGetCellType(dm, p, &ct));
4259     dmin = PetscMin(DMPolytopeTypeGetDim(ct), dmin);
4260     dmax = PetscMax(DMPolytopeTypeGetDim(ct), dmax);
4261   }
4262   PetscCall(PetscMalloc2(dmax + 1, &pMin, dmax + 1, &pMax));
4263   for (PetscInt d = dmin; d <= dmax; ++d) {
4264     pMin[d] = PETSC_MAX_INT;
4265     pMax[d] = PETSC_MIN_INT;
4266   }
4267   for (PetscInt p = pStart; p < pEnd; ++p) {
4268     DMPolytopeType ct;
4269     PetscInt       d;
4270 
4271     PetscCall(DMPlexGetCellType(dm, p, &ct));
4272     d       = DMPolytopeTypeGetDim(ct);
4273     pMin[d] = PetscMin(p, pMin[d]);
4274     pMax[d] = PetscMax(p, pMax[d]);
4275   }
4276   for (PetscInt d = dmin; d <= dmax; ++d) {
4277     if (pMin[d] > pMax[d]) continue;
4278     PetscCall(DMPlexCreateDepthStratum(dm, label, d, pMin[d], pMax[d] + 1));
4279   }
4280   PetscCall(PetscFree2(pMin, pMax));
4281   PetscFunctionReturn(PETSC_SUCCESS);
4282 }
4283 
4284 static PetscErrorCode DMPlexStratify_Topological_Private(DM dm, DMLabel label)
4285 {
4286   PetscInt pStart, pEnd;
4287   PetscInt numRoots = 0, numLeaves = 0;
4288 
4289   PetscFunctionBegin;
4290   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4291   {
4292     /* Initialize roots and count leaves */
4293     PetscInt sMin = PETSC_MAX_INT;
4294     PetscInt sMax = PETSC_MIN_INT;
4295     PetscInt coneSize, supportSize;
4296 
4297     for (PetscInt p = pStart; p < pEnd; ++p) {
4298       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4299       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
4300       if (!coneSize && supportSize) {
4301         sMin = PetscMin(p, sMin);
4302         sMax = PetscMax(p, sMax);
4303         ++numRoots;
4304       } else if (!supportSize && coneSize) {
4305         ++numLeaves;
4306       } else if (!supportSize && !coneSize) {
4307         /* Isolated points */
4308         sMin = PetscMin(p, sMin);
4309         sMax = PetscMax(p, sMax);
4310       }
4311     }
4312     PetscCall(DMPlexCreateDepthStratum(dm, label, 0, sMin, sMax + 1));
4313   }
4314 
4315   if (numRoots + numLeaves == (pEnd - pStart)) {
4316     PetscInt sMin = PETSC_MAX_INT;
4317     PetscInt sMax = PETSC_MIN_INT;
4318     PetscInt coneSize, supportSize;
4319 
4320     for (PetscInt p = pStart; p < pEnd; ++p) {
4321       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4322       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
4323       if (!supportSize && coneSize) {
4324         sMin = PetscMin(p, sMin);
4325         sMax = PetscMax(p, sMax);
4326       }
4327     }
4328     PetscCall(DMPlexCreateDepthStratum(dm, label, 1, sMin, sMax + 1));
4329   } else {
4330     PetscInt level = 0;
4331     PetscInt qStart, qEnd;
4332 
4333     PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4334     while (qEnd > qStart) {
4335       PetscInt sMin = PETSC_MAX_INT;
4336       PetscInt sMax = PETSC_MIN_INT;
4337 
4338       for (PetscInt q = qStart; q < qEnd; ++q) {
4339         const PetscInt *support;
4340         PetscInt        supportSize;
4341 
4342         PetscCall(DMPlexGetSupportSize(dm, q, &supportSize));
4343         PetscCall(DMPlexGetSupport(dm, q, &support));
4344         for (PetscInt s = 0; s < supportSize; ++s) {
4345           sMin = PetscMin(support[s], sMin);
4346           sMax = PetscMax(support[s], sMax);
4347         }
4348       }
4349       PetscCall(DMLabelGetNumValues(label, &level));
4350       PetscCall(DMPlexCreateDepthStratum(dm, label, level, sMin, sMax + 1));
4351       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4352     }
4353   }
4354   PetscFunctionReturn(PETSC_SUCCESS);
4355 }
4356 
4357 /*@
4358   DMPlexStratify - Computes the strata for all points in the `DMPLEX`
4359 
4360   Collective
4361 
4362   Input Parameter:
4363 . dm - The `DMPLEX`
4364 
4365   Level: beginner
4366 
4367   Notes:
4368   The strata group all points of the same grade, and this function calculates the strata. This
4369   grade can be seen as the height (or depth) of the point in the DAG.
4370 
4371   The DAG for most topologies is a graded poset (https://en.wikipedia.org/wiki/Graded_poset), and
4372   can be illustrated by a Hasse Diagram (https://en.wikipedia.org/wiki/Hasse_diagram).
4373   Concretely, `DMPlexStratify()` creates a new label named "depth" containing the depth in the DAG of each point. For cell-vertex
4374   meshes, vertices are depth 0 and cells are depth 1. For fully interpolated meshes, depth 0 for vertices, 1 for edges, and so on
4375   until cells have depth equal to the dimension of the mesh. The depth label can be accessed through `DMPlexGetDepthLabel()` or `DMPlexGetDepthStratum()`, or
4376   manually via `DMGetLabel()`.  The height is defined implicitly by height = maxDimension - depth, and can be accessed
4377   via `DMPlexGetHeightStratum()`.  For example, cells have height 0 and faces have height 1.
4378 
4379   The depth of a point is calculated by executing a breadth-first search (BFS) on the DAG. This could produce surprising results
4380   if run on a partially interpolated mesh, meaning one that had some edges and faces, but not others. For example, suppose that
4381   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
4382   to interpolate only that one (e0), so that
4383 .vb
4384   cone(c0) = {e0, v2}
4385   cone(e0) = {v0, v1}
4386 .ve
4387   If `DMPlexStratify()` is run on this mesh, it will give depths
4388 .vb
4389    depth 0 = {v0, v1, v2}
4390    depth 1 = {e0, c0}
4391 .ve
4392   where the triangle has been given depth 1, instead of 2, because it is reachable from vertex v2.
4393 
4394   `DMPlexStratify()` should be called after all calls to `DMPlexSymmetrize()`
4395 
4396 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexComputeCellTypes()`
4397 @*/
4398 PetscErrorCode DMPlexStratify(DM dm)
4399 {
4400   DM_Plex  *mesh = (DM_Plex *)dm->data;
4401   DMLabel   label;
4402   PetscBool flg = PETSC_FALSE;
4403 
4404   PetscFunctionBegin;
4405   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4406   PetscCall(PetscLogEventBegin(DMPLEX_Stratify, dm, 0, 0, 0));
4407 
4408   // Create depth label
4409   PetscCall(DMCreateLabel(dm, "depth"));
4410   PetscCall(DMPlexGetDepthLabel(dm, &label));
4411 
4412   PetscCall(PetscOptionsGetBool(NULL, dm->hdr.prefix, "-dm_plex_stratify_celltype", &flg, NULL));
4413   if (flg) PetscCall(DMPlexStratify_CellType_Private(dm, label));
4414   else PetscCall(DMPlexStratify_Topological_Private(dm, label));
4415 
4416   { /* just in case there is an empty process */
4417     PetscInt numValues, maxValues = 0, v;
4418 
4419     PetscCall(DMLabelGetNumValues(label, &numValues));
4420     PetscCall(MPIU_Allreduce(&numValues, &maxValues, 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
4421     for (v = numValues; v < maxValues; v++) PetscCall(DMLabelAddStratum(label, v));
4422   }
4423   PetscCall(PetscObjectStateGet((PetscObject)label, &mesh->depthState));
4424   PetscCall(PetscLogEventEnd(DMPLEX_Stratify, dm, 0, 0, 0));
4425   PetscFunctionReturn(PETSC_SUCCESS);
4426 }
4427 
4428 PetscErrorCode DMPlexComputeCellType_Internal(DM dm, PetscInt p, PetscInt pdepth, DMPolytopeType *pt)
4429 {
4430   DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4431   PetscInt       dim, depth, pheight, coneSize;
4432 
4433   PetscFunctionBeginHot;
4434   PetscCall(DMGetDimension(dm, &dim));
4435   PetscCall(DMPlexGetDepth(dm, &depth));
4436   PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4437   pheight = depth - pdepth;
4438   if (depth <= 1) {
4439     switch (pdepth) {
4440     case 0:
4441       ct = DM_POLYTOPE_POINT;
4442       break;
4443     case 1:
4444       switch (coneSize) {
4445       case 2:
4446         ct = DM_POLYTOPE_SEGMENT;
4447         break;
4448       case 3:
4449         ct = DM_POLYTOPE_TRIANGLE;
4450         break;
4451       case 4:
4452         switch (dim) {
4453         case 2:
4454           ct = DM_POLYTOPE_QUADRILATERAL;
4455           break;
4456         case 3:
4457           ct = DM_POLYTOPE_TETRAHEDRON;
4458           break;
4459         default:
4460           break;
4461         }
4462         break;
4463       case 5:
4464         ct = DM_POLYTOPE_PYRAMID;
4465         break;
4466       case 6:
4467         ct = DM_POLYTOPE_TRI_PRISM_TENSOR;
4468         break;
4469       case 8:
4470         ct = DM_POLYTOPE_HEXAHEDRON;
4471         break;
4472       default:
4473         break;
4474       }
4475     }
4476   } else {
4477     if (pdepth == 0) {
4478       ct = DM_POLYTOPE_POINT;
4479     } else if (pheight == 0) {
4480       switch (dim) {
4481       case 1:
4482         switch (coneSize) {
4483         case 2:
4484           ct = DM_POLYTOPE_SEGMENT;
4485           break;
4486         default:
4487           break;
4488         }
4489         break;
4490       case 2:
4491         switch (coneSize) {
4492         case 3:
4493           ct = DM_POLYTOPE_TRIANGLE;
4494           break;
4495         case 4:
4496           ct = DM_POLYTOPE_QUADRILATERAL;
4497           break;
4498         default:
4499           break;
4500         }
4501         break;
4502       case 3:
4503         switch (coneSize) {
4504         case 4:
4505           ct = DM_POLYTOPE_TETRAHEDRON;
4506           break;
4507         case 5: {
4508           const PetscInt *cone;
4509           PetscInt        faceConeSize;
4510 
4511           PetscCall(DMPlexGetCone(dm, p, &cone));
4512           PetscCall(DMPlexGetConeSize(dm, cone[0], &faceConeSize));
4513           switch (faceConeSize) {
4514           case 3:
4515             ct = DM_POLYTOPE_TRI_PRISM_TENSOR;
4516             break;
4517           case 4:
4518             ct = DM_POLYTOPE_PYRAMID;
4519             break;
4520           }
4521         } break;
4522         case 6:
4523           ct = DM_POLYTOPE_HEXAHEDRON;
4524           break;
4525         default:
4526           break;
4527         }
4528         break;
4529       default:
4530         break;
4531       }
4532     } else if (pheight > 0) {
4533       switch (coneSize) {
4534       case 2:
4535         ct = DM_POLYTOPE_SEGMENT;
4536         break;
4537       case 3:
4538         ct = DM_POLYTOPE_TRIANGLE;
4539         break;
4540       case 4:
4541         ct = DM_POLYTOPE_QUADRILATERAL;
4542         break;
4543       default:
4544         break;
4545       }
4546     }
4547   }
4548   *pt = ct;
4549   PetscFunctionReturn(PETSC_SUCCESS);
4550 }
4551 
4552 /*@
4553   DMPlexComputeCellTypes - Infer the polytope type of every cell using its dimension and cone size.
4554 
4555   Collective
4556 
4557   Input Parameter:
4558 . dm - The `DMPLEX`
4559 
4560   Level: developer
4561 
4562   Note:
4563   This function is normally called automatically when a cell type is requested. It creates an
4564   internal `DMLabel` named "celltype" which can be directly accessed using `DMGetLabel()`. A user may disable
4565   automatic creation by creating the label manually, using `DMCreateLabel`(dm, "celltype").
4566 
4567   `DMPlexComputeCellTypes()` should be called after all calls to `DMPlexSymmetrize()` and `DMPlexStratify()`
4568 
4569 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexStratify()`, `DMGetLabel()`, `DMCreateLabel()`
4570 @*/
4571 PetscErrorCode DMPlexComputeCellTypes(DM dm)
4572 {
4573   DM_Plex *mesh;
4574   DMLabel  ctLabel;
4575   PetscInt pStart, pEnd, p;
4576 
4577   PetscFunctionBegin;
4578   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4579   mesh = (DM_Plex *)dm->data;
4580   PetscCall(DMCreateLabel(dm, "celltype"));
4581   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
4582   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4583   PetscCall(PetscFree(mesh->cellTypes));
4584   PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
4585   for (p = pStart; p < pEnd; ++p) {
4586     DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4587     PetscInt       pdepth;
4588 
4589     PetscCall(DMPlexGetPointDepth(dm, p, &pdepth));
4590     PetscCall(DMPlexComputeCellType_Internal(dm, p, pdepth, &ct));
4591     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);
4592     PetscCall(DMLabelSetValue(ctLabel, p, ct));
4593     mesh->cellTypes[p - pStart].value_as_uint8 = ct;
4594   }
4595   PetscCall(PetscObjectStateGet((PetscObject)ctLabel, &mesh->celltypeState));
4596   PetscCall(PetscObjectViewFromOptions((PetscObject)ctLabel, NULL, "-dm_plex_celltypes_view"));
4597   PetscFunctionReturn(PETSC_SUCCESS);
4598 }
4599 
4600 /*@C
4601   DMPlexGetJoin - Get an array for the join of the set of points
4602 
4603   Not Collective
4604 
4605   Input Parameters:
4606 + dm        - The `DMPLEX` object
4607 . numPoints - The number of input points for the join
4608 - points    - The input points
4609 
4610   Output Parameters:
4611 + numCoveredPoints - The number of points in the join
4612 - coveredPoints    - The points in the join
4613 
4614   Level: intermediate
4615 
4616   Note:
4617   Currently, this is restricted to a single level join
4618 
4619   Fortran Notes:
4620   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4621 
4622 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4623 @*/
4624 PetscErrorCode DMPlexGetJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4625 {
4626   DM_Plex  *mesh = (DM_Plex *)dm->data;
4627   PetscInt *join[2];
4628   PetscInt  joinSize, i = 0;
4629   PetscInt  dof, off, p, c, m;
4630   PetscInt  maxSupportSize;
4631 
4632   PetscFunctionBegin;
4633   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4634   PetscAssertPointer(points, 3);
4635   PetscAssertPointer(numCoveredPoints, 4);
4636   PetscAssertPointer(coveredPoints, 5);
4637   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
4638   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[0]));
4639   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[1]));
4640   /* Copy in support of first point */
4641   PetscCall(PetscSectionGetDof(mesh->supportSection, points[0], &dof));
4642   PetscCall(PetscSectionGetOffset(mesh->supportSection, points[0], &off));
4643   for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = mesh->supports[off + joinSize];
4644   /* Check each successive support */
4645   for (p = 1; p < numPoints; ++p) {
4646     PetscInt newJoinSize = 0;
4647 
4648     PetscCall(PetscSectionGetDof(mesh->supportSection, points[p], &dof));
4649     PetscCall(PetscSectionGetOffset(mesh->supportSection, points[p], &off));
4650     for (c = 0; c < dof; ++c) {
4651       const PetscInt point = mesh->supports[off + c];
4652 
4653       for (m = 0; m < joinSize; ++m) {
4654         if (point == join[i][m]) {
4655           join[1 - i][newJoinSize++] = point;
4656           break;
4657         }
4658       }
4659     }
4660     joinSize = newJoinSize;
4661     i        = 1 - i;
4662   }
4663   *numCoveredPoints = joinSize;
4664   *coveredPoints    = join[i];
4665   PetscCall(DMRestoreWorkArray(dm, maxSupportSize, MPIU_INT, &join[1 - i]));
4666   PetscFunctionReturn(PETSC_SUCCESS);
4667 }
4668 
4669 /*@C
4670   DMPlexRestoreJoin - Restore an array for the join of the set of points
4671 
4672   Not Collective
4673 
4674   Input Parameters:
4675 + dm        - The `DMPLEX` object
4676 . numPoints - The number of input points for the join
4677 - points    - The input points
4678 
4679   Output Parameters:
4680 + numCoveredPoints - The number of points in the join
4681 - coveredPoints    - The points in the join
4682 
4683   Level: intermediate
4684 
4685   Fortran Notes:
4686   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4687 
4688 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetJoin()`, `DMPlexGetFullJoin()`, `DMPlexGetMeet()`
4689 @*/
4690 PetscErrorCode DMPlexRestoreJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4691 {
4692   PetscFunctionBegin;
4693   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4694   if (points) PetscAssertPointer(points, 3);
4695   if (numCoveredPoints) PetscAssertPointer(numCoveredPoints, 4);
4696   PetscAssertPointer(coveredPoints, 5);
4697   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
4698   if (numCoveredPoints) *numCoveredPoints = 0;
4699   PetscFunctionReturn(PETSC_SUCCESS);
4700 }
4701 
4702 /*@C
4703   DMPlexGetFullJoin - Get an array for the join of the set of points
4704 
4705   Not Collective
4706 
4707   Input Parameters:
4708 + dm        - The `DMPLEX` object
4709 . numPoints - The number of input points for the join
4710 - points    - The input points
4711 
4712   Output Parameters:
4713 + numCoveredPoints - The number of points in the join
4714 - coveredPoints    - The points in the join
4715 
4716   Level: intermediate
4717 
4718   Fortran Notes:
4719   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4720 
4721 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetJoin()`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4722 @*/
4723 PetscErrorCode DMPlexGetFullJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4724 {
4725   PetscInt *offsets, **closures;
4726   PetscInt *join[2];
4727   PetscInt  depth = 0, maxSize, joinSize = 0, i = 0;
4728   PetscInt  p, d, c, m, ms;
4729 
4730   PetscFunctionBegin;
4731   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4732   PetscAssertPointer(points, 3);
4733   PetscAssertPointer(numCoveredPoints, 4);
4734   PetscAssertPointer(coveredPoints, 5);
4735 
4736   PetscCall(DMPlexGetDepth(dm, &depth));
4737   PetscCall(PetscCalloc1(numPoints, &closures));
4738   PetscCall(DMGetWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
4739   PetscCall(DMPlexGetMaxSizes(dm, NULL, &ms));
4740   maxSize = (ms > 1) ? ((PetscPowInt(ms, depth + 1) - 1) / (ms - 1)) : depth + 1;
4741   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[0]));
4742   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[1]));
4743 
4744   for (p = 0; p < numPoints; ++p) {
4745     PetscInt closureSize;
4746 
4747     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_FALSE, &closureSize, &closures[p]));
4748 
4749     offsets[p * (depth + 2) + 0] = 0;
4750     for (d = 0; d < depth + 1; ++d) {
4751       PetscInt pStart, pEnd, i;
4752 
4753       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
4754       for (i = offsets[p * (depth + 2) + d]; i < closureSize; ++i) {
4755         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
4756           offsets[p * (depth + 2) + d + 1] = i;
4757           break;
4758         }
4759       }
4760       if (i == closureSize) offsets[p * (depth + 2) + d + 1] = i;
4761     }
4762     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);
4763   }
4764   for (d = 0; d < depth + 1; ++d) {
4765     PetscInt dof;
4766 
4767     /* Copy in support of first point */
4768     dof = offsets[d + 1] - offsets[d];
4769     for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = closures[0][(offsets[d] + joinSize) * 2];
4770     /* Check each successive cone */
4771     for (p = 1; p < numPoints && joinSize; ++p) {
4772       PetscInt newJoinSize = 0;
4773 
4774       dof = offsets[p * (depth + 2) + d + 1] - offsets[p * (depth + 2) + d];
4775       for (c = 0; c < dof; ++c) {
4776         const PetscInt point = closures[p][(offsets[p * (depth + 2) + d] + c) * 2];
4777 
4778         for (m = 0; m < joinSize; ++m) {
4779           if (point == join[i][m]) {
4780             join[1 - i][newJoinSize++] = point;
4781             break;
4782           }
4783         }
4784       }
4785       joinSize = newJoinSize;
4786       i        = 1 - i;
4787     }
4788     if (joinSize) break;
4789   }
4790   *numCoveredPoints = joinSize;
4791   *coveredPoints    = join[i];
4792   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_FALSE, NULL, &closures[p]));
4793   PetscCall(PetscFree(closures));
4794   PetscCall(DMRestoreWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
4795   PetscCall(DMRestoreWorkArray(dm, ms, MPIU_INT, &join[1 - i]));
4796   PetscFunctionReturn(PETSC_SUCCESS);
4797 }
4798 
4799 /*@C
4800   DMPlexGetMeet - Get an array for the meet of the set of points
4801 
4802   Not Collective
4803 
4804   Input Parameters:
4805 + dm        - The `DMPLEX` object
4806 . numPoints - The number of input points for the meet
4807 - points    - The input points
4808 
4809   Output Parameters:
4810 + numCoveringPoints - The number of points in the meet
4811 - coveringPoints    - The points in the meet
4812 
4813   Level: intermediate
4814 
4815   Note:
4816   Currently, this is restricted to a single level meet
4817 
4818   Fortran Notes:
4819   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4820 
4821 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
4822 @*/
4823 PetscErrorCode DMPlexGetMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveringPoints, const PetscInt **coveringPoints)
4824 {
4825   DM_Plex  *mesh = (DM_Plex *)dm->data;
4826   PetscInt *meet[2];
4827   PetscInt  meetSize, i = 0;
4828   PetscInt  dof, off, p, c, m;
4829   PetscInt  maxConeSize;
4830 
4831   PetscFunctionBegin;
4832   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4833   PetscAssertPointer(points, 3);
4834   PetscAssertPointer(numCoveringPoints, 4);
4835   PetscAssertPointer(coveringPoints, 5);
4836   PetscCall(PetscSectionGetMaxDof(mesh->coneSection, &maxConeSize));
4837   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[0]));
4838   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[1]));
4839   /* Copy in cone of first point */
4840   PetscCall(PetscSectionGetDof(mesh->coneSection, points[0], &dof));
4841   PetscCall(PetscSectionGetOffset(mesh->coneSection, points[0], &off));
4842   for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = mesh->cones[off + meetSize];
4843   /* Check each successive cone */
4844   for (p = 1; p < numPoints; ++p) {
4845     PetscInt newMeetSize = 0;
4846 
4847     PetscCall(PetscSectionGetDof(mesh->coneSection, points[p], &dof));
4848     PetscCall(PetscSectionGetOffset(mesh->coneSection, points[p], &off));
4849     for (c = 0; c < dof; ++c) {
4850       const PetscInt point = mesh->cones[off + c];
4851 
4852       for (m = 0; m < meetSize; ++m) {
4853         if (point == meet[i][m]) {
4854           meet[1 - i][newMeetSize++] = point;
4855           break;
4856         }
4857       }
4858     }
4859     meetSize = newMeetSize;
4860     i        = 1 - i;
4861   }
4862   *numCoveringPoints = meetSize;
4863   *coveringPoints    = meet[i];
4864   PetscCall(DMRestoreWorkArray(dm, maxConeSize, MPIU_INT, &meet[1 - i]));
4865   PetscFunctionReturn(PETSC_SUCCESS);
4866 }
4867 
4868 /*@C
4869   DMPlexRestoreMeet - Restore an array for the meet of the set of points
4870 
4871   Not Collective
4872 
4873   Input Parameters:
4874 + dm        - The `DMPLEX` object
4875 . numPoints - The number of input points for the meet
4876 - points    - The input points
4877 
4878   Output Parameters:
4879 + numCoveredPoints - The number of points in the meet
4880 - coveredPoints    - The points in the meet
4881 
4882   Level: intermediate
4883 
4884   Fortran Notes:
4885   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4886 
4887 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetMeet()`, `DMPlexGetFullMeet()`, `DMPlexGetJoin()`
4888 @*/
4889 PetscErrorCode DMPlexRestoreMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4890 {
4891   PetscFunctionBegin;
4892   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4893   if (points) PetscAssertPointer(points, 3);
4894   if (numCoveredPoints) PetscAssertPointer(numCoveredPoints, 4);
4895   PetscAssertPointer(coveredPoints, 5);
4896   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
4897   if (numCoveredPoints) *numCoveredPoints = 0;
4898   PetscFunctionReturn(PETSC_SUCCESS);
4899 }
4900 
4901 /*@C
4902   DMPlexGetFullMeet - Get an array for the meet of the set of points
4903 
4904   Not Collective
4905 
4906   Input Parameters:
4907 + dm        - The `DMPLEX` object
4908 . numPoints - The number of input points for the meet
4909 - points    - The input points
4910 
4911   Output Parameters:
4912 + numCoveredPoints - The number of points in the meet
4913 - coveredPoints    - The points in the meet
4914 
4915   Level: intermediate
4916 
4917   Fortran Notes:
4918   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4919 
4920 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetMeet()`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
4921 @*/
4922 PetscErrorCode DMPlexGetFullMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4923 {
4924   PetscInt *offsets, **closures;
4925   PetscInt *meet[2];
4926   PetscInt  height = 0, maxSize, meetSize = 0, i = 0;
4927   PetscInt  p, h, c, m, mc;
4928 
4929   PetscFunctionBegin;
4930   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4931   PetscAssertPointer(points, 3);
4932   PetscAssertPointer(numCoveredPoints, 4);
4933   PetscAssertPointer(coveredPoints, 5);
4934 
4935   PetscCall(DMPlexGetDepth(dm, &height));
4936   PetscCall(PetscMalloc1(numPoints, &closures));
4937   PetscCall(DMGetWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
4938   PetscCall(DMPlexGetMaxSizes(dm, &mc, NULL));
4939   maxSize = (mc > 1) ? ((PetscPowInt(mc, height + 1) - 1) / (mc - 1)) : height + 1;
4940   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[0]));
4941   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[1]));
4942 
4943   for (p = 0; p < numPoints; ++p) {
4944     PetscInt closureSize;
4945 
4946     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_TRUE, &closureSize, &closures[p]));
4947 
4948     offsets[p * (height + 2) + 0] = 0;
4949     for (h = 0; h < height + 1; ++h) {
4950       PetscInt pStart, pEnd, i;
4951 
4952       PetscCall(DMPlexGetHeightStratum(dm, h, &pStart, &pEnd));
4953       for (i = offsets[p * (height + 2) + h]; i < closureSize; ++i) {
4954         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
4955           offsets[p * (height + 2) + h + 1] = i;
4956           break;
4957         }
4958       }
4959       if (i == closureSize) offsets[p * (height + 2) + h + 1] = i;
4960     }
4961     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);
4962   }
4963   for (h = 0; h < height + 1; ++h) {
4964     PetscInt dof;
4965 
4966     /* Copy in cone of first point */
4967     dof = offsets[h + 1] - offsets[h];
4968     for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = closures[0][(offsets[h] + meetSize) * 2];
4969     /* Check each successive cone */
4970     for (p = 1; p < numPoints && meetSize; ++p) {
4971       PetscInt newMeetSize = 0;
4972 
4973       dof = offsets[p * (height + 2) + h + 1] - offsets[p * (height + 2) + h];
4974       for (c = 0; c < dof; ++c) {
4975         const PetscInt point = closures[p][(offsets[p * (height + 2) + h] + c) * 2];
4976 
4977         for (m = 0; m < meetSize; ++m) {
4978           if (point == meet[i][m]) {
4979             meet[1 - i][newMeetSize++] = point;
4980             break;
4981           }
4982         }
4983       }
4984       meetSize = newMeetSize;
4985       i        = 1 - i;
4986     }
4987     if (meetSize) break;
4988   }
4989   *numCoveredPoints = meetSize;
4990   *coveredPoints    = meet[i];
4991   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_TRUE, NULL, &closures[p]));
4992   PetscCall(PetscFree(closures));
4993   PetscCall(DMRestoreWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
4994   PetscCall(DMRestoreWorkArray(dm, mc, MPIU_INT, &meet[1 - i]));
4995   PetscFunctionReturn(PETSC_SUCCESS);
4996 }
4997 
4998 /*@C
4999   DMPlexEqual - Determine if two `DM` have the same topology
5000 
5001   Not Collective
5002 
5003   Input Parameters:
5004 + dmA - A `DMPLEX` object
5005 - dmB - A `DMPLEX` object
5006 
5007   Output Parameter:
5008 . equal - `PETSC_TRUE` if the topologies are identical
5009 
5010   Level: intermediate
5011 
5012   Note:
5013   We are not solving graph isomorphism, so we do not permute.
5014 
5015 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCone()`
5016 @*/
5017 PetscErrorCode DMPlexEqual(DM dmA, DM dmB, PetscBool *equal)
5018 {
5019   PetscInt depth, depthB, pStart, pEnd, pStartB, pEndB, p;
5020 
5021   PetscFunctionBegin;
5022   PetscValidHeaderSpecific(dmA, DM_CLASSID, 1);
5023   PetscValidHeaderSpecific(dmB, DM_CLASSID, 2);
5024   PetscAssertPointer(equal, 3);
5025 
5026   *equal = PETSC_FALSE;
5027   PetscCall(DMPlexGetDepth(dmA, &depth));
5028   PetscCall(DMPlexGetDepth(dmB, &depthB));
5029   if (depth != depthB) PetscFunctionReturn(PETSC_SUCCESS);
5030   PetscCall(DMPlexGetChart(dmA, &pStart, &pEnd));
5031   PetscCall(DMPlexGetChart(dmB, &pStartB, &pEndB));
5032   if ((pStart != pStartB) || (pEnd != pEndB)) PetscFunctionReturn(PETSC_SUCCESS);
5033   for (p = pStart; p < pEnd; ++p) {
5034     const PetscInt *cone, *coneB, *ornt, *orntB, *support, *supportB;
5035     PetscInt        coneSize, coneSizeB, c, supportSize, supportSizeB, s;
5036 
5037     PetscCall(DMPlexGetConeSize(dmA, p, &coneSize));
5038     PetscCall(DMPlexGetCone(dmA, p, &cone));
5039     PetscCall(DMPlexGetConeOrientation(dmA, p, &ornt));
5040     PetscCall(DMPlexGetConeSize(dmB, p, &coneSizeB));
5041     PetscCall(DMPlexGetCone(dmB, p, &coneB));
5042     PetscCall(DMPlexGetConeOrientation(dmB, p, &orntB));
5043     if (coneSize != coneSizeB) PetscFunctionReturn(PETSC_SUCCESS);
5044     for (c = 0; c < coneSize; ++c) {
5045       if (cone[c] != coneB[c]) PetscFunctionReturn(PETSC_SUCCESS);
5046       if (ornt[c] != orntB[c]) PetscFunctionReturn(PETSC_SUCCESS);
5047     }
5048     PetscCall(DMPlexGetSupportSize(dmA, p, &supportSize));
5049     PetscCall(DMPlexGetSupport(dmA, p, &support));
5050     PetscCall(DMPlexGetSupportSize(dmB, p, &supportSizeB));
5051     PetscCall(DMPlexGetSupport(dmB, p, &supportB));
5052     if (supportSize != supportSizeB) PetscFunctionReturn(PETSC_SUCCESS);
5053     for (s = 0; s < supportSize; ++s) {
5054       if (support[s] != supportB[s]) PetscFunctionReturn(PETSC_SUCCESS);
5055     }
5056   }
5057   *equal = PETSC_TRUE;
5058   PetscFunctionReturn(PETSC_SUCCESS);
5059 }
5060 
5061 /*@C
5062   DMPlexGetNumFaceVertices - Returns the number of vertices on a face
5063 
5064   Not Collective
5065 
5066   Input Parameters:
5067 + dm         - The `DMPLEX`
5068 . cellDim    - The cell dimension
5069 - numCorners - The number of vertices on a cell
5070 
5071   Output Parameter:
5072 . numFaceVertices - The number of vertices on a face
5073 
5074   Level: developer
5075 
5076   Note:
5077   Of course this can only work for a restricted set of symmetric shapes
5078 
5079 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCone()`
5080 @*/
5081 PetscErrorCode DMPlexGetNumFaceVertices(DM dm, PetscInt cellDim, PetscInt numCorners, PetscInt *numFaceVertices)
5082 {
5083   MPI_Comm comm;
5084 
5085   PetscFunctionBegin;
5086   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
5087   PetscAssertPointer(numFaceVertices, 4);
5088   switch (cellDim) {
5089   case 0:
5090     *numFaceVertices = 0;
5091     break;
5092   case 1:
5093     *numFaceVertices = 1;
5094     break;
5095   case 2:
5096     switch (numCorners) {
5097     case 3:                 /* triangle */
5098       *numFaceVertices = 2; /* Edge has 2 vertices */
5099       break;
5100     case 4:                 /* quadrilateral */
5101       *numFaceVertices = 2; /* Edge has 2 vertices */
5102       break;
5103     case 6:                 /* quadratic triangle, tri and quad cohesive Lagrange cells */
5104       *numFaceVertices = 3; /* Edge has 3 vertices */
5105       break;
5106     case 9:                 /* quadratic quadrilateral, quadratic quad cohesive Lagrange cells */
5107       *numFaceVertices = 3; /* Edge has 3 vertices */
5108       break;
5109     default:
5110       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
5111     }
5112     break;
5113   case 3:
5114     switch (numCorners) {
5115     case 4:                 /* tetradehdron */
5116       *numFaceVertices = 3; /* Face has 3 vertices */
5117       break;
5118     case 6:                 /* tet cohesive cells */
5119       *numFaceVertices = 4; /* Face has 4 vertices */
5120       break;
5121     case 8:                 /* hexahedron */
5122       *numFaceVertices = 4; /* Face has 4 vertices */
5123       break;
5124     case 9:                 /* tet cohesive Lagrange cells */
5125       *numFaceVertices = 6; /* Face has 6 vertices */
5126       break;
5127     case 10:                /* quadratic tetrahedron */
5128       *numFaceVertices = 6; /* Face has 6 vertices */
5129       break;
5130     case 12:                /* hex cohesive Lagrange cells */
5131       *numFaceVertices = 6; /* Face has 6 vertices */
5132       break;
5133     case 18:                /* quadratic tet cohesive Lagrange cells */
5134       *numFaceVertices = 6; /* Face has 6 vertices */
5135       break;
5136     case 27:                /* quadratic hexahedron, quadratic hex cohesive Lagrange cells */
5137       *numFaceVertices = 9; /* Face has 9 vertices */
5138       break;
5139     default:
5140       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
5141     }
5142     break;
5143   default:
5144     SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid cell dimension %" PetscInt_FMT, cellDim);
5145   }
5146   PetscFunctionReturn(PETSC_SUCCESS);
5147 }
5148 
5149 /*@
5150   DMPlexGetDepthLabel - Get the `DMLabel` recording the depth of each point
5151 
5152   Not Collective
5153 
5154   Input Parameter:
5155 . dm - The `DMPLEX` object
5156 
5157   Output Parameter:
5158 . depthLabel - The `DMLabel` recording point depth
5159 
5160   Level: developer
5161 
5162 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepth()`, `DMPlexGetHeightStratum()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`,
5163 @*/
5164 PetscErrorCode DMPlexGetDepthLabel(DM dm, DMLabel *depthLabel)
5165 {
5166   PetscFunctionBegin;
5167   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5168   PetscAssertPointer(depthLabel, 2);
5169   *depthLabel = dm->depthLabel;
5170   PetscFunctionReturn(PETSC_SUCCESS);
5171 }
5172 
5173 /*@
5174   DMPlexGetDepth - Get the depth of the DAG representing this mesh
5175 
5176   Not Collective
5177 
5178   Input Parameter:
5179 . dm - The `DMPLEX` object
5180 
5181   Output Parameter:
5182 . depth - The number of strata (breadth first levels) in the DAG
5183 
5184   Level: developer
5185 
5186   Notes:
5187   This returns maximum of point depths over all points, i.e. maximum value of the label returned by `DMPlexGetDepthLabel()`.
5188 
5189   The point depth is described more in detail in `DMPlexGetDepthStratum()`.
5190 
5191   An empty mesh gives -1.
5192 
5193 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepthLabel()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`
5194 @*/
5195 PetscErrorCode DMPlexGetDepth(DM dm, PetscInt *depth)
5196 {
5197   DM_Plex *mesh = (DM_Plex *)dm->data;
5198   DMLabel  label;
5199   PetscInt d = 0;
5200 
5201   PetscFunctionBegin;
5202   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5203   PetscAssertPointer(depth, 2);
5204   if (mesh->tr) {
5205     PetscCall(DMPlexTransformGetDepth(mesh->tr, depth));
5206   } else {
5207     PetscCall(DMPlexGetDepthLabel(dm, &label));
5208     if (label) PetscCall(DMLabelGetNumValues(label, &d));
5209     *depth = d - 1;
5210   }
5211   PetscFunctionReturn(PETSC_SUCCESS);
5212 }
5213 
5214 /*@
5215   DMPlexGetDepthStratum - Get the bounds [`start`, `end`) for all points at a certain depth.
5216 
5217   Not Collective
5218 
5219   Input Parameters:
5220 + dm    - The `DMPLEX` object
5221 - depth - The requested depth
5222 
5223   Output Parameters:
5224 + start - The first point at this `depth`
5225 - end   - One beyond the last point at this `depth`
5226 
5227   Level: developer
5228 
5229   Notes:
5230   Depth indexing is related to topological dimension.  Depth stratum 0 contains the lowest topological dimension points,
5231   often "vertices".  If the mesh is "interpolated" (see `DMPlexInterpolate()`), then depth stratum 1 contains the next
5232   higher dimension, e.g., "edges".
5233 
5234 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetHeightStratum()`, `DMPlexGetCellTypeStratum()`, `DMPlexGetDepth()`, `DMPlexGetDepthLabel()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`, `DMPlexInterpolate()`
5235 @*/
5236 PetscErrorCode DMPlexGetDepthStratum(DM dm, PetscInt depth, PetscInt *start, PetscInt *end)
5237 {
5238   DM_Plex *mesh = (DM_Plex *)dm->data;
5239   DMLabel  label;
5240   PetscInt pStart, pEnd;
5241 
5242   PetscFunctionBegin;
5243   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5244   if (start) {
5245     PetscAssertPointer(start, 3);
5246     *start = 0;
5247   }
5248   if (end) {
5249     PetscAssertPointer(end, 4);
5250     *end = 0;
5251   }
5252   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
5253   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
5254   if (depth < 0) {
5255     if (start) *start = pStart;
5256     if (end) *end = pEnd;
5257     PetscFunctionReturn(PETSC_SUCCESS);
5258   }
5259   if (mesh->tr) {
5260     PetscCall(DMPlexTransformGetDepthStratum(mesh->tr, depth, start, end));
5261   } else {
5262     PetscCall(DMPlexGetDepthLabel(dm, &label));
5263     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named depth was found");
5264     PetscCall(DMLabelGetStratumBounds(label, depth, start, end));
5265   }
5266   PetscFunctionReturn(PETSC_SUCCESS);
5267 }
5268 
5269 /*@
5270   DMPlexGetHeightStratum - Get the bounds [`start`, `end`) for all points at a certain height.
5271 
5272   Not Collective
5273 
5274   Input Parameters:
5275 + dm     - The `DMPLEX` object
5276 - height - The requested height
5277 
5278   Output Parameters:
5279 + start - The first point at this `height`
5280 - end   - One beyond the last point at this `height`
5281 
5282   Level: developer
5283 
5284   Notes:
5285   Height indexing is related to topological codimension.  Height stratum 0 contains the highest topological dimension
5286   points, often called "cells" or "elements".  If the mesh is "interpolated" (see `DMPlexInterpolate()`), then height
5287   stratum 1 contains the boundary of these "cells", often called "faces" or "facets".
5288 
5289 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepthStratum()`, `DMPlexGetCellTypeStratum()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
5290 @*/
5291 PetscErrorCode DMPlexGetHeightStratum(DM dm, PetscInt height, PetscInt *start, PetscInt *end)
5292 {
5293   DMLabel  label;
5294   PetscInt depth, pStart, pEnd;
5295 
5296   PetscFunctionBegin;
5297   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5298   if (start) {
5299     PetscAssertPointer(start, 3);
5300     *start = 0;
5301   }
5302   if (end) {
5303     PetscAssertPointer(end, 4);
5304     *end = 0;
5305   }
5306   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
5307   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
5308   if (height < 0) {
5309     if (start) *start = pStart;
5310     if (end) *end = pEnd;
5311     PetscFunctionReturn(PETSC_SUCCESS);
5312   }
5313   PetscCall(DMPlexGetDepthLabel(dm, &label));
5314   if (label) PetscCall(DMLabelGetNumValues(label, &depth));
5315   else PetscCall(DMGetDimension(dm, &depth));
5316   PetscCheck(depth >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Depth not yet computed");
5317   PetscCall(DMPlexGetDepthStratum(dm, depth - 1 - height, start, end));
5318   PetscFunctionReturn(PETSC_SUCCESS);
5319 }
5320 
5321 /*@
5322   DMPlexGetPointDepth - Get the `depth` of a given point
5323 
5324   Not Collective
5325 
5326   Input Parameters:
5327 + dm    - The `DMPLEX` object
5328 - point - The point
5329 
5330   Output Parameter:
5331 . depth - The depth of the `point`
5332 
5333   Level: intermediate
5334 
5335 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
5336 @*/
5337 PetscErrorCode DMPlexGetPointDepth(DM dm, PetscInt point, PetscInt *depth)
5338 {
5339   PetscFunctionBegin;
5340   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5341   PetscAssertPointer(depth, 3);
5342   PetscCall(DMLabelGetValue(dm->depthLabel, point, depth));
5343   PetscFunctionReturn(PETSC_SUCCESS);
5344 }
5345 
5346 /*@
5347   DMPlexGetPointHeight - Get the `height` of a given point
5348 
5349   Not Collective
5350 
5351   Input Parameters:
5352 + dm    - The `DMPLEX` object
5353 - point - The point
5354 
5355   Output Parameter:
5356 . height - The height of the `point`
5357 
5358   Level: intermediate
5359 
5360 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointDepth()`
5361 @*/
5362 PetscErrorCode DMPlexGetPointHeight(DM dm, PetscInt point, PetscInt *height)
5363 {
5364   PetscInt n, pDepth;
5365 
5366   PetscFunctionBegin;
5367   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5368   PetscAssertPointer(height, 3);
5369   PetscCall(DMLabelGetNumValues(dm->depthLabel, &n));
5370   PetscCall(DMLabelGetValue(dm->depthLabel, point, &pDepth));
5371   *height = n - 1 - pDepth; /* DAG depth is n-1 */
5372   PetscFunctionReturn(PETSC_SUCCESS);
5373 }
5374 
5375 /*@
5376   DMPlexGetCellTypeLabel - Get the `DMLabel` recording the polytope type of each cell
5377 
5378   Not Collective
5379 
5380   Input Parameter:
5381 . dm - The `DMPLEX` object
5382 
5383   Output Parameter:
5384 . celltypeLabel - The `DMLabel` recording cell polytope type
5385 
5386   Level: developer
5387 
5388   Note:
5389   This function will trigger automatica computation of cell types. This can be disabled by calling
5390   `DMCreateLabel`(dm, "celltype") beforehand.
5391 
5392 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMCreateLabel()`
5393 @*/
5394 PetscErrorCode DMPlexGetCellTypeLabel(DM dm, DMLabel *celltypeLabel)
5395 {
5396   PetscFunctionBegin;
5397   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5398   PetscAssertPointer(celltypeLabel, 2);
5399   if (!dm->celltypeLabel) PetscCall(DMPlexComputeCellTypes(dm));
5400   *celltypeLabel = dm->celltypeLabel;
5401   PetscFunctionReturn(PETSC_SUCCESS);
5402 }
5403 
5404 /*@
5405   DMPlexGetCellType - Get the polytope type of a given cell
5406 
5407   Not Collective
5408 
5409   Input Parameters:
5410 + dm   - The `DMPLEX` object
5411 - cell - The cell
5412 
5413   Output Parameter:
5414 . celltype - The polytope type of the cell
5415 
5416   Level: intermediate
5417 
5418 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPolytopeType`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`
5419 @*/
5420 PetscErrorCode DMPlexGetCellType(DM dm, PetscInt cell, DMPolytopeType *celltype)
5421 {
5422   DM_Plex *mesh = (DM_Plex *)dm->data;
5423   DMLabel  label;
5424   PetscInt ct;
5425 
5426   PetscFunctionBegin;
5427   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5428   PetscAssertPointer(celltype, 3);
5429   if (mesh->tr) {
5430     PetscCall(DMPlexTransformGetCellType(mesh->tr, cell, celltype));
5431   } else {
5432     PetscInt pStart, pEnd;
5433 
5434     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, NULL));
5435     if (!mesh->cellTypes) { /* XXX remove? optimize? */
5436       PetscCall(PetscSectionGetChart(mesh->coneSection, NULL, &pEnd));
5437       PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
5438       PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5439       for (PetscInt p = pStart; p < pEnd; p++) {
5440         PetscCall(DMLabelGetValue(label, p, &ct));
5441         mesh->cellTypes[p - pStart].value_as_uint8 = (DMPolytopeType)ct;
5442       }
5443     }
5444     *celltype = (DMPolytopeType)mesh->cellTypes[cell - pStart].value_as_uint8;
5445     if (PetscDefined(USE_DEBUG)) {
5446       PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5447       PetscCall(DMLabelGetValue(label, cell, &ct));
5448       PetscCheck(ct >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Cell %" PetscInt_FMT " has not been assigned a cell type", cell);
5449       PetscCheck(ct == (PetscInt)*celltype, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid cellType for %" PetscInt_FMT ": %d != %" PetscInt_FMT, cell, (int)*celltype, ct);
5450     }
5451   }
5452   PetscFunctionReturn(PETSC_SUCCESS);
5453 }
5454 
5455 /*@
5456   DMPlexSetCellType - Set the polytope type of a given cell
5457 
5458   Not Collective
5459 
5460   Input Parameters:
5461 + dm       - The `DMPLEX` object
5462 . cell     - The cell
5463 - celltype - The polytope type of the cell
5464 
5465   Level: advanced
5466 
5467   Note:
5468   By default, cell types will be automatically computed using `DMPlexComputeCellTypes()` before this function
5469   is executed. This function will override the computed type. However, if automatic classification will not succeed
5470   and a user wants to manually specify all types, the classification must be disabled by calling
5471   DMCreateLabel(dm, "celltype") before getting or setting any cell types.
5472 
5473 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexComputeCellTypes()`, `DMCreateLabel()`
5474 @*/
5475 PetscErrorCode DMPlexSetCellType(DM dm, PetscInt cell, DMPolytopeType celltype)
5476 {
5477   DM_Plex *mesh = (DM_Plex *)dm->data;
5478   DMLabel  label;
5479   PetscInt pStart, pEnd;
5480 
5481   PetscFunctionBegin;
5482   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5483   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
5484   PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5485   PetscCall(DMLabelSetValue(label, cell, celltype));
5486   if (!mesh->cellTypes) PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
5487   mesh->cellTypes[cell - pStart].value_as_uint8 = celltype;
5488   PetscFunctionReturn(PETSC_SUCCESS);
5489 }
5490 
5491 PetscErrorCode DMCreateCoordinateDM_Plex(DM dm, DM *cdm)
5492 {
5493   PetscSection section, s;
5494   Mat          m;
5495   PetscInt     maxHeight;
5496   const char  *prefix;
5497 
5498   PetscFunctionBegin;
5499   PetscCall(DMClone(dm, cdm));
5500   PetscCall(PetscObjectGetOptionsPrefix((PetscObject)dm, &prefix));
5501   PetscCall(PetscObjectSetOptionsPrefix((PetscObject)*cdm, prefix));
5502   PetscCall(PetscObjectAppendOptionsPrefix((PetscObject)*cdm, "cdm_"));
5503   PetscCall(DMPlexGetMaxProjectionHeight(dm, &maxHeight));
5504   PetscCall(DMPlexSetMaxProjectionHeight(*cdm, maxHeight));
5505   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
5506   PetscCall(DMSetLocalSection(*cdm, section));
5507   PetscCall(PetscSectionDestroy(&section));
5508   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &s));
5509   PetscCall(MatCreate(PETSC_COMM_SELF, &m));
5510   PetscCall(DMSetDefaultConstraints(*cdm, s, m, NULL));
5511   PetscCall(PetscSectionDestroy(&s));
5512   PetscCall(MatDestroy(&m));
5513 
5514   PetscCall(DMSetNumFields(*cdm, 1));
5515   PetscCall(DMCreateDS(*cdm));
5516   (*cdm)->cloneOpts = PETSC_TRUE;
5517   if (dm->setfromoptionscalled) PetscCall(DMSetFromOptions(*cdm));
5518   PetscFunctionReturn(PETSC_SUCCESS);
5519 }
5520 
5521 PetscErrorCode DMCreateCoordinateField_Plex(DM dm, DMField *field)
5522 {
5523   Vec coordsLocal, cellCoordsLocal;
5524   DM  coordsDM, cellCoordsDM;
5525 
5526   PetscFunctionBegin;
5527   *field = NULL;
5528   PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
5529   PetscCall(DMGetCoordinateDM(dm, &coordsDM));
5530   PetscCall(DMGetCellCoordinatesLocal(dm, &cellCoordsLocal));
5531   PetscCall(DMGetCellCoordinateDM(dm, &cellCoordsDM));
5532   if (coordsLocal && coordsDM) {
5533     if (cellCoordsLocal && cellCoordsDM) PetscCall(DMFieldCreateDSWithDG(coordsDM, cellCoordsDM, 0, coordsLocal, cellCoordsLocal, field));
5534     else PetscCall(DMFieldCreateDS(coordsDM, 0, coordsLocal, field));
5535   }
5536   PetscFunctionReturn(PETSC_SUCCESS);
5537 }
5538 
5539 /*@C
5540   DMPlexGetConeSection - Return a section which describes the layout of cone data
5541 
5542   Not Collective
5543 
5544   Input Parameter:
5545 . dm - The `DMPLEX` object
5546 
5547   Output Parameter:
5548 . section - The `PetscSection` object
5549 
5550   Level: developer
5551 
5552 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSupportSection()`, `DMPlexGetCones()`, `DMPlexGetConeOrientations()`, `PetscSection`
5553 @*/
5554 PetscErrorCode DMPlexGetConeSection(DM dm, PetscSection *section)
5555 {
5556   DM_Plex *mesh = (DM_Plex *)dm->data;
5557 
5558   PetscFunctionBegin;
5559   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5560   if (section) *section = mesh->coneSection;
5561   PetscFunctionReturn(PETSC_SUCCESS);
5562 }
5563 
5564 /*@C
5565   DMPlexGetSupportSection - Return a section which describes the layout of support data
5566 
5567   Not Collective
5568 
5569   Input Parameter:
5570 . dm - The `DMPLEX` object
5571 
5572   Output Parameter:
5573 . section - The `PetscSection` object
5574 
5575   Level: developer
5576 
5577 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `PetscSection`
5578 @*/
5579 PetscErrorCode DMPlexGetSupportSection(DM dm, PetscSection *section)
5580 {
5581   DM_Plex *mesh = (DM_Plex *)dm->data;
5582 
5583   PetscFunctionBegin;
5584   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5585   if (section) *section = mesh->supportSection;
5586   PetscFunctionReturn(PETSC_SUCCESS);
5587 }
5588 
5589 /*@C
5590   DMPlexGetCones - Return cone data
5591 
5592   Not Collective
5593 
5594   Input Parameter:
5595 . dm - The `DMPLEX` object
5596 
5597   Output Parameter:
5598 . cones - The cone for each point
5599 
5600   Level: developer
5601 
5602 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`
5603 @*/
5604 PetscErrorCode DMPlexGetCones(DM dm, PetscInt *cones[])
5605 {
5606   DM_Plex *mesh = (DM_Plex *)dm->data;
5607 
5608   PetscFunctionBegin;
5609   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5610   if (cones) *cones = mesh->cones;
5611   PetscFunctionReturn(PETSC_SUCCESS);
5612 }
5613 
5614 /*@C
5615   DMPlexGetConeOrientations - Return cone orientation data
5616 
5617   Not Collective
5618 
5619   Input Parameter:
5620 . dm - The `DMPLEX` object
5621 
5622   Output Parameter:
5623 . coneOrientations - The array of cone orientations for all points
5624 
5625   Level: developer
5626 
5627   Notes:
5628   The `PetscSection` returned by `DMPlexGetConeSection()` partitions coneOrientations into cone orientations of particular points as returned by `DMPlexGetConeOrientation()`.
5629 
5630   The meaning of coneOrientations values is detailed in `DMPlexGetConeOrientation()`.
5631 
5632 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `DMPlexGetConeOrientation()`, `PetscSection`
5633 @*/
5634 PetscErrorCode DMPlexGetConeOrientations(DM dm, PetscInt *coneOrientations[])
5635 {
5636   DM_Plex *mesh = (DM_Plex *)dm->data;
5637 
5638   PetscFunctionBegin;
5639   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5640   if (coneOrientations) *coneOrientations = mesh->coneOrientations;
5641   PetscFunctionReturn(PETSC_SUCCESS);
5642 }
5643 
5644 /******************************** FEM Support **********************************/
5645 
5646 PetscErrorCode DMPlexGetAllCells_Internal(DM plex, IS *cellIS)
5647 {
5648   PetscInt depth;
5649 
5650   PetscFunctionBegin;
5651   PetscCall(DMPlexGetDepth(plex, &depth));
5652   PetscCall(DMGetStratumIS(plex, "dim", depth, cellIS));
5653   if (!*cellIS) PetscCall(DMGetStratumIS(plex, "depth", depth, cellIS));
5654   PetscFunctionReturn(PETSC_SUCCESS);
5655 }
5656 
5657 PetscErrorCode DMPlexGetAllFaces_Internal(DM plex, IS *faceIS)
5658 {
5659   PetscInt depth;
5660 
5661   PetscFunctionBegin;
5662   PetscCall(DMPlexGetDepth(plex, &depth));
5663   PetscCall(DMGetStratumIS(plex, "dim", depth - 1, faceIS));
5664   if (!*faceIS) PetscCall(DMGetStratumIS(plex, "depth", depth - 1, faceIS));
5665   PetscFunctionReturn(PETSC_SUCCESS);
5666 }
5667 
5668 /*
5669  Returns number of components and tensor degree for the field.  For interpolated meshes, line should be a point
5670  representing a line in the section.
5671 */
5672 static PetscErrorCode PetscSectionFieldGetTensorDegree_Private(DM dm, PetscSection section, PetscInt field, PetscInt line, PetscInt *Nc, PetscInt *k, PetscBool *continuous)
5673 {
5674   PetscObject  obj;
5675   PetscClassId id;
5676   PetscFE      fe = NULL;
5677 
5678   PetscFunctionBeginHot;
5679   PetscCall(PetscSectionGetFieldComponents(section, field, Nc));
5680   PetscCall(DMGetField(dm, field, NULL, &obj));
5681   PetscCall(PetscObjectGetClassId(obj, &id));
5682   if (id == PETSCFE_CLASSID) fe = (PetscFE)obj;
5683 
5684   if (!fe) {
5685     /* Assume the full interpolated mesh is in the chart; lines in particular */
5686     /* An order k SEM disc has k-1 dofs on an edge */
5687     PetscCall(PetscSectionGetFieldDof(section, line, field, k));
5688     *k = *k / *Nc + 1;
5689   } else {
5690     PetscInt       dual_space_size, dim;
5691     PetscDualSpace dual_space;
5692     PetscCall(DMGetDimension(dm, &dim));
5693     PetscCall(PetscFEGetDualSpace(fe, &dual_space));
5694     PetscCall(PetscDualSpaceGetDimension(dual_space, &dual_space_size));
5695     *k = (PetscInt)PetscCeilReal(PetscPowReal(dual_space_size / *Nc, 1.0 / dim)) - 1;
5696     PetscCall(PetscDualSpaceLagrangeGetContinuity(dual_space, continuous));
5697   }
5698   PetscFunctionReturn(PETSC_SUCCESS);
5699 }
5700 
5701 /*@
5702 
5703   DMPlexSetClosurePermutationTensor - Create a permutation from the default (BFS) point ordering in the closure, to a
5704   lexicographic ordering over the tensor product cell (i.e., line, quad, hex, etc.), and set this permutation in the
5705   section provided (or the section of the `DM`).
5706 
5707   Input Parameters:
5708 + dm      - The `DM`
5709 . point   - Either a cell (highest dim point) or an edge (dim 1 point), or `PETSC_DETERMINE`
5710 - section - The `PetscSection` to reorder, or `NULL` for the default section
5711 
5712   Example:
5713   A typical interpolated single-quad mesh might order points as
5714 .vb
5715   [c0, v1, v2, v3, v4, e5, e6, e7, e8]
5716 
5717   v4 -- e6 -- v3
5718   |           |
5719   e7    c0    e8
5720   |           |
5721   v1 -- e5 -- v2
5722 .ve
5723 
5724   (There is no significance to the ordering described here.)  The default section for a Q3 quad might typically assign
5725   dofs in the order of points, e.g.,
5726 .vb
5727     c0 -> [0,1,2,3]
5728     v1 -> [4]
5729     ...
5730     e5 -> [8, 9]
5731 .ve
5732 
5733   which corresponds to the dofs
5734 .vb
5735     6   10  11  7
5736     13  2   3   15
5737     12  0   1   14
5738     4   8   9   5
5739 .ve
5740 
5741   The closure in BFS ordering works through height strata (cells, edges, vertices) to produce the ordering
5742 .vb
5743   0 1 2 3 8 9 14 15 11 10 13 12 4 5 7 6
5744 .ve
5745 
5746   After calling DMPlexSetClosurePermutationTensor(), the closure will be ordered lexicographically,
5747 .vb
5748    4 8 9 5 12 0 1 14 13 2 3 15 6 10 11 7
5749 .ve
5750 
5751   Level: developer
5752 
5753   Notes:
5754   The point is used to determine the number of dofs/field on an edge. For SEM, this is related to the polynomial
5755   degree of the basis.
5756 
5757   This is required to run with libCEED.
5758 
5759 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMGetLocalSection()`, `PetscSectionSetClosurePermutation()`, `DMSetGlobalSection()`
5760 @*/
5761 PetscErrorCode DMPlexSetClosurePermutationTensor(DM dm, PetscInt point, PetscSection section)
5762 {
5763   DMLabel   label;
5764   PetscInt  dim, depth = -1, eStart = -1, Nf;
5765   PetscBool continuous = PETSC_TRUE;
5766 
5767   PetscFunctionBegin;
5768   PetscCall(DMGetDimension(dm, &dim));
5769   if (dim < 1) PetscFunctionReturn(PETSC_SUCCESS);
5770   if (point < 0) {
5771     PetscInt sStart, sEnd;
5772 
5773     PetscCall(DMPlexGetDepthStratum(dm, 1, &sStart, &sEnd));
5774     point = sEnd - sStart ? sStart : point;
5775   }
5776   PetscCall(DMPlexGetDepthLabel(dm, &label));
5777   if (point >= 0) PetscCall(DMLabelGetValue(label, point, &depth));
5778   if (!section) PetscCall(DMGetLocalSection(dm, &section));
5779   if (depth == 1) {
5780     eStart = point;
5781   } else if (depth == dim) {
5782     const PetscInt *cone;
5783 
5784     PetscCall(DMPlexGetCone(dm, point, &cone));
5785     if (dim == 2) eStart = cone[0];
5786     else if (dim == 3) {
5787       const PetscInt *cone2;
5788       PetscCall(DMPlexGetCone(dm, cone[0], &cone2));
5789       eStart = cone2[0];
5790     } 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);
5791   } 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);
5792 
5793   PetscCall(PetscSectionGetNumFields(section, &Nf));
5794   for (PetscInt d = 1; d <= dim; d++) {
5795     PetscInt  k, f, Nc, c, i, j, size = 0, offset = 0, foffset = 0;
5796     PetscInt *perm;
5797 
5798     for (f = 0; f < Nf; ++f) {
5799       PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous));
5800       size += PetscPowInt(k + 1, d) * Nc;
5801     }
5802     PetscCall(PetscMalloc1(size, &perm));
5803     for (f = 0; f < Nf; ++f) {
5804       switch (d) {
5805       case 1:
5806         PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous));
5807         /*
5808          Original ordering is [ edge of length k-1; vtx0; vtx1 ]
5809          We want              [ vtx0; edge of length k-1; vtx1 ]
5810          */
5811         if (continuous) {
5812           for (c = 0; c < Nc; c++, offset++) perm[offset] = (k - 1) * Nc + c + foffset;
5813           for (i = 0; i < k - 1; i++)
5814             for (c = 0; c < Nc; c++, offset++) perm[offset] = i * Nc + c + foffset;
5815           for (c = 0; c < Nc; c++, offset++) perm[offset] = k * Nc + c + foffset;
5816           foffset = offset;
5817         } else {
5818           for (i = offset; i < size; i++) perm[i] = i - offset + foffset;
5819           foffset = offset = size;
5820         }
5821         break;
5822       case 2:
5823         /* The original quad closure is oriented clockwise, {f, e_b, e_r, e_t, e_l, v_lb, v_rb, v_tr, v_tl} */
5824         PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous));
5825         /* The SEM order is
5826 
5827          v_lb, {e_b}, v_rb,
5828          e^{(k-1)-i}_l, {f^{i*(k-1)}}, e^i_r,
5829          v_lt, reverse {e_t}, v_rt
5830          */
5831         if (continuous) {
5832           const PetscInt of   = 0;
5833           const PetscInt oeb  = of + PetscSqr(k - 1);
5834           const PetscInt oer  = oeb + (k - 1);
5835           const PetscInt oet  = oer + (k - 1);
5836           const PetscInt oel  = oet + (k - 1);
5837           const PetscInt ovlb = oel + (k - 1);
5838           const PetscInt ovrb = ovlb + 1;
5839           const PetscInt ovrt = ovrb + 1;
5840           const PetscInt ovlt = ovrt + 1;
5841           PetscInt       o;
5842 
5843           /* bottom */
5844           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlb * Nc + c + foffset;
5845           for (o = oeb; o < oer; ++o)
5846             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5847           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrb * Nc + c + foffset;
5848           /* middle */
5849           for (i = 0; i < k - 1; ++i) {
5850             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oel + (k - 2) - i) * Nc + c + foffset;
5851             for (o = of + (k - 1) * i; o < of + (k - 1) * (i + 1); ++o)
5852               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5853             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oer + i) * Nc + c + foffset;
5854           }
5855           /* top */
5856           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlt * Nc + c + foffset;
5857           for (o = oel - 1; o >= oet; --o)
5858             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5859           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrt * Nc + c + foffset;
5860           foffset = offset;
5861         } else {
5862           for (i = offset; i < size; i++) perm[i] = i - offset + foffset;
5863           foffset = offset = size;
5864         }
5865         break;
5866       case 3:
5867         /* The original hex closure is
5868 
5869          {c,
5870          f_b, f_t, f_f, f_b, f_r, f_l,
5871          e_bl, e_bb, e_br, e_bf,  e_tf, e_tr, e_tb, e_tl,  e_rf, e_lf, e_lb, e_rb,
5872          v_blf, v_blb, v_brb, v_brf, v_tlf, v_trf, v_trb, v_tlb}
5873          */
5874         PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous));
5875         /* The SEM order is
5876          Bottom Slice
5877          v_blf, {e^{(k-1)-n}_bf}, v_brf,
5878          e^{i}_bl, f^{n*(k-1)+(k-1)-i}_b, e^{(k-1)-i}_br,
5879          v_blb, {e_bb}, v_brb,
5880 
5881          Middle Slice (j)
5882          {e^{(k-1)-j}_lf}, {f^{j*(k-1)+n}_f}, e^j_rf,
5883          f^{i*(k-1)+j}_l, {c^{(j*(k-1) + i)*(k-1)+n}_t}, f^{j*(k-1)+i}_r,
5884          e^j_lb, {f^{j*(k-1)+(k-1)-n}_b}, e^{(k-1)-j}_rb,
5885 
5886          Top Slice
5887          v_tlf, {e_tf}, v_trf,
5888          e^{(k-1)-i}_tl, {f^{i*(k-1)}_t}, e^{i}_tr,
5889          v_tlb, {e^{(k-1)-n}_tb}, v_trb,
5890          */
5891         if (continuous) {
5892           const PetscInt oc    = 0;
5893           const PetscInt ofb   = oc + PetscSqr(k - 1) * (k - 1);
5894           const PetscInt oft   = ofb + PetscSqr(k - 1);
5895           const PetscInt off   = oft + PetscSqr(k - 1);
5896           const PetscInt ofk   = off + PetscSqr(k - 1);
5897           const PetscInt ofr   = ofk + PetscSqr(k - 1);
5898           const PetscInt ofl   = ofr + PetscSqr(k - 1);
5899           const PetscInt oebl  = ofl + PetscSqr(k - 1);
5900           const PetscInt oebb  = oebl + (k - 1);
5901           const PetscInt oebr  = oebb + (k - 1);
5902           const PetscInt oebf  = oebr + (k - 1);
5903           const PetscInt oetf  = oebf + (k - 1);
5904           const PetscInt oetr  = oetf + (k - 1);
5905           const PetscInt oetb  = oetr + (k - 1);
5906           const PetscInt oetl  = oetb + (k - 1);
5907           const PetscInt oerf  = oetl + (k - 1);
5908           const PetscInt oelf  = oerf + (k - 1);
5909           const PetscInt oelb  = oelf + (k - 1);
5910           const PetscInt oerb  = oelb + (k - 1);
5911           const PetscInt ovblf = oerb + (k - 1);
5912           const PetscInt ovblb = ovblf + 1;
5913           const PetscInt ovbrb = ovblb + 1;
5914           const PetscInt ovbrf = ovbrb + 1;
5915           const PetscInt ovtlf = ovbrf + 1;
5916           const PetscInt ovtrf = ovtlf + 1;
5917           const PetscInt ovtrb = ovtrf + 1;
5918           const PetscInt ovtlb = ovtrb + 1;
5919           PetscInt       o, n;
5920 
5921           /* Bottom Slice */
5922           /*   bottom */
5923           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblf * Nc + c + foffset;
5924           for (o = oetf - 1; o >= oebf; --o)
5925             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5926           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrf * Nc + c + foffset;
5927           /*   middle */
5928           for (i = 0; i < k - 1; ++i) {
5929             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebl + i) * Nc + c + foffset;
5930             for (n = 0; n < k - 1; ++n) {
5931               o = ofb + n * (k - 1) + i;
5932               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5933             }
5934             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebr + (k - 2) - i) * Nc + c + foffset;
5935           }
5936           /*   top */
5937           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblb * Nc + c + foffset;
5938           for (o = oebb; o < oebr; ++o)
5939             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5940           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrb * Nc + c + foffset;
5941 
5942           /* Middle Slice */
5943           for (j = 0; j < k - 1; ++j) {
5944             /*   bottom */
5945             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelf + (k - 2) - j) * Nc + c + foffset;
5946             for (o = off + j * (k - 1); o < off + (j + 1) * (k - 1); ++o)
5947               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5948             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerf + j) * Nc + c + foffset;
5949             /*   middle */
5950             for (i = 0; i < k - 1; ++i) {
5951               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofl + i * (k - 1) + j) * Nc + c + foffset;
5952               for (n = 0; n < k - 1; ++n)
5953                 for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oc + (j * (k - 1) + i) * (k - 1) + n) * Nc + c + foffset;
5954               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofr + j * (k - 1) + i) * Nc + c + foffset;
5955             }
5956             /*   top */
5957             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelb + j) * Nc + c + foffset;
5958             for (o = ofk + j * (k - 1) + (k - 2); o >= ofk + j * (k - 1); --o)
5959               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5960             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerb + (k - 2) - j) * Nc + c + foffset;
5961           }
5962 
5963           /* Top Slice */
5964           /*   bottom */
5965           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlf * Nc + c + foffset;
5966           for (o = oetf; o < oetr; ++o)
5967             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5968           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrf * Nc + c + foffset;
5969           /*   middle */
5970           for (i = 0; i < k - 1; ++i) {
5971             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetl + (k - 2) - i) * Nc + c + foffset;
5972             for (n = 0; n < k - 1; ++n)
5973               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oft + i * (k - 1) + n) * Nc + c + foffset;
5974             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetr + i) * Nc + c + foffset;
5975           }
5976           /*   top */
5977           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlb * Nc + c + foffset;
5978           for (o = oetl - 1; o >= oetb; --o)
5979             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5980           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrb * Nc + c + foffset;
5981 
5982           foffset = offset;
5983         } else {
5984           for (i = offset; i < size; i++) perm[i] = i - offset + foffset;
5985           foffset = offset = size;
5986         }
5987         break;
5988       default:
5989         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "No spectral ordering for dimension %" PetscInt_FMT, d);
5990       }
5991     }
5992     PetscCheck(offset == size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Number of permutation entries %" PetscInt_FMT " != %" PetscInt_FMT, offset, size);
5993     /* Check permutation */
5994     {
5995       PetscInt *check;
5996 
5997       PetscCall(PetscMalloc1(size, &check));
5998       for (i = 0; i < size; ++i) {
5999         check[i] = -1;
6000         PetscCheck(perm[i] >= 0 && perm[i] < size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid permutation index p[%" PetscInt_FMT "] = %" PetscInt_FMT, i, perm[i]);
6001       }
6002       for (i = 0; i < size; ++i) check[perm[i]] = i;
6003       for (i = 0; i < size; ++i) PetscCheck(check[i] >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Missing permutation index %" PetscInt_FMT, i);
6004       PetscCall(PetscFree(check));
6005     }
6006     PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size, PETSC_OWN_POINTER, perm));
6007     if (d == dim) { // Add permutation for localized (in case this is a coordinate DM)
6008       PetscInt *loc_perm;
6009       PetscCall(PetscMalloc1(size * 2, &loc_perm));
6010       for (PetscInt i = 0; i < size; i++) {
6011         loc_perm[i]        = perm[i];
6012         loc_perm[size + i] = size + perm[i];
6013       }
6014       PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size * 2, PETSC_OWN_POINTER, loc_perm));
6015     }
6016   }
6017   PetscFunctionReturn(PETSC_SUCCESS);
6018 }
6019 
6020 PetscErrorCode DMPlexGetPointDualSpaceFEM(DM dm, PetscInt point, PetscInt field, PetscDualSpace *dspace)
6021 {
6022   PetscDS  prob;
6023   PetscInt depth, Nf, h;
6024   DMLabel  label;
6025 
6026   PetscFunctionBeginHot;
6027   PetscCall(DMGetDS(dm, &prob));
6028   Nf      = prob->Nf;
6029   label   = dm->depthLabel;
6030   *dspace = NULL;
6031   if (field < Nf) {
6032     PetscObject disc = prob->disc[field];
6033 
6034     if (disc->classid == PETSCFE_CLASSID) {
6035       PetscDualSpace dsp;
6036 
6037       PetscCall(PetscFEGetDualSpace((PetscFE)disc, &dsp));
6038       PetscCall(DMLabelGetNumValues(label, &depth));
6039       PetscCall(DMLabelGetValue(label, point, &h));
6040       h = depth - 1 - h;
6041       if (h) {
6042         PetscCall(PetscDualSpaceGetHeightSubspace(dsp, h, dspace));
6043       } else {
6044         *dspace = dsp;
6045       }
6046     }
6047   }
6048   PetscFunctionReturn(PETSC_SUCCESS);
6049 }
6050 
6051 static inline PetscErrorCode DMPlexVecGetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6052 {
6053   PetscScalar       *array;
6054   const PetscScalar *vArray;
6055   const PetscInt    *cone, *coneO;
6056   PetscInt           pStart, pEnd, p, numPoints, size = 0, offset = 0;
6057 
6058   PetscFunctionBeginHot;
6059   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6060   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
6061   PetscCall(DMPlexGetCone(dm, point, &cone));
6062   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
6063   if (!values || !*values) {
6064     if ((point >= pStart) && (point < pEnd)) {
6065       PetscInt dof;
6066 
6067       PetscCall(PetscSectionGetDof(section, point, &dof));
6068       size += dof;
6069     }
6070     for (p = 0; p < numPoints; ++p) {
6071       const PetscInt cp = cone[p];
6072       PetscInt       dof;
6073 
6074       if ((cp < pStart) || (cp >= pEnd)) continue;
6075       PetscCall(PetscSectionGetDof(section, cp, &dof));
6076       size += dof;
6077     }
6078     if (!values) {
6079       if (csize) *csize = size;
6080       PetscFunctionReturn(PETSC_SUCCESS);
6081     }
6082     PetscCall(DMGetWorkArray(dm, size, MPIU_SCALAR, &array));
6083   } else {
6084     array = *values;
6085   }
6086   size = 0;
6087   PetscCall(VecGetArrayRead(v, &vArray));
6088   if ((point >= pStart) && (point < pEnd)) {
6089     PetscInt           dof, off, d;
6090     const PetscScalar *varr;
6091 
6092     PetscCall(PetscSectionGetDof(section, point, &dof));
6093     PetscCall(PetscSectionGetOffset(section, point, &off));
6094     varr = PetscSafePointerPlusOffset(vArray, off);
6095     for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
6096     size += dof;
6097   }
6098   for (p = 0; p < numPoints; ++p) {
6099     const PetscInt     cp = cone[p];
6100     PetscInt           o  = coneO[p];
6101     PetscInt           dof, off, d;
6102     const PetscScalar *varr;
6103 
6104     if ((cp < pStart) || (cp >= pEnd)) continue;
6105     PetscCall(PetscSectionGetDof(section, cp, &dof));
6106     PetscCall(PetscSectionGetOffset(section, cp, &off));
6107     varr = PetscSafePointerPlusOffset(vArray, off);
6108     if (o >= 0) {
6109       for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
6110     } else {
6111       for (d = dof - 1; d >= 0; --d, ++offset) array[offset] = varr[d];
6112     }
6113     size += dof;
6114   }
6115   PetscCall(VecRestoreArrayRead(v, &vArray));
6116   if (!*values) {
6117     if (csize) *csize = size;
6118     *values = array;
6119   } else {
6120     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
6121     *csize = size;
6122   }
6123   PetscFunctionReturn(PETSC_SUCCESS);
6124 }
6125 
6126 /* Compress out points not in the section */
6127 static inline PetscErrorCode CompressPoints_Private(PetscSection section, PetscInt *numPoints, PetscInt points[])
6128 {
6129   const PetscInt np = *numPoints;
6130   PetscInt       pStart, pEnd, p, q;
6131 
6132   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6133   for (p = 0, q = 0; p < np; ++p) {
6134     const PetscInt r = points[p * 2];
6135     if ((r >= pStart) && (r < pEnd)) {
6136       points[q * 2]     = r;
6137       points[q * 2 + 1] = points[p * 2 + 1];
6138       ++q;
6139     }
6140   }
6141   *numPoints = q;
6142   return PETSC_SUCCESS;
6143 }
6144 
6145 /* Compressed closure does not apply closure permutation */
6146 PetscErrorCode DMPlexGetCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt ornt, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
6147 {
6148   const PetscInt *cla = NULL;
6149   PetscInt        np, *pts = NULL;
6150 
6151   PetscFunctionBeginHot;
6152   PetscCall(PetscSectionGetClosureIndex(section, (PetscObject)dm, clSec, clPoints));
6153   if (!ornt && *clPoints) {
6154     PetscInt dof, off;
6155 
6156     PetscCall(PetscSectionGetDof(*clSec, point, &dof));
6157     PetscCall(PetscSectionGetOffset(*clSec, point, &off));
6158     PetscCall(ISGetIndices(*clPoints, &cla));
6159     np  = dof / 2;
6160     pts = PetscSafePointerPlusOffset((PetscInt *)cla, off);
6161   } else {
6162     PetscCall(DMPlexGetTransitiveClosure_Internal(dm, point, ornt, PETSC_TRUE, &np, &pts));
6163     PetscCall(CompressPoints_Private(section, &np, pts));
6164   }
6165   *numPoints = np;
6166   *points    = pts;
6167   *clp       = cla;
6168   PetscFunctionReturn(PETSC_SUCCESS);
6169 }
6170 
6171 PetscErrorCode DMPlexRestoreCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
6172 {
6173   PetscFunctionBeginHot;
6174   if (!*clPoints) {
6175     PetscCall(DMPlexRestoreTransitiveClosure(dm, point, PETSC_TRUE, numPoints, points));
6176   } else {
6177     PetscCall(ISRestoreIndices(*clPoints, clp));
6178   }
6179   *numPoints = 0;
6180   *points    = NULL;
6181   *clSec     = NULL;
6182   *clPoints  = NULL;
6183   *clp       = NULL;
6184   PetscFunctionReturn(PETSC_SUCCESS);
6185 }
6186 
6187 static inline PetscErrorCode DMPlexVecGetClosure_Static(DM dm, PetscSection section, PetscInt numPoints, const PetscInt points[], const PetscInt clperm[], const PetscScalar vArray[], PetscInt *size, PetscScalar array[])
6188 {
6189   PetscInt            offset = 0, p;
6190   const PetscInt    **perms  = NULL;
6191   const PetscScalar **flips  = NULL;
6192 
6193   PetscFunctionBeginHot;
6194   *size = 0;
6195   PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
6196   for (p = 0; p < numPoints; p++) {
6197     const PetscInt     point = points[2 * p];
6198     const PetscInt    *perm  = perms ? perms[p] : NULL;
6199     const PetscScalar *flip  = flips ? flips[p] : NULL;
6200     PetscInt           dof, off, d;
6201     const PetscScalar *varr;
6202 
6203     PetscCall(PetscSectionGetDof(section, point, &dof));
6204     PetscCall(PetscSectionGetOffset(section, point, &off));
6205     varr = PetscSafePointerPlusOffset(vArray, off);
6206     if (clperm) {
6207       if (perm) {
6208         for (d = 0; d < dof; d++) array[clperm[offset + perm[d]]] = varr[d];
6209       } else {
6210         for (d = 0; d < dof; d++) array[clperm[offset + d]] = varr[d];
6211       }
6212       if (flip) {
6213         for (d = 0; d < dof; d++) array[clperm[offset + d]] *= flip[d];
6214       }
6215     } else {
6216       if (perm) {
6217         for (d = 0; d < dof; d++) array[offset + perm[d]] = varr[d];
6218       } else {
6219         for (d = 0; d < dof; d++) array[offset + d] = varr[d];
6220       }
6221       if (flip) {
6222         for (d = 0; d < dof; d++) array[offset + d] *= flip[d];
6223       }
6224     }
6225     offset += dof;
6226   }
6227   PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
6228   *size = offset;
6229   PetscFunctionReturn(PETSC_SUCCESS);
6230 }
6231 
6232 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[])
6233 {
6234   PetscInt offset = 0, f;
6235 
6236   PetscFunctionBeginHot;
6237   *size = 0;
6238   for (f = 0; f < numFields; ++f) {
6239     PetscInt            p;
6240     const PetscInt    **perms = NULL;
6241     const PetscScalar **flips = NULL;
6242 
6243     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6244     for (p = 0; p < numPoints; p++) {
6245       const PetscInt     point = points[2 * p];
6246       PetscInt           fdof, foff, b;
6247       const PetscScalar *varr;
6248       const PetscInt    *perm = perms ? perms[p] : NULL;
6249       const PetscScalar *flip = flips ? flips[p] : NULL;
6250 
6251       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6252       PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6253       varr = &vArray[foff];
6254       if (clperm) {
6255         if (perm) {
6256           for (b = 0; b < fdof; b++) array[clperm[offset + perm[b]]] = varr[b];
6257         } else {
6258           for (b = 0; b < fdof; b++) array[clperm[offset + b]] = varr[b];
6259         }
6260         if (flip) {
6261           for (b = 0; b < fdof; b++) array[clperm[offset + b]] *= flip[b];
6262         }
6263       } else {
6264         if (perm) {
6265           for (b = 0; b < fdof; b++) array[offset + perm[b]] = varr[b];
6266         } else {
6267           for (b = 0; b < fdof; b++) array[offset + b] = varr[b];
6268         }
6269         if (flip) {
6270           for (b = 0; b < fdof; b++) array[offset + b] *= flip[b];
6271         }
6272       }
6273       offset += fdof;
6274     }
6275     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6276   }
6277   *size = offset;
6278   PetscFunctionReturn(PETSC_SUCCESS);
6279 }
6280 
6281 PetscErrorCode DMPlexVecGetOrientedClosure_Internal(DM dm, PetscSection section, PetscBool useClPerm, Vec v, PetscInt point, PetscInt ornt, PetscInt *csize, PetscScalar *values[])
6282 {
6283   PetscSection    clSection;
6284   IS              clPoints;
6285   PetscInt       *points = NULL;
6286   const PetscInt *clp, *perm = NULL;
6287   PetscInt        depth, numFields, numPoints, asize;
6288 
6289   PetscFunctionBeginHot;
6290   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6291   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6292   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6293   PetscValidHeaderSpecific(v, VEC_CLASSID, 4);
6294   PetscCall(DMPlexGetDepth(dm, &depth));
6295   PetscCall(PetscSectionGetNumFields(section, &numFields));
6296   if (depth == 1 && numFields < 2) {
6297     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6298     PetscFunctionReturn(PETSC_SUCCESS);
6299   }
6300   /* Get points */
6301   PetscCall(DMPlexGetCompressedClosure(dm, section, point, ornt, &numPoints, &points, &clSection, &clPoints, &clp));
6302   /* Get sizes */
6303   asize = 0;
6304   for (PetscInt p = 0; p < numPoints * 2; p += 2) {
6305     PetscInt dof;
6306     PetscCall(PetscSectionGetDof(section, points[p], &dof));
6307     asize += dof;
6308   }
6309   if (values) {
6310     const PetscScalar *vArray;
6311     PetscInt           size;
6312 
6313     if (*values) {
6314       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);
6315     } else PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, values));
6316     if (useClPerm) PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, asize, &perm));
6317     PetscCall(VecGetArrayRead(v, &vArray));
6318     /* Get values */
6319     if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, numPoints, points, numFields, perm, vArray, &size, *values));
6320     else PetscCall(DMPlexVecGetClosure_Static(dm, section, numPoints, points, perm, vArray, &size, *values));
6321     PetscCheck(asize == size, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Section size %" PetscInt_FMT " does not match Vec closure size %" PetscInt_FMT, asize, size);
6322     /* Cleanup array */
6323     PetscCall(VecRestoreArrayRead(v, &vArray));
6324   }
6325   if (csize) *csize = asize;
6326   /* Cleanup points */
6327   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6328   PetscFunctionReturn(PETSC_SUCCESS);
6329 }
6330 
6331 /*@C
6332   DMPlexVecGetClosure - Get an array of the values on the closure of 'point'
6333 
6334   Not collective
6335 
6336   Input Parameters:
6337 + dm      - The `DM`
6338 . section - The section describing the layout in `v`, or `NULL` to use the default section
6339 . v       - The local vector
6340 - point   - The point in the `DM`
6341 
6342   Input/Output Parameters:
6343 + csize  - The size of the input values array, or `NULL`; on output the number of values in the closure
6344 - values - An array to use for the values, or `NULL` to have it allocated automatically;
6345            if the user provided `NULL`, it is a borrowed array and should not be freed
6346 
6347   Level: intermediate
6348 
6349   Notes:
6350   `DMPlexVecGetClosure()`/`DMPlexVecRestoreClosure()` only allocates the values array if it set to `NULL` in the
6351   calling function. This is because `DMPlexVecGetClosure()` is typically called in the inner loop of a `Vec` or `Mat`
6352   assembly function, and a user may already have allocated storage for this operation.
6353 
6354   A typical use could be
6355 .vb
6356    values = NULL;
6357    PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
6358    for (cl = 0; cl < clSize; ++cl) {
6359      <Compute on closure>
6360    }
6361    PetscCall(DMPlexVecRestoreClosure(dm, NULL, v, p, &clSize, &values));
6362 .ve
6363   or
6364 .vb
6365    PetscMalloc1(clMaxSize, &values);
6366    for (p = pStart; p < pEnd; ++p) {
6367      clSize = clMaxSize;
6368      PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
6369      for (cl = 0; cl < clSize; ++cl) {
6370        <Compute on closure>
6371      }
6372    }
6373    PetscFree(values);
6374 .ve
6375 
6376   Fortran Notes:
6377   The `csize` argument is not present in the Fortran binding since it is internal to the array.
6378 
6379 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecRestoreClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6380 @*/
6381 PetscErrorCode DMPlexVecGetClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6382 {
6383   PetscFunctionBeginHot;
6384   PetscCall(DMPlexVecGetOrientedClosure_Internal(dm, section, PETSC_TRUE, v, point, 0, csize, values));
6385   PetscFunctionReturn(PETSC_SUCCESS);
6386 }
6387 
6388 PetscErrorCode DMPlexVecGetClosureAtDepth_Internal(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt depth, PetscInt *csize, PetscScalar *values[])
6389 {
6390   DMLabel            depthLabel;
6391   PetscSection       clSection;
6392   IS                 clPoints;
6393   PetscScalar       *array;
6394   const PetscScalar *vArray;
6395   PetscInt          *points = NULL;
6396   const PetscInt    *clp, *perm = NULL;
6397   PetscInt           mdepth, numFields, numPoints, Np = 0, p, clsize, size;
6398 
6399   PetscFunctionBeginHot;
6400   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6401   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6402   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6403   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6404   PetscCall(DMPlexGetDepth(dm, &mdepth));
6405   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
6406   PetscCall(PetscSectionGetNumFields(section, &numFields));
6407   if (mdepth == 1 && numFields < 2) {
6408     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6409     PetscFunctionReturn(PETSC_SUCCESS);
6410   }
6411   /* Get points */
6412   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
6413   for (clsize = 0, p = 0; p < Np; p++) {
6414     PetscInt dof;
6415     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
6416     clsize += dof;
6417   }
6418   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &perm));
6419   /* Filter points */
6420   for (p = 0; p < numPoints * 2; p += 2) {
6421     PetscInt dep;
6422 
6423     PetscCall(DMLabelGetValue(depthLabel, points[p], &dep));
6424     if (dep != depth) continue;
6425     points[Np * 2 + 0] = points[p];
6426     points[Np * 2 + 1] = points[p + 1];
6427     ++Np;
6428   }
6429   /* Get array */
6430   if (!values || !*values) {
6431     PetscInt asize = 0, dof;
6432 
6433     for (p = 0; p < Np * 2; p += 2) {
6434       PetscCall(PetscSectionGetDof(section, points[p], &dof));
6435       asize += dof;
6436     }
6437     if (!values) {
6438       PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6439       if (csize) *csize = asize;
6440       PetscFunctionReturn(PETSC_SUCCESS);
6441     }
6442     PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, &array));
6443   } else {
6444     array = *values;
6445   }
6446   PetscCall(VecGetArrayRead(v, &vArray));
6447   /* Get values */
6448   if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, Np, points, numFields, perm, vArray, &size, array));
6449   else PetscCall(DMPlexVecGetClosure_Static(dm, section, Np, points, perm, vArray, &size, array));
6450   /* Cleanup points */
6451   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6452   /* Cleanup array */
6453   PetscCall(VecRestoreArrayRead(v, &vArray));
6454   if (!*values) {
6455     if (csize) *csize = size;
6456     *values = array;
6457   } else {
6458     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
6459     *csize = size;
6460   }
6461   PetscFunctionReturn(PETSC_SUCCESS);
6462 }
6463 
6464 /*@C
6465   DMPlexVecRestoreClosure - Restore the array of the values on the closure of 'point'
6466 
6467   Not collective
6468 
6469   Input Parameters:
6470 + dm      - The `DM`
6471 . section - The section describing the layout in `v`, or `NULL` to use the default section
6472 . v       - The local vector
6473 . point   - The point in the `DM`
6474 . csize   - The number of values in the closure, or `NULL`
6475 - values  - The array of values, which is a borrowed array and should not be freed
6476 
6477   Level: intermediate
6478 
6479   Note:
6480   The array values are discarded and not copied back into `v`. In order to copy values back to `v`, use `DMPlexVecSetClosure()`
6481 
6482   Fortran Notes:
6483   The `csize` argument is not present in the Fortran binding since it is internal to the array.
6484 
6485 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6486 @*/
6487 PetscErrorCode DMPlexVecRestoreClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6488 {
6489   PetscInt size = 0;
6490 
6491   PetscFunctionBegin;
6492   /* Should work without recalculating size */
6493   PetscCall(DMRestoreWorkArray(dm, size, MPIU_SCALAR, (void *)values));
6494   *values = NULL;
6495   PetscFunctionReturn(PETSC_SUCCESS);
6496 }
6497 
6498 static inline void add(PetscScalar *x, PetscScalar y)
6499 {
6500   *x += y;
6501 }
6502 static inline void insert(PetscScalar *x, PetscScalar y)
6503 {
6504   *x = y;
6505 }
6506 
6507 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[])
6508 {
6509   PetscInt        cdof;  /* The number of constraints on this point */
6510   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6511   PetscScalar    *a;
6512   PetscInt        off, cind = 0, k;
6513 
6514   PetscFunctionBegin;
6515   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6516   PetscCall(PetscSectionGetOffset(section, point, &off));
6517   a = &array[off];
6518   if (!cdof || setBC) {
6519     if (clperm) {
6520       if (perm) {
6521         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6522       } else {
6523         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6524       }
6525     } else {
6526       if (perm) {
6527         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6528       } else {
6529         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6530       }
6531     }
6532   } else {
6533     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6534     if (clperm) {
6535       if (perm) {
6536         for (k = 0; k < dof; ++k) {
6537           if ((cind < cdof) && (k == cdofs[cind])) {
6538             ++cind;
6539             continue;
6540           }
6541           fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6542         }
6543       } else {
6544         for (k = 0; k < dof; ++k) {
6545           if ((cind < cdof) && (k == cdofs[cind])) {
6546             ++cind;
6547             continue;
6548           }
6549           fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6550         }
6551       }
6552     } else {
6553       if (perm) {
6554         for (k = 0; k < dof; ++k) {
6555           if ((cind < cdof) && (k == cdofs[cind])) {
6556             ++cind;
6557             continue;
6558           }
6559           fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6560         }
6561       } else {
6562         for (k = 0; k < dof; ++k) {
6563           if ((cind < cdof) && (k == cdofs[cind])) {
6564             ++cind;
6565             continue;
6566           }
6567           fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6568         }
6569       }
6570     }
6571   }
6572   PetscFunctionReturn(PETSC_SUCCESS);
6573 }
6574 
6575 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[])
6576 {
6577   PetscInt        cdof;  /* The number of constraints on this point */
6578   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6579   PetscScalar    *a;
6580   PetscInt        off, cind = 0, k;
6581 
6582   PetscFunctionBegin;
6583   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6584   PetscCall(PetscSectionGetOffset(section, point, &off));
6585   a = &array[off];
6586   if (cdof) {
6587     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6588     if (clperm) {
6589       if (perm) {
6590         for (k = 0; k < dof; ++k) {
6591           if ((cind < cdof) && (k == cdofs[cind])) {
6592             fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6593             cind++;
6594           }
6595         }
6596       } else {
6597         for (k = 0; k < dof; ++k) {
6598           if ((cind < cdof) && (k == cdofs[cind])) {
6599             fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6600             cind++;
6601           }
6602         }
6603       }
6604     } else {
6605       if (perm) {
6606         for (k = 0; k < dof; ++k) {
6607           if ((cind < cdof) && (k == cdofs[cind])) {
6608             fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6609             cind++;
6610           }
6611         }
6612       } else {
6613         for (k = 0; k < dof; ++k) {
6614           if ((cind < cdof) && (k == cdofs[cind])) {
6615             fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6616             cind++;
6617           }
6618         }
6619       }
6620     }
6621   }
6622   PetscFunctionReturn(PETSC_SUCCESS);
6623 }
6624 
6625 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[])
6626 {
6627   PetscScalar    *a;
6628   PetscInt        fdof, foff, fcdof, foffset = *offset;
6629   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6630   PetscInt        cind = 0, b;
6631 
6632   PetscFunctionBegin;
6633   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6634   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6635   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6636   a = &array[foff];
6637   if (!fcdof || setBC) {
6638     if (clperm) {
6639       if (perm) {
6640         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6641       } else {
6642         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6643       }
6644     } else {
6645       if (perm) {
6646         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6647       } else {
6648         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6649       }
6650     }
6651   } else {
6652     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6653     if (clperm) {
6654       if (perm) {
6655         for (b = 0; b < fdof; b++) {
6656           if ((cind < fcdof) && (b == fcdofs[cind])) {
6657             ++cind;
6658             continue;
6659           }
6660           fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6661         }
6662       } else {
6663         for (b = 0; b < fdof; b++) {
6664           if ((cind < fcdof) && (b == fcdofs[cind])) {
6665             ++cind;
6666             continue;
6667           }
6668           fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6669         }
6670       }
6671     } else {
6672       if (perm) {
6673         for (b = 0; b < fdof; b++) {
6674           if ((cind < fcdof) && (b == fcdofs[cind])) {
6675             ++cind;
6676             continue;
6677           }
6678           fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6679         }
6680       } else {
6681         for (b = 0; b < fdof; b++) {
6682           if ((cind < fcdof) && (b == fcdofs[cind])) {
6683             ++cind;
6684             continue;
6685           }
6686           fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6687         }
6688       }
6689     }
6690   }
6691   *offset += fdof;
6692   PetscFunctionReturn(PETSC_SUCCESS);
6693 }
6694 
6695 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[])
6696 {
6697   PetscScalar    *a;
6698   PetscInt        fdof, foff, fcdof, foffset = *offset;
6699   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6700   PetscInt        Nc, cind = 0, ncind = 0, b;
6701   PetscBool       ncSet, fcSet;
6702 
6703   PetscFunctionBegin;
6704   PetscCall(PetscSectionGetFieldComponents(section, f, &Nc));
6705   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6706   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6707   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6708   a = &array[foff];
6709   if (fcdof) {
6710     /* We just override fcdof and fcdofs with Ncc and comps */
6711     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6712     if (clperm) {
6713       if (perm) {
6714         if (comps) {
6715           for (b = 0; b < fdof; b++) {
6716             ncSet = fcSet = PETSC_FALSE;
6717             if (b % Nc == comps[ncind]) {
6718               ncind = (ncind + 1) % Ncc;
6719               ncSet = PETSC_TRUE;
6720             }
6721             if ((cind < fcdof) && (b == fcdofs[cind])) {
6722               ++cind;
6723               fcSet = PETSC_TRUE;
6724             }
6725             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6726           }
6727         } else {
6728           for (b = 0; b < fdof; b++) {
6729             if ((cind < fcdof) && (b == fcdofs[cind])) {
6730               fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6731               ++cind;
6732             }
6733           }
6734         }
6735       } else {
6736         if (comps) {
6737           for (b = 0; b < fdof; b++) {
6738             ncSet = fcSet = PETSC_FALSE;
6739             if (b % Nc == comps[ncind]) {
6740               ncind = (ncind + 1) % Ncc;
6741               ncSet = PETSC_TRUE;
6742             }
6743             if ((cind < fcdof) && (b == fcdofs[cind])) {
6744               ++cind;
6745               fcSet = PETSC_TRUE;
6746             }
6747             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6748           }
6749         } else {
6750           for (b = 0; b < fdof; b++) {
6751             if ((cind < fcdof) && (b == fcdofs[cind])) {
6752               fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6753               ++cind;
6754             }
6755           }
6756         }
6757       }
6758     } else {
6759       if (perm) {
6760         if (comps) {
6761           for (b = 0; b < fdof; b++) {
6762             ncSet = fcSet = PETSC_FALSE;
6763             if (b % Nc == comps[ncind]) {
6764               ncind = (ncind + 1) % Ncc;
6765               ncSet = PETSC_TRUE;
6766             }
6767             if ((cind < fcdof) && (b == fcdofs[cind])) {
6768               ++cind;
6769               fcSet = PETSC_TRUE;
6770             }
6771             if (ncSet && fcSet) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6772           }
6773         } else {
6774           for (b = 0; b < fdof; b++) {
6775             if ((cind < fcdof) && (b == fcdofs[cind])) {
6776               fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6777               ++cind;
6778             }
6779           }
6780         }
6781       } else {
6782         if (comps) {
6783           for (b = 0; b < fdof; b++) {
6784             ncSet = fcSet = PETSC_FALSE;
6785             if (b % Nc == comps[ncind]) {
6786               ncind = (ncind + 1) % Ncc;
6787               ncSet = PETSC_TRUE;
6788             }
6789             if ((cind < fcdof) && (b == fcdofs[cind])) {
6790               ++cind;
6791               fcSet = PETSC_TRUE;
6792             }
6793             if (ncSet && fcSet) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6794           }
6795         } else {
6796           for (b = 0; b < fdof; b++) {
6797             if ((cind < fcdof) && (b == fcdofs[cind])) {
6798               fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6799               ++cind;
6800             }
6801           }
6802         }
6803       }
6804     }
6805   }
6806   *offset += fdof;
6807   PetscFunctionReturn(PETSC_SUCCESS);
6808 }
6809 
6810 static inline PetscErrorCode DMPlexVecSetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6811 {
6812   PetscScalar    *array;
6813   const PetscInt *cone, *coneO;
6814   PetscInt        pStart, pEnd, p, numPoints, off, dof;
6815 
6816   PetscFunctionBeginHot;
6817   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6818   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
6819   PetscCall(DMPlexGetCone(dm, point, &cone));
6820   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
6821   PetscCall(VecGetArray(v, &array));
6822   for (p = 0, off = 0; p <= numPoints; ++p, off += dof) {
6823     const PetscInt cp = !p ? point : cone[p - 1];
6824     const PetscInt o  = !p ? 0 : coneO[p - 1];
6825 
6826     if ((cp < pStart) || (cp >= pEnd)) {
6827       dof = 0;
6828       continue;
6829     }
6830     PetscCall(PetscSectionGetDof(section, cp, &dof));
6831     /* ADD_VALUES */
6832     {
6833       const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6834       PetscScalar    *a;
6835       PetscInt        cdof, coff, cind = 0, k;
6836 
6837       PetscCall(PetscSectionGetConstraintDof(section, cp, &cdof));
6838       PetscCall(PetscSectionGetOffset(section, cp, &coff));
6839       a = &array[coff];
6840       if (!cdof) {
6841         if (o >= 0) {
6842           for (k = 0; k < dof; ++k) a[k] += values[off + k];
6843         } else {
6844           for (k = 0; k < dof; ++k) a[k] += values[off + dof - k - 1];
6845         }
6846       } else {
6847         PetscCall(PetscSectionGetConstraintIndices(section, cp, &cdofs));
6848         if (o >= 0) {
6849           for (k = 0; k < dof; ++k) {
6850             if ((cind < cdof) && (k == cdofs[cind])) {
6851               ++cind;
6852               continue;
6853             }
6854             a[k] += values[off + k];
6855           }
6856         } else {
6857           for (k = 0; k < dof; ++k) {
6858             if ((cind < cdof) && (k == cdofs[cind])) {
6859               ++cind;
6860               continue;
6861             }
6862             a[k] += values[off + dof - k - 1];
6863           }
6864         }
6865       }
6866     }
6867   }
6868   PetscCall(VecRestoreArray(v, &array));
6869   PetscFunctionReturn(PETSC_SUCCESS);
6870 }
6871 
6872 /*@C
6873   DMPlexVecSetClosure - Set an array of the values on the closure of `point`
6874 
6875   Not collective
6876 
6877   Input Parameters:
6878 + dm      - The `DM`
6879 . section - The section describing the layout in `v`, or `NULL` to use the default section
6880 . v       - The local vector
6881 . point   - The point in the `DM`
6882 . values  - The array of values
6883 - mode    - The insert mode. One of `INSERT_ALL_VALUES`, `ADD_ALL_VALUES`, `INSERT_VALUES`, `ADD_VALUES`, `INSERT_BC_VALUES`, and `ADD_BC_VALUES`,
6884          where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions.
6885 
6886   Level: intermediate
6887 
6888 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`
6889 @*/
6890 PetscErrorCode DMPlexVecSetClosure(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6891 {
6892   PetscSection    clSection;
6893   IS              clPoints;
6894   PetscScalar    *array;
6895   PetscInt       *points = NULL;
6896   const PetscInt *clp, *clperm = NULL;
6897   PetscInt        depth, numFields, numPoints, p, clsize;
6898 
6899   PetscFunctionBeginHot;
6900   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6901   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6902   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6903   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6904   PetscCall(DMPlexGetDepth(dm, &depth));
6905   PetscCall(PetscSectionGetNumFields(section, &numFields));
6906   if (depth == 1 && numFields < 2 && mode == ADD_VALUES) {
6907     PetscCall(DMPlexVecSetClosure_Depth1_Static(dm, section, v, point, values, mode));
6908     PetscFunctionReturn(PETSC_SUCCESS);
6909   }
6910   /* Get points */
6911   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
6912   for (clsize = 0, p = 0; p < numPoints; p++) {
6913     PetscInt dof;
6914     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
6915     clsize += dof;
6916   }
6917   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
6918   /* Get array */
6919   PetscCall(VecGetArray(v, &array));
6920   /* Get values */
6921   if (numFields > 0) {
6922     PetscInt offset = 0, f;
6923     for (f = 0; f < numFields; ++f) {
6924       const PetscInt    **perms = NULL;
6925       const PetscScalar **flips = NULL;
6926 
6927       PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6928       switch (mode) {
6929       case INSERT_VALUES:
6930         for (p = 0; p < numPoints; p++) {
6931           const PetscInt     point = points[2 * p];
6932           const PetscInt    *perm  = perms ? perms[p] : NULL;
6933           const PetscScalar *flip  = flips ? flips[p] : NULL;
6934           PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, clperm, values, &offset, array));
6935         }
6936         break;
6937       case INSERT_ALL_VALUES:
6938         for (p = 0; p < numPoints; p++) {
6939           const PetscInt     point = points[2 * p];
6940           const PetscInt    *perm  = perms ? perms[p] : NULL;
6941           const PetscScalar *flip  = flips ? flips[p] : NULL;
6942           PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, clperm, values, &offset, array));
6943         }
6944         break;
6945       case INSERT_BC_VALUES:
6946         for (p = 0; p < numPoints; p++) {
6947           const PetscInt     point = points[2 * p];
6948           const PetscInt    *perm  = perms ? perms[p] : NULL;
6949           const PetscScalar *flip  = flips ? flips[p] : NULL;
6950           PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, insert, clperm, values, &offset, array));
6951         }
6952         break;
6953       case ADD_VALUES:
6954         for (p = 0; p < numPoints; p++) {
6955           const PetscInt     point = points[2 * p];
6956           const PetscInt    *perm  = perms ? perms[p] : NULL;
6957           const PetscScalar *flip  = flips ? flips[p] : NULL;
6958           PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, clperm, values, &offset, array));
6959         }
6960         break;
6961       case ADD_ALL_VALUES:
6962         for (p = 0; p < numPoints; p++) {
6963           const PetscInt     point = points[2 * p];
6964           const PetscInt    *perm  = perms ? perms[p] : NULL;
6965           const PetscScalar *flip  = flips ? flips[p] : NULL;
6966           PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, clperm, values, &offset, array));
6967         }
6968         break;
6969       case ADD_BC_VALUES:
6970         for (p = 0; p < numPoints; p++) {
6971           const PetscInt     point = points[2 * p];
6972           const PetscInt    *perm  = perms ? perms[p] : NULL;
6973           const PetscScalar *flip  = flips ? flips[p] : NULL;
6974           PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, add, clperm, values, &offset, array));
6975         }
6976         break;
6977       default:
6978         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6979       }
6980       PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6981     }
6982   } else {
6983     PetscInt            dof, off;
6984     const PetscInt    **perms = NULL;
6985     const PetscScalar **flips = NULL;
6986 
6987     PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
6988     switch (mode) {
6989     case INSERT_VALUES:
6990       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6991         const PetscInt     point = points[2 * p];
6992         const PetscInt    *perm  = perms ? perms[p] : NULL;
6993         const PetscScalar *flip  = flips ? flips[p] : NULL;
6994         PetscCall(PetscSectionGetDof(section, point, &dof));
6995         PetscCall(updatePoint_private(section, point, dof, insert, PETSC_FALSE, perm, flip, clperm, values, off, array));
6996       }
6997       break;
6998     case INSERT_ALL_VALUES:
6999       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7000         const PetscInt     point = points[2 * p];
7001         const PetscInt    *perm  = perms ? perms[p] : NULL;
7002         const PetscScalar *flip  = flips ? flips[p] : NULL;
7003         PetscCall(PetscSectionGetDof(section, point, &dof));
7004         PetscCall(updatePoint_private(section, point, dof, insert, PETSC_TRUE, perm, flip, clperm, values, off, array));
7005       }
7006       break;
7007     case INSERT_BC_VALUES:
7008       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7009         const PetscInt     point = points[2 * p];
7010         const PetscInt    *perm  = perms ? perms[p] : NULL;
7011         const PetscScalar *flip  = flips ? flips[p] : NULL;
7012         PetscCall(PetscSectionGetDof(section, point, &dof));
7013         PetscCall(updatePointBC_private(section, point, dof, insert, perm, flip, clperm, values, off, array));
7014       }
7015       break;
7016     case ADD_VALUES:
7017       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7018         const PetscInt     point = points[2 * p];
7019         const PetscInt    *perm  = perms ? perms[p] : NULL;
7020         const PetscScalar *flip  = flips ? flips[p] : NULL;
7021         PetscCall(PetscSectionGetDof(section, point, &dof));
7022         PetscCall(updatePoint_private(section, point, dof, add, PETSC_FALSE, perm, flip, clperm, values, off, array));
7023       }
7024       break;
7025     case ADD_ALL_VALUES:
7026       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7027         const PetscInt     point = points[2 * p];
7028         const PetscInt    *perm  = perms ? perms[p] : NULL;
7029         const PetscScalar *flip  = flips ? flips[p] : NULL;
7030         PetscCall(PetscSectionGetDof(section, point, &dof));
7031         PetscCall(updatePoint_private(section, point, dof, add, PETSC_TRUE, perm, flip, clperm, values, off, array));
7032       }
7033       break;
7034     case ADD_BC_VALUES:
7035       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7036         const PetscInt     point = points[2 * p];
7037         const PetscInt    *perm  = perms ? perms[p] : NULL;
7038         const PetscScalar *flip  = flips ? flips[p] : NULL;
7039         PetscCall(PetscSectionGetDof(section, point, &dof));
7040         PetscCall(updatePointBC_private(section, point, dof, add, perm, flip, clperm, values, off, array));
7041       }
7042       break;
7043     default:
7044       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
7045     }
7046     PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
7047   }
7048   /* Cleanup points */
7049   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
7050   /* Cleanup array */
7051   PetscCall(VecRestoreArray(v, &array));
7052   PetscFunctionReturn(PETSC_SUCCESS);
7053 }
7054 
7055 /* Check whether the given point is in the label. If not, update the offset to skip this point */
7056 static inline PetscErrorCode CheckPoint_Private(DMLabel label, PetscInt labelId, PetscSection section, PetscInt point, PetscInt f, PetscInt *offset, PetscBool *contains)
7057 {
7058   PetscFunctionBegin;
7059   *contains = PETSC_TRUE;
7060   if (label) {
7061     PetscInt fdof;
7062 
7063     PetscCall(DMLabelStratumHasPoint(label, labelId, point, contains));
7064     if (!*contains) {
7065       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7066       *offset += fdof;
7067       PetscFunctionReturn(PETSC_SUCCESS);
7068     }
7069   }
7070   PetscFunctionReturn(PETSC_SUCCESS);
7071 }
7072 
7073 /* Unlike DMPlexVecSetClosure(), this uses plex-native closure permutation, not a user-specified permutation such as DMPlexSetClosurePermutationTensor(). */
7074 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)
7075 {
7076   PetscSection    clSection;
7077   IS              clPoints;
7078   PetscScalar    *array;
7079   PetscInt       *points = NULL;
7080   const PetscInt *clp;
7081   PetscInt        numFields, numPoints, p;
7082   PetscInt        offset = 0, f;
7083 
7084   PetscFunctionBeginHot;
7085   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7086   if (!section) PetscCall(DMGetLocalSection(dm, &section));
7087   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7088   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
7089   PetscCall(PetscSectionGetNumFields(section, &numFields));
7090   /* Get points */
7091   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
7092   /* Get array */
7093   PetscCall(VecGetArray(v, &array));
7094   /* Get values */
7095   for (f = 0; f < numFields; ++f) {
7096     const PetscInt    **perms = NULL;
7097     const PetscScalar **flips = NULL;
7098     PetscBool           contains;
7099 
7100     if (!fieldActive[f]) {
7101       for (p = 0; p < numPoints * 2; p += 2) {
7102         PetscInt fdof;
7103         PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
7104         offset += fdof;
7105       }
7106       continue;
7107     }
7108     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7109     switch (mode) {
7110     case INSERT_VALUES:
7111       for (p = 0; p < numPoints; p++) {
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(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7116         if (!contains) continue;
7117         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, NULL, values, &offset, array));
7118       }
7119       break;
7120     case INSERT_ALL_VALUES:
7121       for (p = 0; p < numPoints; p++) {
7122         const PetscInt     point = points[2 * p];
7123         const PetscInt    *perm  = perms ? perms[p] : NULL;
7124         const PetscScalar *flip  = flips ? flips[p] : NULL;
7125         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7126         if (!contains) continue;
7127         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, NULL, values, &offset, array));
7128       }
7129       break;
7130     case INSERT_BC_VALUES:
7131       for (p = 0; p < numPoints; p++) {
7132         const PetscInt     point = points[2 * p];
7133         const PetscInt    *perm  = perms ? perms[p] : NULL;
7134         const PetscScalar *flip  = flips ? flips[p] : NULL;
7135         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7136         if (!contains) continue;
7137         PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, Ncc, comps, insert, NULL, values, &offset, array));
7138       }
7139       break;
7140     case ADD_VALUES:
7141       for (p = 0; p < numPoints; p++) {
7142         const PetscInt     point = points[2 * p];
7143         const PetscInt    *perm  = perms ? perms[p] : NULL;
7144         const PetscScalar *flip  = flips ? flips[p] : NULL;
7145         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7146         if (!contains) continue;
7147         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, NULL, values, &offset, array));
7148       }
7149       break;
7150     case ADD_ALL_VALUES:
7151       for (p = 0; p < numPoints; p++) {
7152         const PetscInt     point = points[2 * p];
7153         const PetscInt    *perm  = perms ? perms[p] : NULL;
7154         const PetscScalar *flip  = flips ? flips[p] : NULL;
7155         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7156         if (!contains) continue;
7157         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, NULL, values, &offset, array));
7158       }
7159       break;
7160     default:
7161       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
7162     }
7163     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7164   }
7165   /* Cleanup points */
7166   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
7167   /* Cleanup array */
7168   PetscCall(VecRestoreArray(v, &array));
7169   PetscFunctionReturn(PETSC_SUCCESS);
7170 }
7171 
7172 static PetscErrorCode DMPlexPrintMatSetValues(PetscViewer viewer, Mat A, PetscInt point, PetscInt numRIndices, const PetscInt rindices[], PetscInt numCIndices, const PetscInt cindices[], const PetscScalar values[])
7173 {
7174   PetscMPIInt rank;
7175   PetscInt    i, j;
7176 
7177   PetscFunctionBegin;
7178   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7179   PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat for point %" PetscInt_FMT "\n", rank, point));
7180   for (i = 0; i < numRIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat row indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, rindices[i]));
7181   for (i = 0; i < numCIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat col indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, cindices[i]));
7182   numCIndices = numCIndices ? numCIndices : numRIndices;
7183   if (!values) PetscFunctionReturn(PETSC_SUCCESS);
7184   for (i = 0; i < numRIndices; i++) {
7185     PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]", rank));
7186     for (j = 0; j < numCIndices; j++) {
7187 #if defined(PETSC_USE_COMPLEX)
7188       PetscCall(PetscViewerASCIIPrintf(viewer, " (%g,%g)", (double)PetscRealPart(values[i * numCIndices + j]), (double)PetscImaginaryPart(values[i * numCIndices + j])));
7189 #else
7190       PetscCall(PetscViewerASCIIPrintf(viewer, " %g", (double)values[i * numCIndices + j]));
7191 #endif
7192     }
7193     PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
7194   }
7195   PetscFunctionReturn(PETSC_SUCCESS);
7196 }
7197 
7198 /*
7199   DMPlexGetIndicesPoint_Internal - Add the indices for dofs on a point to an index array
7200 
7201   Input Parameters:
7202 + section - The section for this data layout
7203 . islocal - Is the section (and thus indices being requested) local or global?
7204 . point   - The point contributing dofs with these indices
7205 . off     - The global offset of this point
7206 . loff    - The local offset of each field
7207 . setBC   - The flag determining whether to include indices of boundary values
7208 . perm    - A permutation of the dofs on this point, or NULL
7209 - indperm - A permutation of the entire indices array, or NULL
7210 
7211   Output Parameter:
7212 . indices - Indices for dofs on this point
7213 
7214   Level: developer
7215 
7216   Note: The indices could be local or global, depending on the value of 'off'.
7217 */
7218 PetscErrorCode DMPlexGetIndicesPoint_Internal(PetscSection section, PetscBool islocal, PetscInt point, PetscInt off, PetscInt *loff, PetscBool setBC, const PetscInt perm[], const PetscInt indperm[], PetscInt indices[])
7219 {
7220   PetscInt        dof;   /* The number of unknowns on this point */
7221   PetscInt        cdof;  /* The number of constraints on this point */
7222   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
7223   PetscInt        cind = 0, k;
7224 
7225   PetscFunctionBegin;
7226   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
7227   PetscCall(PetscSectionGetDof(section, point, &dof));
7228   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
7229   if (!cdof || setBC) {
7230     for (k = 0; k < dof; ++k) {
7231       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
7232       const PetscInt ind    = indperm ? indperm[preind] : preind;
7233 
7234       indices[ind] = off + k;
7235     }
7236   } else {
7237     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
7238     for (k = 0; k < dof; ++k) {
7239       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
7240       const PetscInt ind    = indperm ? indperm[preind] : preind;
7241 
7242       if ((cind < cdof) && (k == cdofs[cind])) {
7243         /* Insert check for returning constrained indices */
7244         indices[ind] = -(off + k + 1);
7245         ++cind;
7246       } else {
7247         indices[ind] = off + k - (islocal ? 0 : cind);
7248       }
7249     }
7250   }
7251   *loff += dof;
7252   PetscFunctionReturn(PETSC_SUCCESS);
7253 }
7254 
7255 /*
7256  DMPlexGetIndicesPointFields_Internal - gets section indices for a point in its canonical ordering.
7257 
7258  Input Parameters:
7259 + section - a section (global or local)
7260 - islocal - `PETSC_TRUE` if requesting local indices (i.e., section is local); `PETSC_FALSE` for global
7261 . point - point within section
7262 . off - The offset of this point in the (local or global) indexed space - should match islocal and (usually) the section
7263 . foffs - array of length numFields containing the offset in canonical point ordering (the location in indices) of each field
7264 . setBC - identify constrained (boundary condition) points via involution.
7265 . perms - perms[f][permsoff][:] is a permutation of dofs within each field
7266 . permsoff - offset
7267 - indperm - index permutation
7268 
7269  Output Parameter:
7270 . foffs - each entry is incremented by the number of (unconstrained if setBC=FALSE) dofs in that field
7271 . indices - array to hold indices (as defined by section) of each dof associated with point
7272 
7273  Notes:
7274  If section is local and setBC=true, there is no distinction between constrained and unconstrained dofs.
7275  If section is local and setBC=false, the indices for constrained points are the involution -(i+1) of their position
7276  in the local vector.
7277 
7278  If section is global and setBC=false, the indices for constrained points are negative (and their value is not
7279  significant).  It is invalid to call with a global section and setBC=true.
7280 
7281  Developer Note:
7282  The section is only used for field layout, so islocal is technically a statement about the offset (off).  At some point
7283  in the future, global sections may have fields set, in which case we could pass the global section and obtain the
7284  offset could be obtained from the section instead of passing it explicitly as we do now.
7285 
7286  Example:
7287  Suppose a point contains one field with three components, and for which the unconstrained indices are {10, 11, 12}.
7288  When the middle component is constrained, we get the array {10, -12, 12} for (islocal=TRUE, setBC=FALSE).
7289  Note that -12 is the involution of 11, so the user can involute negative indices to recover local indices.
7290  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.
7291 
7292  Level: developer
7293 */
7294 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[])
7295 {
7296   PetscInt numFields, foff, f;
7297 
7298   PetscFunctionBegin;
7299   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
7300   PetscCall(PetscSectionGetNumFields(section, &numFields));
7301   for (f = 0, foff = 0; f < numFields; ++f) {
7302     PetscInt        fdof, cfdof;
7303     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7304     PetscInt        cind = 0, b;
7305     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7306 
7307     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7308     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7309     if (!cfdof || setBC) {
7310       for (b = 0; b < fdof; ++b) {
7311         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7312         const PetscInt ind    = indperm ? indperm[preind] : preind;
7313 
7314         indices[ind] = off + foff + b;
7315       }
7316     } else {
7317       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7318       for (b = 0; b < fdof; ++b) {
7319         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7320         const PetscInt ind    = indperm ? indperm[preind] : preind;
7321 
7322         if ((cind < cfdof) && (b == fcdofs[cind])) {
7323           indices[ind] = -(off + foff + b + 1);
7324           ++cind;
7325         } else {
7326           indices[ind] = off + foff + b - (islocal ? 0 : cind);
7327         }
7328       }
7329     }
7330     foff += (setBC || islocal ? fdof : (fdof - cfdof));
7331     foffs[f] += fdof;
7332   }
7333   PetscFunctionReturn(PETSC_SUCCESS);
7334 }
7335 
7336 /*
7337   This version believes the globalSection offsets for each field, rather than just the point offset
7338 
7339  . foffs - The offset into 'indices' for each field, since it is segregated by field
7340 
7341  Notes:
7342  The semantics of this function relate to that of setBC=FALSE in DMPlexGetIndicesPointFields_Internal.
7343  Since this function uses global indices, setBC=TRUE would be invalid, so no such argument exists.
7344 */
7345 static PetscErrorCode DMPlexGetIndicesPointFieldsSplit_Internal(PetscSection section, PetscSection globalSection, PetscInt point, PetscInt foffs[], const PetscInt ***perms, PetscInt permsoff, const PetscInt indperm[], PetscInt indices[])
7346 {
7347   PetscInt numFields, foff, f;
7348 
7349   PetscFunctionBegin;
7350   PetscCall(PetscSectionGetNumFields(section, &numFields));
7351   for (f = 0; f < numFields; ++f) {
7352     PetscInt        fdof, cfdof;
7353     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7354     PetscInt        cind = 0, b;
7355     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7356 
7357     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7358     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7359     PetscCall(PetscSectionGetFieldOffset(globalSection, point, f, &foff));
7360     if (!cfdof) {
7361       for (b = 0; b < fdof; ++b) {
7362         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7363         const PetscInt ind    = indperm ? indperm[preind] : preind;
7364 
7365         indices[ind] = foff + b;
7366       }
7367     } else {
7368       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7369       for (b = 0; b < fdof; ++b) {
7370         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7371         const PetscInt ind    = indperm ? indperm[preind] : preind;
7372 
7373         if ((cind < cfdof) && (b == fcdofs[cind])) {
7374           indices[ind] = -(foff + b + 1);
7375           ++cind;
7376         } else {
7377           indices[ind] = foff + b - cind;
7378         }
7379       }
7380     }
7381     foffs[f] += fdof;
7382   }
7383   PetscFunctionReturn(PETSC_SUCCESS);
7384 }
7385 
7386 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)
7387 {
7388   Mat             cMat;
7389   PetscSection    aSec, cSec;
7390   IS              aIS;
7391   PetscInt        aStart = -1, aEnd = -1;
7392   const PetscInt *anchors;
7393   PetscInt        numFields, f, p, q, newP = 0;
7394   PetscInt        newNumPoints = 0, newNumIndices = 0;
7395   PetscInt       *newPoints, *indices, *newIndices;
7396   PetscInt        maxAnchor, maxDof;
7397   PetscInt        newOffsets[32];
7398   PetscInt       *pointMatOffsets[32];
7399   PetscInt       *newPointOffsets[32];
7400   PetscScalar    *pointMat[32];
7401   PetscScalar    *newValues      = NULL, *tmpValues;
7402   PetscBool       anyConstrained = PETSC_FALSE;
7403 
7404   PetscFunctionBegin;
7405   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7406   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7407   PetscCall(PetscSectionGetNumFields(section, &numFields));
7408 
7409   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
7410   /* if there are point-to-point constraints */
7411   if (aSec) {
7412     PetscCall(PetscArrayzero(newOffsets, 32));
7413     PetscCall(ISGetIndices(aIS, &anchors));
7414     PetscCall(PetscSectionGetChart(aSec, &aStart, &aEnd));
7415     /* figure out how many points are going to be in the new element matrix
7416      * (we allow double counting, because it's all just going to be summed
7417      * into the global matrix anyway) */
7418     for (p = 0; p < 2 * numPoints; p += 2) {
7419       PetscInt b    = points[p];
7420       PetscInt bDof = 0, bSecDof;
7421 
7422       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7423       if (!bSecDof) continue;
7424       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7425       if (bDof) {
7426         /* this point is constrained */
7427         /* it is going to be replaced by its anchors */
7428         PetscInt bOff, q;
7429 
7430         anyConstrained = PETSC_TRUE;
7431         newNumPoints += bDof;
7432         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7433         for (q = 0; q < bDof; q++) {
7434           PetscInt a = anchors[bOff + q];
7435           PetscInt aDof;
7436 
7437           PetscCall(PetscSectionGetDof(section, a, &aDof));
7438           newNumIndices += aDof;
7439           for (f = 0; f < numFields; ++f) {
7440             PetscInt fDof;
7441 
7442             PetscCall(PetscSectionGetFieldDof(section, a, f, &fDof));
7443             newOffsets[f + 1] += fDof;
7444           }
7445         }
7446       } else {
7447         /* this point is not constrained */
7448         newNumPoints++;
7449         newNumIndices += bSecDof;
7450         for (f = 0; f < numFields; ++f) {
7451           PetscInt fDof;
7452 
7453           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7454           newOffsets[f + 1] += fDof;
7455         }
7456       }
7457     }
7458   }
7459   if (!anyConstrained) {
7460     if (outNumPoints) *outNumPoints = 0;
7461     if (outNumIndices) *outNumIndices = 0;
7462     if (outPoints) *outPoints = NULL;
7463     if (outValues) *outValues = NULL;
7464     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7465     PetscFunctionReturn(PETSC_SUCCESS);
7466   }
7467 
7468   if (outNumPoints) *outNumPoints = newNumPoints;
7469   if (outNumIndices) *outNumIndices = newNumIndices;
7470 
7471   for (f = 0; f < numFields; ++f) newOffsets[f + 1] += newOffsets[f];
7472 
7473   if (!outPoints && !outValues) {
7474     if (offsets) {
7475       for (f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7476     }
7477     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7478     PetscFunctionReturn(PETSC_SUCCESS);
7479   }
7480 
7481   PetscCheck(!numFields || newOffsets[numFields] == newNumIndices, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, newOffsets[numFields], newNumIndices);
7482 
7483   PetscCall(DMGetDefaultConstraints(dm, &cSec, &cMat, NULL));
7484 
7485   /* workspaces */
7486   if (numFields) {
7487     for (f = 0; f < numFields; f++) {
7488       PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[f]));
7489       PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[f]));
7490     }
7491   } else {
7492     PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[0]));
7493     PetscCall(DMGetWorkArray(dm, numPoints, MPIU_INT, &newPointOffsets[0]));
7494   }
7495 
7496   /* get workspaces for the point-to-point matrices */
7497   if (numFields) {
7498     PetscInt totalOffset, totalMatOffset;
7499 
7500     for (p = 0; p < numPoints; p++) {
7501       PetscInt b    = points[2 * p];
7502       PetscInt bDof = 0, bSecDof;
7503 
7504       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7505       if (!bSecDof) {
7506         for (f = 0; f < numFields; f++) {
7507           newPointOffsets[f][p + 1] = 0;
7508           pointMatOffsets[f][p + 1] = 0;
7509         }
7510         continue;
7511       }
7512       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7513       if (bDof) {
7514         for (f = 0; f < numFields; f++) {
7515           PetscInt fDof, q, bOff, allFDof = 0;
7516 
7517           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7518           PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7519           for (q = 0; q < bDof; q++) {
7520             PetscInt a = anchors[bOff + q];
7521             PetscInt aFDof;
7522 
7523             PetscCall(PetscSectionGetFieldDof(section, a, f, &aFDof));
7524             allFDof += aFDof;
7525           }
7526           newPointOffsets[f][p + 1] = allFDof;
7527           pointMatOffsets[f][p + 1] = fDof * allFDof;
7528         }
7529       } else {
7530         for (f = 0; f < numFields; f++) {
7531           PetscInt fDof;
7532 
7533           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7534           newPointOffsets[f][p + 1] = fDof;
7535           pointMatOffsets[f][p + 1] = 0;
7536         }
7537       }
7538     }
7539     for (f = 0, totalOffset = 0, totalMatOffset = 0; f < numFields; f++) {
7540       newPointOffsets[f][0] = totalOffset;
7541       pointMatOffsets[f][0] = totalMatOffset;
7542       for (p = 0; p < numPoints; p++) {
7543         newPointOffsets[f][p + 1] += newPointOffsets[f][p];
7544         pointMatOffsets[f][p + 1] += pointMatOffsets[f][p];
7545       }
7546       totalOffset    = newPointOffsets[f][numPoints];
7547       totalMatOffset = pointMatOffsets[f][numPoints];
7548       PetscCall(DMGetWorkArray(dm, pointMatOffsets[f][numPoints], MPIU_SCALAR, &pointMat[f]));
7549     }
7550   } else {
7551     for (p = 0; p < numPoints; p++) {
7552       PetscInt b    = points[2 * p];
7553       PetscInt bDof = 0, bSecDof;
7554 
7555       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7556       if (!bSecDof) {
7557         newPointOffsets[0][p + 1] = 0;
7558         pointMatOffsets[0][p + 1] = 0;
7559         continue;
7560       }
7561       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7562       if (bDof) {
7563         PetscInt bOff, q, allDof = 0;
7564 
7565         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7566         for (q = 0; q < bDof; q++) {
7567           PetscInt a = anchors[bOff + q], aDof;
7568 
7569           PetscCall(PetscSectionGetDof(section, a, &aDof));
7570           allDof += aDof;
7571         }
7572         newPointOffsets[0][p + 1] = allDof;
7573         pointMatOffsets[0][p + 1] = bSecDof * allDof;
7574       } else {
7575         newPointOffsets[0][p + 1] = bSecDof;
7576         pointMatOffsets[0][p + 1] = 0;
7577       }
7578     }
7579     newPointOffsets[0][0] = 0;
7580     pointMatOffsets[0][0] = 0;
7581     for (p = 0; p < numPoints; p++) {
7582       newPointOffsets[0][p + 1] += newPointOffsets[0][p];
7583       pointMatOffsets[0][p + 1] += pointMatOffsets[0][p];
7584     }
7585     PetscCall(DMGetWorkArray(dm, pointMatOffsets[0][numPoints], MPIU_SCALAR, &pointMat[0]));
7586   }
7587 
7588   /* output arrays */
7589   PetscCall(DMGetWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7590 
7591   /* get the point-to-point matrices; construct newPoints */
7592   PetscCall(PetscSectionGetMaxDof(aSec, &maxAnchor));
7593   PetscCall(PetscSectionGetMaxDof(section, &maxDof));
7594   PetscCall(DMGetWorkArray(dm, maxDof, MPIU_INT, &indices));
7595   PetscCall(DMGetWorkArray(dm, maxAnchor * maxDof, MPIU_INT, &newIndices));
7596   if (numFields) {
7597     for (p = 0, newP = 0; p < numPoints; p++) {
7598       PetscInt b    = points[2 * p];
7599       PetscInt o    = points[2 * p + 1];
7600       PetscInt bDof = 0, bSecDof;
7601 
7602       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7603       if (!bSecDof) continue;
7604       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7605       if (bDof) {
7606         PetscInt fStart[32], fEnd[32], fAnchorStart[32], fAnchorEnd[32], bOff, q;
7607 
7608         fStart[0] = 0;
7609         fEnd[0]   = 0;
7610         for (f = 0; f < numFields; f++) {
7611           PetscInt fDof;
7612 
7613           PetscCall(PetscSectionGetFieldDof(cSec, b, f, &fDof));
7614           fStart[f + 1] = fStart[f] + fDof;
7615           fEnd[f + 1]   = fStart[f + 1];
7616         }
7617         PetscCall(PetscSectionGetOffset(cSec, b, &bOff));
7618         PetscCall(DMPlexGetIndicesPointFields_Internal(cSec, PETSC_TRUE, b, bOff, fEnd, PETSC_TRUE, perms, p, NULL, indices));
7619 
7620         fAnchorStart[0] = 0;
7621         fAnchorEnd[0]   = 0;
7622         for (f = 0; f < numFields; f++) {
7623           PetscInt fDof = newPointOffsets[f][p + 1] - newPointOffsets[f][p];
7624 
7625           fAnchorStart[f + 1] = fAnchorStart[f] + fDof;
7626           fAnchorEnd[f + 1]   = fAnchorStart[f + 1];
7627         }
7628         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7629         for (q = 0; q < bDof; q++) {
7630           PetscInt a = anchors[bOff + q], aOff;
7631 
7632           /* we take the orientation of ap into account in the order that we constructed the indices above: the newly added points have no orientation */
7633           newPoints[2 * (newP + q)]     = a;
7634           newPoints[2 * (newP + q) + 1] = 0;
7635           PetscCall(PetscSectionGetOffset(section, a, &aOff));
7636           PetscCall(DMPlexGetIndicesPointFields_Internal(section, PETSC_TRUE, a, aOff, fAnchorEnd, PETSC_TRUE, NULL, -1, NULL, newIndices));
7637         }
7638         newP += bDof;
7639 
7640         if (outValues) {
7641           /* get the point-to-point submatrix */
7642           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]));
7643         }
7644       } else {
7645         newPoints[2 * newP]     = b;
7646         newPoints[2 * newP + 1] = o;
7647         newP++;
7648       }
7649     }
7650   } else {
7651     for (p = 0; p < numPoints; p++) {
7652       PetscInt b    = points[2 * p];
7653       PetscInt o    = points[2 * p + 1];
7654       PetscInt bDof = 0, bSecDof;
7655 
7656       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7657       if (!bSecDof) continue;
7658       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7659       if (bDof) {
7660         PetscInt bEnd = 0, bAnchorEnd = 0, bOff;
7661 
7662         PetscCall(PetscSectionGetOffset(cSec, b, &bOff));
7663         PetscCall(DMPlexGetIndicesPoint_Internal(cSec, PETSC_TRUE, b, bOff, &bEnd, PETSC_TRUE, (perms && perms[0]) ? perms[0][p] : NULL, NULL, indices));
7664 
7665         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7666         for (q = 0; q < bDof; q++) {
7667           PetscInt a = anchors[bOff + q], aOff;
7668 
7669           /* we take the orientation of ap into account in the order that we constructed the indices above: the newly added points have no orientation */
7670 
7671           newPoints[2 * (newP + q)]     = a;
7672           newPoints[2 * (newP + q) + 1] = 0;
7673           PetscCall(PetscSectionGetOffset(section, a, &aOff));
7674           PetscCall(DMPlexGetIndicesPoint_Internal(section, PETSC_TRUE, a, aOff, &bAnchorEnd, PETSC_TRUE, NULL, NULL, newIndices));
7675         }
7676         newP += bDof;
7677 
7678         /* get the point-to-point submatrix */
7679         if (outValues) PetscCall(MatGetValues(cMat, bEnd, indices, bAnchorEnd, newIndices, pointMat[0] + pointMatOffsets[0][p]));
7680       } else {
7681         newPoints[2 * newP]     = b;
7682         newPoints[2 * newP + 1] = o;
7683         newP++;
7684       }
7685     }
7686   }
7687 
7688   if (outValues) {
7689     PetscCall(DMGetWorkArray(dm, newNumIndices * numIndices, MPIU_SCALAR, &tmpValues));
7690     PetscCall(PetscArrayzero(tmpValues, newNumIndices * numIndices));
7691     /* multiply constraints on the right */
7692     if (numFields) {
7693       for (f = 0; f < numFields; f++) {
7694         PetscInt oldOff = offsets[f];
7695 
7696         for (p = 0; p < numPoints; p++) {
7697           PetscInt cStart = newPointOffsets[f][p];
7698           PetscInt b      = points[2 * p];
7699           PetscInt c, r, k;
7700           PetscInt dof;
7701 
7702           PetscCall(PetscSectionGetFieldDof(section, b, f, &dof));
7703           if (!dof) continue;
7704           if (pointMatOffsets[f][p] < pointMatOffsets[f][p + 1]) {
7705             PetscInt           nCols = newPointOffsets[f][p + 1] - cStart;
7706             const PetscScalar *mat   = pointMat[f] + pointMatOffsets[f][p];
7707 
7708             for (r = 0; r < numIndices; r++) {
7709               for (c = 0; c < nCols; c++) {
7710                 for (k = 0; k < dof; k++) tmpValues[r * newNumIndices + cStart + c] += values[r * numIndices + oldOff + k] * mat[k * nCols + c];
7711               }
7712             }
7713           } else {
7714             /* copy this column as is */
7715             for (r = 0; r < numIndices; r++) {
7716               for (c = 0; c < dof; c++) tmpValues[r * newNumIndices + cStart + c] = values[r * numIndices + oldOff + c];
7717             }
7718           }
7719           oldOff += dof;
7720         }
7721       }
7722     } else {
7723       PetscInt oldOff = 0;
7724       for (p = 0; p < numPoints; p++) {
7725         PetscInt cStart = newPointOffsets[0][p];
7726         PetscInt b      = points[2 * p];
7727         PetscInt c, r, k;
7728         PetscInt dof;
7729 
7730         PetscCall(PetscSectionGetDof(section, b, &dof));
7731         if (!dof) continue;
7732         if (pointMatOffsets[0][p] < pointMatOffsets[0][p + 1]) {
7733           PetscInt           nCols = newPointOffsets[0][p + 1] - cStart;
7734           const PetscScalar *mat   = pointMat[0] + pointMatOffsets[0][p];
7735 
7736           for (r = 0; r < numIndices; r++) {
7737             for (c = 0; c < nCols; c++) {
7738               for (k = 0; k < dof; k++) tmpValues[r * newNumIndices + cStart + c] += mat[k * nCols + c] * values[r * numIndices + oldOff + k];
7739             }
7740           }
7741         } else {
7742           /* copy this column as is */
7743           for (r = 0; r < numIndices; r++) {
7744             for (c = 0; c < dof; c++) tmpValues[r * newNumIndices + cStart + c] = values[r * numIndices + oldOff + c];
7745           }
7746         }
7747         oldOff += dof;
7748       }
7749     }
7750 
7751     if (multiplyLeft) {
7752       PetscCall(DMGetWorkArray(dm, newNumIndices * newNumIndices, MPIU_SCALAR, &newValues));
7753       PetscCall(PetscArrayzero(newValues, newNumIndices * newNumIndices));
7754       /* multiply constraints transpose on the left */
7755       if (numFields) {
7756         for (f = 0; f < numFields; f++) {
7757           PetscInt oldOff = offsets[f];
7758 
7759           for (p = 0; p < numPoints; p++) {
7760             PetscInt rStart = newPointOffsets[f][p];
7761             PetscInt b      = points[2 * p];
7762             PetscInt c, r, k;
7763             PetscInt dof;
7764 
7765             PetscCall(PetscSectionGetFieldDof(section, b, f, &dof));
7766             if (pointMatOffsets[f][p] < pointMatOffsets[f][p + 1]) {
7767               PetscInt                          nRows = newPointOffsets[f][p + 1] - rStart;
7768               const PetscScalar *PETSC_RESTRICT mat   = pointMat[f] + pointMatOffsets[f][p];
7769 
7770               for (r = 0; r < nRows; r++) {
7771                 for (c = 0; c < newNumIndices; c++) {
7772                   for (k = 0; k < dof; k++) newValues[(rStart + r) * newNumIndices + c] += mat[k * nRows + r] * tmpValues[(oldOff + k) * newNumIndices + c];
7773                 }
7774               }
7775             } else {
7776               /* copy this row as is */
7777               for (r = 0; r < dof; r++) {
7778                 for (c = 0; c < newNumIndices; c++) newValues[(rStart + r) * newNumIndices + c] = tmpValues[(oldOff + r) * newNumIndices + c];
7779               }
7780             }
7781             oldOff += dof;
7782           }
7783         }
7784       } else {
7785         PetscInt oldOff = 0;
7786 
7787         for (p = 0; p < numPoints; p++) {
7788           PetscInt rStart = newPointOffsets[0][p];
7789           PetscInt b      = points[2 * p];
7790           PetscInt c, r, k;
7791           PetscInt dof;
7792 
7793           PetscCall(PetscSectionGetDof(section, b, &dof));
7794           if (pointMatOffsets[0][p] < pointMatOffsets[0][p + 1]) {
7795             PetscInt                          nRows = newPointOffsets[0][p + 1] - rStart;
7796             const PetscScalar *PETSC_RESTRICT mat   = pointMat[0] + pointMatOffsets[0][p];
7797 
7798             for (r = 0; r < nRows; r++) {
7799               for (c = 0; c < newNumIndices; c++) {
7800                 for (k = 0; k < dof; k++) newValues[(rStart + r) * newNumIndices + c] += mat[k * nRows + r] * tmpValues[(oldOff + k) * newNumIndices + c];
7801               }
7802             }
7803           } else {
7804             /* copy this row as is */
7805             for (r = 0; r < dof; r++) {
7806               for (c = 0; c < newNumIndices; c++) newValues[(rStart + r) * newNumIndices + c] = tmpValues[(oldOff + r) * newNumIndices + c];
7807             }
7808           }
7809           oldOff += dof;
7810         }
7811       }
7812 
7813       PetscCall(DMRestoreWorkArray(dm, newNumIndices * numIndices, MPIU_SCALAR, &tmpValues));
7814     } else {
7815       newValues = tmpValues;
7816     }
7817   }
7818 
7819   /* clean up */
7820   PetscCall(DMRestoreWorkArray(dm, maxDof, MPIU_INT, &indices));
7821   PetscCall(DMRestoreWorkArray(dm, maxAnchor * maxDof, MPIU_INT, &newIndices));
7822 
7823   if (numFields) {
7824     for (f = 0; f < numFields; f++) {
7825       PetscCall(DMRestoreWorkArray(dm, pointMatOffsets[f][numPoints], MPIU_SCALAR, &pointMat[f]));
7826       PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[f]));
7827       PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[f]));
7828     }
7829   } else {
7830     PetscCall(DMRestoreWorkArray(dm, pointMatOffsets[0][numPoints], MPIU_SCALAR, &pointMat[0]));
7831     PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[0]));
7832     PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[0]));
7833   }
7834   PetscCall(ISRestoreIndices(aIS, &anchors));
7835 
7836   /* output */
7837   if (outPoints) {
7838     *outPoints = newPoints;
7839   } else {
7840     PetscCall(DMRestoreWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7841   }
7842   if (outValues) *outValues = newValues;
7843   for (f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7844   PetscFunctionReturn(PETSC_SUCCESS);
7845 }
7846 
7847 /*@C
7848   DMPlexGetClosureIndices - Gets the global dof indices associated with the closure of the given point within the provided sections.
7849 
7850   Not collective
7851 
7852   Input Parameters:
7853 + dm         - The `DM`
7854 . section    - The `PetscSection` describing the points (a local section)
7855 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
7856 . point      - The point defining the closure
7857 - useClPerm  - Use the closure point permutation if available
7858 
7859   Output Parameters:
7860 + numIndices - The number of dof indices in the closure of point with the input sections
7861 . indices    - The dof indices
7862 . outOffsets - Array to write the field offsets into, or `NULL`
7863 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or `NULL`
7864 
7865   Level: advanced
7866 
7867   Notes:
7868   Must call `DMPlexRestoreClosureIndices()` to free allocated memory
7869 
7870   If `idxSection` is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
7871   of those indices is not significant.  If `idxSection` is local, the constrained dofs will yield the involution -(idx+1)
7872   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
7873   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when `idxSection` == section, otherwise global
7874   indices (with the above semantics) are implied.
7875 
7876 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`,
7877           `PetscSection`, `DMGetGlobalSection()`
7878 @*/
7879 PetscErrorCode DMPlexGetClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
7880 {
7881   /* Closure ordering */
7882   PetscSection    clSection;
7883   IS              clPoints;
7884   const PetscInt *clp;
7885   PetscInt       *points;
7886   const PetscInt *clperm = NULL;
7887   /* Dof permutation and sign flips */
7888   const PetscInt    **perms[32] = {NULL};
7889   const PetscScalar **flips[32] = {NULL};
7890   PetscScalar        *valCopy   = NULL;
7891   /* Hanging node constraints */
7892   PetscInt    *pointsC = NULL;
7893   PetscScalar *valuesC = NULL;
7894   PetscInt     NclC, NiC;
7895 
7896   PetscInt *idx;
7897   PetscInt  Nf, Ncl, Ni = 0, offsets[32], p, f;
7898   PetscBool isLocal = (section == idxSection) ? PETSC_TRUE : PETSC_FALSE;
7899 
7900   PetscFunctionBeginHot;
7901   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7902   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7903   PetscValidHeaderSpecific(idxSection, PETSC_SECTION_CLASSID, 3);
7904   if (numIndices) PetscAssertPointer(numIndices, 6);
7905   if (indices) PetscAssertPointer(indices, 7);
7906   if (outOffsets) PetscAssertPointer(outOffsets, 8);
7907   if (values) PetscAssertPointer(values, 9);
7908   PetscCall(PetscSectionGetNumFields(section, &Nf));
7909   PetscCheck(Nf <= 31, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", Nf);
7910   PetscCall(PetscArrayzero(offsets, 32));
7911   /* 1) Get points in closure */
7912   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &Ncl, &points, &clSection, &clPoints, &clp));
7913   if (useClPerm) {
7914     PetscInt depth, clsize;
7915     PetscCall(DMPlexGetPointDepth(dm, point, &depth));
7916     for (clsize = 0, p = 0; p < Ncl; p++) {
7917       PetscInt dof;
7918       PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
7919       clsize += dof;
7920     }
7921     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
7922   }
7923   /* 2) Get number of indices on these points and field offsets from section */
7924   for (p = 0; p < Ncl * 2; p += 2) {
7925     PetscInt dof, fdof;
7926 
7927     PetscCall(PetscSectionGetDof(section, points[p], &dof));
7928     for (f = 0; f < Nf; ++f) {
7929       PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
7930       offsets[f + 1] += fdof;
7931     }
7932     Ni += dof;
7933   }
7934   for (f = 1; f < Nf; ++f) offsets[f + 1] += offsets[f];
7935   PetscCheck(!Nf || offsets[Nf] == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, offsets[Nf], Ni);
7936   /* 3) Get symmetries and sign flips. Apply sign flips to values if passed in (only works for square values matrix) */
7937   for (f = 0; f < PetscMax(1, Nf); ++f) {
7938     if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7939     else PetscCall(PetscSectionGetPointSyms(section, Ncl, points, &perms[f], &flips[f]));
7940     /* may need to apply sign changes to the element matrix */
7941     if (values && flips[f]) {
7942       PetscInt foffset = offsets[f];
7943 
7944       for (p = 0; p < Ncl; ++p) {
7945         PetscInt           pnt  = points[2 * p], fdof;
7946         const PetscScalar *flip = flips[f] ? flips[f][p] : NULL;
7947 
7948         if (!Nf) PetscCall(PetscSectionGetDof(section, pnt, &fdof));
7949         else PetscCall(PetscSectionGetFieldDof(section, pnt, f, &fdof));
7950         if (flip) {
7951           PetscInt i, j, k;
7952 
7953           if (!valCopy) {
7954             PetscCall(DMGetWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
7955             for (j = 0; j < Ni * Ni; ++j) valCopy[j] = (*values)[j];
7956             *values = valCopy;
7957           }
7958           for (i = 0; i < fdof; ++i) {
7959             PetscScalar fval = flip[i];
7960 
7961             for (k = 0; k < Ni; ++k) {
7962               valCopy[Ni * (foffset + i) + k] *= fval;
7963               valCopy[Ni * k + (foffset + i)] *= fval;
7964             }
7965           }
7966         }
7967         foffset += fdof;
7968       }
7969     }
7970   }
7971   /* 4) Apply hanging node constraints. Get new symmetries and replace all storage with constrained storage */
7972   PetscCall(DMPlexAnchorsModifyMat(dm, section, Ncl, Ni, points, perms, values ? *values : NULL, &NclC, &NiC, &pointsC, values ? &valuesC : NULL, offsets, PETSC_TRUE));
7973   if (NclC) {
7974     if (valCopy) PetscCall(DMRestoreWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
7975     for (f = 0; f < PetscMax(1, Nf); ++f) {
7976       if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7977       else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
7978     }
7979     for (f = 0; f < PetscMax(1, Nf); ++f) {
7980       if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, NclC, pointsC, &perms[f], &flips[f]));
7981       else PetscCall(PetscSectionGetPointSyms(section, NclC, pointsC, &perms[f], &flips[f]));
7982     }
7983     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7984     Ncl    = NclC;
7985     Ni     = NiC;
7986     points = pointsC;
7987     if (values) *values = valuesC;
7988   }
7989   /* 5) Calculate indices */
7990   PetscCall(DMGetWorkArray(dm, Ni, MPIU_INT, &idx));
7991   if (Nf) {
7992     PetscInt  idxOff;
7993     PetscBool useFieldOffsets;
7994 
7995     if (outOffsets) {
7996       for (f = 0; f <= Nf; f++) outOffsets[f] = offsets[f];
7997     }
7998     PetscCall(PetscSectionGetUseFieldOffsets(idxSection, &useFieldOffsets));
7999     if (useFieldOffsets) {
8000       for (p = 0; p < Ncl; ++p) {
8001         const PetscInt pnt = points[p * 2];
8002 
8003         PetscCall(DMPlexGetIndicesPointFieldsSplit_Internal(section, idxSection, pnt, offsets, perms, p, clperm, idx));
8004       }
8005     } else {
8006       for (p = 0; p < Ncl; ++p) {
8007         const PetscInt pnt = points[p * 2];
8008 
8009         PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
8010         /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
8011          * not (at the time of this writing) have fields set. They probably should, in which case we would pass the
8012          * global section. */
8013         PetscCall(DMPlexGetIndicesPointFields_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, offsets, PETSC_FALSE, perms, p, clperm, idx));
8014       }
8015     }
8016   } else {
8017     PetscInt off = 0, idxOff;
8018 
8019     for (p = 0; p < Ncl; ++p) {
8020       const PetscInt  pnt  = points[p * 2];
8021       const PetscInt *perm = perms[0] ? perms[0][p] : NULL;
8022 
8023       PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
8024       /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
8025        * not (at the time of this writing) have fields set. They probably should, in which case we would pass the global section. */
8026       PetscCall(DMPlexGetIndicesPoint_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, &off, PETSC_FALSE, perm, clperm, idx));
8027     }
8028   }
8029   /* 6) Cleanup */
8030   for (f = 0; f < PetscMax(1, Nf); ++f) {
8031     if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
8032     else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
8033   }
8034   if (NclC) {
8035     PetscCall(DMRestoreWorkArray(dm, NclC * 2, MPIU_INT, &pointsC));
8036   } else {
8037     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
8038   }
8039 
8040   if (numIndices) *numIndices = Ni;
8041   if (indices) *indices = idx;
8042   PetscFunctionReturn(PETSC_SUCCESS);
8043 }
8044 
8045 /*@C
8046   DMPlexRestoreClosureIndices - Restores the global dof indices associated with the closure of the given point within the provided sections.
8047 
8048   Not collective
8049 
8050   Input Parameters:
8051 + dm         - The `DM`
8052 . section    - The `PetscSection` describing the points (a local section)
8053 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
8054 . point      - The point defining the closure
8055 - useClPerm  - Use the closure point permutation if available
8056 
8057   Output Parameters:
8058 + numIndices - The number of dof indices in the closure of point with the input sections
8059 . indices    - The dof indices
8060 . outOffsets - Array to write the field offsets into, or `NULL`
8061 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or `NULL`
8062 
8063   Level: advanced
8064 
8065   Notes:
8066   If values were modified, the user is responsible for calling `DMRestoreWorkArray`(dm, 0, `MPIU_SCALAR`, &values).
8067 
8068   If idxSection is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
8069   of those indices is not significant.  If idxSection is local, the constrained dofs will yield the involution -(idx+1)
8070   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
8071   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when idxSection == section, otherwise global
8072   indices (with the above semantics) are implied.
8073 
8074 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`, `DMGetGlobalSection()`
8075 @*/
8076 PetscErrorCode DMPlexRestoreClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
8077 {
8078   PetscFunctionBegin;
8079   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8080   PetscAssertPointer(indices, 7);
8081   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, indices));
8082   PetscFunctionReturn(PETSC_SUCCESS);
8083 }
8084 
8085 PetscErrorCode DMPlexMatSetClosure_Internal(DM dm, PetscSection section, PetscSection globalSection, PetscBool useClPerm, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8086 {
8087   DM_Plex           *mesh = (DM_Plex *)dm->data;
8088   PetscInt          *indices;
8089   PetscInt           numIndices;
8090   const PetscScalar *valuesOrig = values;
8091   PetscErrorCode     ierr;
8092 
8093   PetscFunctionBegin;
8094   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8095   if (!section) PetscCall(DMGetLocalSection(dm, &section));
8096   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
8097   if (!globalSection) PetscCall(DMGetGlobalSection(dm, &globalSection));
8098   PetscValidHeaderSpecific(globalSection, PETSC_SECTION_CLASSID, 3);
8099   PetscValidHeaderSpecific(A, MAT_CLASSID, 5);
8100 
8101   PetscCall(DMPlexGetClosureIndices(dm, section, globalSection, point, useClPerm, &numIndices, &indices, NULL, (PetscScalar **)&values));
8102 
8103   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndices, indices, 0, NULL, values));
8104   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8105   ierr = MatSetValues(A, numIndices, indices, numIndices, indices, values, mode);
8106   if (ierr) {
8107     PetscMPIInt rank;
8108 
8109     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8110     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8111     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndices, indices, 0, NULL, values));
8112     PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
8113     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
8114     SETERRQ(PetscObjectComm((PetscObject)dm), ierr, "Not possible to set matrix values");
8115   }
8116   if (mesh->printFEM > 1) {
8117     PetscInt i;
8118     PetscCall(PetscPrintf(PETSC_COMM_SELF, "  Indices:"));
8119     for (i = 0; i < numIndices; ++i) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, indices[i]));
8120     PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8121   }
8122 
8123   PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
8124   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
8125   PetscFunctionReturn(PETSC_SUCCESS);
8126 }
8127 
8128 /*@C
8129   DMPlexMatSetClosure - Set an array of the values on the closure of 'point'
8130 
8131   Not collective
8132 
8133   Input Parameters:
8134 + dm            - The `DM`
8135 . section       - The section describing the layout in `v`, or `NULL` to use the default section
8136 . globalSection - The section describing the layout in `v`, or `NULL` to use the default global section
8137 . A             - The matrix
8138 . point         - The point in the `DM`
8139 . values        - The array of values
8140 - mode          - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
8141 
8142   Level: intermediate
8143 
8144 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosureGeneral()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
8145 @*/
8146 PetscErrorCode DMPlexMatSetClosure(DM dm, PetscSection section, PetscSection globalSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8147 {
8148   PetscFunctionBegin;
8149   PetscCall(DMPlexMatSetClosure_Internal(dm, section, globalSection, PETSC_TRUE, A, point, values, mode));
8150   PetscFunctionReturn(PETSC_SUCCESS);
8151 }
8152 
8153 /*@C
8154   DMPlexMatSetClosureGeneral - Set an array of the values on the closure of 'point' using a different row and column section
8155 
8156   Not collective
8157 
8158   Input Parameters:
8159 + dmRow            - The `DM` for the row fields
8160 . sectionRow       - The section describing the layout, or `NULL` to use the default section in `dmRow`
8161 . useRowPerm       - The flag to use the closure permutation of the `dmRow` if available
8162 . globalSectionRow - The section describing the layout, or `NULL` to use the default global section in `dmRow`
8163 . dmCol            - The `DM` for the column fields
8164 . sectionCol       - The section describing the layout, or `NULL` to use the default section in `dmCol`
8165 . useColPerm       - The flag to use the closure permutation of the `dmCol` if available
8166 . globalSectionCol - The section describing the layout, or `NULL` to use the default global section in `dmCol`
8167 . A                - The matrix
8168 . point            - The point in the `DM`
8169 . values           - The array of values
8170 - mode             - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
8171 
8172   Level: intermediate
8173 
8174 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosure()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
8175 @*/
8176 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)
8177 {
8178   DM_Plex           *mesh = (DM_Plex *)dmRow->data;
8179   PetscInt          *indicesRow, *indicesCol;
8180   PetscInt           numIndicesRow, numIndicesCol;
8181   const PetscScalar *valuesOrig = values;
8182   PetscErrorCode     ierr;
8183 
8184   PetscFunctionBegin;
8185   PetscValidHeaderSpecific(dmRow, DM_CLASSID, 1);
8186   if (!sectionRow) PetscCall(DMGetLocalSection(dmRow, &sectionRow));
8187   PetscValidHeaderSpecific(sectionRow, PETSC_SECTION_CLASSID, 2);
8188   if (!globalSectionRow) PetscCall(DMGetGlobalSection(dmRow, &globalSectionRow));
8189   PetscValidHeaderSpecific(globalSectionRow, PETSC_SECTION_CLASSID, 3);
8190   PetscValidHeaderSpecific(dmCol, DM_CLASSID, 5);
8191   if (!sectionCol) PetscCall(DMGetLocalSection(dmCol, &sectionCol));
8192   PetscValidHeaderSpecific(sectionCol, PETSC_SECTION_CLASSID, 6);
8193   if (!globalSectionCol) PetscCall(DMGetGlobalSection(dmCol, &globalSectionCol));
8194   PetscValidHeaderSpecific(globalSectionCol, PETSC_SECTION_CLASSID, 7);
8195   PetscValidHeaderSpecific(A, MAT_CLASSID, 9);
8196 
8197   PetscCall(DMPlexGetClosureIndices(dmRow, sectionRow, globalSectionRow, point, useRowPerm, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&values));
8198   PetscCall(DMPlexGetClosureIndices(dmCol, sectionCol, globalSectionCol, point, useColPerm, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&values));
8199 
8200   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
8201   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8202   ierr = MatSetValues(A, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values, mode);
8203   if (ierr) {
8204     PetscMPIInt rank;
8205 
8206     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8207     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8208     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
8209     PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&values));
8210     PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesRow, NULL, (PetscScalar **)&values));
8211     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &values));
8212   }
8213 
8214   PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, useRowPerm, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&values));
8215   PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, useColPerm, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&values));
8216   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &values));
8217   PetscFunctionReturn(PETSC_SUCCESS);
8218 }
8219 
8220 PetscErrorCode DMPlexMatSetClosureRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8221 {
8222   DM_Plex        *mesh    = (DM_Plex *)dmf->data;
8223   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
8224   PetscInt       *cpoints = NULL;
8225   PetscInt       *findices, *cindices;
8226   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
8227   PetscInt        foffsets[32], coffsets[32];
8228   DMPolytopeType  ct;
8229   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
8230   PetscErrorCode  ierr;
8231 
8232   PetscFunctionBegin;
8233   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
8234   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
8235   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
8236   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
8237   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
8238   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
8239   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
8240   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
8241   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
8242   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
8243   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
8244   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
8245   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
8246   PetscCall(PetscArrayzero(foffsets, 32));
8247   PetscCall(PetscArrayzero(coffsets, 32));
8248   /* Column indices */
8249   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8250   maxFPoints = numCPoints;
8251   /* Compress out points not in the section */
8252   /*   TODO: Squeeze out points with 0 dof as well */
8253   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
8254   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
8255     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
8256       cpoints[q * 2]     = cpoints[p];
8257       cpoints[q * 2 + 1] = cpoints[p + 1];
8258       ++q;
8259     }
8260   }
8261   numCPoints = q;
8262   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
8263     PetscInt fdof;
8264 
8265     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
8266     if (!dof) continue;
8267     for (f = 0; f < numFields; ++f) {
8268       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
8269       coffsets[f + 1] += fdof;
8270     }
8271     numCIndices += dof;
8272   }
8273   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
8274   /* Row indices */
8275   PetscCall(DMPlexGetCellType(dmc, point, &ct));
8276   {
8277     DMPlexTransform tr;
8278     DMPolytopeType *rct;
8279     PetscInt       *rsize, *rcone, *rornt, Nt;
8280 
8281     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8282     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8283     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8284     numSubcells = rsize[Nt - 1];
8285     PetscCall(DMPlexTransformDestroy(&tr));
8286   }
8287   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8288   for (r = 0, q = 0; r < numSubcells; ++r) {
8289     /* TODO Map from coarse to fine cells */
8290     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8291     /* Compress out points not in the section */
8292     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8293     for (p = 0; p < numFPoints * 2; p += 2) {
8294       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8295         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8296         if (!dof) continue;
8297         for (s = 0; s < q; ++s)
8298           if (fpoints[p] == ftotpoints[s * 2]) break;
8299         if (s < q) continue;
8300         ftotpoints[q * 2]     = fpoints[p];
8301         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8302         ++q;
8303       }
8304     }
8305     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8306   }
8307   numFPoints = q;
8308   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8309     PetscInt fdof;
8310 
8311     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8312     if (!dof) continue;
8313     for (f = 0; f < numFields; ++f) {
8314       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8315       foffsets[f + 1] += fdof;
8316     }
8317     numFIndices += dof;
8318   }
8319   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8320 
8321   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8322   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8323   PetscCall(DMGetWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8324   PetscCall(DMGetWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8325   if (numFields) {
8326     const PetscInt **permsF[32] = {NULL};
8327     const PetscInt **permsC[32] = {NULL};
8328 
8329     for (f = 0; f < numFields; f++) {
8330       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8331       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8332     }
8333     for (p = 0; p < numFPoints; p++) {
8334       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8335       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8336     }
8337     for (p = 0; p < numCPoints; p++) {
8338       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8339       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8340     }
8341     for (f = 0; f < numFields; f++) {
8342       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8343       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8344     }
8345   } else {
8346     const PetscInt **permsF = NULL;
8347     const PetscInt **permsC = NULL;
8348 
8349     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8350     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8351     for (p = 0, off = 0; p < numFPoints; p++) {
8352       const PetscInt *perm = permsF ? permsF[p] : NULL;
8353 
8354       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8355       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8356     }
8357     for (p = 0, off = 0; p < numCPoints; p++) {
8358       const PetscInt *perm = permsC ? permsC[p] : NULL;
8359 
8360       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8361       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8362     }
8363     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8364     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8365   }
8366   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8367   /* TODO: flips */
8368   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8369   ierr = MatSetValues(A, numFIndices, findices, numCIndices, cindices, values, mode);
8370   if (ierr) {
8371     PetscMPIInt rank;
8372 
8373     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8374     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8375     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8376     PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8377     PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8378   }
8379   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8380   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8381   PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8382   PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8383   PetscFunctionReturn(PETSC_SUCCESS);
8384 }
8385 
8386 PetscErrorCode DMPlexMatGetClosureIndicesRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, PetscInt point, PetscInt cindices[], PetscInt findices[])
8387 {
8388   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
8389   PetscInt       *cpoints = NULL;
8390   PetscInt        foffsets[32], coffsets[32];
8391   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
8392   DMPolytopeType  ct;
8393   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
8394 
8395   PetscFunctionBegin;
8396   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
8397   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
8398   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
8399   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
8400   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
8401   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
8402   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
8403   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
8404   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
8405   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
8406   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
8407   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
8408   PetscCall(PetscArrayzero(foffsets, 32));
8409   PetscCall(PetscArrayzero(coffsets, 32));
8410   /* Column indices */
8411   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8412   maxFPoints = numCPoints;
8413   /* Compress out points not in the section */
8414   /*   TODO: Squeeze out points with 0 dof as well */
8415   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
8416   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
8417     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
8418       cpoints[q * 2]     = cpoints[p];
8419       cpoints[q * 2 + 1] = cpoints[p + 1];
8420       ++q;
8421     }
8422   }
8423   numCPoints = q;
8424   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
8425     PetscInt fdof;
8426 
8427     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
8428     if (!dof) continue;
8429     for (f = 0; f < numFields; ++f) {
8430       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
8431       coffsets[f + 1] += fdof;
8432     }
8433     numCIndices += dof;
8434   }
8435   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
8436   /* Row indices */
8437   PetscCall(DMPlexGetCellType(dmc, point, &ct));
8438   {
8439     DMPlexTransform tr;
8440     DMPolytopeType *rct;
8441     PetscInt       *rsize, *rcone, *rornt, Nt;
8442 
8443     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8444     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8445     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8446     numSubcells = rsize[Nt - 1];
8447     PetscCall(DMPlexTransformDestroy(&tr));
8448   }
8449   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8450   for (r = 0, q = 0; r < numSubcells; ++r) {
8451     /* TODO Map from coarse to fine cells */
8452     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8453     /* Compress out points not in the section */
8454     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8455     for (p = 0; p < numFPoints * 2; p += 2) {
8456       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8457         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8458         if (!dof) continue;
8459         for (s = 0; s < q; ++s)
8460           if (fpoints[p] == ftotpoints[s * 2]) break;
8461         if (s < q) continue;
8462         ftotpoints[q * 2]     = fpoints[p];
8463         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8464         ++q;
8465       }
8466     }
8467     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8468   }
8469   numFPoints = q;
8470   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8471     PetscInt fdof;
8472 
8473     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8474     if (!dof) continue;
8475     for (f = 0; f < numFields; ++f) {
8476       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8477       foffsets[f + 1] += fdof;
8478     }
8479     numFIndices += dof;
8480   }
8481   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8482 
8483   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8484   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8485   if (numFields) {
8486     const PetscInt **permsF[32] = {NULL};
8487     const PetscInt **permsC[32] = {NULL};
8488 
8489     for (f = 0; f < numFields; f++) {
8490       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8491       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8492     }
8493     for (p = 0; p < numFPoints; p++) {
8494       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8495       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8496     }
8497     for (p = 0; p < numCPoints; p++) {
8498       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8499       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8500     }
8501     for (f = 0; f < numFields; f++) {
8502       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8503       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8504     }
8505   } else {
8506     const PetscInt **permsF = NULL;
8507     const PetscInt **permsC = NULL;
8508 
8509     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8510     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8511     for (p = 0, off = 0; p < numFPoints; p++) {
8512       const PetscInt *perm = permsF ? permsF[p] : NULL;
8513 
8514       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8515       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8516     }
8517     for (p = 0, off = 0; p < numCPoints; p++) {
8518       const PetscInt *perm = permsC ? permsC[p] : NULL;
8519 
8520       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8521       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8522     }
8523     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8524     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8525   }
8526   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8527   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8528   PetscFunctionReturn(PETSC_SUCCESS);
8529 }
8530 
8531 /*@C
8532   DMPlexGetVTKCellHeight - Returns the height in the DAG used to determine which points are cells (normally 0)
8533 
8534   Input Parameter:
8535 . dm - The `DMPLEX` object
8536 
8537   Output Parameter:
8538 . cellHeight - The height of a cell
8539 
8540   Level: developer
8541 
8542 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetVTKCellHeight()`
8543 @*/
8544 PetscErrorCode DMPlexGetVTKCellHeight(DM dm, PetscInt *cellHeight)
8545 {
8546   DM_Plex *mesh = (DM_Plex *)dm->data;
8547 
8548   PetscFunctionBegin;
8549   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8550   PetscAssertPointer(cellHeight, 2);
8551   *cellHeight = mesh->vtkCellHeight;
8552   PetscFunctionReturn(PETSC_SUCCESS);
8553 }
8554 
8555 /*@C
8556   DMPlexSetVTKCellHeight - Sets the height in the DAG used to determine which points are cells (normally 0)
8557 
8558   Input Parameters:
8559 + dm         - The `DMPLEX` object
8560 - cellHeight - The height of a cell
8561 
8562   Level: developer
8563 
8564 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetVTKCellHeight()`
8565 @*/
8566 PetscErrorCode DMPlexSetVTKCellHeight(DM dm, PetscInt cellHeight)
8567 {
8568   DM_Plex *mesh = (DM_Plex *)dm->data;
8569 
8570   PetscFunctionBegin;
8571   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8572   mesh->vtkCellHeight = cellHeight;
8573   PetscFunctionReturn(PETSC_SUCCESS);
8574 }
8575 
8576 /*@
8577   DMPlexGetCellTypeStratum - Get the range of cells of a given celltype
8578 
8579   Input Parameters:
8580 + dm - The `DMPLEX` object
8581 - ct - The `DMPolytopeType` of the cell
8582 
8583   Output Parameters:
8584 + start - The first cell of this type, or `NULL`
8585 - end   - The upper bound on this celltype, or `NULL`
8586 
8587   Level: advanced
8588 
8589 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexConstructGhostCells()`, `DMPlexGetDepthStratum()`, `DMPlexGetHeightStratum()`
8590 @*/
8591 PetscErrorCode DMPlexGetCellTypeStratum(DM dm, DMPolytopeType ct, PetscInt *start, PetscInt *end)
8592 {
8593   DM_Plex *mesh = (DM_Plex *)dm->data;
8594   DMLabel  label;
8595   PetscInt pStart, pEnd;
8596 
8597   PetscFunctionBegin;
8598   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8599   if (start) {
8600     PetscAssertPointer(start, 3);
8601     *start = 0;
8602   }
8603   if (end) {
8604     PetscAssertPointer(end, 4);
8605     *end = 0;
8606   }
8607   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8608   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
8609   if (mesh->tr) {
8610     PetscCall(DMPlexTransformGetCellTypeStratum(mesh->tr, ct, start, end));
8611   } else {
8612     PetscCall(DMPlexGetCellTypeLabel(dm, &label));
8613     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named celltype was found");
8614     PetscCall(DMLabelGetStratumBounds(label, ct, start, end));
8615   }
8616   PetscFunctionReturn(PETSC_SUCCESS);
8617 }
8618 
8619 PetscErrorCode DMPlexCreateNumbering_Plex(DM dm, PetscInt pStart, PetscInt pEnd, PetscInt shift, PetscInt *globalSize, PetscSF sf, IS *numbering)
8620 {
8621   PetscSection section, globalSection;
8622   PetscInt    *numbers, p;
8623 
8624   PetscFunctionBegin;
8625   if (PetscDefined(USE_DEBUG)) PetscCall(DMPlexCheckPointSF(dm, sf, PETSC_TRUE));
8626   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
8627   PetscCall(PetscSectionSetChart(section, pStart, pEnd));
8628   for (p = pStart; p < pEnd; ++p) PetscCall(PetscSectionSetDof(section, p, 1));
8629   PetscCall(PetscSectionSetUp(section));
8630   PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_FALSE, PETSC_FALSE, &globalSection));
8631   PetscCall(PetscMalloc1(pEnd - pStart, &numbers));
8632   for (p = pStart; p < pEnd; ++p) {
8633     PetscCall(PetscSectionGetOffset(globalSection, p, &numbers[p - pStart]));
8634     if (numbers[p - pStart] < 0) numbers[p - pStart] -= shift;
8635     else numbers[p - pStart] += shift;
8636   }
8637   PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), pEnd - pStart, numbers, PETSC_OWN_POINTER, numbering));
8638   if (globalSize) {
8639     PetscLayout layout;
8640     PetscCall(PetscSectionGetPointLayout(PetscObjectComm((PetscObject)dm), globalSection, &layout));
8641     PetscCall(PetscLayoutGetSize(layout, globalSize));
8642     PetscCall(PetscLayoutDestroy(&layout));
8643   }
8644   PetscCall(PetscSectionDestroy(&section));
8645   PetscCall(PetscSectionDestroy(&globalSection));
8646   PetscFunctionReturn(PETSC_SUCCESS);
8647 }
8648 
8649 PetscErrorCode DMPlexCreateCellNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalCellNumbers)
8650 {
8651   PetscInt cellHeight, cStart, cEnd;
8652 
8653   PetscFunctionBegin;
8654   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8655   if (includeHybrid) PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8656   else PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd));
8657   PetscCall(DMPlexCreateNumbering_Plex(dm, cStart, cEnd, 0, NULL, dm->sf, globalCellNumbers));
8658   PetscFunctionReturn(PETSC_SUCCESS);
8659 }
8660 
8661 /*@
8662   DMPlexGetCellNumbering - Get a global cell numbering for all cells on this process
8663 
8664   Input Parameter:
8665 . dm - The `DMPLEX` object
8666 
8667   Output Parameter:
8668 . globalCellNumbers - Global cell numbers for all cells on this process
8669 
8670   Level: developer
8671 
8672 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetVertexNumbering()`
8673 @*/
8674 PetscErrorCode DMPlexGetCellNumbering(DM dm, IS *globalCellNumbers)
8675 {
8676   DM_Plex *mesh = (DM_Plex *)dm->data;
8677 
8678   PetscFunctionBegin;
8679   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8680   if (!mesh->globalCellNumbers) PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_FALSE, &mesh->globalCellNumbers));
8681   *globalCellNumbers = mesh->globalCellNumbers;
8682   PetscFunctionReturn(PETSC_SUCCESS);
8683 }
8684 
8685 PetscErrorCode DMPlexCreateVertexNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalVertexNumbers)
8686 {
8687   PetscInt vStart, vEnd;
8688 
8689   PetscFunctionBegin;
8690   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8691   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8692   PetscCall(DMPlexCreateNumbering_Plex(dm, vStart, vEnd, 0, NULL, dm->sf, globalVertexNumbers));
8693   PetscFunctionReturn(PETSC_SUCCESS);
8694 }
8695 
8696 /*@
8697   DMPlexGetVertexNumbering - Get a global vertex numbering for all vertices on this process
8698 
8699   Input Parameter:
8700 . dm - The `DMPLEX` object
8701 
8702   Output Parameter:
8703 . globalVertexNumbers - Global vertex numbers for all vertices on this process
8704 
8705   Level: developer
8706 
8707 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
8708 @*/
8709 PetscErrorCode DMPlexGetVertexNumbering(DM dm, IS *globalVertexNumbers)
8710 {
8711   DM_Plex *mesh = (DM_Plex *)dm->data;
8712 
8713   PetscFunctionBegin;
8714   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8715   if (!mesh->globalVertexNumbers) PetscCall(DMPlexCreateVertexNumbering_Internal(dm, PETSC_FALSE, &mesh->globalVertexNumbers));
8716   *globalVertexNumbers = mesh->globalVertexNumbers;
8717   PetscFunctionReturn(PETSC_SUCCESS);
8718 }
8719 
8720 /*@
8721   DMPlexCreatePointNumbering - Create a global numbering for all points.
8722 
8723   Collective
8724 
8725   Input Parameter:
8726 . dm - The `DMPLEX` object
8727 
8728   Output Parameter:
8729 . globalPointNumbers - Global numbers for all points on this process
8730 
8731   Level: developer
8732 
8733   Notes:
8734   The point numbering `IS` is parallel, with local portion indexed by local points (see `DMGetLocalSection()`). The global
8735   points are taken as stratified, with each MPI rank owning a contiguous subset of each stratum. In the IS, owned points
8736   will have their non-negative value while points owned by different ranks will be involuted -(idx+1). As an example,
8737   consider a parallel mesh in which the first two elements and first two vertices are owned by rank 0.
8738 
8739   The partitioned mesh is
8740   ```
8741   (2)--0--(3)--1--(4)    (1)--0--(2)
8742   ```
8743   and its global numbering is
8744   ```
8745   (3)--0--(4)--1--(5)--2--(6)
8746   ```
8747   Then the global numbering is provided as
8748   ```
8749   [0] Number of indices in set 5
8750   [0] 0 0
8751   [0] 1 1
8752   [0] 2 3
8753   [0] 3 4
8754   [0] 4 -6
8755   [1] Number of indices in set 3
8756   [1] 0 2
8757   [1] 1 5
8758   [1] 2 6
8759   ```
8760 
8761 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
8762 @*/
8763 PetscErrorCode DMPlexCreatePointNumbering(DM dm, IS *globalPointNumbers)
8764 {
8765   IS        nums[4];
8766   PetscInt  depths[4], gdepths[4], starts[4];
8767   PetscInt  depth, d, shift = 0;
8768   PetscBool empty = PETSC_FALSE;
8769 
8770   PetscFunctionBegin;
8771   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8772   PetscCall(DMPlexGetDepth(dm, &depth));
8773   // For unstratified meshes use dim instead of depth
8774   if (depth < 0) PetscCall(DMGetDimension(dm, &depth));
8775   // If any stratum is empty, we must mark all empty
8776   for (d = 0; d <= depth; ++d) {
8777     PetscInt end;
8778 
8779     depths[d] = depth - d;
8780     PetscCall(DMPlexGetDepthStratum(dm, depths[d], &starts[d], &end));
8781     if (!(starts[d] - end)) empty = PETSC_TRUE;
8782   }
8783   if (empty)
8784     for (d = 0; d <= depth; ++d) {
8785       depths[d] = -1;
8786       starts[d] = -1;
8787     }
8788   else PetscCall(PetscSortIntWithArray(depth + 1, starts, depths));
8789   PetscCall(MPIU_Allreduce(depths, gdepths, depth + 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
8790   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]);
8791   // Note here that 'shift' is collective, so that the numbering is stratified by depth
8792   for (d = 0; d <= depth; ++d) {
8793     PetscInt pStart, pEnd, gsize;
8794 
8795     PetscCall(DMPlexGetDepthStratum(dm, gdepths[d], &pStart, &pEnd));
8796     PetscCall(DMPlexCreateNumbering_Plex(dm, pStart, pEnd, shift, &gsize, dm->sf, &nums[d]));
8797     shift += gsize;
8798   }
8799   PetscCall(ISConcatenate(PETSC_COMM_SELF, depth + 1, nums, globalPointNumbers));
8800   for (d = 0; d <= depth; ++d) PetscCall(ISDestroy(&nums[d]));
8801   PetscFunctionReturn(PETSC_SUCCESS);
8802 }
8803 
8804 /*@
8805   DMPlexCreateRankField - Create a cell field whose value is the rank of the owner
8806 
8807   Input Parameter:
8808 . dm - The `DMPLEX` object
8809 
8810   Output Parameter:
8811 . ranks - The rank field
8812 
8813   Options Database Key:
8814 . -dm_partition_view - Adds the rank field into the `DM` output from `-dm_view` using the same viewer
8815 
8816   Level: intermediate
8817 
8818 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`
8819 @*/
8820 PetscErrorCode DMPlexCreateRankField(DM dm, Vec *ranks)
8821 {
8822   DM             rdm;
8823   PetscFE        fe;
8824   PetscScalar   *r;
8825   PetscMPIInt    rank;
8826   DMPolytopeType ct;
8827   PetscInt       dim, cStart, cEnd, c;
8828   PetscBool      simplex;
8829 
8830   PetscFunctionBeginUser;
8831   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8832   PetscAssertPointer(ranks, 2);
8833   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
8834   PetscCall(DMClone(dm, &rdm));
8835   PetscCall(DMGetDimension(rdm, &dim));
8836   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
8837   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
8838   simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct) + 1 ? PETSC_TRUE : PETSC_FALSE;
8839   PetscCall(PetscFECreateDefault(PETSC_COMM_SELF, dim, 1, simplex, "PETSc___rank_", -1, &fe));
8840   PetscCall(PetscObjectSetName((PetscObject)fe, "rank"));
8841   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
8842   PetscCall(PetscFEDestroy(&fe));
8843   PetscCall(DMCreateDS(rdm));
8844   PetscCall(DMCreateGlobalVector(rdm, ranks));
8845   PetscCall(PetscObjectSetName((PetscObject)*ranks, "partition"));
8846   PetscCall(VecGetArray(*ranks, &r));
8847   for (c = cStart; c < cEnd; ++c) {
8848     PetscScalar *lr;
8849 
8850     PetscCall(DMPlexPointGlobalRef(rdm, c, r, &lr));
8851     if (lr) *lr = rank;
8852   }
8853   PetscCall(VecRestoreArray(*ranks, &r));
8854   PetscCall(DMDestroy(&rdm));
8855   PetscFunctionReturn(PETSC_SUCCESS);
8856 }
8857 
8858 /*@
8859   DMPlexCreateLabelField - Create a cell field whose value is the label value for that cell
8860 
8861   Input Parameters:
8862 + dm    - The `DMPLEX`
8863 - label - The `DMLabel`
8864 
8865   Output Parameter:
8866 . val - The label value field
8867 
8868   Options Database Key:
8869 . -dm_label_view - Adds the label value field into the `DM` output from `-dm_view` using the same viewer
8870 
8871   Level: intermediate
8872 
8873 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`
8874 @*/
8875 PetscErrorCode DMPlexCreateLabelField(DM dm, DMLabel label, Vec *val)
8876 {
8877   DM           rdm;
8878   PetscFE      fe;
8879   PetscScalar *v;
8880   PetscInt     dim, cStart, cEnd, c;
8881 
8882   PetscFunctionBeginUser;
8883   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8884   PetscAssertPointer(label, 2);
8885   PetscAssertPointer(val, 3);
8886   PetscCall(DMClone(dm, &rdm));
8887   PetscCall(DMGetDimension(rdm, &dim));
8888   PetscCall(PetscFECreateDefault(PetscObjectComm((PetscObject)rdm), dim, 1, PETSC_TRUE, "PETSc___label_value_", -1, &fe));
8889   PetscCall(PetscObjectSetName((PetscObject)fe, "label_value"));
8890   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
8891   PetscCall(PetscFEDestroy(&fe));
8892   PetscCall(DMCreateDS(rdm));
8893   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
8894   PetscCall(DMCreateGlobalVector(rdm, val));
8895   PetscCall(PetscObjectSetName((PetscObject)*val, "label_value"));
8896   PetscCall(VecGetArray(*val, &v));
8897   for (c = cStart; c < cEnd; ++c) {
8898     PetscScalar *lv;
8899     PetscInt     cval;
8900 
8901     PetscCall(DMPlexPointGlobalRef(rdm, c, v, &lv));
8902     PetscCall(DMLabelGetValue(label, c, &cval));
8903     *lv = cval;
8904   }
8905   PetscCall(VecRestoreArray(*val, &v));
8906   PetscCall(DMDestroy(&rdm));
8907   PetscFunctionReturn(PETSC_SUCCESS);
8908 }
8909 
8910 /*@
8911   DMPlexCheckSymmetry - Check that the adjacency information in the mesh is symmetric.
8912 
8913   Input Parameter:
8914 . dm - The `DMPLEX` object
8915 
8916   Level: developer
8917 
8918   Notes:
8919   This is a useful diagnostic when creating meshes programmatically.
8920 
8921   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
8922 
8923 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
8924 @*/
8925 PetscErrorCode DMPlexCheckSymmetry(DM dm)
8926 {
8927   PetscSection    coneSection, supportSection;
8928   const PetscInt *cone, *support;
8929   PetscInt        coneSize, c, supportSize, s;
8930   PetscInt        pStart, pEnd, p, pp, csize, ssize;
8931   PetscBool       storagecheck = PETSC_TRUE;
8932 
8933   PetscFunctionBegin;
8934   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8935   PetscCall(DMViewFromOptions(dm, NULL, "-sym_dm_view"));
8936   PetscCall(DMPlexGetConeSection(dm, &coneSection));
8937   PetscCall(DMPlexGetSupportSection(dm, &supportSection));
8938   /* Check that point p is found in the support of its cone points, and vice versa */
8939   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8940   for (p = pStart; p < pEnd; ++p) {
8941     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
8942     PetscCall(DMPlexGetCone(dm, p, &cone));
8943     for (c = 0; c < coneSize; ++c) {
8944       PetscBool dup = PETSC_FALSE;
8945       PetscInt  d;
8946       for (d = c - 1; d >= 0; --d) {
8947         if (cone[c] == cone[d]) {
8948           dup = PETSC_TRUE;
8949           break;
8950         }
8951       }
8952       PetscCall(DMPlexGetSupportSize(dm, cone[c], &supportSize));
8953       PetscCall(DMPlexGetSupport(dm, cone[c], &support));
8954       for (s = 0; s < supportSize; ++s) {
8955         if (support[s] == p) break;
8956       }
8957       if ((s >= supportSize) || (dup && (support[s + 1] != p))) {
8958         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", p));
8959         for (s = 0; s < coneSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[s]));
8960         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8961         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", cone[c]));
8962         for (s = 0; s < supportSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[s]));
8963         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8964         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]);
8965         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in support of cone point %" PetscInt_FMT, p, cone[c]);
8966       }
8967     }
8968     PetscCall(DMPlexGetTreeParent(dm, p, &pp, NULL));
8969     if (p != pp) {
8970       storagecheck = PETSC_FALSE;
8971       continue;
8972     }
8973     PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
8974     PetscCall(DMPlexGetSupport(dm, p, &support));
8975     for (s = 0; s < supportSize; ++s) {
8976       PetscCall(DMPlexGetConeSize(dm, support[s], &coneSize));
8977       PetscCall(DMPlexGetCone(dm, support[s], &cone));
8978       for (c = 0; c < coneSize; ++c) {
8979         PetscCall(DMPlexGetTreeParent(dm, cone[c], &pp, NULL));
8980         if (cone[c] != pp) {
8981           c = 0;
8982           break;
8983         }
8984         if (cone[c] == p) break;
8985       }
8986       if (c >= coneSize) {
8987         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", p));
8988         for (c = 0; c < supportSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[c]));
8989         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8990         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", support[s]));
8991         for (c = 0; c < coneSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[c]));
8992         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8993         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in cone of support point %" PetscInt_FMT, p, support[s]);
8994       }
8995     }
8996   }
8997   if (storagecheck) {
8998     PetscCall(PetscSectionGetStorageSize(coneSection, &csize));
8999     PetscCall(PetscSectionGetStorageSize(supportSection, &ssize));
9000     PetscCheck(csize == ssize, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Total cone size %" PetscInt_FMT " != Total support size %" PetscInt_FMT, csize, ssize);
9001   }
9002   PetscFunctionReturn(PETSC_SUCCESS);
9003 }
9004 
9005 /*
9006   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.
9007 */
9008 static PetscErrorCode DMPlexCellUnsplitVertices_Private(DM dm, PetscInt c, DMPolytopeType ct, PetscInt *unsplit)
9009 {
9010   DMPolytopeType  cct;
9011   PetscInt        ptpoints[4];
9012   const PetscInt *cone, *ccone, *ptcone;
9013   PetscInt        coneSize, cp, cconeSize, ccp, npt = 0, pt;
9014 
9015   PetscFunctionBegin;
9016   *unsplit = 0;
9017   switch (ct) {
9018   case DM_POLYTOPE_POINT_PRISM_TENSOR:
9019     ptpoints[npt++] = c;
9020     break;
9021   case DM_POLYTOPE_SEG_PRISM_TENSOR:
9022     PetscCall(DMPlexGetCone(dm, c, &cone));
9023     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9024     for (cp = 0; cp < coneSize; ++cp) {
9025       PetscCall(DMPlexGetCellType(dm, cone[cp], &cct));
9026       if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) ptpoints[npt++] = cone[cp];
9027     }
9028     break;
9029   case DM_POLYTOPE_TRI_PRISM_TENSOR:
9030   case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9031     PetscCall(DMPlexGetCone(dm, c, &cone));
9032     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9033     for (cp = 0; cp < coneSize; ++cp) {
9034       PetscCall(DMPlexGetCone(dm, cone[cp], &ccone));
9035       PetscCall(DMPlexGetConeSize(dm, cone[cp], &cconeSize));
9036       for (ccp = 0; ccp < cconeSize; ++ccp) {
9037         PetscCall(DMPlexGetCellType(dm, ccone[ccp], &cct));
9038         if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) {
9039           PetscInt p;
9040           for (p = 0; p < npt; ++p)
9041             if (ptpoints[p] == ccone[ccp]) break;
9042           if (p == npt) ptpoints[npt++] = ccone[ccp];
9043         }
9044       }
9045     }
9046     break;
9047   default:
9048     break;
9049   }
9050   for (pt = 0; pt < npt; ++pt) {
9051     PetscCall(DMPlexGetCone(dm, ptpoints[pt], &ptcone));
9052     if (ptcone[0] == ptcone[1]) ++(*unsplit);
9053   }
9054   PetscFunctionReturn(PETSC_SUCCESS);
9055 }
9056 
9057 /*@
9058   DMPlexCheckSkeleton - Check that each cell has the correct number of vertices
9059 
9060   Input Parameters:
9061 + dm         - The `DMPLEX` object
9062 - cellHeight - Normally 0
9063 
9064   Level: developer
9065 
9066   Notes:
9067   This is a useful diagnostic when creating meshes programmatically.
9068   Currently applicable only to homogeneous simplex or tensor meshes.
9069 
9070   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9071 
9072 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9073 @*/
9074 PetscErrorCode DMPlexCheckSkeleton(DM dm, PetscInt cellHeight)
9075 {
9076   DMPlexInterpolatedFlag interp;
9077   DMPolytopeType         ct;
9078   PetscInt               vStart, vEnd, cStart, cEnd, c;
9079 
9080   PetscFunctionBegin;
9081   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9082   PetscCall(DMPlexIsInterpolated(dm, &interp));
9083   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9084   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
9085   for (c = cStart; c < cEnd; ++c) {
9086     PetscInt *closure = NULL;
9087     PetscInt  coneSize, closureSize, cl, Nv = 0;
9088 
9089     PetscCall(DMPlexGetCellType(dm, c, &ct));
9090     PetscCheck((PetscInt)ct >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell %" PetscInt_FMT " has no cell type", c);
9091     if (ct == DM_POLYTOPE_UNKNOWN) continue;
9092     if (interp == DMPLEX_INTERPOLATED_FULL) {
9093       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9094       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));
9095     }
9096     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9097     for (cl = 0; cl < closureSize * 2; cl += 2) {
9098       const PetscInt p = closure[cl];
9099       if ((p >= vStart) && (p < vEnd)) ++Nv;
9100     }
9101     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9102     /* Special Case: Tensor faces with identified vertices */
9103     if (Nv < DMPolytopeTypeGetNumVertices(ct)) {
9104       PetscInt unsplit;
9105 
9106       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9107       if (Nv + unsplit == DMPolytopeTypeGetNumVertices(ct)) continue;
9108     }
9109     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));
9110   }
9111   PetscFunctionReturn(PETSC_SUCCESS);
9112 }
9113 
9114 /*@
9115   DMPlexCheckFaces - Check that the faces of each cell give a vertex order this is consistent with what we expect from the cell type
9116 
9117   Collective
9118 
9119   Input Parameters:
9120 + dm         - The `DMPLEX` object
9121 - cellHeight - Normally 0
9122 
9123   Level: developer
9124 
9125   Notes:
9126   This is a useful diagnostic when creating meshes programmatically.
9127   This routine is only relevant for meshes that are fully interpolated across all ranks.
9128   It will error out if a partially interpolated mesh is given on some rank.
9129   It will do nothing for locally uninterpolated mesh (as there is nothing to check).
9130 
9131   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9132 
9133 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMPlexGetVTKCellHeight()`, `DMSetFromOptions()`
9134 @*/
9135 PetscErrorCode DMPlexCheckFaces(DM dm, PetscInt cellHeight)
9136 {
9137   PetscInt               dim, depth, vStart, vEnd, cStart, cEnd, c, h;
9138   DMPlexInterpolatedFlag interpEnum;
9139 
9140   PetscFunctionBegin;
9141   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9142   PetscCall(DMPlexIsInterpolatedCollective(dm, &interpEnum));
9143   if (interpEnum == DMPLEX_INTERPOLATED_NONE) PetscFunctionReturn(PETSC_SUCCESS);
9144   if (interpEnum != DMPLEX_INTERPOLATED_FULL) {
9145     PetscCall(PetscPrintf(PetscObjectComm((PetscObject)dm), "DMPlexCheckFaces() warning: Mesh is only partially interpolated, this is currently not supported"));
9146     PetscFunctionReturn(PETSC_SUCCESS);
9147   }
9148 
9149   PetscCall(DMGetDimension(dm, &dim));
9150   PetscCall(DMPlexGetDepth(dm, &depth));
9151   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
9152   for (h = cellHeight; h < PetscMin(depth, dim); ++h) {
9153     PetscCall(DMPlexGetHeightStratum(dm, h, &cStart, &cEnd));
9154     for (c = cStart; c < cEnd; ++c) {
9155       const PetscInt       *cone, *ornt, *faceSizes, *faces;
9156       const DMPolytopeType *faceTypes;
9157       DMPolytopeType        ct;
9158       PetscInt              numFaces, coneSize, f;
9159       PetscInt             *closure = NULL, closureSize, cl, numCorners = 0, fOff = 0, unsplit;
9160 
9161       PetscCall(DMPlexGetCellType(dm, c, &ct));
9162       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9163       if (unsplit) continue;
9164       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9165       PetscCall(DMPlexGetCone(dm, c, &cone));
9166       PetscCall(DMPlexGetConeOrientation(dm, c, &ornt));
9167       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9168       for (cl = 0; cl < closureSize * 2; cl += 2) {
9169         const PetscInt p = closure[cl];
9170         if ((p >= vStart) && (p < vEnd)) closure[numCorners++] = p;
9171       }
9172       PetscCall(DMPlexGetRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
9173       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);
9174       for (f = 0; f < numFaces; ++f) {
9175         DMPolytopeType fct;
9176         PetscInt      *fclosure = NULL, fclosureSize, cl, fnumCorners = 0, v;
9177 
9178         PetscCall(DMPlexGetCellType(dm, cone[f], &fct));
9179         PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[f], ornt[f], PETSC_TRUE, &fclosureSize, &fclosure));
9180         for (cl = 0; cl < fclosureSize * 2; cl += 2) {
9181           const PetscInt p = fclosure[cl];
9182           if ((p >= vStart) && (p < vEnd)) fclosure[fnumCorners++] = p;
9183         }
9184         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]);
9185         for (v = 0; v < fnumCorners; ++v) {
9186           if (fclosure[v] != faces[fOff + v]) {
9187             PetscInt v1;
9188 
9189             PetscCall(PetscPrintf(PETSC_COMM_SELF, "face closure:"));
9190             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, fclosure[v1]));
9191             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\ncell face:"));
9192             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, faces[fOff + v1]));
9193             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9194             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]);
9195           }
9196         }
9197         PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[f], PETSC_TRUE, &fclosureSize, &fclosure));
9198         fOff += faceSizes[f];
9199       }
9200       PetscCall(DMPlexRestoreRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
9201       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9202     }
9203   }
9204   PetscFunctionReturn(PETSC_SUCCESS);
9205 }
9206 
9207 /*@
9208   DMPlexCheckGeometry - Check the geometry of mesh cells
9209 
9210   Input Parameter:
9211 . dm - The `DMPLEX` object
9212 
9213   Level: developer
9214 
9215   Notes:
9216   This is a useful diagnostic when creating meshes programmatically.
9217 
9218   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9219 
9220 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9221 @*/
9222 PetscErrorCode DMPlexCheckGeometry(DM dm)
9223 {
9224   Vec       coordinates;
9225   PetscReal detJ, J[9], refVol = 1.0;
9226   PetscReal vol;
9227   PetscInt  dim, depth, dE, d, cStart, cEnd, c;
9228 
9229   PetscFunctionBegin;
9230   PetscCall(DMGetDimension(dm, &dim));
9231   PetscCall(DMGetCoordinateDim(dm, &dE));
9232   if (dim != dE) PetscFunctionReturn(PETSC_SUCCESS);
9233   PetscCall(DMPlexGetDepth(dm, &depth));
9234   for (d = 0; d < dim; ++d) refVol *= 2.0;
9235   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
9236   /* Make sure local coordinates are created, because that step is collective */
9237   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
9238   if (!coordinates) PetscFunctionReturn(PETSC_SUCCESS);
9239   for (c = cStart; c < cEnd; ++c) {
9240     DMPolytopeType ct;
9241     PetscInt       unsplit;
9242     PetscBool      ignoreZeroVol = PETSC_FALSE;
9243 
9244     PetscCall(DMPlexGetCellType(dm, c, &ct));
9245     switch (ct) {
9246     case DM_POLYTOPE_SEG_PRISM_TENSOR:
9247     case DM_POLYTOPE_TRI_PRISM_TENSOR:
9248     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9249       ignoreZeroVol = PETSC_TRUE;
9250       break;
9251     default:
9252       break;
9253     }
9254     switch (ct) {
9255     case DM_POLYTOPE_TRI_PRISM:
9256     case DM_POLYTOPE_TRI_PRISM_TENSOR:
9257     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9258     case DM_POLYTOPE_PYRAMID:
9259       continue;
9260     default:
9261       break;
9262     }
9263     PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9264     if (unsplit) continue;
9265     PetscCall(DMPlexComputeCellGeometryFEM(dm, c, NULL, NULL, J, NULL, &detJ));
9266     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);
9267     PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FEM Volume %g\n", c, (double)(detJ * refVol)));
9268     /* This should work with periodicity since DG coordinates should be used */
9269     if (depth > 1) {
9270       PetscCall(DMPlexComputeCellGeometryFVM(dm, c, &vol, NULL, NULL));
9271       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);
9272       PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FVM Volume %g\n", c, (double)vol));
9273     }
9274   }
9275   PetscFunctionReturn(PETSC_SUCCESS);
9276 }
9277 
9278 /*@
9279   DMPlexCheckPointSF - Check that several necessary conditions are met for the point `PetscSF` of this plex.
9280 
9281   Collective
9282 
9283   Input Parameters:
9284 + dm              - The `DMPLEX` object
9285 . pointSF         - The `PetscSF`, or `NULL` for `PointSF` attached to `DM`
9286 - allowExtraRoots - Flag to allow extra points not present in the `DM`
9287 
9288   Level: developer
9289 
9290   Notes:
9291   This is mainly intended for debugging/testing purposes.
9292 
9293   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9294 
9295   Extra roots can come from periodic cuts, where additional points appear on the boundary
9296 
9297 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMGetPointSF()`, `DMSetFromOptions()`
9298 @*/
9299 PetscErrorCode DMPlexCheckPointSF(DM dm, PetscSF pointSF, PetscBool allowExtraRoots)
9300 {
9301   PetscInt           l, nleaves, nroots, overlap;
9302   const PetscInt    *locals;
9303   const PetscSFNode *remotes;
9304   PetscBool          distributed;
9305   MPI_Comm           comm;
9306   PetscMPIInt        rank;
9307 
9308   PetscFunctionBegin;
9309   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9310   if (pointSF) PetscValidHeaderSpecific(pointSF, PETSCSF_CLASSID, 2);
9311   else pointSF = dm->sf;
9312   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9313   PetscCheck(pointSF, comm, PETSC_ERR_ARG_WRONGSTATE, "DMPlex must have Point SF attached");
9314   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9315   {
9316     PetscMPIInt mpiFlag;
9317 
9318     PetscCallMPI(MPI_Comm_compare(comm, PetscObjectComm((PetscObject)pointSF), &mpiFlag));
9319     PetscCheck(mpiFlag == MPI_CONGRUENT || mpiFlag == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "DM and Point SF have different communicators (flag %d)", mpiFlag);
9320   }
9321   PetscCall(PetscSFGetGraph(pointSF, &nroots, &nleaves, &locals, &remotes));
9322   PetscCall(DMPlexIsDistributed(dm, &distributed));
9323   if (!distributed) {
9324     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);
9325     PetscFunctionReturn(PETSC_SUCCESS);
9326   }
9327   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);
9328   PetscCall(DMPlexGetOverlap(dm, &overlap));
9329 
9330   /* Check SF graph is compatible with DMPlex chart */
9331   {
9332     PetscInt pStart, pEnd, maxLeaf;
9333 
9334     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
9335     PetscCall(PetscSFGetLeafRange(pointSF, NULL, &maxLeaf));
9336     PetscCheck(allowExtraRoots || pEnd - pStart == nroots, PETSC_COMM_SELF, PETSC_ERR_PLIB, "pEnd - pStart = %" PetscInt_FMT " != nroots = %" PetscInt_FMT, pEnd - pStart, nroots);
9337     PetscCheck(maxLeaf < pEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "maxLeaf = %" PetscInt_FMT " >= pEnd = %" PetscInt_FMT, maxLeaf, pEnd);
9338   }
9339 
9340   /* Check Point SF has no local points referenced */
9341   for (l = 0; l < nleaves; l++) {
9342     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);
9343   }
9344 
9345   /* Check there are no cells in interface */
9346   if (!overlap) {
9347     PetscInt cellHeight, cStart, cEnd;
9348 
9349     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9350     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9351     for (l = 0; l < nleaves; ++l) {
9352       const PetscInt point = locals ? locals[l] : l;
9353 
9354       PetscCheck(point < cStart || point >= cEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " which is a cell", point);
9355     }
9356   }
9357 
9358   /* If some point is in interface, then all its cone points must be also in interface (either as leaves or roots) */
9359   {
9360     const PetscInt *rootdegree;
9361 
9362     PetscCall(PetscSFComputeDegreeBegin(pointSF, &rootdegree));
9363     PetscCall(PetscSFComputeDegreeEnd(pointSF, &rootdegree));
9364     for (l = 0; l < nleaves; ++l) {
9365       const PetscInt  point = locals ? locals[l] : l;
9366       const PetscInt *cone;
9367       PetscInt        coneSize, c, idx;
9368 
9369       PetscCall(DMPlexGetConeSize(dm, point, &coneSize));
9370       PetscCall(DMPlexGetCone(dm, point, &cone));
9371       for (c = 0; c < coneSize; ++c) {
9372         if (!rootdegree[cone[c]]) {
9373           if (locals) {
9374             PetscCall(PetscFindInt(cone[c], nleaves, locals, &idx));
9375           } else {
9376             idx = (cone[c] < nleaves) ? cone[c] : -1;
9377           }
9378           PetscCheck(idx >= 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " but not %" PetscInt_FMT " from its cone", point, cone[c]);
9379         }
9380       }
9381     }
9382   }
9383   PetscFunctionReturn(PETSC_SUCCESS);
9384 }
9385 
9386 /*@
9387   DMPlexCheck - Perform various checks of `DMPLEX` sanity
9388 
9389   Input Parameter:
9390 . dm - The `DMPLEX` object
9391 
9392   Level: developer
9393 
9394   Notes:
9395   This is a useful diagnostic when creating meshes programmatically.
9396 
9397   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9398 
9399   Currently does not include `DMPlexCheckCellShape()`.
9400 
9401 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9402 @*/
9403 PetscErrorCode DMPlexCheck(DM dm)
9404 {
9405   PetscInt cellHeight;
9406 
9407   PetscFunctionBegin;
9408   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9409   PetscCall(DMPlexCheckSymmetry(dm));
9410   PetscCall(DMPlexCheckSkeleton(dm, cellHeight));
9411   PetscCall(DMPlexCheckFaces(dm, cellHeight));
9412   PetscCall(DMPlexCheckGeometry(dm));
9413   PetscCall(DMPlexCheckPointSF(dm, NULL, PETSC_FALSE));
9414   PetscCall(DMPlexCheckInterfaceCones(dm));
9415   PetscFunctionReturn(PETSC_SUCCESS);
9416 }
9417 
9418 typedef struct cell_stats {
9419   PetscReal min, max, sum, squaresum;
9420   PetscInt  count;
9421 } cell_stats_t;
9422 
9423 static void MPIAPI cell_stats_reduce(void *a, void *b, int *len, MPI_Datatype *datatype)
9424 {
9425   PetscInt i, N = *len;
9426 
9427   for (i = 0; i < N; i++) {
9428     cell_stats_t *A = (cell_stats_t *)a;
9429     cell_stats_t *B = (cell_stats_t *)b;
9430 
9431     B->min = PetscMin(A->min, B->min);
9432     B->max = PetscMax(A->max, B->max);
9433     B->sum += A->sum;
9434     B->squaresum += A->squaresum;
9435     B->count += A->count;
9436   }
9437 }
9438 
9439 /*@
9440   DMPlexCheckCellShape - Checks the Jacobian of the mapping from reference to real cells and computes some minimal statistics.
9441 
9442   Collective
9443 
9444   Input Parameters:
9445 + dm        - The `DMPLEX` object
9446 . output    - If true, statistics will be displayed on `stdout`
9447 - condLimit - Display all cells above this condition number, or `PETSC_DETERMINE` for no cell output
9448 
9449   Level: developer
9450 
9451   Notes:
9452   This is mainly intended for debugging/testing purposes.
9453 
9454   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9455 
9456 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexComputeOrthogonalQuality()`
9457 @*/
9458 PetscErrorCode DMPlexCheckCellShape(DM dm, PetscBool output, PetscReal condLimit)
9459 {
9460   DM           dmCoarse;
9461   cell_stats_t stats, globalStats;
9462   MPI_Comm     comm = PetscObjectComm((PetscObject)dm);
9463   PetscReal   *J, *invJ, min = 0, max = 0, mean = 0, stdev = 0;
9464   PetscReal    limit = condLimit > 0 ? condLimit : PETSC_MAX_REAL;
9465   PetscInt     cdim, cStart, cEnd, c, eStart, eEnd, count = 0;
9466   PetscMPIInt  rank, size;
9467 
9468   PetscFunctionBegin;
9469   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9470   stats.min = PETSC_MAX_REAL;
9471   stats.max = PETSC_MIN_REAL;
9472   stats.sum = stats.squaresum = 0.;
9473   stats.count                 = 0;
9474 
9475   PetscCallMPI(MPI_Comm_size(comm, &size));
9476   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9477   PetscCall(DMGetCoordinateDim(dm, &cdim));
9478   PetscCall(PetscMalloc2(PetscSqr(cdim), &J, PetscSqr(cdim), &invJ));
9479   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
9480   PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
9481   for (c = cStart; c < cEnd; c++) {
9482     PetscInt  i;
9483     PetscReal frobJ = 0., frobInvJ = 0., cond2, cond, detJ;
9484 
9485     PetscCall(DMPlexComputeCellGeometryAffineFEM(dm, c, NULL, J, invJ, &detJ));
9486     PetscCheck(detJ >= 0.0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Mesh cell %" PetscInt_FMT " is inverted", c);
9487     for (i = 0; i < PetscSqr(cdim); ++i) {
9488       frobJ += J[i] * J[i];
9489       frobInvJ += invJ[i] * invJ[i];
9490     }
9491     cond2 = frobJ * frobInvJ;
9492     cond  = PetscSqrtReal(cond2);
9493 
9494     stats.min = PetscMin(stats.min, cond);
9495     stats.max = PetscMax(stats.max, cond);
9496     stats.sum += cond;
9497     stats.squaresum += cond2;
9498     stats.count++;
9499     if (output && cond > limit) {
9500       PetscSection coordSection;
9501       Vec          coordsLocal;
9502       PetscScalar *coords = NULL;
9503       PetscInt     Nv, d, clSize, cl, *closure = NULL;
9504 
9505       PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
9506       PetscCall(DMGetCoordinateSection(dm, &coordSection));
9507       PetscCall(DMPlexVecGetClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9508       PetscCall(PetscSynchronizedPrintf(comm, "[%d] Cell %" PetscInt_FMT " cond %g\n", rank, c, (double)cond));
9509       for (i = 0; i < Nv / cdim; ++i) {
9510         PetscCall(PetscSynchronizedPrintf(comm, "  Vertex %" PetscInt_FMT ": (", i));
9511         for (d = 0; d < cdim; ++d) {
9512           if (d > 0) PetscCall(PetscSynchronizedPrintf(comm, ", "));
9513           PetscCall(PetscSynchronizedPrintf(comm, "%g", (double)PetscRealPart(coords[i * cdim + d])));
9514         }
9515         PetscCall(PetscSynchronizedPrintf(comm, ")\n"));
9516       }
9517       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9518       for (cl = 0; cl < clSize * 2; cl += 2) {
9519         const PetscInt edge = closure[cl];
9520 
9521         if ((edge >= eStart) && (edge < eEnd)) {
9522           PetscReal len;
9523 
9524           PetscCall(DMPlexComputeCellGeometryFVM(dm, edge, &len, NULL, NULL));
9525           PetscCall(PetscSynchronizedPrintf(comm, "  Edge %" PetscInt_FMT ": length %g\n", edge, (double)len));
9526         }
9527       }
9528       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9529       PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9530     }
9531   }
9532   if (output) PetscCall(PetscSynchronizedFlush(comm, NULL));
9533 
9534   if (size > 1) {
9535     PetscMPIInt  blockLengths[2] = {4, 1};
9536     MPI_Aint     blockOffsets[2] = {offsetof(cell_stats_t, min), offsetof(cell_stats_t, count)};
9537     MPI_Datatype blockTypes[2]   = {MPIU_REAL, MPIU_INT}, statType;
9538     MPI_Op       statReduce;
9539 
9540     PetscCallMPI(MPI_Type_create_struct(2, blockLengths, blockOffsets, blockTypes, &statType));
9541     PetscCallMPI(MPI_Type_commit(&statType));
9542     PetscCallMPI(MPI_Op_create(cell_stats_reduce, PETSC_TRUE, &statReduce));
9543     PetscCallMPI(MPI_Reduce(&stats, &globalStats, 1, statType, statReduce, 0, comm));
9544     PetscCallMPI(MPI_Op_free(&statReduce));
9545     PetscCallMPI(MPI_Type_free(&statType));
9546   } else {
9547     PetscCall(PetscArraycpy(&globalStats, &stats, 1));
9548   }
9549   if (rank == 0) {
9550     count = globalStats.count;
9551     min   = globalStats.min;
9552     max   = globalStats.max;
9553     mean  = globalStats.sum / globalStats.count;
9554     stdev = globalStats.count > 1 ? PetscSqrtReal(PetscMax((globalStats.squaresum - globalStats.count * mean * mean) / (globalStats.count - 1), 0)) : 0.0;
9555   }
9556 
9557   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));
9558   PetscCall(PetscFree2(J, invJ));
9559 
9560   PetscCall(DMGetCoarseDM(dm, &dmCoarse));
9561   if (dmCoarse) {
9562     PetscBool isplex;
9563 
9564     PetscCall(PetscObjectTypeCompare((PetscObject)dmCoarse, DMPLEX, &isplex));
9565     if (isplex) PetscCall(DMPlexCheckCellShape(dmCoarse, output, condLimit));
9566   }
9567   PetscFunctionReturn(PETSC_SUCCESS);
9568 }
9569 
9570 /*@
9571   DMPlexComputeOrthogonalQuality - Compute cell-wise orthogonal quality mesh statistic. Optionally tags all cells with
9572   orthogonal quality below given tolerance.
9573 
9574   Collective
9575 
9576   Input Parameters:
9577 + dm   - The `DMPLEX` object
9578 . fv   - Optional `PetscFV` object for pre-computed cell/face centroid information
9579 - atol - [0, 1] Absolute tolerance for tagging cells.
9580 
9581   Output Parameters:
9582 + OrthQual      - `Vec` containing orthogonal quality per cell
9583 - OrthQualLabel - `DMLabel` tagging cells below atol with `DM_ADAPT_REFINE`
9584 
9585   Options Database Keys:
9586 + -dm_plex_orthogonal_quality_label_view - view OrthQualLabel if label is requested. Currently only `PETSCVIEWERASCII` is supported.
9587 - -dm_plex_orthogonal_quality_vec_view   - view OrthQual vector.
9588 
9589   Level: intermediate
9590 
9591   Notes:
9592   Orthogonal quality is given by the following formula\:
9593 
9594   $ \min \left[ \frac{A_i \cdot f_i}{\|A_i\| \|f_i\|} , \frac{A_i \cdot c_i}{\|A_i\| \|c_i\|} \right]$
9595 
9596   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
9597   is the vector from the current cells centroid to the centroid of its i'th neighbor (which shares a face with the
9598   current cell). This computes the vector similarity between each cell face and its corresponding neighbor centroid by
9599   calculating the cosine of the angle between these vectors.
9600 
9601   Orthogonal quality ranges from 1 (best) to 0 (worst).
9602 
9603   This routine is mainly useful for FVM, however is not restricted to only FVM. The `PetscFV` object is optionally used to check for
9604   pre-computed FVM cell data, but if it is not passed in then this data will be computed.
9605 
9606   Cells are tagged if they have an orthogonal quality less than or equal to the absolute tolerance.
9607 
9608 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCheckCellShape()`, `DMCreateLabel()`, `PetscFV`, `DMLabel`, `Vec`
9609 @*/
9610 PetscErrorCode DMPlexComputeOrthogonalQuality(DM dm, PetscFV fv, PetscReal atol, Vec *OrthQual, DMLabel *OrthQualLabel)
9611 {
9612   PetscInt               nc, cellHeight, cStart, cEnd, cell, cellIter = 0;
9613   PetscInt              *idx;
9614   PetscScalar           *oqVals;
9615   const PetscScalar     *cellGeomArr, *faceGeomArr;
9616   PetscReal             *ci, *fi, *Ai;
9617   MPI_Comm               comm;
9618   Vec                    cellgeom, facegeom;
9619   DM                     dmFace, dmCell;
9620   IS                     glob;
9621   ISLocalToGlobalMapping ltog;
9622   PetscViewer            vwr;
9623 
9624   PetscFunctionBegin;
9625   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9626   if (fv) PetscValidHeaderSpecific(fv, PETSCFV_CLASSID, 2);
9627   PetscAssertPointer(OrthQual, 4);
9628   PetscCheck(atol >= 0.0 && atol <= 1.0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Absolute tolerance %g not in [0,1]", (double)atol);
9629   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9630   PetscCall(DMGetDimension(dm, &nc));
9631   PetscCheck(nc >= 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must have dimension >= 2 (current %" PetscInt_FMT ")", nc);
9632   {
9633     DMPlexInterpolatedFlag interpFlag;
9634 
9635     PetscCall(DMPlexIsInterpolated(dm, &interpFlag));
9636     if (interpFlag != DMPLEX_INTERPOLATED_FULL) {
9637       PetscMPIInt rank;
9638 
9639       PetscCallMPI(MPI_Comm_rank(comm, &rank));
9640       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must be fully interpolated, DM on rank %d is not fully interpolated", rank);
9641     }
9642   }
9643   if (OrthQualLabel) {
9644     PetscAssertPointer(OrthQualLabel, 5);
9645     PetscCall(DMCreateLabel(dm, "Orthogonal_Quality"));
9646     PetscCall(DMGetLabel(dm, "Orthogonal_Quality", OrthQualLabel));
9647   } else {
9648     *OrthQualLabel = NULL;
9649   }
9650   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9651   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9652   PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_TRUE, &glob));
9653   PetscCall(ISLocalToGlobalMappingCreateIS(glob, &ltog));
9654   PetscCall(ISLocalToGlobalMappingSetType(ltog, ISLOCALTOGLOBALMAPPINGHASH));
9655   PetscCall(VecCreate(comm, OrthQual));
9656   PetscCall(VecSetType(*OrthQual, VECSTANDARD));
9657   PetscCall(VecSetSizes(*OrthQual, cEnd - cStart, PETSC_DETERMINE));
9658   PetscCall(VecSetLocalToGlobalMapping(*OrthQual, ltog));
9659   PetscCall(VecSetUp(*OrthQual));
9660   PetscCall(ISDestroy(&glob));
9661   PetscCall(ISLocalToGlobalMappingDestroy(&ltog));
9662   PetscCall(DMPlexGetDataFVM(dm, fv, &cellgeom, &facegeom, NULL));
9663   PetscCall(VecGetArrayRead(cellgeom, &cellGeomArr));
9664   PetscCall(VecGetArrayRead(facegeom, &faceGeomArr));
9665   PetscCall(VecGetDM(cellgeom, &dmCell));
9666   PetscCall(VecGetDM(facegeom, &dmFace));
9667   PetscCall(PetscMalloc5(cEnd - cStart, &idx, cEnd - cStart, &oqVals, nc, &ci, nc, &fi, nc, &Ai));
9668   for (cell = cStart; cell < cEnd; cellIter++, cell++) {
9669     PetscInt         cellneigh, cellneighiter = 0, adjSize = PETSC_DETERMINE;
9670     PetscInt         cellarr[2], *adj = NULL;
9671     PetscScalar     *cArr, *fArr;
9672     PetscReal        minvalc = 1.0, minvalf = 1.0;
9673     PetscFVCellGeom *cg;
9674 
9675     idx[cellIter] = cell - cStart;
9676     cellarr[0]    = cell;
9677     /* Make indexing into cellGeom easier */
9678     PetscCall(DMPlexPointLocalRead(dmCell, cell, cellGeomArr, &cg));
9679     PetscCall(DMPlexGetAdjacency_Internal(dm, cell, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &adjSize, &adj));
9680     /* Technically 1 too big, but easier than fiddling with empty adjacency array */
9681     PetscCall(PetscCalloc2(adjSize, &cArr, adjSize, &fArr));
9682     for (cellneigh = 0; cellneigh < adjSize; cellneighiter++, cellneigh++) {
9683       PetscInt         i;
9684       const PetscInt   neigh  = adj[cellneigh];
9685       PetscReal        normci = 0, normfi = 0, normai = 0;
9686       PetscFVCellGeom *cgneigh;
9687       PetscFVFaceGeom *fg;
9688 
9689       /* Don't count ourselves in the neighbor list */
9690       if (neigh == cell) continue;
9691       PetscCall(DMPlexPointLocalRead(dmCell, neigh, cellGeomArr, &cgneigh));
9692       cellarr[1] = neigh;
9693       {
9694         PetscInt        numcovpts;
9695         const PetscInt *covpts;
9696 
9697         PetscCall(DMPlexGetMeet(dm, 2, cellarr, &numcovpts, &covpts));
9698         PetscCall(DMPlexPointLocalRead(dmFace, covpts[0], faceGeomArr, &fg));
9699         PetscCall(DMPlexRestoreMeet(dm, 2, cellarr, &numcovpts, &covpts));
9700       }
9701 
9702       /* Compute c_i, f_i and their norms */
9703       for (i = 0; i < nc; i++) {
9704         ci[i] = cgneigh->centroid[i] - cg->centroid[i];
9705         fi[i] = fg->centroid[i] - cg->centroid[i];
9706         Ai[i] = fg->normal[i];
9707         normci += PetscPowReal(ci[i], 2);
9708         normfi += PetscPowReal(fi[i], 2);
9709         normai += PetscPowReal(Ai[i], 2);
9710       }
9711       normci = PetscSqrtReal(normci);
9712       normfi = PetscSqrtReal(normfi);
9713       normai = PetscSqrtReal(normai);
9714 
9715       /* Normalize and compute for each face-cell-normal pair */
9716       for (i = 0; i < nc; i++) {
9717         ci[i] = ci[i] / normci;
9718         fi[i] = fi[i] / normfi;
9719         Ai[i] = Ai[i] / normai;
9720         /* PetscAbs because I don't know if normals are guaranteed to point out */
9721         cArr[cellneighiter] += PetscAbs(Ai[i] * ci[i]);
9722         fArr[cellneighiter] += PetscAbs(Ai[i] * fi[i]);
9723       }
9724       if (PetscRealPart(cArr[cellneighiter]) < minvalc) minvalc = PetscRealPart(cArr[cellneighiter]);
9725       if (PetscRealPart(fArr[cellneighiter]) < minvalf) minvalf = PetscRealPart(fArr[cellneighiter]);
9726     }
9727     PetscCall(PetscFree(adj));
9728     PetscCall(PetscFree2(cArr, fArr));
9729     /* Defer to cell if they're equal */
9730     oqVals[cellIter] = PetscMin(minvalf, minvalc);
9731     if (OrthQualLabel) {
9732       if (PetscRealPart(oqVals[cellIter]) <= atol) PetscCall(DMLabelSetValue(*OrthQualLabel, cell, DM_ADAPT_REFINE));
9733     }
9734   }
9735   PetscCall(VecSetValuesLocal(*OrthQual, cEnd - cStart, idx, oqVals, INSERT_VALUES));
9736   PetscCall(VecAssemblyBegin(*OrthQual));
9737   PetscCall(VecAssemblyEnd(*OrthQual));
9738   PetscCall(VecRestoreArrayRead(cellgeom, &cellGeomArr));
9739   PetscCall(VecRestoreArrayRead(facegeom, &faceGeomArr));
9740   PetscCall(PetscOptionsGetViewer(comm, NULL, NULL, "-dm_plex_orthogonal_quality_label_view", &vwr, NULL, NULL));
9741   if (OrthQualLabel) {
9742     if (vwr) PetscCall(DMLabelView(*OrthQualLabel, vwr));
9743   }
9744   PetscCall(PetscFree5(idx, oqVals, ci, fi, Ai));
9745   PetscCall(PetscOptionsRestoreViewer(&vwr));
9746   PetscCall(VecViewFromOptions(*OrthQual, NULL, "-dm_plex_orthogonal_quality_vec_view"));
9747   PetscFunctionReturn(PETSC_SUCCESS);
9748 }
9749 
9750 /* this is here instead of DMGetOutputDM because output DM still has constraints in the local indices that affect
9751  * interpolator construction */
9752 static PetscErrorCode DMGetFullDM(DM dm, DM *odm)
9753 {
9754   PetscSection section, newSection, gsection;
9755   PetscSF      sf;
9756   PetscBool    hasConstraints, ghasConstraints;
9757 
9758   PetscFunctionBegin;
9759   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9760   PetscAssertPointer(odm, 2);
9761   PetscCall(DMGetLocalSection(dm, &section));
9762   PetscCall(PetscSectionHasConstraints(section, &hasConstraints));
9763   PetscCall(MPIU_Allreduce(&hasConstraints, &ghasConstraints, 1, MPIU_BOOL, MPI_LOR, PetscObjectComm((PetscObject)dm)));
9764   if (!ghasConstraints) {
9765     PetscCall(PetscObjectReference((PetscObject)dm));
9766     *odm = dm;
9767     PetscFunctionReturn(PETSC_SUCCESS);
9768   }
9769   PetscCall(DMClone(dm, odm));
9770   PetscCall(DMCopyFields(dm, *odm));
9771   PetscCall(DMGetLocalSection(*odm, &newSection));
9772   PetscCall(DMGetPointSF(*odm, &sf));
9773   PetscCall(PetscSectionCreateGlobalSection(newSection, sf, PETSC_TRUE, PETSC_FALSE, &gsection));
9774   PetscCall(DMSetGlobalSection(*odm, gsection));
9775   PetscCall(PetscSectionDestroy(&gsection));
9776   PetscFunctionReturn(PETSC_SUCCESS);
9777 }
9778 
9779 static PetscErrorCode DMCreateAffineInterpolationCorrection_Plex(DM dmc, DM dmf, Vec *shift)
9780 {
9781   DM        dmco, dmfo;
9782   Mat       interpo;
9783   Vec       rscale;
9784   Vec       cglobalo, clocal;
9785   Vec       fglobal, fglobalo, flocal;
9786   PetscBool regular;
9787 
9788   PetscFunctionBegin;
9789   PetscCall(DMGetFullDM(dmc, &dmco));
9790   PetscCall(DMGetFullDM(dmf, &dmfo));
9791   PetscCall(DMSetCoarseDM(dmfo, dmco));
9792   PetscCall(DMPlexGetRegularRefinement(dmf, &regular));
9793   PetscCall(DMPlexSetRegularRefinement(dmfo, regular));
9794   PetscCall(DMCreateInterpolation(dmco, dmfo, &interpo, &rscale));
9795   PetscCall(DMCreateGlobalVector(dmco, &cglobalo));
9796   PetscCall(DMCreateLocalVector(dmc, &clocal));
9797   PetscCall(VecSet(cglobalo, 0.));
9798   PetscCall(VecSet(clocal, 0.));
9799   PetscCall(DMCreateGlobalVector(dmf, &fglobal));
9800   PetscCall(DMCreateGlobalVector(dmfo, &fglobalo));
9801   PetscCall(DMCreateLocalVector(dmf, &flocal));
9802   PetscCall(VecSet(fglobal, 0.));
9803   PetscCall(VecSet(fglobalo, 0.));
9804   PetscCall(VecSet(flocal, 0.));
9805   PetscCall(DMPlexInsertBoundaryValues(dmc, PETSC_TRUE, clocal, 0., NULL, NULL, NULL));
9806   PetscCall(DMLocalToGlobalBegin(dmco, clocal, INSERT_VALUES, cglobalo));
9807   PetscCall(DMLocalToGlobalEnd(dmco, clocal, INSERT_VALUES, cglobalo));
9808   PetscCall(MatMult(interpo, cglobalo, fglobalo));
9809   PetscCall(DMGlobalToLocalBegin(dmfo, fglobalo, INSERT_VALUES, flocal));
9810   PetscCall(DMGlobalToLocalEnd(dmfo, fglobalo, INSERT_VALUES, flocal));
9811   PetscCall(DMLocalToGlobalBegin(dmf, flocal, INSERT_VALUES, fglobal));
9812   PetscCall(DMLocalToGlobalEnd(dmf, flocal, INSERT_VALUES, fglobal));
9813   *shift = fglobal;
9814   PetscCall(VecDestroy(&flocal));
9815   PetscCall(VecDestroy(&fglobalo));
9816   PetscCall(VecDestroy(&clocal));
9817   PetscCall(VecDestroy(&cglobalo));
9818   PetscCall(VecDestroy(&rscale));
9819   PetscCall(MatDestroy(&interpo));
9820   PetscCall(DMDestroy(&dmfo));
9821   PetscCall(DMDestroy(&dmco));
9822   PetscFunctionReturn(PETSC_SUCCESS);
9823 }
9824 
9825 PETSC_INTERN PetscErrorCode DMInterpolateSolution_Plex(DM coarse, DM fine, Mat interp, Vec coarseSol, Vec fineSol)
9826 {
9827   PetscObject shifto;
9828   Vec         shift;
9829 
9830   PetscFunctionBegin;
9831   if (!interp) {
9832     Vec rscale;
9833 
9834     PetscCall(DMCreateInterpolation(coarse, fine, &interp, &rscale));
9835     PetscCall(VecDestroy(&rscale));
9836   } else {
9837     PetscCall(PetscObjectReference((PetscObject)interp));
9838   }
9839   PetscCall(PetscObjectQuery((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", &shifto));
9840   if (!shifto) {
9841     PetscCall(DMCreateAffineInterpolationCorrection_Plex(coarse, fine, &shift));
9842     PetscCall(PetscObjectCompose((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", (PetscObject)shift));
9843     shifto = (PetscObject)shift;
9844     PetscCall(VecDestroy(&shift));
9845   }
9846   shift = (Vec)shifto;
9847   PetscCall(MatInterpolate(interp, coarseSol, fineSol));
9848   PetscCall(VecAXPY(fineSol, 1.0, shift));
9849   PetscCall(MatDestroy(&interp));
9850   PetscFunctionReturn(PETSC_SUCCESS);
9851 }
9852 
9853 /* Pointwise interpolation
9854      Just code FEM for now
9855      u^f = I u^c
9856      sum_k u^f_k phi^f_k = I sum_j u^c_j phi^c_j
9857      u^f_i = sum_j psi^f_i I phi^c_j u^c_j
9858      I_{ij} = psi^f_i phi^c_j
9859 */
9860 PetscErrorCode DMCreateInterpolation_Plex(DM dmCoarse, DM dmFine, Mat *interpolation, Vec *scaling)
9861 {
9862   PetscSection gsc, gsf;
9863   PetscInt     m, n;
9864   void        *ctx;
9865   DM           cdm;
9866   PetscBool    regular, ismatis, isRefined = dmCoarse->data == dmFine->data ? PETSC_FALSE : PETSC_TRUE;
9867 
9868   PetscFunctionBegin;
9869   PetscCall(DMGetGlobalSection(dmFine, &gsf));
9870   PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
9871   PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
9872   PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
9873 
9874   PetscCall(PetscStrcmp(dmCoarse->mattype, MATIS, &ismatis));
9875   PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), interpolation));
9876   PetscCall(MatSetSizes(*interpolation, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
9877   PetscCall(MatSetType(*interpolation, ismatis ? MATAIJ : dmCoarse->mattype));
9878   PetscCall(DMGetApplicationContext(dmFine, &ctx));
9879 
9880   PetscCall(DMGetCoarseDM(dmFine, &cdm));
9881   PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
9882   if (!isRefined || (regular && cdm == dmCoarse)) PetscCall(DMPlexComputeInterpolatorNested(dmCoarse, dmFine, isRefined, *interpolation, ctx));
9883   else PetscCall(DMPlexComputeInterpolatorGeneral(dmCoarse, dmFine, *interpolation, ctx));
9884   PetscCall(MatViewFromOptions(*interpolation, NULL, "-interp_mat_view"));
9885   if (scaling) {
9886     /* Use naive scaling */
9887     PetscCall(DMCreateInterpolationScale(dmCoarse, dmFine, *interpolation, scaling));
9888   }
9889   PetscFunctionReturn(PETSC_SUCCESS);
9890 }
9891 
9892 PetscErrorCode DMCreateInjection_Plex(DM dmCoarse, DM dmFine, Mat *mat)
9893 {
9894   VecScatter ctx;
9895 
9896   PetscFunctionBegin;
9897   PetscCall(DMPlexComputeInjectorFEM(dmCoarse, dmFine, &ctx, NULL));
9898   PetscCall(MatCreateScatter(PetscObjectComm((PetscObject)ctx), ctx, mat));
9899   PetscCall(VecScatterDestroy(&ctx));
9900   PetscFunctionReturn(PETSC_SUCCESS);
9901 }
9902 
9903 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[])
9904 {
9905   const PetscInt Nc = uOff[1] - uOff[0];
9906   PetscInt       c;
9907   for (c = 0; c < Nc; ++c) g0[c * Nc + c] = 1.0;
9908 }
9909 
9910 PetscErrorCode DMCreateMassMatrixLumped_Plex(DM dm, Vec *mass)
9911 {
9912   DM           dmc;
9913   PetscDS      ds;
9914   Vec          ones, locmass;
9915   IS           cellIS;
9916   PetscFormKey key;
9917   PetscInt     depth;
9918 
9919   PetscFunctionBegin;
9920   PetscCall(DMClone(dm, &dmc));
9921   PetscCall(DMCopyDisc(dm, dmc));
9922   PetscCall(DMGetDS(dmc, &ds));
9923   PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
9924   PetscCall(DMCreateGlobalVector(dmc, mass));
9925   PetscCall(DMGetLocalVector(dmc, &ones));
9926   PetscCall(DMGetLocalVector(dmc, &locmass));
9927   PetscCall(DMPlexGetDepth(dmc, &depth));
9928   PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
9929   PetscCall(VecSet(locmass, 0.0));
9930   PetscCall(VecSet(ones, 1.0));
9931   key.label = NULL;
9932   key.value = 0;
9933   key.field = 0;
9934   key.part  = 0;
9935   PetscCall(DMPlexComputeJacobian_Action_Internal(dmc, key, cellIS, 0.0, 0.0, ones, NULL, ones, locmass, NULL));
9936   PetscCall(ISDestroy(&cellIS));
9937   PetscCall(VecSet(*mass, 0.0));
9938   PetscCall(DMLocalToGlobalBegin(dmc, locmass, ADD_VALUES, *mass));
9939   PetscCall(DMLocalToGlobalEnd(dmc, locmass, ADD_VALUES, *mass));
9940   PetscCall(DMRestoreLocalVector(dmc, &ones));
9941   PetscCall(DMRestoreLocalVector(dmc, &locmass));
9942   PetscCall(DMDestroy(&dmc));
9943   PetscFunctionReturn(PETSC_SUCCESS);
9944 }
9945 
9946 PetscErrorCode DMCreateMassMatrix_Plex(DM dmCoarse, DM dmFine, Mat *mass)
9947 {
9948   PetscSection gsc, gsf;
9949   PetscInt     m, n;
9950   void        *ctx;
9951   DM           cdm;
9952   PetscBool    regular;
9953 
9954   PetscFunctionBegin;
9955   if (dmFine == dmCoarse) {
9956     DM            dmc;
9957     PetscDS       ds;
9958     PetscWeakForm wf;
9959     Vec           u;
9960     IS            cellIS;
9961     PetscFormKey  key;
9962     PetscInt      depth;
9963 
9964     PetscCall(DMClone(dmFine, &dmc));
9965     PetscCall(DMCopyDisc(dmFine, dmc));
9966     PetscCall(DMGetDS(dmc, &ds));
9967     PetscCall(PetscDSGetWeakForm(ds, &wf));
9968     PetscCall(PetscWeakFormClear(wf));
9969     PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
9970     PetscCall(DMCreateMatrix(dmc, mass));
9971     PetscCall(DMGetLocalVector(dmc, &u));
9972     PetscCall(DMPlexGetDepth(dmc, &depth));
9973     PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
9974     PetscCall(MatZeroEntries(*mass));
9975     key.label = NULL;
9976     key.value = 0;
9977     key.field = 0;
9978     key.part  = 0;
9979     PetscCall(DMPlexComputeJacobian_Internal(dmc, key, cellIS, 0.0, 0.0, u, NULL, *mass, *mass, NULL));
9980     PetscCall(ISDestroy(&cellIS));
9981     PetscCall(DMRestoreLocalVector(dmc, &u));
9982     PetscCall(DMDestroy(&dmc));
9983   } else {
9984     PetscCall(DMGetGlobalSection(dmFine, &gsf));
9985     PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
9986     PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
9987     PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
9988 
9989     PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), mass));
9990     PetscCall(MatSetSizes(*mass, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
9991     PetscCall(MatSetType(*mass, dmCoarse->mattype));
9992     PetscCall(DMGetApplicationContext(dmFine, &ctx));
9993 
9994     PetscCall(DMGetCoarseDM(dmFine, &cdm));
9995     PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
9996     if (regular && cdm == dmCoarse) PetscCall(DMPlexComputeMassMatrixNested(dmCoarse, dmFine, *mass, ctx));
9997     else PetscCall(DMPlexComputeMassMatrixGeneral(dmCoarse, dmFine, *mass, ctx));
9998   }
9999   PetscCall(MatViewFromOptions(*mass, NULL, "-mass_mat_view"));
10000   PetscFunctionReturn(PETSC_SUCCESS);
10001 }
10002 
10003 /*@
10004   DMPlexGetRegularRefinement - Get the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
10005 
10006   Input Parameter:
10007 . dm - The `DMPLEX` object
10008 
10009   Output Parameter:
10010 . regular - The flag
10011 
10012   Level: intermediate
10013 
10014 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetRegularRefinement()`
10015 @*/
10016 PetscErrorCode DMPlexGetRegularRefinement(DM dm, PetscBool *regular)
10017 {
10018   PetscFunctionBegin;
10019   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10020   PetscAssertPointer(regular, 2);
10021   *regular = ((DM_Plex *)dm->data)->regularRefinement;
10022   PetscFunctionReturn(PETSC_SUCCESS);
10023 }
10024 
10025 /*@
10026   DMPlexSetRegularRefinement - Set the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
10027 
10028   Input Parameters:
10029 + dm      - The `DMPLEX` object
10030 - regular - The flag
10031 
10032   Level: intermediate
10033 
10034 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetRegularRefinement()`
10035 @*/
10036 PetscErrorCode DMPlexSetRegularRefinement(DM dm, PetscBool regular)
10037 {
10038   PetscFunctionBegin;
10039   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10040   ((DM_Plex *)dm->data)->regularRefinement = regular;
10041   PetscFunctionReturn(PETSC_SUCCESS);
10042 }
10043 
10044 /*@
10045   DMPlexGetAnchors - Get the layout of the anchor (point-to-point) constraints.  Typically, the user will not have to
10046   call DMPlexGetAnchors() directly: if there are anchors, then `DMPlexGetAnchors()` is called during `DMGetDefaultConstraints()`.
10047 
10048   Not Collective
10049 
10050   Input Parameter:
10051 . dm - The `DMPLEX` object
10052 
10053   Output Parameters:
10054 + anchorSection - If not `NULL`, set to the section describing which points anchor the constrained points.
10055 - anchorIS      - If not `NULL`, set to the list of anchors indexed by `anchorSection`
10056 
10057   Level: intermediate
10058 
10059 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`, `IS`, `PetscSection`
10060 @*/
10061 PetscErrorCode DMPlexGetAnchors(DM dm, PetscSection *anchorSection, IS *anchorIS)
10062 {
10063   DM_Plex *plex = (DM_Plex *)dm->data;
10064 
10065   PetscFunctionBegin;
10066   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10067   if (!plex->anchorSection && !plex->anchorIS && plex->createanchors) PetscCall((*plex->createanchors)(dm));
10068   if (anchorSection) *anchorSection = plex->anchorSection;
10069   if (anchorIS) *anchorIS = plex->anchorIS;
10070   PetscFunctionReturn(PETSC_SUCCESS);
10071 }
10072 
10073 /*@
10074   DMPlexSetAnchors - Set the layout of the local anchor (point-to-point) constraints.
10075 
10076   Collective
10077 
10078   Input Parameters:
10079 + dm            - The `DMPLEX` object
10080 . anchorSection - The section that describes the mapping from constrained points to the anchor points listed in anchorIS.
10081                   Must have a local communicator (`PETSC_COMM_SELF` or derivative).
10082 - anchorIS      - The list of all anchor points.  Must have a local communicator (`PETSC_COMM_SELF` or derivative).
10083 
10084   Level: intermediate
10085 
10086   Notes:
10087   Unlike boundary conditions, when a point's degrees of freedom in a section are constrained to
10088   an outside value, the anchor constraints set a point's degrees of freedom to be a linear
10089   combination of other points' degrees of freedom.
10090 
10091   After specifying the layout of constraints with `DMPlexSetAnchors()`, one specifies the constraints by calling
10092   `DMGetDefaultConstraints()` and filling in the entries in the constraint matrix.
10093 
10094   The reference counts of `anchorSection` and `anchorIS` are incremented.
10095 
10096 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`
10097 @*/
10098 PetscErrorCode DMPlexSetAnchors(DM dm, PetscSection anchorSection, IS anchorIS)
10099 {
10100   DM_Plex    *plex = (DM_Plex *)dm->data;
10101   PetscMPIInt result;
10102 
10103   PetscFunctionBegin;
10104   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10105   if (anchorSection) {
10106     PetscValidHeaderSpecific(anchorSection, PETSC_SECTION_CLASSID, 2);
10107     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorSection), &result));
10108     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor section must have local communicator");
10109   }
10110   if (anchorIS) {
10111     PetscValidHeaderSpecific(anchorIS, IS_CLASSID, 3);
10112     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorIS), &result));
10113     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor IS must have local communicator");
10114   }
10115 
10116   PetscCall(PetscObjectReference((PetscObject)anchorSection));
10117   PetscCall(PetscSectionDestroy(&plex->anchorSection));
10118   plex->anchorSection = anchorSection;
10119 
10120   PetscCall(PetscObjectReference((PetscObject)anchorIS));
10121   PetscCall(ISDestroy(&plex->anchorIS));
10122   plex->anchorIS = anchorIS;
10123 
10124   if (PetscUnlikelyDebug(anchorIS && anchorSection)) {
10125     PetscInt        size, a, pStart, pEnd;
10126     const PetscInt *anchors;
10127 
10128     PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
10129     PetscCall(ISGetLocalSize(anchorIS, &size));
10130     PetscCall(ISGetIndices(anchorIS, &anchors));
10131     for (a = 0; a < size; a++) {
10132       PetscInt p;
10133 
10134       p = anchors[a];
10135       if (p >= pStart && p < pEnd) {
10136         PetscInt dof;
10137 
10138         PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
10139         if (dof) {
10140           PetscCall(ISRestoreIndices(anchorIS, &anchors));
10141           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Point %" PetscInt_FMT " cannot be constrained and an anchor", p);
10142         }
10143       }
10144     }
10145     PetscCall(ISRestoreIndices(anchorIS, &anchors));
10146   }
10147   /* reset the generic constraints */
10148   PetscCall(DMSetDefaultConstraints(dm, NULL, NULL, NULL));
10149   PetscFunctionReturn(PETSC_SUCCESS);
10150 }
10151 
10152 static PetscErrorCode DMPlexCreateConstraintSection_Anchors(DM dm, PetscSection section, PetscSection *cSec)
10153 {
10154   PetscSection anchorSection;
10155   PetscInt     pStart, pEnd, sStart, sEnd, p, dof, numFields, f;
10156 
10157   PetscFunctionBegin;
10158   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10159   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
10160   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, cSec));
10161   PetscCall(PetscSectionGetNumFields(section, &numFields));
10162   if (numFields) {
10163     PetscInt f;
10164     PetscCall(PetscSectionSetNumFields(*cSec, numFields));
10165 
10166     for (f = 0; f < numFields; f++) {
10167       PetscInt numComp;
10168 
10169       PetscCall(PetscSectionGetFieldComponents(section, f, &numComp));
10170       PetscCall(PetscSectionSetFieldComponents(*cSec, f, numComp));
10171     }
10172   }
10173   PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
10174   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
10175   pStart = PetscMax(pStart, sStart);
10176   pEnd   = PetscMin(pEnd, sEnd);
10177   pEnd   = PetscMax(pStart, pEnd);
10178   PetscCall(PetscSectionSetChart(*cSec, pStart, pEnd));
10179   for (p = pStart; p < pEnd; p++) {
10180     PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
10181     if (dof) {
10182       PetscCall(PetscSectionGetDof(section, p, &dof));
10183       PetscCall(PetscSectionSetDof(*cSec, p, dof));
10184       for (f = 0; f < numFields; f++) {
10185         PetscCall(PetscSectionGetFieldDof(section, p, f, &dof));
10186         PetscCall(PetscSectionSetFieldDof(*cSec, p, f, dof));
10187       }
10188     }
10189   }
10190   PetscCall(PetscSectionSetUp(*cSec));
10191   PetscCall(PetscObjectSetName((PetscObject)*cSec, "Constraint Section"));
10192   PetscFunctionReturn(PETSC_SUCCESS);
10193 }
10194 
10195 static PetscErrorCode DMPlexCreateConstraintMatrix_Anchors(DM dm, PetscSection section, PetscSection cSec, Mat *cMat)
10196 {
10197   PetscSection    aSec;
10198   PetscInt        pStart, pEnd, p, sStart, sEnd, dof, aDof, aOff, off, nnz, annz, m, n, q, a, offset, *i, *j;
10199   const PetscInt *anchors;
10200   PetscInt        numFields, f;
10201   IS              aIS;
10202   MatType         mtype;
10203   PetscBool       iscuda, iskokkos;
10204 
10205   PetscFunctionBegin;
10206   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10207   PetscCall(PetscSectionGetStorageSize(cSec, &m));
10208   PetscCall(PetscSectionGetStorageSize(section, &n));
10209   PetscCall(MatCreate(PETSC_COMM_SELF, cMat));
10210   PetscCall(MatSetSizes(*cMat, m, n, m, n));
10211   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJCUSPARSE, &iscuda));
10212   if (!iscuda) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJCUSPARSE, &iscuda));
10213   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJKOKKOS, &iskokkos));
10214   if (!iskokkos) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJKOKKOS, &iskokkos));
10215   if (iscuda) mtype = MATSEQAIJCUSPARSE;
10216   else if (iskokkos) mtype = MATSEQAIJKOKKOS;
10217   else mtype = MATSEQAIJ;
10218   PetscCall(MatSetType(*cMat, mtype));
10219   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
10220   PetscCall(ISGetIndices(aIS, &anchors));
10221   /* cSec will be a subset of aSec and section */
10222   PetscCall(PetscSectionGetChart(cSec, &pStart, &pEnd));
10223   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
10224   PetscCall(PetscMalloc1(m + 1, &i));
10225   i[0] = 0;
10226   PetscCall(PetscSectionGetNumFields(section, &numFields));
10227   for (p = pStart; p < pEnd; p++) {
10228     PetscInt rDof, rOff, r;
10229 
10230     PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10231     if (!rDof) continue;
10232     PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10233     if (numFields) {
10234       for (f = 0; f < numFields; f++) {
10235         annz = 0;
10236         for (r = 0; r < rDof; r++) {
10237           a = anchors[rOff + r];
10238           if (a < sStart || a >= sEnd) continue;
10239           PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
10240           annz += aDof;
10241         }
10242         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
10243         PetscCall(PetscSectionGetFieldOffset(cSec, p, f, &off));
10244         for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
10245       }
10246     } else {
10247       annz = 0;
10248       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10249       for (q = 0; q < dof; q++) {
10250         a = anchors[rOff + q];
10251         if (a < sStart || a >= sEnd) continue;
10252         PetscCall(PetscSectionGetDof(section, a, &aDof));
10253         annz += aDof;
10254       }
10255       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10256       PetscCall(PetscSectionGetOffset(cSec, p, &off));
10257       for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
10258     }
10259   }
10260   nnz = i[m];
10261   PetscCall(PetscMalloc1(nnz, &j));
10262   offset = 0;
10263   for (p = pStart; p < pEnd; p++) {
10264     if (numFields) {
10265       for (f = 0; f < numFields; f++) {
10266         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
10267         for (q = 0; q < dof; q++) {
10268           PetscInt rDof, rOff, r;
10269           PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10270           PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10271           for (r = 0; r < rDof; r++) {
10272             PetscInt s;
10273 
10274             a = anchors[rOff + r];
10275             if (a < sStart || a >= sEnd) continue;
10276             PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
10277             PetscCall(PetscSectionGetFieldOffset(section, a, f, &aOff));
10278             for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
10279           }
10280         }
10281       }
10282     } else {
10283       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10284       for (q = 0; q < dof; q++) {
10285         PetscInt rDof, rOff, r;
10286         PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10287         PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10288         for (r = 0; r < rDof; r++) {
10289           PetscInt s;
10290 
10291           a = anchors[rOff + r];
10292           if (a < sStart || a >= sEnd) continue;
10293           PetscCall(PetscSectionGetDof(section, a, &aDof));
10294           PetscCall(PetscSectionGetOffset(section, a, &aOff));
10295           for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
10296         }
10297       }
10298     }
10299   }
10300   PetscCall(MatSeqAIJSetPreallocationCSR(*cMat, i, j, NULL));
10301   PetscCall(PetscFree(i));
10302   PetscCall(PetscFree(j));
10303   PetscCall(ISRestoreIndices(aIS, &anchors));
10304   PetscFunctionReturn(PETSC_SUCCESS);
10305 }
10306 
10307 PetscErrorCode DMCreateDefaultConstraints_Plex(DM dm)
10308 {
10309   DM_Plex     *plex = (DM_Plex *)dm->data;
10310   PetscSection anchorSection, section, cSec;
10311   Mat          cMat;
10312 
10313   PetscFunctionBegin;
10314   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10315   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
10316   if (anchorSection) {
10317     PetscInt Nf;
10318 
10319     PetscCall(DMGetLocalSection(dm, &section));
10320     PetscCall(DMPlexCreateConstraintSection_Anchors(dm, section, &cSec));
10321     PetscCall(DMPlexCreateConstraintMatrix_Anchors(dm, section, cSec, &cMat));
10322     PetscCall(DMGetNumFields(dm, &Nf));
10323     if (Nf && plex->computeanchormatrix) PetscCall((*plex->computeanchormatrix)(dm, section, cSec, cMat));
10324     PetscCall(DMSetDefaultConstraints(dm, cSec, cMat, NULL));
10325     PetscCall(PetscSectionDestroy(&cSec));
10326     PetscCall(MatDestroy(&cMat));
10327   }
10328   PetscFunctionReturn(PETSC_SUCCESS);
10329 }
10330 
10331 PetscErrorCode DMCreateSubDomainDM_Plex(DM dm, DMLabel label, PetscInt value, IS *is, DM *subdm)
10332 {
10333   IS           subis;
10334   PetscSection section, subsection;
10335 
10336   PetscFunctionBegin;
10337   PetscCall(DMGetLocalSection(dm, &section));
10338   PetscCheck(section, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set default section for DM before splitting subdomain");
10339   PetscCheck(subdm, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set output subDM for splitting subdomain");
10340   /* Create subdomain */
10341   PetscCall(DMPlexFilter(dm, label, value, subdm));
10342   /* Create submodel */
10343   PetscCall(DMPlexGetSubpointIS(*subdm, &subis));
10344   PetscCall(PetscSectionCreateSubmeshSection(section, subis, &subsection));
10345   PetscCall(DMSetLocalSection(*subdm, subsection));
10346   PetscCall(PetscSectionDestroy(&subsection));
10347   PetscCall(DMCopyDisc(dm, *subdm));
10348   /* Create map from submodel to global model */
10349   if (is) {
10350     PetscSection    sectionGlobal, subsectionGlobal;
10351     IS              spIS;
10352     const PetscInt *spmap;
10353     PetscInt       *subIndices;
10354     PetscInt        subSize = 0, subOff = 0, pStart, pEnd, p;
10355     PetscInt        Nf, f, bs = -1, bsLocal[2], bsMinMax[2];
10356 
10357     PetscCall(DMPlexGetSubpointIS(*subdm, &spIS));
10358     PetscCall(ISGetIndices(spIS, &spmap));
10359     PetscCall(PetscSectionGetNumFields(section, &Nf));
10360     PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
10361     PetscCall(DMGetGlobalSection(*subdm, &subsectionGlobal));
10362     PetscCall(PetscSectionGetChart(subsection, &pStart, &pEnd));
10363     for (p = pStart; p < pEnd; ++p) {
10364       PetscInt gdof, pSubSize = 0;
10365 
10366       PetscCall(PetscSectionGetDof(sectionGlobal, p, &gdof));
10367       if (gdof > 0) {
10368         for (f = 0; f < Nf; ++f) {
10369           PetscInt fdof, fcdof;
10370 
10371           PetscCall(PetscSectionGetFieldDof(subsection, p, f, &fdof));
10372           PetscCall(PetscSectionGetFieldConstraintDof(subsection, p, f, &fcdof));
10373           pSubSize += fdof - fcdof;
10374         }
10375         subSize += pSubSize;
10376         if (pSubSize) {
10377           if (bs < 0) {
10378             bs = pSubSize;
10379           } else if (bs != pSubSize) {
10380             /* Layout does not admit a pointwise block size */
10381             bs = 1;
10382           }
10383         }
10384       }
10385     }
10386     /* Must have same blocksize on all procs (some might have no points) */
10387     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs;
10388     bsLocal[1] = bs;
10389     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
10390     if (bsMinMax[0] != bsMinMax[1]) {
10391       bs = 1;
10392     } else {
10393       bs = bsMinMax[0];
10394     }
10395     PetscCall(PetscMalloc1(subSize, &subIndices));
10396     for (p = pStart; p < pEnd; ++p) {
10397       PetscInt gdof, goff;
10398 
10399       PetscCall(PetscSectionGetDof(subsectionGlobal, p, &gdof));
10400       if (gdof > 0) {
10401         const PetscInt point = spmap[p];
10402 
10403         PetscCall(PetscSectionGetOffset(sectionGlobal, point, &goff));
10404         for (f = 0; f < Nf; ++f) {
10405           PetscInt fdof, fcdof, fc, f2, poff = 0;
10406 
10407           /* Can get rid of this loop by storing field information in the global section */
10408           for (f2 = 0; f2 < f; ++f2) {
10409             PetscCall(PetscSectionGetFieldDof(section, p, f2, &fdof));
10410             PetscCall(PetscSectionGetFieldConstraintDof(section, p, f2, &fcdof));
10411             poff += fdof - fcdof;
10412           }
10413           PetscCall(PetscSectionGetFieldDof(section, p, f, &fdof));
10414           PetscCall(PetscSectionGetFieldConstraintDof(section, p, f, &fcdof));
10415           for (fc = 0; fc < fdof - fcdof; ++fc, ++subOff) subIndices[subOff] = goff + poff + fc;
10416         }
10417       }
10418     }
10419     PetscCall(ISRestoreIndices(spIS, &spmap));
10420     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), subSize, subIndices, PETSC_OWN_POINTER, is));
10421     if (bs > 1) {
10422       /* We need to check that the block size does not come from non-contiguous fields */
10423       PetscInt i, j, set = 1;
10424       for (i = 0; i < subSize; i += bs) {
10425         for (j = 0; j < bs; ++j) {
10426           if (subIndices[i + j] != subIndices[i] + j) {
10427             set = 0;
10428             break;
10429           }
10430         }
10431       }
10432       if (set) PetscCall(ISSetBlockSize(*is, bs));
10433     }
10434     /* Attach nullspace */
10435     for (f = 0; f < Nf; ++f) {
10436       (*subdm)->nullspaceConstructors[f] = dm->nullspaceConstructors[f];
10437       if ((*subdm)->nullspaceConstructors[f]) break;
10438     }
10439     if (f < Nf) {
10440       MatNullSpace nullSpace;
10441       PetscCall((*(*subdm)->nullspaceConstructors[f])(*subdm, f, f, &nullSpace));
10442 
10443       PetscCall(PetscObjectCompose((PetscObject)*is, "nullspace", (PetscObject)nullSpace));
10444       PetscCall(MatNullSpaceDestroy(&nullSpace));
10445     }
10446   }
10447   PetscFunctionReturn(PETSC_SUCCESS);
10448 }
10449 
10450 /*@
10451   DMPlexMonitorThroughput - Report the cell throughput of FE integration
10452 
10453   Input Parameters:
10454 + dm    - The `DM`
10455 - dummy - unused argument
10456 
10457   Options Database Key:
10458 . -dm_plex_monitor_throughput - Activate the monitor
10459 
10460   Level: developer
10461 
10462 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexCreate()`
10463 @*/
10464 PetscErrorCode DMPlexMonitorThroughput(DM dm, void *dummy)
10465 {
10466   PetscLogHandler default_handler;
10467 
10468   PetscFunctionBegin;
10469   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10470   PetscCall(PetscLogGetDefaultHandler(&default_handler));
10471   if (default_handler) {
10472     PetscLogEvent      event;
10473     PetscEventPerfInfo eventInfo;
10474     PetscReal          cellRate, flopRate;
10475     PetscInt           cStart, cEnd, Nf, N;
10476     const char        *name;
10477 
10478     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
10479     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
10480     PetscCall(DMGetNumFields(dm, &Nf));
10481     PetscCall(PetscLogEventGetId("DMPlexResidualFE", &event));
10482     PetscCall(PetscLogEventGetPerfInfo(PETSC_DEFAULT, event, &eventInfo));
10483     N        = (cEnd - cStart) * Nf * eventInfo.count;
10484     flopRate = eventInfo.flops / eventInfo.time;
10485     cellRate = N / eventInfo.time;
10486     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)));
10487   } else {
10488     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.");
10489   }
10490   PetscFunctionReturn(PETSC_SUCCESS);
10491 }
10492