xref: /petsc/src/dm/impls/plex/plex.c (revision 375d23e45bc7f27f90f326da17aaea364b3bd8d3)
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     PetscCall(PetscOptionsGetBoolArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_colors_depth", drawColors, &n, &flg));
1088     PetscCheck(!flg || n == dim + 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim + 1);
1089     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_labels", names, &numLabels, &useLabels));
1090     if (!useLabels) numLabels = 0;
1091     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_colors", colors, &numColors, &useColors));
1092     if (!useColors) {
1093       numColors = 3;
1094       for (c = 0; c < numColors; ++c) PetscCall(PetscStrallocpy(defcolors[c], &colors[c]));
1095     }
1096     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_lcolors", lcolors, &numLColors, &useColors));
1097     if (!useColors) {
1098       numLColors = 4;
1099       for (c = 0; c < numLColors; ++c) PetscCall(PetscStrallocpy(deflcolors[c], &lcolors[c]));
1100     }
1101     PetscCall(PetscOptionsGetString(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_label_filter", lname, sizeof(lname), &lflg));
1102     plotEdges = (PetscBool)(depth > 1 && drawNumbers[1] && dim < 3);
1103     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_edges", &plotEdges, &flg));
1104     PetscCheck(!flg || !plotEdges || depth >= dim, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Mesh must be interpolated");
1105     if (depth < dim) plotEdges = PETSC_FALSE;
1106     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_hasse", &drawHasse, NULL));
1107 
1108     /* filter points with labelvalue != labeldefaultvalue */
1109     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
1110     PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1111     PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
1112     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1113     if (lflg) {
1114       DMLabel lbl;
1115 
1116       PetscCall(DMGetLabel(dm, lname, &lbl));
1117       if (lbl) {
1118         PetscInt val, defval;
1119 
1120         PetscCall(DMLabelGetDefaultValue(lbl, &defval));
1121         PetscCall(PetscBTCreate(pEnd - pStart, &wp));
1122         for (c = pStart; c < pEnd; c++) {
1123           PetscInt *closure = NULL;
1124           PetscInt  closureSize;
1125 
1126           PetscCall(DMLabelGetValue(lbl, c, &val));
1127           if (val == defval) continue;
1128 
1129           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1130           for (p = 0; p < closureSize * 2; p += 2) PetscCall(PetscBTSet(wp, closure[p] - pStart));
1131           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1132         }
1133       }
1134     }
1135 
1136     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1137     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
1138     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1139     PetscCall(PetscViewerASCIIPrintf(viewer, "\
1140 \\documentclass[tikz]{standalone}\n\n\
1141 \\usepackage{pgflibraryshapes}\n\
1142 \\usetikzlibrary{backgrounds}\n\
1143 \\usetikzlibrary{arrows}\n\
1144 \\begin{document}\n"));
1145     if (size > 1) {
1146       PetscCall(PetscViewerASCIIPrintf(viewer, "%s for process ", name));
1147       for (p = 0; p < size; ++p) {
1148         if (p) PetscCall(PetscViewerASCIIPrintf(viewer, (p == size - 1) ? ", and " : ", "));
1149         PetscCall(PetscViewerASCIIPrintf(viewer, "{\\textcolor{%s}%" PetscInt_FMT "}", colors[p % numColors], p));
1150       }
1151       PetscCall(PetscViewerASCIIPrintf(viewer, ".\n\n\n"));
1152     }
1153     if (drawHasse) {
1154       PetscInt maxStratum = PetscMax(vEnd - vStart, PetscMax(eEnd - eStart, cEnd - cStart));
1155 
1156       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vStart}{%" PetscInt_FMT "}\n", vStart));
1157       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vEnd}{%" PetscInt_FMT "}\n", vEnd - 1));
1158       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numVertices}{%" PetscInt_FMT "}\n", vEnd - vStart));
1159       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vShift}{%.2f}\n", 3 + (maxStratum - (vEnd - vStart)) / 2.));
1160       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eStart}{%" PetscInt_FMT "}\n", eStart));
1161       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eEnd}{%" PetscInt_FMT "}\n", eEnd - 1));
1162       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eShift}{%.2f}\n", 3 + (maxStratum - (eEnd - eStart)) / 2.));
1163       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numEdges}{%" PetscInt_FMT "}\n", eEnd - eStart));
1164       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cStart}{%" PetscInt_FMT "}\n", cStart));
1165       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cEnd}{%" PetscInt_FMT "}\n", cEnd - 1));
1166       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numCells}{%" PetscInt_FMT "}\n", cEnd - cStart));
1167       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cShift}{%.2f}\n", 3 + (maxStratum - (cEnd - cStart)) / 2.));
1168     }
1169     PetscCall(PetscViewerASCIIPrintf(viewer, "\\begin{tikzpicture}[scale = %g,font=\\fontsize{8}{8}\\selectfont]\n", (double)tikzscale));
1170 
1171     /* Plot vertices */
1172     PetscCall(VecGetArray(coordinates, &coords));
1173     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
1174     for (v = vStart; v < vEnd; ++v) {
1175       PetscInt  off, dof, d;
1176       PetscBool isLabeled = PETSC_FALSE;
1177 
1178       if (wp && !PetscBTLookup(wp, v - pStart)) continue;
1179       PetscCall(PetscSectionGetDof(coordSection, v, &dof));
1180       PetscCall(PetscSectionGetOffset(coordSection, v, &off));
1181       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1182       PetscCheck(dof <= 3, PETSC_COMM_SELF, PETSC_ERR_PLIB, "coordSection vertex %" PetscInt_FMT " has dof %" PetscInt_FMT " > 3", v, dof);
1183       for (d = 0; d < dof; ++d) {
1184         tcoords[d] = (double)(scale * PetscRealPart(coords[off + d]));
1185         tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1186       }
1187       /* Rotate coordinates since PGF makes z point out of the page instead of up */
1188       if (dim == 3) {
1189         PetscReal tmp = tcoords[1];
1190         tcoords[1]    = tcoords[2];
1191         tcoords[2]    = -tmp;
1192       }
1193       for (d = 0; d < dof; ++d) {
1194         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1195         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)tcoords[d]));
1196       }
1197       if (drawHasse) color = colors[0 % numColors];
1198       else color = colors[rank % numColors];
1199       for (l = 0; l < numLabels; ++l) {
1200         PetscInt val;
1201         PetscCall(DMGetLabelValue(dm, names[l], v, &val));
1202         if (val >= 0) {
1203           color     = lcolors[l % numLColors];
1204           isLabeled = PETSC_TRUE;
1205           break;
1206         }
1207       }
1208       if (drawNumbers[0]) {
1209         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", v, rank, color, v));
1210       } else if (drawColors[0]) {
1211         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", v, rank, !isLabeled ? 1 : 2, color));
1212       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", v, rank));
1213     }
1214     PetscCall(VecRestoreArray(coordinates, &coords));
1215     PetscCall(PetscViewerFlush(viewer));
1216     /* Plot edges */
1217     if (plotEdges) {
1218       PetscCall(VecGetArray(coordinates, &coords));
1219       PetscCall(PetscViewerASCIIPrintf(viewer, "\\path\n"));
1220       for (e = eStart; e < eEnd; ++e) {
1221         const PetscInt *cone;
1222         PetscInt        coneSize, offA, offB, dof, d;
1223 
1224         if (wp && !PetscBTLookup(wp, e - pStart)) continue;
1225         PetscCall(DMPlexGetConeSize(dm, e, &coneSize));
1226         PetscCheck(coneSize == 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Edge %" PetscInt_FMT " cone should have two vertices, not %" PetscInt_FMT, e, coneSize);
1227         PetscCall(DMPlexGetCone(dm, e, &cone));
1228         PetscCall(PetscSectionGetDof(coordSection, cone[0], &dof));
1229         PetscCall(PetscSectionGetOffset(coordSection, cone[0], &offA));
1230         PetscCall(PetscSectionGetOffset(coordSection, cone[1], &offB));
1231         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "("));
1232         for (d = 0; d < dof; ++d) {
1233           tcoords[d] = (double)(0.5 * scale * PetscRealPart(coords[offA + d] + coords[offB + d]));
1234           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1235         }
1236         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1237         if (dim == 3) {
1238           PetscReal tmp = tcoords[1];
1239           tcoords[1]    = tcoords[2];
1240           tcoords[2]    = -tmp;
1241         }
1242         for (d = 0; d < dof; ++d) {
1243           if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1244           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)tcoords[d]));
1245         }
1246         if (drawHasse) color = colors[1 % numColors];
1247         else color = colors[rank % numColors];
1248         for (l = 0; l < numLabels; ++l) {
1249           PetscInt val;
1250           PetscCall(DMGetLabelValue(dm, names[l], v, &val));
1251           if (val >= 0) {
1252             color = lcolors[l % numLColors];
1253             break;
1254           }
1255         }
1256         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "} --\n", e, rank, color, e));
1257       }
1258       PetscCall(VecRestoreArray(coordinates, &coords));
1259       PetscCall(PetscViewerFlush(viewer));
1260       PetscCall(PetscViewerASCIIPrintf(viewer, "(0,0);\n"));
1261     }
1262     /* Plot cells */
1263     if (dim == 3 || !drawNumbers[1]) {
1264       for (e = eStart; e < eEnd; ++e) {
1265         const PetscInt *cone;
1266 
1267         if (wp && !PetscBTLookup(wp, e - pStart)) continue;
1268         color = colors[rank % numColors];
1269         for (l = 0; l < numLabels; ++l) {
1270           PetscInt val;
1271           PetscCall(DMGetLabelValue(dm, names[l], e, &val));
1272           if (val >= 0) {
1273             color = lcolors[l % numLColors];
1274             break;
1275           }
1276         }
1277         PetscCall(DMPlexGetCone(dm, e, &cone));
1278         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", color, cone[0], rank, cone[1], rank));
1279       }
1280     } else {
1281       DMPolytopeType ct;
1282 
1283       /* Drawing a 2D polygon */
1284       for (c = cStart; c < cEnd; ++c) {
1285         if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1286         PetscCall(DMPlexGetCellType(dm, c, &ct));
1287         if (DMPolytopeTypeIsHybrid(ct)) {
1288           const PetscInt *cone;
1289           PetscInt        coneSize, e;
1290 
1291           PetscCall(DMPlexGetCone(dm, c, &cone));
1292           PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
1293           for (e = 0; e < coneSize; ++e) {
1294             const PetscInt *econe;
1295 
1296             PetscCall(DMPlexGetCone(dm, cone[e], &econe));
1297             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));
1298           }
1299         } else {
1300           PetscInt *closure = NULL;
1301           PetscInt  closureSize, Nv = 0, v;
1302 
1303           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1304           for (p = 0; p < closureSize * 2; p += 2) {
1305             const PetscInt point = closure[p];
1306 
1307             if ((point >= vStart) && (point < vEnd)) closure[Nv++] = point;
1308           }
1309           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] ", colors[rank % numColors]));
1310           for (v = 0; v <= Nv; ++v) {
1311             const PetscInt vertex = closure[v % Nv];
1312 
1313             if (v > 0) {
1314               if (plotEdges) {
1315                 const PetscInt *edge;
1316                 PetscInt        endpoints[2], ne;
1317 
1318                 endpoints[0] = closure[v - 1];
1319                 endpoints[1] = vertex;
1320                 PetscCall(DMPlexGetJoin(dm, 2, endpoints, &ne, &edge));
1321                 PetscCheck(ne == 1, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Could not find edge for vertices %" PetscInt_FMT ", %" PetscInt_FMT, endpoints[0], endpoints[1]);
1322                 PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- (%" PetscInt_FMT "_%d) -- ", edge[0], rank));
1323                 PetscCall(DMPlexRestoreJoin(dm, 2, endpoints, &ne, &edge));
1324               } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- "));
1325             }
1326             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "(%" PetscInt_FMT "_%d)", vertex, rank));
1327           }
1328           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ";\n"));
1329           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1330         }
1331       }
1332     }
1333     for (c = cStart; c < cEnd; ++c) {
1334       double             ccoords[3] = {0.0, 0.0, 0.0};
1335       PetscBool          isLabeled  = PETSC_FALSE;
1336       PetscScalar       *cellCoords = NULL;
1337       const PetscScalar *array;
1338       PetscInt           numCoords, cdim, d;
1339       PetscBool          isDG;
1340 
1341       if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1342       PetscCall(DMGetCoordinateDim(dm, &cdim));
1343       PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1344       PetscCheck(!(numCoords % cdim), PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "coordinate dim %" PetscInt_FMT " does not divide numCoords %" PetscInt_FMT, cdim, numCoords);
1345       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1346       for (p = 0; p < numCoords / cdim; ++p) {
1347         for (d = 0; d < cdim; ++d) {
1348           tcoords[d] = (double)(scale * PetscRealPart(cellCoords[p * cdim + d]));
1349           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1350         }
1351         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1352         if (cdim == 3) {
1353           PetscReal tmp = tcoords[1];
1354           tcoords[1]    = tcoords[2];
1355           tcoords[2]    = -tmp;
1356         }
1357         for (d = 0; d < dim; ++d) ccoords[d] += tcoords[d];
1358       }
1359       for (d = 0; d < cdim; ++d) ccoords[d] /= (numCoords / cdim);
1360       PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1361       for (d = 0; d < cdim; ++d) {
1362         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1363         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)ccoords[d]));
1364       }
1365       if (drawHasse) color = colors[depth % numColors];
1366       else color = colors[rank % numColors];
1367       for (l = 0; l < numLabels; ++l) {
1368         PetscInt val;
1369         PetscCall(DMGetLabelValue(dm, names[l], c, &val));
1370         if (val >= 0) {
1371           color     = lcolors[l % numLColors];
1372           isLabeled = PETSC_TRUE;
1373           break;
1374         }
1375       }
1376       if (drawNumbers[dim]) {
1377         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", c, rank, color, c));
1378       } else if (drawColors[dim]) {
1379         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", c, rank, !isLabeled ? 1 : 2, color));
1380       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", c, rank));
1381     }
1382     if (drawHasse) {
1383       color = colors[depth % numColors];
1384       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Cells\n"));
1385       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\c in {\\cStart,...,\\cEnd}\n"));
1386       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1387       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\c_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\cShift+\\c-\\cStart,0) {\\c};\n", rank, color));
1388       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1389 
1390       color = colors[1 % numColors];
1391       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Edges\n"));
1392       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\e in {\\eStart,...,\\eEnd}\n"));
1393       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1394       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\e_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\eShift+\\e-\\eStart,1) {\\e};\n", rank, color));
1395       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1396 
1397       color = colors[0 % numColors];
1398       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Vertices\n"));
1399       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\v in {\\vStart,...,\\vEnd}\n"));
1400       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1401       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\v_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\vShift+\\v-\\vStart,2) {\\v};\n", rank, color));
1402       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1403 
1404       for (p = pStart; p < pEnd; ++p) {
1405         const PetscInt *cone;
1406         PetscInt        coneSize, cp;
1407 
1408         PetscCall(DMPlexGetCone(dm, p, &cone));
1409         PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
1410         for (cp = 0; cp < coneSize; ++cp) PetscCall(PetscViewerASCIIPrintf(viewer, "\\draw[->, shorten >=1pt] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", cone[cp], rank, p, rank));
1411       }
1412     }
1413     PetscCall(PetscViewerFlush(viewer));
1414     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
1415     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{tikzpicture}\n"));
1416     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{document}\n"));
1417     for (l = 0; l < numLabels; ++l) PetscCall(PetscFree(names[l]));
1418     for (c = 0; c < numColors; ++c) PetscCall(PetscFree(colors[c]));
1419     for (c = 0; c < numLColors; ++c) PetscCall(PetscFree(lcolors[c]));
1420     PetscCall(PetscFree3(names, colors, lcolors));
1421     PetscCall(PetscBTDestroy(&wp));
1422   } else if (format == PETSC_VIEWER_LOAD_BALANCE) {
1423     Vec                    cown, acown;
1424     VecScatter             sct;
1425     ISLocalToGlobalMapping g2l;
1426     IS                     gid, acis;
1427     MPI_Comm               comm, ncomm = MPI_COMM_NULL;
1428     MPI_Group              ggroup, ngroup;
1429     PetscScalar           *array, nid;
1430     const PetscInt        *idxs;
1431     PetscInt              *idxs2, *start, *adjacency, *work;
1432     PetscInt64             lm[3], gm[3];
1433     PetscInt               i, c, cStart, cEnd, cum, numVertices, ect, ectn, cellHeight;
1434     PetscMPIInt            d1, d2, rank;
1435 
1436     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1437     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1438 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1439     PetscCallMPI(MPI_Comm_split_type(comm, MPI_COMM_TYPE_SHARED, rank, MPI_INFO_NULL, &ncomm));
1440 #endif
1441     if (ncomm != MPI_COMM_NULL) {
1442       PetscCallMPI(MPI_Comm_group(comm, &ggroup));
1443       PetscCallMPI(MPI_Comm_group(ncomm, &ngroup));
1444       d1 = 0;
1445       PetscCallMPI(MPI_Group_translate_ranks(ngroup, 1, &d1, ggroup, &d2));
1446       nid = d2;
1447       PetscCallMPI(MPI_Group_free(&ggroup));
1448       PetscCallMPI(MPI_Group_free(&ngroup));
1449       PetscCallMPI(MPI_Comm_free(&ncomm));
1450     } else nid = 0.0;
1451 
1452     /* Get connectivity */
1453     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1454     PetscCall(DMPlexCreatePartitionerGraph(dm, cellHeight, &numVertices, &start, &adjacency, &gid));
1455 
1456     /* filter overlapped local cells */
1457     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
1458     PetscCall(ISGetIndices(gid, &idxs));
1459     PetscCall(ISGetLocalSize(gid, &cum));
1460     PetscCall(PetscMalloc1(cum, &idxs2));
1461     for (c = cStart, cum = 0; c < cEnd; c++) {
1462       if (idxs[c - cStart] < 0) continue;
1463       idxs2[cum++] = idxs[c - cStart];
1464     }
1465     PetscCall(ISRestoreIndices(gid, &idxs));
1466     PetscCheck(numVertices == cum, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Unexpected %" PetscInt_FMT " != %" PetscInt_FMT, numVertices, cum);
1467     PetscCall(ISDestroy(&gid));
1468     PetscCall(ISCreateGeneral(comm, numVertices, idxs2, PETSC_OWN_POINTER, &gid));
1469 
1470     /* support for node-aware cell locality */
1471     PetscCall(ISCreateGeneral(comm, start[numVertices], adjacency, PETSC_USE_POINTER, &acis));
1472     PetscCall(VecCreateSeq(PETSC_COMM_SELF, start[numVertices], &acown));
1473     PetscCall(VecCreateMPI(comm, numVertices, PETSC_DECIDE, &cown));
1474     PetscCall(VecGetArray(cown, &array));
1475     for (c = 0; c < numVertices; c++) array[c] = nid;
1476     PetscCall(VecRestoreArray(cown, &array));
1477     PetscCall(VecScatterCreate(cown, acis, acown, NULL, &sct));
1478     PetscCall(VecScatterBegin(sct, cown, acown, INSERT_VALUES, SCATTER_FORWARD));
1479     PetscCall(VecScatterEnd(sct, cown, acown, INSERT_VALUES, SCATTER_FORWARD));
1480     PetscCall(ISDestroy(&acis));
1481     PetscCall(VecScatterDestroy(&sct));
1482     PetscCall(VecDestroy(&cown));
1483 
1484     /* compute edgeCut */
1485     for (c = 0, cum = 0; c < numVertices; c++) cum = PetscMax(cum, start[c + 1] - start[c]);
1486     PetscCall(PetscMalloc1(cum, &work));
1487     PetscCall(ISLocalToGlobalMappingCreateIS(gid, &g2l));
1488     PetscCall(ISLocalToGlobalMappingSetType(g2l, ISLOCALTOGLOBALMAPPINGHASH));
1489     PetscCall(ISDestroy(&gid));
1490     PetscCall(VecGetArray(acown, &array));
1491     for (c = 0, ect = 0, ectn = 0; c < numVertices; c++) {
1492       PetscInt totl;
1493 
1494       totl = start[c + 1] - start[c];
1495       PetscCall(ISGlobalToLocalMappingApply(g2l, IS_GTOLM_MASK, totl, adjacency + start[c], NULL, work));
1496       for (i = 0; i < totl; i++) {
1497         if (work[i] < 0) {
1498           ect += 1;
1499           ectn += (array[i + start[c]] != nid) ? 0 : 1;
1500         }
1501       }
1502     }
1503     PetscCall(PetscFree(work));
1504     PetscCall(VecRestoreArray(acown, &array));
1505     lm[0] = numVertices > 0 ? numVertices : PETSC_MAX_INT;
1506     lm[1] = -numVertices;
1507     PetscCall(MPIU_Allreduce(lm, gm, 2, MPIU_INT64, MPI_MIN, comm));
1508     PetscCall(PetscViewerASCIIPrintf(viewer, "  Cell balance: %.2f (max %" PetscInt_FMT ", min %" PetscInt_FMT, -((double)gm[1]) / ((double)gm[0]), -(PetscInt)gm[1], (PetscInt)gm[0]));
1509     lm[0] = ect;                     /* edgeCut */
1510     lm[1] = ectn;                    /* node-aware edgeCut */
1511     lm[2] = numVertices > 0 ? 0 : 1; /* empty processes */
1512     PetscCall(MPIU_Allreduce(lm, gm, 3, MPIU_INT64, MPI_SUM, comm));
1513     PetscCall(PetscViewerASCIIPrintf(viewer, ", empty %" PetscInt_FMT ")\n", (PetscInt)gm[2]));
1514 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1515     PetscCall(PetscViewerASCIIPrintf(viewer, "  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n", (PetscInt)(gm[0] / 2), gm[0] ? ((double)(gm[1])) / ((double)gm[0]) : 1.));
1516 #else
1517     PetscCall(PetscViewerASCIIPrintf(viewer, "  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n", (PetscInt)(gm[0] / 2), 0.0));
1518 #endif
1519     PetscCall(ISLocalToGlobalMappingDestroy(&g2l));
1520     PetscCall(PetscFree(start));
1521     PetscCall(PetscFree(adjacency));
1522     PetscCall(VecDestroy(&acown));
1523   } else {
1524     const char    *name;
1525     PetscInt      *sizes, *hybsizes, *ghostsizes;
1526     PetscInt       locDepth, depth, cellHeight, dim, d;
1527     PetscInt       pStart, pEnd, p, gcStart, gcEnd, gcNum;
1528     PetscInt       numLabels, l, maxSize = 17;
1529     DMPolytopeType ct0 = DM_POLYTOPE_UNKNOWN;
1530     MPI_Comm       comm;
1531     PetscMPIInt    size, rank;
1532 
1533     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1534     PetscCallMPI(MPI_Comm_size(comm, &size));
1535     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1536     PetscCall(DMGetDimension(dm, &dim));
1537     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1538     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1539     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
1540     else PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
1541     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
1542     PetscCall(DMPlexGetDepth(dm, &locDepth));
1543     PetscCall(MPIU_Allreduce(&locDepth, &depth, 1, MPIU_INT, MPI_MAX, comm));
1544     PetscCall(DMPlexGetCellTypeStratum(dm, DM_POLYTOPE_FV_GHOST, &gcStart, &gcEnd));
1545     gcNum = gcEnd - gcStart;
1546     if (size < maxSize) PetscCall(PetscCalloc3(size, &sizes, size, &hybsizes, size, &ghostsizes));
1547     else PetscCall(PetscCalloc3(3, &sizes, 3, &hybsizes, 3, &ghostsizes));
1548     for (d = 0; d <= depth; d++) {
1549       PetscInt Nc[2] = {0, 0}, ict;
1550 
1551       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
1552       if (pStart < pEnd) PetscCall(DMPlexGetCellType(dm, pStart, &ct0));
1553       ict = ct0;
1554       PetscCallMPI(MPI_Bcast(&ict, 1, MPIU_INT, 0, comm));
1555       ct0 = (DMPolytopeType)ict;
1556       for (p = pStart; p < pEnd; ++p) {
1557         DMPolytopeType ct;
1558 
1559         PetscCall(DMPlexGetCellType(dm, p, &ct));
1560         if (ct == ct0) ++Nc[0];
1561         else ++Nc[1];
1562       }
1563       if (size < maxSize) {
1564         PetscCallMPI(MPI_Gather(&Nc[0], 1, MPIU_INT, sizes, 1, MPIU_INT, 0, comm));
1565         PetscCallMPI(MPI_Gather(&Nc[1], 1, MPIU_INT, hybsizes, 1, MPIU_INT, 0, comm));
1566         if (d == depth) PetscCallMPI(MPI_Gather(&gcNum, 1, MPIU_INT, ghostsizes, 1, MPIU_INT, 0, comm));
1567         PetscCall(PetscViewerASCIIPrintf(viewer, "  Number of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1568         for (p = 0; p < size; ++p) {
1569           if (rank == 0) {
1570             PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT, sizes[p] + hybsizes[p]));
1571             if (hybsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT ")", hybsizes[p]));
1572             if (ghostsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "]", ghostsizes[p]));
1573           }
1574         }
1575       } else {
1576         PetscInt locMinMax[2];
1577 
1578         locMinMax[0] = Nc[0] + Nc[1];
1579         locMinMax[1] = Nc[0] + Nc[1];
1580         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, sizes));
1581         locMinMax[0] = Nc[1];
1582         locMinMax[1] = Nc[1];
1583         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, hybsizes));
1584         if (d == depth) {
1585           locMinMax[0] = gcNum;
1586           locMinMax[1] = gcNum;
1587           PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, ghostsizes));
1588         }
1589         PetscCall(PetscViewerASCIIPrintf(viewer, "  Min/Max of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1590         PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT "/%" PetscInt_FMT, sizes[0], sizes[1]));
1591         if (hybsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT "/%" PetscInt_FMT ")", hybsizes[0], hybsizes[1]));
1592         if (ghostsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "/%" PetscInt_FMT "]", ghostsizes[0], ghostsizes[1]));
1593       }
1594       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
1595     }
1596     PetscCall(PetscFree3(sizes, hybsizes, ghostsizes));
1597     {
1598       const PetscReal *maxCell;
1599       const PetscReal *L;
1600       PetscBool        localized;
1601 
1602       PetscCall(DMGetPeriodicity(dm, &maxCell, NULL, &L));
1603       PetscCall(DMGetCoordinatesLocalized(dm, &localized));
1604       if (L || localized) {
1605         PetscCall(PetscViewerASCIIPrintf(viewer, "Periodic mesh"));
1606         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1607         if (L) {
1608           PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
1609           for (d = 0; d < dim; ++d) {
1610             if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1611             PetscCall(PetscViewerASCIIPrintf(viewer, "%s", L[d] > 0.0 ? "PERIODIC" : "NONE"));
1612           }
1613           PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
1614         }
1615         PetscCall(PetscViewerASCIIPrintf(viewer, " coordinates %s\n", localized ? "localized" : "not localized"));
1616         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1617       }
1618     }
1619     PetscCall(DMGetNumLabels(dm, &numLabels));
1620     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
1621     for (l = 0; l < numLabels; ++l) {
1622       DMLabel         label;
1623       const char     *name;
1624       IS              valueIS;
1625       const PetscInt *values;
1626       PetscInt        numValues, v;
1627 
1628       PetscCall(DMGetLabelName(dm, l, &name));
1629       PetscCall(DMGetLabel(dm, name, &label));
1630       PetscCall(DMLabelGetNumValues(label, &numValues));
1631       PetscCall(PetscViewerASCIIPrintf(viewer, "  %s: %" PetscInt_FMT " strata with value/size (", name, numValues));
1632       PetscCall(DMLabelGetValueIS(label, &valueIS));
1633       PetscCall(ISGetIndices(valueIS, &values));
1634       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1635       for (v = 0; v < numValues; ++v) {
1636         PetscInt size;
1637 
1638         PetscCall(DMLabelGetStratumSize(label, values[v], &size));
1639         if (v > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1640         PetscCall(PetscViewerASCIIPrintf(viewer, "%" PetscInt_FMT " (%" PetscInt_FMT ")", values[v], size));
1641       }
1642       PetscCall(PetscViewerASCIIPrintf(viewer, ")\n"));
1643       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1644       PetscCall(ISRestoreIndices(valueIS, &values));
1645       PetscCall(ISDestroy(&valueIS));
1646     }
1647     {
1648       char    **labelNames;
1649       PetscInt  Nl = numLabels;
1650       PetscBool flg;
1651 
1652       PetscCall(PetscMalloc1(Nl, &labelNames));
1653       PetscCall(PetscOptionsGetStringArray(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_plex_view_labels", labelNames, &Nl, &flg));
1654       for (l = 0; l < Nl; ++l) {
1655         DMLabel label;
1656 
1657         PetscCall(DMHasLabel(dm, labelNames[l], &flg));
1658         if (flg) {
1659           PetscCall(DMGetLabel(dm, labelNames[l], &label));
1660           PetscCall(DMLabelView(label, viewer));
1661         }
1662         PetscCall(PetscFree(labelNames[l]));
1663       }
1664       PetscCall(PetscFree(labelNames));
1665     }
1666     /* If no fields are specified, people do not want to see adjacency */
1667     if (dm->Nf) {
1668       PetscInt f;
1669 
1670       for (f = 0; f < dm->Nf; ++f) {
1671         const char *name;
1672 
1673         PetscCall(PetscObjectGetName(dm->fields[f].disc, &name));
1674         if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Field %s:\n", name));
1675         PetscCall(PetscViewerASCIIPushTab(viewer));
1676         if (dm->fields[f].label) PetscCall(DMLabelView(dm->fields[f].label, viewer));
1677         if (dm->fields[f].adjacency[0]) {
1678           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM++\n"));
1679           else PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM\n"));
1680         } else {
1681           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FEM\n"));
1682           else PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FUNKY\n"));
1683         }
1684         PetscCall(PetscViewerASCIIPopTab(viewer));
1685       }
1686     }
1687     PetscCall(DMGetCoarseDM(dm, &cdm));
1688     if (cdm) {
1689       PetscCall(PetscViewerASCIIPushTab(viewer));
1690       PetscCall(PetscViewerASCIIPrintf(viewer, "Defined by transform from:\n"));
1691       PetscCall(DMPlexView_Ascii(cdm, viewer));
1692       PetscCall(PetscViewerASCIIPopTab(viewer));
1693     }
1694   }
1695   PetscFunctionReturn(PETSC_SUCCESS);
1696 }
1697 
1698 static PetscErrorCode DMPlexDrawCell(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[])
1699 {
1700   DMPolytopeType ct;
1701   PetscMPIInt    rank;
1702   PetscInt       cdim;
1703 
1704   PetscFunctionBegin;
1705   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1706   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1707   PetscCall(DMGetCoordinateDim(dm, &cdim));
1708   switch (ct) {
1709   case DM_POLYTOPE_SEGMENT:
1710   case DM_POLYTOPE_POINT_PRISM_TENSOR:
1711     switch (cdim) {
1712     case 1: {
1713       const PetscReal y  = 0.5;  /* TODO Put it in the middle of the viewport */
1714       const PetscReal dy = 0.05; /* TODO Make it a fraction of the total length */
1715 
1716       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y, PetscRealPart(coords[1]), y, PETSC_DRAW_BLACK));
1717       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y + dy, PetscRealPart(coords[0]), y - dy, PETSC_DRAW_BLACK));
1718       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[1]), y + dy, PetscRealPart(coords[1]), y - dy, PETSC_DRAW_BLACK));
1719     } break;
1720     case 2: {
1721       const PetscReal dx = (PetscRealPart(coords[3]) - PetscRealPart(coords[1]));
1722       const PetscReal dy = (PetscRealPart(coords[2]) - PetscRealPart(coords[0]));
1723       const PetscReal l  = 0.1 / PetscSqrtReal(dx * dx + dy * dy);
1724 
1725       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1726       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));
1727       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));
1728     } break;
1729     default:
1730       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of dimension %" PetscInt_FMT, cdim);
1731     }
1732     break;
1733   case DM_POLYTOPE_TRIANGLE:
1734     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));
1735     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1736     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1737     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1738     break;
1739   case DM_POLYTOPE_QUADRILATERAL:
1740     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));
1741     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));
1742     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1743     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1744     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_BLACK));
1745     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1746     break;
1747   case DM_POLYTOPE_SEG_PRISM_TENSOR:
1748     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));
1749     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));
1750     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1751     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_BLACK));
1752     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1753     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1754     break;
1755   case DM_POLYTOPE_FV_GHOST:
1756     break;
1757   default:
1758     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1759   }
1760   PetscFunctionReturn(PETSC_SUCCESS);
1761 }
1762 
1763 static PetscErrorCode DMPlexDrawCellHighOrder(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[], PetscInt edgeDiv, PetscReal refCoords[], PetscReal edgeCoords[])
1764 {
1765   DMPolytopeType ct;
1766   PetscReal      centroid[2] = {0., 0.};
1767   PetscMPIInt    rank;
1768   PetscInt       fillColor, v, e, d;
1769 
1770   PetscFunctionBegin;
1771   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1772   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1773   fillColor = PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2;
1774   switch (ct) {
1775   case DM_POLYTOPE_TRIANGLE: {
1776     PetscReal refVertices[6] = {-1., -1., 1., -1., -1., 1.};
1777 
1778     for (v = 0; v < 3; ++v) {
1779       centroid[0] += PetscRealPart(coords[v * 2 + 0]) / 3.;
1780       centroid[1] += PetscRealPart(coords[v * 2 + 1]) / 3.;
1781     }
1782     for (e = 0; e < 3; ++e) {
1783       refCoords[0] = refVertices[e * 2 + 0];
1784       refCoords[1] = refVertices[e * 2 + 1];
1785       for (d = 1; d <= edgeDiv; ++d) {
1786         refCoords[d * 2 + 0] = refCoords[0] + (refVertices[(e + 1) % 3 * 2 + 0] - refCoords[0]) * d / edgeDiv;
1787         refCoords[d * 2 + 1] = refCoords[1] + (refVertices[(e + 1) % 3 * 2 + 1] - refCoords[1]) * d / edgeDiv;
1788       }
1789       PetscCall(DMPlexReferenceToCoordinates(dm, cell, edgeDiv + 1, refCoords, edgeCoords));
1790       for (d = 0; d < edgeDiv; ++d) {
1791         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));
1792         PetscCall(PetscDrawLine(draw, edgeCoords[d * 2 + 0], edgeCoords[d * 2 + 1], edgeCoords[(d + 1) * 2 + 0], edgeCoords[(d + 1) * 2 + 1], PETSC_DRAW_BLACK));
1793       }
1794     }
1795   } break;
1796   default:
1797     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1798   }
1799   PetscFunctionReturn(PETSC_SUCCESS);
1800 }
1801 
1802 static PetscErrorCode DMPlexView_Draw(DM dm, PetscViewer viewer)
1803 {
1804   PetscDraw    draw;
1805   DM           cdm;
1806   PetscSection coordSection;
1807   Vec          coordinates;
1808   PetscReal    xyl[3], xyr[3];
1809   PetscReal   *refCoords, *edgeCoords;
1810   PetscBool    isnull, drawAffine = PETSC_TRUE;
1811   PetscInt     dim, vStart, vEnd, cStart, cEnd, c, edgeDiv = 4;
1812 
1813   PetscFunctionBegin;
1814   PetscCall(DMGetCoordinateDim(dm, &dim));
1815   PetscCheck(dim <= 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT, dim);
1816   PetscCall(PetscOptionsGetBool(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_view_draw_affine", &drawAffine, NULL));
1817   if (!drawAffine) PetscCall(PetscMalloc2((edgeDiv + 1) * dim, &refCoords, (edgeDiv + 1) * dim, &edgeCoords));
1818   PetscCall(DMGetCoordinateDM(dm, &cdm));
1819   PetscCall(DMGetLocalSection(cdm, &coordSection));
1820   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1821   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1822   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1823 
1824   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
1825   PetscCall(PetscDrawIsNull(draw, &isnull));
1826   if (isnull) PetscFunctionReturn(PETSC_SUCCESS);
1827   PetscCall(PetscDrawSetTitle(draw, "Mesh"));
1828 
1829   PetscCall(DMGetBoundingBox(dm, xyl, xyr));
1830   PetscCall(PetscDrawSetCoordinates(draw, xyl[0], xyl[1], xyr[0], xyr[1]));
1831   PetscCall(PetscDrawClear(draw));
1832 
1833   for (c = cStart; c < cEnd; ++c) {
1834     PetscScalar       *coords = NULL;
1835     const PetscScalar *coords_arr;
1836     PetscInt           numCoords;
1837     PetscBool          isDG;
1838 
1839     PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
1840     if (drawAffine) PetscCall(DMPlexDrawCell(dm, draw, c, coords));
1841     else PetscCall(DMPlexDrawCellHighOrder(dm, draw, c, coords, edgeDiv, refCoords, edgeCoords));
1842     PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
1843   }
1844   if (!drawAffine) PetscCall(PetscFree2(refCoords, edgeCoords));
1845   PetscCall(PetscDrawFlush(draw));
1846   PetscCall(PetscDrawPause(draw));
1847   PetscCall(PetscDrawSave(draw));
1848   PetscFunctionReturn(PETSC_SUCCESS);
1849 }
1850 
1851 static PetscErrorCode DMPlexCreateHighOrderSurrogate_Internal(DM dm, DM *hdm)
1852 {
1853   DM           odm = dm, rdm = dm, cdm;
1854   PetscFE      fe;
1855   PetscSpace   sp;
1856   PetscClassId id;
1857   PetscInt     degree;
1858   PetscBool    hoView = PETSC_TRUE;
1859 
1860   PetscFunctionBegin;
1861   PetscObjectOptionsBegin((PetscObject)dm);
1862   PetscCall(PetscOptionsBool("-dm_plex_high_order_view", "Subsample to view meshes with high order coordinates", "DMPlexCreateHighOrderSurrogate_Internal", hoView, &hoView, NULL));
1863   PetscOptionsEnd();
1864   PetscCall(PetscObjectReference((PetscObject)dm));
1865   *hdm = dm;
1866   if (!hoView) PetscFunctionReturn(PETSC_SUCCESS);
1867   PetscCall(DMGetCoordinateDM(dm, &cdm));
1868   PetscCall(DMGetField(cdm, 0, NULL, (PetscObject *)&fe));
1869   PetscCall(PetscObjectGetClassId((PetscObject)fe, &id));
1870   if (id != PETSCFE_CLASSID) PetscFunctionReturn(PETSC_SUCCESS);
1871   PetscCall(PetscFEGetBasisSpace(fe, &sp));
1872   PetscCall(PetscSpaceGetDegree(sp, &degree, NULL));
1873   for (PetscInt r = 0, rd = PetscCeilReal(((PetscReal)degree) / 2.); r < (PetscInt)PetscCeilReal(PetscLog2Real(degree)); ++r, rd = PetscCeilReal(((PetscReal)rd) / 2.)) {
1874     DM  cdm, rcdm;
1875     Mat In;
1876     Vec cl, rcl;
1877 
1878     PetscCall(DMRefine(odm, PetscObjectComm((PetscObject)odm), &rdm));
1879     if (rd > 1) PetscCall(DMPlexCreateCoordinateSpace(rdm, rd, PETSC_FALSE, NULL));
1880     PetscCall(PetscObjectSetName((PetscObject)rdm, "Refined Mesh with Linear Coordinates"));
1881     PetscCall(DMGetCoordinateDM(odm, &cdm));
1882     PetscCall(DMGetCoordinateDM(rdm, &rcdm));
1883     PetscCall(DMGetCoordinatesLocal(odm, &cl));
1884     PetscCall(DMGetCoordinatesLocal(rdm, &rcl));
1885     PetscCall(DMSetCoarseDM(rcdm, cdm));
1886     PetscCall(DMCreateInterpolation(cdm, rcdm, &In, NULL));
1887     PetscCall(MatMult(In, cl, rcl));
1888     PetscCall(MatDestroy(&In));
1889     PetscCall(DMSetCoordinatesLocal(rdm, rcl));
1890     PetscCall(DMDestroy(&odm));
1891     odm = rdm;
1892   }
1893   *hdm = rdm;
1894   PetscFunctionReturn(PETSC_SUCCESS);
1895 }
1896 
1897 #if defined(PETSC_HAVE_EXODUSII)
1898   #include <exodusII.h>
1899   #include <petscviewerexodusii.h>
1900 #endif
1901 
1902 PetscErrorCode DMView_Plex(DM dm, PetscViewer viewer)
1903 {
1904   PetscBool iascii, ishdf5, isvtk, isdraw, flg, isglvis, isexodus, iscgns;
1905   char      name[PETSC_MAX_PATH_LEN];
1906 
1907   PetscFunctionBegin;
1908   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1909   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1910   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &iascii));
1911   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
1912   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1913   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
1914   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
1915   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodus));
1916   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
1917   if (iascii) {
1918     PetscViewerFormat format;
1919     PetscCall(PetscViewerGetFormat(viewer, &format));
1920     if (format == PETSC_VIEWER_ASCII_GLVIS) PetscCall(DMPlexView_GLVis(dm, viewer));
1921     else PetscCall(DMPlexView_Ascii(dm, viewer));
1922   } else if (ishdf5) {
1923 #if defined(PETSC_HAVE_HDF5)
1924     PetscCall(DMPlexView_HDF5_Internal(dm, viewer));
1925 #else
1926     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1927 #endif
1928   } else if (isvtk) {
1929     PetscCall(DMPlexVTKWriteAll((PetscObject)dm, viewer));
1930   } else if (isdraw) {
1931     DM hdm;
1932 
1933     PetscCall(DMPlexCreateHighOrderSurrogate_Internal(dm, &hdm));
1934     PetscCall(DMPlexView_Draw(hdm, viewer));
1935     PetscCall(DMDestroy(&hdm));
1936   } else if (isglvis) {
1937     PetscCall(DMPlexView_GLVis(dm, viewer));
1938 #if defined(PETSC_HAVE_EXODUSII)
1939   } else if (isexodus) {
1940     /*
1941       exodusII requires that all sets be part of exactly one cell set.
1942       If the dm does not have a "Cell Sets" label defined, we create one
1943       with ID 1, containing all cells.
1944       Note that if the Cell Sets label is defined but does not cover all cells,
1945       we may still have a problem. This should probably be checked here or in the viewer;
1946     */
1947     PetscInt numCS;
1948     PetscCall(DMGetLabelSize(dm, "Cell Sets", &numCS));
1949     if (!numCS) {
1950       PetscInt cStart, cEnd, c;
1951       PetscCall(DMCreateLabel(dm, "Cell Sets"));
1952       PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1953       for (c = cStart; c < cEnd; ++c) PetscCall(DMSetLabelValue(dm, "Cell Sets", c, 1));
1954     }
1955     PetscCall(DMView_PlexExodusII(dm, viewer));
1956 #endif
1957 #if defined(PETSC_HAVE_CGNS)
1958   } else if (iscgns) {
1959     PetscCall(DMView_PlexCGNS(dm, viewer));
1960 #endif
1961   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex writing", ((PetscObject)viewer)->type_name);
1962   /* Optionally view the partition */
1963   PetscCall(PetscOptionsHasName(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_partition_view", &flg));
1964   if (flg) {
1965     Vec ranks;
1966     PetscCall(DMPlexCreateRankField(dm, &ranks));
1967     PetscCall(VecView(ranks, viewer));
1968     PetscCall(VecDestroy(&ranks));
1969   }
1970   /* Optionally view a label */
1971   PetscCall(PetscOptionsGetString(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_label_view", name, sizeof(name), &flg));
1972   if (flg) {
1973     DMLabel label;
1974     Vec     val;
1975 
1976     PetscCall(DMGetLabel(dm, name, &label));
1977     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Label %s provided to -dm_label_view does not exist in this DM", name);
1978     PetscCall(DMPlexCreateLabelField(dm, label, &val));
1979     PetscCall(VecView(val, viewer));
1980     PetscCall(VecDestroy(&val));
1981   }
1982   PetscFunctionReturn(PETSC_SUCCESS);
1983 }
1984 
1985 /*@
1986   DMPlexTopologyView - Saves a `DMPLEX` topology into a file
1987 
1988   Collective
1989 
1990   Input Parameters:
1991 + dm     - The `DM` whose topology is to be saved
1992 - viewer - The `PetscViewer` to save it in
1993 
1994   Level: advanced
1995 
1996 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexTopologyLoad()`, `PetscViewer`
1997 @*/
1998 PetscErrorCode DMPlexTopologyView(DM dm, PetscViewer viewer)
1999 {
2000   PetscBool ishdf5;
2001 
2002   PetscFunctionBegin;
2003   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2004   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2005   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2006   PetscCall(PetscLogEventBegin(DMPLEX_TopologyView, viewer, 0, 0, 0));
2007   if (ishdf5) {
2008 #if defined(PETSC_HAVE_HDF5)
2009     PetscViewerFormat format;
2010     PetscCall(PetscViewerGetFormat(viewer, &format));
2011     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2012       IS globalPointNumbering;
2013 
2014       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
2015       PetscCall(DMPlexTopologyView_HDF5_Internal(dm, globalPointNumbering, viewer));
2016       PetscCall(ISDestroy(&globalPointNumbering));
2017     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
2018 #else
2019     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2020 #endif
2021   }
2022   PetscCall(PetscLogEventEnd(DMPLEX_TopologyView, viewer, 0, 0, 0));
2023   PetscFunctionReturn(PETSC_SUCCESS);
2024 }
2025 
2026 /*@
2027   DMPlexCoordinatesView - Saves `DMPLEX` coordinates into a file
2028 
2029   Collective
2030 
2031   Input Parameters:
2032 + dm     - The `DM` whose coordinates are to be saved
2033 - viewer - The `PetscViewer` for saving
2034 
2035   Level: advanced
2036 
2037 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexLabelsView()`, `DMPlexCoordinatesLoad()`, `PetscViewer`
2038 @*/
2039 PetscErrorCode DMPlexCoordinatesView(DM dm, PetscViewer viewer)
2040 {
2041   PetscBool ishdf5;
2042 
2043   PetscFunctionBegin;
2044   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2045   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2046   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2047   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
2048   if (ishdf5) {
2049 #if defined(PETSC_HAVE_HDF5)
2050     PetscViewerFormat format;
2051     PetscCall(PetscViewerGetFormat(viewer, &format));
2052     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2053       PetscCall(DMPlexCoordinatesView_HDF5_Internal(dm, viewer));
2054     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
2055 #else
2056     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2057 #endif
2058   }
2059   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
2060   PetscFunctionReturn(PETSC_SUCCESS);
2061 }
2062 
2063 /*@
2064   DMPlexLabelsView - Saves `DMPLEX` labels into a file
2065 
2066   Collective
2067 
2068   Input Parameters:
2069 + dm     - The `DM` whose labels are to be saved
2070 - viewer - The `PetscViewer` for saving
2071 
2072   Level: advanced
2073 
2074 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsLoad()`, `PetscViewer`
2075 @*/
2076 PetscErrorCode DMPlexLabelsView(DM dm, PetscViewer viewer)
2077 {
2078   PetscBool ishdf5;
2079 
2080   PetscFunctionBegin;
2081   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2082   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2083   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2084   PetscCall(PetscLogEventBegin(DMPLEX_LabelsView, viewer, 0, 0, 0));
2085   if (ishdf5) {
2086 #if defined(PETSC_HAVE_HDF5)
2087     IS                globalPointNumbering;
2088     PetscViewerFormat format;
2089 
2090     PetscCall(PetscViewerGetFormat(viewer, &format));
2091     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2092       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
2093       PetscCall(DMPlexLabelsView_HDF5_Internal(dm, globalPointNumbering, viewer));
2094       PetscCall(ISDestroy(&globalPointNumbering));
2095     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2096 #else
2097     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2098 #endif
2099   }
2100   PetscCall(PetscLogEventEnd(DMPLEX_LabelsView, viewer, 0, 0, 0));
2101   PetscFunctionReturn(PETSC_SUCCESS);
2102 }
2103 
2104 /*@
2105   DMPlexSectionView - Saves a section associated with a `DMPLEX`
2106 
2107   Collective
2108 
2109   Input Parameters:
2110 + dm        - The `DM` that contains the topology on which the section to be saved is defined
2111 . viewer    - The `PetscViewer` for saving
2112 - sectiondm - The `DM` that contains the section to be saved
2113 
2114   Level: advanced
2115 
2116   Notes:
2117   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.
2118 
2119   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.
2120 
2121 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`, `PetscSectionView()`, `DMPlexSectionLoad()`, `PetscViewer`
2122 @*/
2123 PetscErrorCode DMPlexSectionView(DM dm, PetscViewer viewer, DM sectiondm)
2124 {
2125   PetscBool ishdf5;
2126 
2127   PetscFunctionBegin;
2128   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2129   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2130   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2131   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2132   PetscCall(PetscLogEventBegin(DMPLEX_SectionView, viewer, 0, 0, 0));
2133   if (ishdf5) {
2134 #if defined(PETSC_HAVE_HDF5)
2135     PetscCall(DMPlexSectionView_HDF5_Internal(dm, viewer, sectiondm));
2136 #else
2137     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2138 #endif
2139   }
2140   PetscCall(PetscLogEventEnd(DMPLEX_SectionView, viewer, 0, 0, 0));
2141   PetscFunctionReturn(PETSC_SUCCESS);
2142 }
2143 
2144 /*@
2145   DMPlexGlobalVectorView - Saves a global vector
2146 
2147   Collective
2148 
2149   Input Parameters:
2150 + dm        - The `DM` that represents the topology
2151 . viewer    - The `PetscViewer` to save data with
2152 . sectiondm - The `DM` that contains the global section on which vec is defined
2153 - vec       - The global vector to be saved
2154 
2155   Level: advanced
2156 
2157   Notes:
2158   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.
2159 
2160   Calling sequence:
2161 .vb
2162        DMCreate(PETSC_COMM_WORLD, &dm);
2163        DMSetType(dm, DMPLEX);
2164        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2165        DMClone(dm, &sectiondm);
2166        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2167        PetscSectionCreate(PETSC_COMM_WORLD, &section);
2168        DMPlexGetChart(sectiondm, &pStart, &pEnd);
2169        PetscSectionSetChart(section, pStart, pEnd);
2170        PetscSectionSetUp(section);
2171        DMSetLocalSection(sectiondm, section);
2172        PetscSectionDestroy(&section);
2173        DMGetGlobalVector(sectiondm, &vec);
2174        PetscObjectSetName((PetscObject)vec, "vec_name");
2175        DMPlexTopologyView(dm, viewer);
2176        DMPlexSectionView(dm, viewer, sectiondm);
2177        DMPlexGlobalVectorView(dm, viewer, sectiondm, vec);
2178        DMRestoreGlobalVector(sectiondm, &vec);
2179        DMDestroy(&sectiondm);
2180        DMDestroy(&dm);
2181 .ve
2182 
2183 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexLocalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2184 @*/
2185 PetscErrorCode DMPlexGlobalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2186 {
2187   PetscBool ishdf5;
2188 
2189   PetscFunctionBegin;
2190   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2191   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2192   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2193   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2194   /* Check consistency */
2195   {
2196     PetscSection section;
2197     PetscBool    includesConstraints;
2198     PetscInt     m, m1;
2199 
2200     PetscCall(VecGetLocalSize(vec, &m1));
2201     PetscCall(DMGetGlobalSection(sectiondm, &section));
2202     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2203     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2204     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2205     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2206   }
2207   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2208   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorView, viewer, 0, 0, 0));
2209   if (ishdf5) {
2210 #if defined(PETSC_HAVE_HDF5)
2211     PetscCall(DMPlexGlobalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2212 #else
2213     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2214 #endif
2215   }
2216   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorView, viewer, 0, 0, 0));
2217   PetscFunctionReturn(PETSC_SUCCESS);
2218 }
2219 
2220 /*@
2221   DMPlexLocalVectorView - Saves a local vector
2222 
2223   Collective
2224 
2225   Input Parameters:
2226 + dm        - The `DM` that represents the topology
2227 . viewer    - The `PetscViewer` to save data with
2228 . sectiondm - The `DM` that contains the local section on which `vec` is defined; may be the same as `dm`
2229 - vec       - The local vector to be saved
2230 
2231   Level: advanced
2232 
2233   Note:
2234   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.
2235 
2236   Calling sequence:
2237 .vb
2238        DMCreate(PETSC_COMM_WORLD, &dm);
2239        DMSetType(dm, DMPLEX);
2240        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2241        DMClone(dm, &sectiondm);
2242        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2243        PetscSectionCreate(PETSC_COMM_WORLD, &section);
2244        DMPlexGetChart(sectiondm, &pStart, &pEnd);
2245        PetscSectionSetChart(section, pStart, pEnd);
2246        PetscSectionSetUp(section);
2247        DMSetLocalSection(sectiondm, section);
2248        DMGetLocalVector(sectiondm, &vec);
2249        PetscObjectSetName((PetscObject)vec, "vec_name");
2250        DMPlexTopologyView(dm, viewer);
2251        DMPlexSectionView(dm, viewer, sectiondm);
2252        DMPlexLocalVectorView(dm, viewer, sectiondm, vec);
2253        DMRestoreLocalVector(sectiondm, &vec);
2254        DMDestroy(&sectiondm);
2255        DMDestroy(&dm);
2256 .ve
2257 
2258 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexGlobalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2259 @*/
2260 PetscErrorCode DMPlexLocalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2261 {
2262   PetscBool ishdf5;
2263 
2264   PetscFunctionBegin;
2265   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2266   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2267   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2268   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2269   /* Check consistency */
2270   {
2271     PetscSection section;
2272     PetscBool    includesConstraints;
2273     PetscInt     m, m1;
2274 
2275     PetscCall(VecGetLocalSize(vec, &m1));
2276     PetscCall(DMGetLocalSection(sectiondm, &section));
2277     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2278     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2279     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2280     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2281   }
2282   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2283   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorView, viewer, 0, 0, 0));
2284   if (ishdf5) {
2285 #if defined(PETSC_HAVE_HDF5)
2286     PetscCall(DMPlexLocalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2287 #else
2288     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2289 #endif
2290   }
2291   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorView, viewer, 0, 0, 0));
2292   PetscFunctionReturn(PETSC_SUCCESS);
2293 }
2294 
2295 PetscErrorCode DMLoad_Plex(DM dm, PetscViewer viewer)
2296 {
2297   PetscBool ishdf5;
2298 
2299   PetscFunctionBegin;
2300   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2301   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2302   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2303   if (ishdf5) {
2304 #if defined(PETSC_HAVE_HDF5)
2305     PetscViewerFormat format;
2306     PetscCall(PetscViewerGetFormat(viewer, &format));
2307     if (format == PETSC_VIEWER_HDF5_XDMF || format == PETSC_VIEWER_HDF5_VIZ) {
2308       PetscCall(DMPlexLoad_HDF5_Xdmf_Internal(dm, viewer));
2309     } else if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2310       PetscCall(DMPlexLoad_HDF5_Internal(dm, viewer));
2311     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2312     PetscFunctionReturn(PETSC_SUCCESS);
2313 #else
2314     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2315 #endif
2316   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex loading", ((PetscObject)viewer)->type_name);
2317 }
2318 
2319 /*@
2320   DMPlexTopologyLoad - Loads a topology into a `DMPLEX`
2321 
2322   Collective
2323 
2324   Input Parameters:
2325 + dm     - The `DM` into which the topology is loaded
2326 - viewer - The `PetscViewer` for the saved topology
2327 
2328   Output Parameter:
2329 . 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
2330 
2331   Level: advanced
2332 
2333 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2334           `PetscViewer`, `PetscSF`
2335 @*/
2336 PetscErrorCode DMPlexTopologyLoad(DM dm, PetscViewer viewer, PetscSF *globalToLocalPointSF)
2337 {
2338   PetscBool ishdf5;
2339 
2340   PetscFunctionBegin;
2341   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2342   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2343   if (globalToLocalPointSF) PetscAssertPointer(globalToLocalPointSF, 3);
2344   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2345   PetscCall(PetscLogEventBegin(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2346   if (ishdf5) {
2347 #if defined(PETSC_HAVE_HDF5)
2348     PetscViewerFormat format;
2349     PetscCall(PetscViewerGetFormat(viewer, &format));
2350     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2351       PetscCall(DMPlexTopologyLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2352     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2353 #else
2354     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2355 #endif
2356   }
2357   PetscCall(PetscLogEventEnd(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2358   PetscFunctionReturn(PETSC_SUCCESS);
2359 }
2360 
2361 /*@
2362   DMPlexCoordinatesLoad - Loads coordinates into a `DMPLEX`
2363 
2364   Collective
2365 
2366   Input Parameters:
2367 + dm                   - The `DM` into which the coordinates are loaded
2368 . viewer               - The `PetscViewer` for the saved coordinates
2369 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad()` when loading dm from viewer
2370 
2371   Level: advanced
2372 
2373 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2374           `PetscSF`, `PetscViewer`
2375 @*/
2376 PetscErrorCode DMPlexCoordinatesLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2377 {
2378   PetscBool ishdf5;
2379 
2380   PetscFunctionBegin;
2381   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2382   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2383   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2384   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2385   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesLoad, viewer, 0, 0, 0));
2386   if (ishdf5) {
2387 #if defined(PETSC_HAVE_HDF5)
2388     PetscViewerFormat format;
2389     PetscCall(PetscViewerGetFormat(viewer, &format));
2390     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2391       PetscCall(DMPlexCoordinatesLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2392     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2393 #else
2394     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2395 #endif
2396   }
2397   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesLoad, viewer, 0, 0, 0));
2398   PetscFunctionReturn(PETSC_SUCCESS);
2399 }
2400 
2401 /*@
2402   DMPlexLabelsLoad - Loads labels into a `DMPLEX`
2403 
2404   Collective
2405 
2406   Input Parameters:
2407 + dm                   - The `DM` into which the labels are loaded
2408 . viewer               - The `PetscViewer` for the saved labels
2409 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad()` when loading `dm` from viewer
2410 
2411   Level: advanced
2412 
2413   Note:
2414   The `PetscSF` argument must not be NULL if the `DM` is distributed, otherwise an error occurs.
2415 
2416 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2417           `PetscSF`, `PetscViewer`
2418 @*/
2419 PetscErrorCode DMPlexLabelsLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2420 {
2421   PetscBool ishdf5;
2422 
2423   PetscFunctionBegin;
2424   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2425   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2426   if (globalToLocalPointSF) PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2427   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2428   PetscCall(PetscLogEventBegin(DMPLEX_LabelsLoad, viewer, 0, 0, 0));
2429   if (ishdf5) {
2430 #if defined(PETSC_HAVE_HDF5)
2431     PetscViewerFormat format;
2432 
2433     PetscCall(PetscViewerGetFormat(viewer, &format));
2434     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2435       PetscCall(DMPlexLabelsLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2436     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2437 #else
2438     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2439 #endif
2440   }
2441   PetscCall(PetscLogEventEnd(DMPLEX_LabelsLoad, viewer, 0, 0, 0));
2442   PetscFunctionReturn(PETSC_SUCCESS);
2443 }
2444 
2445 /*@
2446   DMPlexSectionLoad - Loads section into a `DMPLEX`
2447 
2448   Collective
2449 
2450   Input Parameters:
2451 + dm                   - The `DM` that represents the topology
2452 . viewer               - The `PetscViewer` that represents the on-disk section (sectionA)
2453 . sectiondm            - The `DM` into which the on-disk section (sectionA) is migrated
2454 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad(`) when loading dm from viewer
2455 
2456   Output Parameters:
2457 + 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)
2458 - 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)
2459 
2460   Level: advanced
2461 
2462   Notes:
2463   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.
2464 
2465   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.
2466 
2467   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.
2468 
2469   Example using 2 processes:
2470 .vb
2471   NX (number of points on dm): 4
2472   sectionA                   : the on-disk section
2473   vecA                       : a vector associated with sectionA
2474   sectionB                   : sectiondm's local section constructed in this function
2475   vecB (local)               : a vector associated with sectiondm's local section
2476   vecB (global)              : a vector associated with sectiondm's global section
2477 
2478                                      rank 0    rank 1
2479   vecA (global)                  : [.0 .4 .1 | .2 .3]        <- to be loaded in DMPlexGlobalVectorLoad() or DMPlexLocalVectorLoad()
2480   sectionA->atlasOff             :       0 2 | 1             <- loaded in PetscSectionLoad()
2481   sectionA->atlasDof             :       1 3 | 1             <- loaded in PetscSectionLoad()
2482   sectionA's global point numbers:       0 2 | 3             <- loaded in DMPlexSectionLoad()
2483   [0, NX)                        :       0 1 | 2 3           <- conceptual partition used in globalToLocalPointSF
2484   sectionB's global point numbers:     0 1 3 | 3 2           <- associated with [0, NX) by globalToLocalPointSF
2485   sectionB->atlasDof             :     1 0 1 | 1 3
2486   sectionB->atlasOff (no perm)   :     0 1 1 | 0 1
2487   vecB (local)                   :   [.0 .4] | [.4 .1 .2 .3] <- to be constructed by calling DMPlexLocalVectorLoad() with localDofSF
2488   vecB (global)                  :    [.0 .4 | .1 .2 .3]     <- to be constructed by calling DMPlexGlobalVectorLoad() with globalDofSF
2489 .ve
2490   where "|" represents a partition of loaded data, and global point 3 is assumed to be owned by rank 0.
2491 
2492 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`, `PetscSectionLoad()`, `DMPlexSectionView()`, `PetscSF`, `PetscViewer`
2493 @*/
2494 PetscErrorCode DMPlexSectionLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF globalToLocalPointSF, PetscSF *globalDofSF, PetscSF *localDofSF)
2495 {
2496   PetscBool ishdf5;
2497 
2498   PetscFunctionBegin;
2499   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2500   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2501   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2502   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 4);
2503   if (globalDofSF) PetscAssertPointer(globalDofSF, 5);
2504   if (localDofSF) PetscAssertPointer(localDofSF, 6);
2505   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2506   PetscCall(PetscLogEventBegin(DMPLEX_SectionLoad, viewer, 0, 0, 0));
2507   if (ishdf5) {
2508 #if defined(PETSC_HAVE_HDF5)
2509     PetscCall(DMPlexSectionLoad_HDF5_Internal(dm, viewer, sectiondm, globalToLocalPointSF, globalDofSF, localDofSF));
2510 #else
2511     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2512 #endif
2513   }
2514   PetscCall(PetscLogEventEnd(DMPLEX_SectionLoad, viewer, 0, 0, 0));
2515   PetscFunctionReturn(PETSC_SUCCESS);
2516 }
2517 
2518 /*@
2519   DMPlexGlobalVectorLoad - Loads on-disk vector data into a global vector
2520 
2521   Collective
2522 
2523   Input Parameters:
2524 + dm        - The `DM` that represents the topology
2525 . viewer    - The `PetscViewer` that represents the on-disk vector data
2526 . sectiondm - The `DM` that contains the global section on which vec is defined
2527 . sf        - The `PetscSF` that migrates the on-disk vector data into vec
2528 - vec       - The global vector to set values of
2529 
2530   Level: advanced
2531 
2532   Notes:
2533   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.
2534 
2535   Calling sequence:
2536 .vb
2537        DMCreate(PETSC_COMM_WORLD, &dm);
2538        DMSetType(dm, DMPLEX);
2539        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2540        DMPlexTopologyLoad(dm, viewer, &sfX);
2541        DMClone(dm, &sectiondm);
2542        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2543        DMPlexSectionLoad(dm, viewer, sectiondm, sfX, &gsf, NULL);
2544        DMGetGlobalVector(sectiondm, &vec);
2545        PetscObjectSetName((PetscObject)vec, "vec_name");
2546        DMPlexGlobalVectorLoad(dm, viewer, sectiondm, gsf, vec);
2547        DMRestoreGlobalVector(sectiondm, &vec);
2548        PetscSFDestroy(&gsf);
2549        PetscSFDestroy(&sfX);
2550        DMDestroy(&sectiondm);
2551        DMDestroy(&dm);
2552 .ve
2553 
2554 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexLocalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`,
2555           `PetscSF`, `PetscViewer`
2556 @*/
2557 PetscErrorCode DMPlexGlobalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2558 {
2559   PetscBool ishdf5;
2560 
2561   PetscFunctionBegin;
2562   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2563   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2564   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2565   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2566   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2567   /* Check consistency */
2568   {
2569     PetscSection section;
2570     PetscBool    includesConstraints;
2571     PetscInt     m, m1;
2572 
2573     PetscCall(VecGetLocalSize(vec, &m1));
2574     PetscCall(DMGetGlobalSection(sectiondm, &section));
2575     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2576     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2577     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2578     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2579   }
2580   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2581   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorLoad, viewer, 0, 0, 0));
2582   if (ishdf5) {
2583 #if defined(PETSC_HAVE_HDF5)
2584     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2585 #else
2586     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2587 #endif
2588   }
2589   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorLoad, viewer, 0, 0, 0));
2590   PetscFunctionReturn(PETSC_SUCCESS);
2591 }
2592 
2593 /*@
2594   DMPlexLocalVectorLoad - Loads on-disk vector data into a local vector
2595 
2596   Collective
2597 
2598   Input Parameters:
2599 + dm        - The `DM` that represents the topology
2600 . viewer    - The `PetscViewer` that represents the on-disk vector data
2601 . sectiondm - The `DM` that contains the local section on which vec is defined
2602 . sf        - The `PetscSF` that migrates the on-disk vector data into vec
2603 - vec       - The local vector to set values of
2604 
2605   Level: advanced
2606 
2607   Notes:
2608   In general `dm` and `sectiondm` are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2609 
2610   Calling sequence:
2611 .vb
2612        DMCreate(PETSC_COMM_WORLD, &dm);
2613        DMSetType(dm, DMPLEX);
2614        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2615        DMPlexTopologyLoad(dm, viewer, &sfX);
2616        DMClone(dm, &sectiondm);
2617        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2618        DMPlexSectionLoad(dm, viewer, sectiondm, sfX, NULL, &lsf);
2619        DMGetLocalVector(sectiondm, &vec);
2620        PetscObjectSetName((PetscObject)vec, "vec_name");
2621        DMPlexLocalVectorLoad(dm, viewer, sectiondm, lsf, vec);
2622        DMRestoreLocalVector(sectiondm, &vec);
2623        PetscSFDestroy(&lsf);
2624        PetscSFDestroy(&sfX);
2625        DMDestroy(&sectiondm);
2626        DMDestroy(&dm);
2627 .ve
2628 
2629 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`,
2630           `PetscSF`, `PetscViewer`
2631 @*/
2632 PetscErrorCode DMPlexLocalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2633 {
2634   PetscBool ishdf5;
2635 
2636   PetscFunctionBegin;
2637   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2638   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2639   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2640   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2641   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2642   /* Check consistency */
2643   {
2644     PetscSection section;
2645     PetscBool    includesConstraints;
2646     PetscInt     m, m1;
2647 
2648     PetscCall(VecGetLocalSize(vec, &m1));
2649     PetscCall(DMGetLocalSection(sectiondm, &section));
2650     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2651     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2652     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2653     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2654   }
2655   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2656   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorLoad, viewer, 0, 0, 0));
2657   if (ishdf5) {
2658 #if defined(PETSC_HAVE_HDF5)
2659     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2660 #else
2661     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2662 #endif
2663   }
2664   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorLoad, viewer, 0, 0, 0));
2665   PetscFunctionReturn(PETSC_SUCCESS);
2666 }
2667 
2668 PetscErrorCode DMDestroy_Plex(DM dm)
2669 {
2670   DM_Plex *mesh = (DM_Plex *)dm->data;
2671 
2672   PetscFunctionBegin;
2673   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMSetUpGLVisViewer_C", NULL));
2674   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertBoundaryValues_C", NULL));
2675   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMCreateNeumannOverlap_C", NULL));
2676   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMInterpolateSolution_C", NULL));
2677   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertTimeDerivativeBoundaryValues_C", NULL));
2678   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", NULL));
2679   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeGetDefault_C", NULL));
2680   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeSetDefault_C", NULL));
2681   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "MatComputeNeumannOverlap_C", NULL));
2682   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderGetDefault_C", NULL));
2683   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderSetDefault_C", NULL));
2684   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", NULL));
2685   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexSetOverlap_C", NULL));
2686   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetUseCeed_C", NULL));
2687   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexSetUseCeed_C", NULL));
2688   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMGetIsoperiodicPointSF_C", NULL));
2689   if (--mesh->refct > 0) PetscFunctionReturn(PETSC_SUCCESS);
2690   PetscCall(PetscSectionDestroy(&mesh->coneSection));
2691   PetscCall(PetscFree(mesh->cones));
2692   PetscCall(PetscFree(mesh->coneOrientations));
2693   PetscCall(PetscSectionDestroy(&mesh->supportSection));
2694   PetscCall(PetscSectionDestroy(&mesh->subdomainSection));
2695   PetscCall(PetscFree(mesh->supports));
2696   PetscCall(PetscFree(mesh->cellTypes));
2697   PetscCall(DMPlexTransformDestroy(&mesh->tr));
2698   PetscCall(PetscFree(mesh->tetgenOpts));
2699   PetscCall(PetscFree(mesh->triangleOpts));
2700   PetscCall(PetscFree(mesh->transformType));
2701   PetscCall(PetscFree(mesh->distributionName));
2702   PetscCall(PetscPartitionerDestroy(&mesh->partitioner));
2703   PetscCall(DMLabelDestroy(&mesh->subpointMap));
2704   PetscCall(ISDestroy(&mesh->subpointIS));
2705   PetscCall(ISDestroy(&mesh->globalVertexNumbers));
2706   PetscCall(ISDestroy(&mesh->globalCellNumbers));
2707   PetscCall(PetscSFDestroy(&mesh->periodic.face_sf));
2708   PetscCall(PetscSFDestroy(&mesh->periodic.composed_sf));
2709   PetscCall(ISDestroy(&mesh->periodic.periodic_points));
2710   PetscCall(PetscSectionDestroy(&mesh->anchorSection));
2711   PetscCall(ISDestroy(&mesh->anchorIS));
2712   PetscCall(PetscSectionDestroy(&mesh->parentSection));
2713   PetscCall(PetscFree(mesh->parents));
2714   PetscCall(PetscFree(mesh->childIDs));
2715   PetscCall(PetscSectionDestroy(&mesh->childSection));
2716   PetscCall(PetscFree(mesh->children));
2717   PetscCall(DMDestroy(&mesh->referenceTree));
2718   PetscCall(PetscGridHashDestroy(&mesh->lbox));
2719   PetscCall(PetscFree(mesh->neighbors));
2720   if (mesh->metricCtx) PetscCall(PetscFree(mesh->metricCtx));
2721   /* This was originally freed in DMDestroy(), but that prevents reference counting of backend objects */
2722   PetscCall(PetscFree(mesh));
2723   PetscFunctionReturn(PETSC_SUCCESS);
2724 }
2725 
2726 PetscErrorCode DMCreateMatrix_Plex(DM dm, Mat *J)
2727 {
2728   PetscSection           sectionGlobal;
2729   PetscInt               bs = -1, mbs;
2730   PetscInt               localSize, localStart = 0;
2731   PetscBool              isShell, isBlock, isSeqBlock, isMPIBlock, isSymBlock, isSymSeqBlock, isSymMPIBlock, isMatIS;
2732   MatType                mtype;
2733   ISLocalToGlobalMapping ltog;
2734 
2735   PetscFunctionBegin;
2736   PetscCall(MatInitializePackage());
2737   mtype = dm->mattype;
2738   PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
2739   /* PetscCall(PetscSectionGetStorageSize(sectionGlobal, &localSize)); */
2740   PetscCall(PetscSectionGetConstrainedStorageSize(sectionGlobal, &localSize));
2741   PetscCallMPI(MPI_Exscan(&localSize, &localStart, 1, MPIU_INT, MPI_SUM, PetscObjectComm((PetscObject)dm)));
2742   PetscCall(MatCreate(PetscObjectComm((PetscObject)dm), J));
2743   PetscCall(MatSetSizes(*J, localSize, localSize, PETSC_DETERMINE, PETSC_DETERMINE));
2744   PetscCall(MatSetType(*J, mtype));
2745   PetscCall(MatSetFromOptions(*J));
2746   PetscCall(MatGetBlockSize(*J, &mbs));
2747   if (mbs > 1) bs = mbs;
2748   PetscCall(PetscStrcmp(mtype, MATSHELL, &isShell));
2749   PetscCall(PetscStrcmp(mtype, MATBAIJ, &isBlock));
2750   PetscCall(PetscStrcmp(mtype, MATSEQBAIJ, &isSeqBlock));
2751   PetscCall(PetscStrcmp(mtype, MATMPIBAIJ, &isMPIBlock));
2752   PetscCall(PetscStrcmp(mtype, MATSBAIJ, &isSymBlock));
2753   PetscCall(PetscStrcmp(mtype, MATSEQSBAIJ, &isSymSeqBlock));
2754   PetscCall(PetscStrcmp(mtype, MATMPISBAIJ, &isSymMPIBlock));
2755   PetscCall(PetscStrcmp(mtype, MATIS, &isMatIS));
2756   if (!isShell) {
2757     PetscBool fillMatrix = (PetscBool)(!dm->prealloc_only && !isMatIS);
2758     PetscInt *dnz, *onz, *dnzu, *onzu, bsLocal[2], bsMinMax[2], *pblocks;
2759     PetscInt  pStart, pEnd, p, dof, cdof, num_fields;
2760 
2761     PetscCall(DMGetLocalToGlobalMapping(dm, &ltog));
2762 
2763     PetscCall(PetscCalloc1(localSize, &pblocks));
2764     PetscCall(PetscSectionGetChart(sectionGlobal, &pStart, &pEnd));
2765     PetscCall(PetscSectionGetNumFields(sectionGlobal, &num_fields));
2766     for (p = pStart; p < pEnd; ++p) {
2767       switch (dm->blocking_type) {
2768       case DM_BLOCKING_TOPOLOGICAL_POINT: { // One block per topological point
2769         PetscInt bdof, offset;
2770 
2771         PetscCall(PetscSectionGetDof(sectionGlobal, p, &dof));
2772         PetscCall(PetscSectionGetOffset(sectionGlobal, p, &offset));
2773         PetscCall(PetscSectionGetConstraintDof(sectionGlobal, p, &cdof));
2774         for (PetscInt i = 0; i < dof - cdof; i++) pblocks[offset - localStart + i] = dof - cdof;
2775         dof  = dof < 0 ? -(dof + 1) : dof;
2776         bdof = cdof && (dof - cdof) ? 1 : dof;
2777         if (dof) {
2778           if (bs < 0) {
2779             bs = bdof;
2780           } else if (bs != bdof) {
2781             bs = 1;
2782           }
2783         }
2784       } break;
2785       case DM_BLOCKING_FIELD_NODE: {
2786         for (PetscInt field = 0; field < num_fields; field++) {
2787           PetscInt num_comp, bdof, offset;
2788           PetscCall(PetscSectionGetFieldComponents(sectionGlobal, field, &num_comp));
2789           PetscCall(PetscSectionGetFieldDof(sectionGlobal, p, field, &dof));
2790           if (dof < 0) continue;
2791           PetscCall(PetscSectionGetFieldOffset(sectionGlobal, p, field, &offset));
2792           PetscCall(PetscSectionGetFieldConstraintDof(sectionGlobal, p, field, &cdof));
2793           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);
2794           PetscInt num_nodes = dof / num_comp;
2795           for (PetscInt i = 0; i < dof - cdof; i++) pblocks[offset - localStart + i] = (dof - cdof) / num_nodes;
2796           // Handle possibly constant block size (unlikely)
2797           bdof = cdof && (dof - cdof) ? 1 : dof;
2798           if (dof) {
2799             if (bs < 0) {
2800               bs = bdof;
2801             } else if (bs != bdof) {
2802               bs = 1;
2803             }
2804           }
2805         }
2806       } break;
2807       }
2808     }
2809     /* Must have same blocksize on all procs (some might have no points) */
2810     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs;
2811     bsLocal[1] = bs;
2812     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
2813     if (bsMinMax[0] != bsMinMax[1]) bs = 1;
2814     else bs = bsMinMax[0];
2815     bs = PetscMax(1, bs);
2816     PetscCall(MatSetLocalToGlobalMapping(*J, ltog, ltog));
2817     if (dm->prealloc_skip) { // User will likely use MatSetPreallocationCOO(), but still set structural parameters
2818       PetscCall(MatSetBlockSize(*J, bs));
2819       PetscCall(MatSetUp(*J));
2820     } else {
2821       PetscCall(PetscCalloc4(localSize / bs, &dnz, localSize / bs, &onz, localSize / bs, &dnzu, localSize / bs, &onzu));
2822       PetscCall(DMPlexPreallocateOperator(dm, bs, dnz, onz, dnzu, onzu, *J, fillMatrix));
2823       PetscCall(PetscFree4(dnz, onz, dnzu, onzu));
2824     }
2825     { // Consolidate blocks
2826       PetscInt nblocks = 0;
2827       for (PetscInt i = 0; i < localSize; i += PetscMax(1, pblocks[i])) {
2828         if (pblocks[i] == 0) continue;
2829         pblocks[nblocks++] = pblocks[i]; // nblocks always <= i
2830         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]);
2831       }
2832       PetscCall(MatSetVariableBlockSizes(*J, nblocks, pblocks));
2833     }
2834     PetscCall(PetscFree(pblocks));
2835   }
2836   PetscCall(MatSetDM(*J, dm));
2837   PetscFunctionReturn(PETSC_SUCCESS);
2838 }
2839 
2840 /*@
2841   DMPlexGetSubdomainSection - Returns the section associated with the subdomain
2842 
2843   Not Collective
2844 
2845   Input Parameter:
2846 . dm - The `DMPLEX`
2847 
2848   Output Parameter:
2849 . subsection - The subdomain section
2850 
2851   Level: developer
2852 
2853 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `PetscSection`
2854 @*/
2855 PetscErrorCode DMPlexGetSubdomainSection(DM dm, PetscSection *subsection)
2856 {
2857   DM_Plex *mesh = (DM_Plex *)dm->data;
2858 
2859   PetscFunctionBegin;
2860   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2861   if (!mesh->subdomainSection) {
2862     PetscSection section;
2863     PetscSF      sf;
2864 
2865     PetscCall(PetscSFCreate(PETSC_COMM_SELF, &sf));
2866     PetscCall(DMGetLocalSection(dm, &section));
2867     PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_FALSE, PETSC_TRUE, &mesh->subdomainSection));
2868     PetscCall(PetscSFDestroy(&sf));
2869   }
2870   *subsection = mesh->subdomainSection;
2871   PetscFunctionReturn(PETSC_SUCCESS);
2872 }
2873 
2874 /*@
2875   DMPlexGetChart - Return the interval for all mesh points [`pStart`, `pEnd`)
2876 
2877   Not Collective
2878 
2879   Input Parameter:
2880 . dm - The `DMPLEX`
2881 
2882   Output Parameters:
2883 + pStart - The first mesh point
2884 - pEnd   - The upper bound for mesh points
2885 
2886   Level: beginner
2887 
2888 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetChart()`
2889 @*/
2890 PetscErrorCode DMPlexGetChart(DM dm, PetscInt *pStart, PetscInt *pEnd)
2891 {
2892   DM_Plex *mesh = (DM_Plex *)dm->data;
2893 
2894   PetscFunctionBegin;
2895   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2896   if (mesh->tr) PetscCall(DMPlexTransformGetChart(mesh->tr, pStart, pEnd));
2897   else PetscCall(PetscSectionGetChart(mesh->coneSection, pStart, pEnd));
2898   PetscFunctionReturn(PETSC_SUCCESS);
2899 }
2900 
2901 /*@
2902   DMPlexSetChart - Set the interval for all mesh points [`pStart`, `pEnd`)
2903 
2904   Not Collective
2905 
2906   Input Parameters:
2907 + dm     - The `DMPLEX`
2908 . pStart - The first mesh point
2909 - pEnd   - The upper bound for mesh points
2910 
2911   Level: beginner
2912 
2913 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetChart()`
2914 @*/
2915 PetscErrorCode DMPlexSetChart(DM dm, PetscInt pStart, PetscInt pEnd)
2916 {
2917   DM_Plex *mesh = (DM_Plex *)dm->data;
2918 
2919   PetscFunctionBegin;
2920   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2921   PetscCall(PetscSectionSetChart(mesh->coneSection, pStart, pEnd));
2922   PetscCall(PetscSectionSetChart(mesh->supportSection, pStart, pEnd));
2923   PetscCall(PetscFree(mesh->cellTypes));
2924   PetscFunctionReturn(PETSC_SUCCESS);
2925 }
2926 
2927 /*@
2928   DMPlexGetConeSize - Return the number of in-edges for this point in the DAG
2929 
2930   Not Collective
2931 
2932   Input Parameters:
2933 + dm - The `DMPLEX`
2934 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
2935 
2936   Output Parameter:
2937 . size - The cone size for point `p`
2938 
2939   Level: beginner
2940 
2941 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
2942 @*/
2943 PetscErrorCode DMPlexGetConeSize(DM dm, PetscInt p, PetscInt *size)
2944 {
2945   DM_Plex *mesh = (DM_Plex *)dm->data;
2946 
2947   PetscFunctionBegin;
2948   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2949   PetscAssertPointer(size, 3);
2950   if (mesh->tr) PetscCall(DMPlexTransformGetConeSize(mesh->tr, p, size));
2951   else PetscCall(PetscSectionGetDof(mesh->coneSection, p, size));
2952   PetscFunctionReturn(PETSC_SUCCESS);
2953 }
2954 
2955 /*@
2956   DMPlexSetConeSize - Set the number of in-edges for this point in the DAG
2957 
2958   Not Collective
2959 
2960   Input Parameters:
2961 + dm   - The `DMPLEX`
2962 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
2963 - size - The cone size for point `p`
2964 
2965   Level: beginner
2966 
2967   Note:
2968   This should be called after `DMPlexSetChart()`.
2969 
2970 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetConeSize()`, `DMPlexSetChart()`
2971 @*/
2972 PetscErrorCode DMPlexSetConeSize(DM dm, PetscInt p, PetscInt size)
2973 {
2974   DM_Plex *mesh = (DM_Plex *)dm->data;
2975 
2976   PetscFunctionBegin;
2977   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2978   PetscCheck(!mesh->tr, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Cannot call DMPlexSetConeSize() on a mesh with a transform defined.");
2979   PetscCall(PetscSectionSetDof(mesh->coneSection, p, size));
2980   PetscFunctionReturn(PETSC_SUCCESS);
2981 }
2982 
2983 /*@C
2984   DMPlexGetCone - Return the points on the in-edges for this point in the DAG
2985 
2986   Not Collective
2987 
2988   Input Parameters:
2989 + dm - The `DMPLEX`
2990 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
2991 
2992   Output Parameter:
2993 . cone - An array of points which are on the in-edges for point `p`
2994 
2995   Level: beginner
2996 
2997   Fortran Notes:
2998   You must also call `DMPlexRestoreCone()` after you finish using the returned array.
2999   `DMPlexRestoreCone()` is not needed/available in C.
3000 
3001 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSize()`, `DMPlexSetCone()`, `DMPlexGetConeTuple()`, `DMPlexSetChart()`, `DMPlexRestoreCone()`
3002 @*/
3003 PetscErrorCode DMPlexGetCone(DM dm, PetscInt p, const PetscInt *cone[])
3004 {
3005   DM_Plex *mesh = (DM_Plex *)dm->data;
3006   PetscInt off;
3007 
3008   PetscFunctionBegin;
3009   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3010   PetscAssertPointer(cone, 3);
3011   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3012   *cone = &mesh->cones[off];
3013   PetscFunctionReturn(PETSC_SUCCESS);
3014 }
3015 
3016 /*@C
3017   DMPlexGetConeTuple - Return the points on the in-edges of several points in the DAG
3018 
3019   Not Collective
3020 
3021   Input Parameters:
3022 + dm - The `DMPLEX`
3023 - p  - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3024 
3025   Output Parameters:
3026 + pConesSection - `PetscSection` describing the layout of `pCones`
3027 - pCones        - An array of points which are on the in-edges for the point set `p`
3028 
3029   Level: intermediate
3030 
3031 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeRecursive()`, `DMPlexSetChart()`, `PetscSection`, `IS`
3032 @*/
3033 PetscErrorCode DMPlexGetConeTuple(DM dm, IS p, PetscSection *pConesSection, IS *pCones)
3034 {
3035   PetscSection cs, newcs;
3036   PetscInt    *cones;
3037   PetscInt    *newarr = NULL;
3038   PetscInt     n;
3039 
3040   PetscFunctionBegin;
3041   PetscCall(DMPlexGetCones(dm, &cones));
3042   PetscCall(DMPlexGetConeSection(dm, &cs));
3043   PetscCall(PetscSectionExtractDofsFromArray(cs, MPIU_INT, cones, p, &newcs, pCones ? ((void **)&newarr) : NULL));
3044   if (pConesSection) *pConesSection = newcs;
3045   if (pCones) {
3046     PetscCall(PetscSectionGetStorageSize(newcs, &n));
3047     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)p), n, newarr, PETSC_OWN_POINTER, pCones));
3048   }
3049   PetscFunctionReturn(PETSC_SUCCESS);
3050 }
3051 
3052 /*@
3053   DMPlexGetConeRecursiveVertices - Expand each given point into its cone points and do that recursively until we end up just with vertices.
3054 
3055   Not Collective
3056 
3057   Input Parameters:
3058 + dm     - The `DMPLEX`
3059 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3060 
3061   Output Parameter:
3062 . expandedPoints - An array of vertices recursively expanded from input points
3063 
3064   Level: advanced
3065 
3066   Notes:
3067   Like `DMPlexGetConeRecursive()` but returns only the 0-depth `IS` (i.e. vertices only) and no sections.
3068 
3069   There is no corresponding Restore function, just call `ISDestroy()` on the returned `IS` to deallocate.
3070 
3071 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexRestoreConeRecursive()`,
3072           `DMPlexGetDepth()`, `IS`
3073 @*/
3074 PetscErrorCode DMPlexGetConeRecursiveVertices(DM dm, IS points, IS *expandedPoints)
3075 {
3076   IS      *expandedPointsAll;
3077   PetscInt depth;
3078 
3079   PetscFunctionBegin;
3080   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3081   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
3082   PetscAssertPointer(expandedPoints, 3);
3083   PetscCall(DMPlexGetConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
3084   *expandedPoints = expandedPointsAll[0];
3085   PetscCall(PetscObjectReference((PetscObject)expandedPointsAll[0]));
3086   PetscCall(DMPlexRestoreConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
3087   PetscFunctionReturn(PETSC_SUCCESS);
3088 }
3089 
3090 /*@
3091   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).
3092 
3093   Not Collective
3094 
3095   Input Parameters:
3096 + dm     - The `DMPLEX`
3097 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3098 
3099   Output Parameters:
3100 + depth          - (optional) Size of the output arrays, equal to `DMPLEX` depth, returned by `DMPlexGetDepth()`
3101 . expandedPoints - (optional) An array of index sets with recursively expanded cones
3102 - sections       - (optional) An array of sections which describe mappings from points to their cone points
3103 
3104   Level: advanced
3105 
3106   Notes:
3107   Like `DMPlexGetConeTuple()` but recursive.
3108 
3109   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.
3110   For example, for d=0 it contains only vertices, for d=1 it can contain vertices and edges, etc.
3111 
3112   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\:
3113   (1) DAG points in `expandedPoints`[d+1] with `depth` d+1 to their cone points in `expandedPoints`[d];
3114   (2) DAG points in `expandedPoints`[d+1] with `depth` in [0,d] to the same points in `expandedPoints`[d].
3115 
3116 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexRestoreConeRecursive()`, `DMPlexGetConeRecursiveVertices()`,
3117           `DMPlexGetDepth()`, `PetscSection`, `IS`
3118 @*/
3119 PetscErrorCode DMPlexGetConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
3120 {
3121   const PetscInt *arr0 = NULL, *cone = NULL;
3122   PetscInt       *arr = NULL, *newarr = NULL;
3123   PetscInt        d, depth_, i, n, newn, cn, co, start, end;
3124   IS             *expandedPoints_;
3125   PetscSection   *sections_;
3126 
3127   PetscFunctionBegin;
3128   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3129   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
3130   if (depth) PetscAssertPointer(depth, 3);
3131   if (expandedPoints) PetscAssertPointer(expandedPoints, 4);
3132   if (sections) PetscAssertPointer(sections, 5);
3133   PetscCall(ISGetLocalSize(points, &n));
3134   PetscCall(ISGetIndices(points, &arr0));
3135   PetscCall(DMPlexGetDepth(dm, &depth_));
3136   PetscCall(PetscCalloc1(depth_, &expandedPoints_));
3137   PetscCall(PetscCalloc1(depth_, &sections_));
3138   arr = (PetscInt *)arr0; /* this is ok because first generation of arr is not modified */
3139   for (d = depth_ - 1; d >= 0; d--) {
3140     PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &sections_[d]));
3141     PetscCall(PetscSectionSetChart(sections_[d], 0, n));
3142     for (i = 0; i < n; i++) {
3143       PetscCall(DMPlexGetDepthStratum(dm, d + 1, &start, &end));
3144       if (arr[i] >= start && arr[i] < end) {
3145         PetscCall(DMPlexGetConeSize(dm, arr[i], &cn));
3146         PetscCall(PetscSectionSetDof(sections_[d], i, cn));
3147       } else {
3148         PetscCall(PetscSectionSetDof(sections_[d], i, 1));
3149       }
3150     }
3151     PetscCall(PetscSectionSetUp(sections_[d]));
3152     PetscCall(PetscSectionGetStorageSize(sections_[d], &newn));
3153     PetscCall(PetscMalloc1(newn, &newarr));
3154     for (i = 0; i < n; i++) {
3155       PetscCall(PetscSectionGetDof(sections_[d], i, &cn));
3156       PetscCall(PetscSectionGetOffset(sections_[d], i, &co));
3157       if (cn > 1) {
3158         PetscCall(DMPlexGetCone(dm, arr[i], &cone));
3159         PetscCall(PetscMemcpy(&newarr[co], cone, cn * sizeof(PetscInt)));
3160       } else {
3161         newarr[co] = arr[i];
3162       }
3163     }
3164     PetscCall(ISCreateGeneral(PETSC_COMM_SELF, newn, newarr, PETSC_OWN_POINTER, &expandedPoints_[d]));
3165     arr = newarr;
3166     n   = newn;
3167   }
3168   PetscCall(ISRestoreIndices(points, &arr0));
3169   *depth = depth_;
3170   if (expandedPoints) *expandedPoints = expandedPoints_;
3171   else {
3172     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&expandedPoints_[d]));
3173     PetscCall(PetscFree(expandedPoints_));
3174   }
3175   if (sections) *sections = sections_;
3176   else {
3177     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&sections_[d]));
3178     PetscCall(PetscFree(sections_));
3179   }
3180   PetscFunctionReturn(PETSC_SUCCESS);
3181 }
3182 
3183 /*@
3184   DMPlexRestoreConeRecursive - Deallocates arrays created by `DMPlexGetConeRecursive()`
3185 
3186   Not Collective
3187 
3188   Input Parameters:
3189 + dm     - The `DMPLEX`
3190 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3191 
3192   Output Parameters:
3193 + depth          - (optional) Size of the output arrays, equal to `DMPLEX` depth, returned by `DMPlexGetDepth()`
3194 . expandedPoints - (optional) An array of recursively expanded cones
3195 - sections       - (optional) An array of sections which describe mappings from points to their cone points
3196 
3197   Level: advanced
3198 
3199   Note:
3200   See `DMPlexGetConeRecursive()`
3201 
3202 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexGetConeRecursiveVertices()`,
3203           `DMPlexGetDepth()`, `IS`, `PetscSection`
3204 @*/
3205 PetscErrorCode DMPlexRestoreConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
3206 {
3207   PetscInt d, depth_;
3208 
3209   PetscFunctionBegin;
3210   PetscCall(DMPlexGetDepth(dm, &depth_));
3211   PetscCheck(!depth || *depth == depth_, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "depth changed since last call to DMPlexGetConeRecursive");
3212   if (depth) *depth = 0;
3213   if (expandedPoints) {
3214     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&((*expandedPoints)[d])));
3215     PetscCall(PetscFree(*expandedPoints));
3216   }
3217   if (sections) {
3218     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&((*sections)[d])));
3219     PetscCall(PetscFree(*sections));
3220   }
3221   PetscFunctionReturn(PETSC_SUCCESS);
3222 }
3223 
3224 /*@
3225   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
3226 
3227   Not Collective
3228 
3229   Input Parameters:
3230 + dm   - The `DMPLEX`
3231 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3232 - cone - An array of points which are on the in-edges for point `p`
3233 
3234   Level: beginner
3235 
3236   Note:
3237   This should be called after all calls to `DMPlexSetConeSize()` and `DMSetUp()`.
3238 
3239 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`, `DMPlexSetSupport()`, `DMPlexSetSupportSize()`
3240 @*/
3241 PetscErrorCode DMPlexSetCone(DM dm, PetscInt p, const PetscInt cone[])
3242 {
3243   DM_Plex *mesh = (DM_Plex *)dm->data;
3244   PetscInt dof, off, c;
3245 
3246   PetscFunctionBegin;
3247   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3248   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3249   if (dof) PetscAssertPointer(cone, 3);
3250   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3251   if (PetscDefined(USE_DEBUG)) {
3252     PetscInt pStart, pEnd;
3253     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3254     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);
3255     for (c = 0; c < dof; ++c) {
3256       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);
3257       mesh->cones[off + c] = cone[c];
3258     }
3259   } else {
3260     for (c = 0; c < dof; ++c) mesh->cones[off + c] = cone[c];
3261   }
3262   PetscFunctionReturn(PETSC_SUCCESS);
3263 }
3264 
3265 /*@C
3266   DMPlexGetConeOrientation - Return the orientations on the in-edges for this point in the DAG
3267 
3268   Not Collective
3269 
3270   Input Parameters:
3271 + dm - The `DMPLEX`
3272 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3273 
3274   Output Parameter:
3275 . coneOrientation - An array of orientations which are on the in-edges for point `p`. An orientation is an
3276                     integer giving the prescription for cone traversal.
3277 
3278   Level: beginner
3279 
3280   Note:
3281   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3282   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3283   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3284   with the identity.
3285 
3286   Fortran Notes:
3287   You must also call `DMPlexRestoreConeOrientation()` after you finish using the returned array.
3288   `DMPlexRestoreConeOrientation()` is not needed/available in C.
3289 
3290 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPolytopeTypeComposeOrientation()`, `DMPolytopeTypeComposeOrientationInv()`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetCone()`, `DMPlexSetChart()`
3291 @*/
3292 PetscErrorCode DMPlexGetConeOrientation(DM dm, PetscInt p, const PetscInt *coneOrientation[])
3293 {
3294   DM_Plex *mesh = (DM_Plex *)dm->data;
3295   PetscInt off;
3296 
3297   PetscFunctionBegin;
3298   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3299   if (PetscDefined(USE_DEBUG)) {
3300     PetscInt dof;
3301     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3302     if (dof) PetscAssertPointer(coneOrientation, 3);
3303   }
3304   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3305 
3306   *coneOrientation = &mesh->coneOrientations[off];
3307   PetscFunctionReturn(PETSC_SUCCESS);
3308 }
3309 
3310 /*@
3311   DMPlexSetConeOrientation - Set the orientations on the in-edges for this point in the DAG
3312 
3313   Not Collective
3314 
3315   Input Parameters:
3316 + dm              - The `DMPLEX`
3317 . p               - The point, which must lie in the chart set with `DMPlexSetChart()`
3318 - coneOrientation - An array of orientations
3319 
3320   Level: beginner
3321 
3322   Notes:
3323   This should be called after all calls to `DMPlexSetConeSize()` and `DMSetUp()`.
3324 
3325   The meaning of coneOrientation is detailed in `DMPlexGetConeOrientation()`.
3326 
3327 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetConeOrientation()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3328 @*/
3329 PetscErrorCode DMPlexSetConeOrientation(DM dm, PetscInt p, const PetscInt coneOrientation[])
3330 {
3331   DM_Plex *mesh = (DM_Plex *)dm->data;
3332   PetscInt pStart, pEnd;
3333   PetscInt dof, off, c;
3334 
3335   PetscFunctionBegin;
3336   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3337   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3338   if (dof) PetscAssertPointer(coneOrientation, 3);
3339   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3340   if (PetscDefined(USE_DEBUG)) {
3341     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3342     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);
3343     for (c = 0; c < dof; ++c) {
3344       PetscInt cdof, o = coneOrientation[c];
3345 
3346       PetscCall(PetscSectionGetDof(mesh->coneSection, mesh->cones[off + c], &cdof));
3347       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);
3348       mesh->coneOrientations[off + c] = o;
3349     }
3350   } else {
3351     for (c = 0; c < dof; ++c) mesh->coneOrientations[off + c] = coneOrientation[c];
3352   }
3353   PetscFunctionReturn(PETSC_SUCCESS);
3354 }
3355 
3356 /*@
3357   DMPlexInsertCone - Insert a point into the in-edges for the point p in the DAG
3358 
3359   Not Collective
3360 
3361   Input Parameters:
3362 + dm        - The `DMPLEX`
3363 . p         - The point, which must lie in the chart set with `DMPlexSetChart()`
3364 . conePos   - The local index in the cone where the point should be put
3365 - conePoint - The mesh point to insert
3366 
3367   Level: beginner
3368 
3369 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3370 @*/
3371 PetscErrorCode DMPlexInsertCone(DM dm, PetscInt p, PetscInt conePos, PetscInt conePoint)
3372 {
3373   DM_Plex *mesh = (DM_Plex *)dm->data;
3374   PetscInt pStart, pEnd;
3375   PetscInt dof, off;
3376 
3377   PetscFunctionBegin;
3378   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3379   if (PetscDefined(USE_DEBUG)) {
3380     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3381     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);
3382     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);
3383     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3384     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);
3385   }
3386   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3387   mesh->cones[off + conePos] = conePoint;
3388   PetscFunctionReturn(PETSC_SUCCESS);
3389 }
3390 
3391 /*@
3392   DMPlexInsertConeOrientation - Insert a point orientation for the in-edge for the point p in the DAG
3393 
3394   Not Collective
3395 
3396   Input Parameters:
3397 + dm              - The `DMPLEX`
3398 . p               - The point, which must lie in the chart set with `DMPlexSetChart()`
3399 . conePos         - The local index in the cone where the point should be put
3400 - coneOrientation - The point orientation to insert
3401 
3402   Level: beginner
3403 
3404   Note:
3405   The meaning of coneOrientation values is detailed in `DMPlexGetConeOrientation()`.
3406 
3407 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3408 @*/
3409 PetscErrorCode DMPlexInsertConeOrientation(DM dm, PetscInt p, PetscInt conePos, PetscInt coneOrientation)
3410 {
3411   DM_Plex *mesh = (DM_Plex *)dm->data;
3412   PetscInt pStart, pEnd;
3413   PetscInt dof, off;
3414 
3415   PetscFunctionBegin;
3416   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3417   if (PetscDefined(USE_DEBUG)) {
3418     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3419     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);
3420     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3421     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);
3422   }
3423   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3424   mesh->coneOrientations[off + conePos] = coneOrientation;
3425   PetscFunctionReturn(PETSC_SUCCESS);
3426 }
3427 
3428 /*@C
3429   DMPlexGetOrientedCone - Return the points and orientations on the in-edges for this point in the DAG
3430 
3431   Not collective
3432 
3433   Input Parameters:
3434 + dm - The DMPlex
3435 - p  - The point, which must lie in the chart set with DMPlexSetChart()
3436 
3437   Output Parameters:
3438 + cone - An array of points which are on the in-edges for point `p`
3439 - ornt - An array of orientations which are on the in-edges for point `p`. An orientation is an
3440         integer giving the prescription for cone traversal.
3441 
3442   Level: beginner
3443 
3444   Notes:
3445   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3446   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3447   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3448   with the identity.
3449 
3450   Fortran Notes:
3451   You must also call `DMPlexRestoreCone()` after you finish using the returned array.
3452   `DMPlexRestoreCone()` is not needed/available in C.
3453 
3454 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreOrientedCone()`, `DMPlexGetConeSize()`, `DMPlexGetCone()`, `DMPlexGetChart()`
3455 @*/
3456 PetscErrorCode DMPlexGetOrientedCone(DM dm, PetscInt p, const PetscInt *cone[], const PetscInt *ornt[])
3457 {
3458   DM_Plex *mesh = (DM_Plex *)dm->data;
3459 
3460   PetscFunctionBegin;
3461   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3462   if (mesh->tr) {
3463     PetscCall(DMPlexTransformGetCone(mesh->tr, p, cone, ornt));
3464   } else {
3465     PetscInt off;
3466     if (PetscDefined(USE_DEBUG)) {
3467       PetscInt dof;
3468       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3469       if (dof) {
3470         if (cone) PetscAssertPointer(cone, 3);
3471         if (ornt) PetscAssertPointer(ornt, 4);
3472       }
3473     }
3474     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3475     if (cone) *cone = mesh->cones ? mesh->cones + off : NULL; // NULL + 0 is UB
3476     if (ornt) *ornt = mesh->coneOrientations ? mesh->coneOrientations + off : NULL;
3477   }
3478   PetscFunctionReturn(PETSC_SUCCESS);
3479 }
3480 
3481 /*@C
3482   DMPlexRestoreOrientedCone - Restore the points and orientations on the in-edges for this point in the DAG
3483 
3484   Not Collective
3485 
3486   Input Parameters:
3487 + dm   - The DMPlex
3488 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3489 . cone - An array of points which are on the in-edges for point p
3490 - ornt - An array of orientations which are on the in-edges for point `p`. An orientation is an
3491         integer giving the prescription for cone traversal.
3492 
3493   Level: beginner
3494 
3495   Notes:
3496   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3497   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3498   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3499   with the identity.
3500 
3501   Fortran Notes:
3502   You must also call `DMPlexRestoreCone()` after you finish using the returned array.
3503   `DMPlexRestoreCone()` is not needed/available in C.
3504 
3505 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetOrientedCone()`, `DMPlexGetConeSize()`, `DMPlexGetCone()`, `DMPlexGetChart()`
3506 @*/
3507 PetscErrorCode DMPlexRestoreOrientedCone(DM dm, PetscInt p, const PetscInt *cone[], const PetscInt *ornt[])
3508 {
3509   DM_Plex *mesh = (DM_Plex *)dm->data;
3510 
3511   PetscFunctionBegin;
3512   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3513   if (mesh->tr) PetscCall(DMPlexTransformRestoreCone(mesh->tr, p, cone, ornt));
3514   PetscFunctionReturn(PETSC_SUCCESS);
3515 }
3516 
3517 /*@
3518   DMPlexGetSupportSize - Return the number of out-edges for this point in the DAG
3519 
3520   Not Collective
3521 
3522   Input Parameters:
3523 + dm - The `DMPLEX`
3524 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3525 
3526   Output Parameter:
3527 . size - The support size for point `p`
3528 
3529   Level: beginner
3530 
3531 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`, `DMPlexGetConeSize()`
3532 @*/
3533 PetscErrorCode DMPlexGetSupportSize(DM dm, PetscInt p, PetscInt *size)
3534 {
3535   DM_Plex *mesh = (DM_Plex *)dm->data;
3536 
3537   PetscFunctionBegin;
3538   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3539   PetscAssertPointer(size, 3);
3540   PetscCall(PetscSectionGetDof(mesh->supportSection, p, size));
3541   PetscFunctionReturn(PETSC_SUCCESS);
3542 }
3543 
3544 /*@
3545   DMPlexSetSupportSize - Set the number of out-edges for this point in the DAG
3546 
3547   Not Collective
3548 
3549   Input Parameters:
3550 + dm   - The `DMPLEX`
3551 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3552 - size - The support size for point `p`
3553 
3554   Level: beginner
3555 
3556   Note:
3557   This should be called after `DMPlexSetChart()`.
3558 
3559 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetSupportSize()`, `DMPlexSetChart()`
3560 @*/
3561 PetscErrorCode DMPlexSetSupportSize(DM dm, PetscInt p, PetscInt size)
3562 {
3563   DM_Plex *mesh = (DM_Plex *)dm->data;
3564 
3565   PetscFunctionBegin;
3566   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3567   PetscCall(PetscSectionSetDof(mesh->supportSection, p, size));
3568   PetscFunctionReturn(PETSC_SUCCESS);
3569 }
3570 
3571 /*@C
3572   DMPlexGetSupport - Return the points on the out-edges for this point in the DAG
3573 
3574   Not Collective
3575 
3576   Input Parameters:
3577 + dm - The `DMPLEX`
3578 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3579 
3580   Output Parameter:
3581 . support - An array of points which are on the out-edges for point `p`
3582 
3583   Level: beginner
3584 
3585   Fortran Notes:
3586   You must also call `DMPlexRestoreSupport()` after you finish using the returned array.
3587   `DMPlexRestoreSupport()` is not needed/available in C.
3588 
3589 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSupportSize()`, `DMPlexSetSupport()`, `DMPlexGetCone()`, `DMPlexSetChart()`
3590 @*/
3591 PetscErrorCode DMPlexGetSupport(DM dm, PetscInt p, const PetscInt *support[])
3592 {
3593   DM_Plex *mesh = (DM_Plex *)dm->data;
3594   PetscInt off;
3595 
3596   PetscFunctionBegin;
3597   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3598   PetscAssertPointer(support, 3);
3599   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3600   *support = mesh->supports ? mesh->supports + off : NULL; //NULL + 0 is UB
3601   PetscFunctionReturn(PETSC_SUCCESS);
3602 }
3603 
3604 /*@
3605   DMPlexSetSupport - Set the points on the out-edges for this point in the DAG, that is the list of points that this point covers
3606 
3607   Not Collective
3608 
3609   Input Parameters:
3610 + dm      - The `DMPLEX`
3611 . p       - The point, which must lie in the chart set with `DMPlexSetChart()`
3612 - support - An array of points which are on the out-edges for point `p`
3613 
3614   Level: beginner
3615 
3616   Note:
3617   This should be called after all calls to `DMPlexSetSupportSize()` and `DMSetUp()`.
3618 
3619 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetCone()`, `DMPlexSetConeSize()`, `DMPlexCreate()`, `DMPlexGetSupport()`, `DMPlexSetChart()`, `DMPlexSetSupportSize()`, `DMSetUp()`
3620 @*/
3621 PetscErrorCode DMPlexSetSupport(DM dm, PetscInt p, const PetscInt support[])
3622 {
3623   DM_Plex *mesh = (DM_Plex *)dm->data;
3624   PetscInt pStart, pEnd;
3625   PetscInt dof, off, c;
3626 
3627   PetscFunctionBegin;
3628   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3629   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3630   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3631   if (dof) PetscAssertPointer(support, 3);
3632   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3633   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);
3634   for (c = 0; c < dof; ++c) {
3635     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);
3636     mesh->supports[off + c] = support[c];
3637   }
3638   PetscFunctionReturn(PETSC_SUCCESS);
3639 }
3640 
3641 /*@
3642   DMPlexInsertSupport - Insert a point into the out-edges for the point p in the DAG
3643 
3644   Not Collective
3645 
3646   Input Parameters:
3647 + dm           - The `DMPLEX`
3648 . p            - The point, which must lie in the chart set with `DMPlexSetChart()`
3649 . supportPos   - The local index in the cone where the point should be put
3650 - supportPoint - The mesh point to insert
3651 
3652   Level: beginner
3653 
3654 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3655 @*/
3656 PetscErrorCode DMPlexInsertSupport(DM dm, PetscInt p, PetscInt supportPos, PetscInt supportPoint)
3657 {
3658   DM_Plex *mesh = (DM_Plex *)dm->data;
3659   PetscInt pStart, pEnd;
3660   PetscInt dof, off;
3661 
3662   PetscFunctionBegin;
3663   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3664   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3665   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3666   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3667   PetscCheck(!(p < pStart) && !(p >= pEnd), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Mesh point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", p, pStart, pEnd);
3668   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);
3669   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);
3670   mesh->supports[off + supportPos] = supportPoint;
3671   PetscFunctionReturn(PETSC_SUCCESS);
3672 }
3673 
3674 /* Converts an orientation o in the current numbering to the previous scheme used in Plex */
3675 PetscInt DMPolytopeConvertNewOrientation_Internal(DMPolytopeType ct, PetscInt o)
3676 {
3677   switch (ct) {
3678   case DM_POLYTOPE_SEGMENT:
3679     if (o == -1) return -2;
3680     break;
3681   case DM_POLYTOPE_TRIANGLE:
3682     if (o == -3) return -1;
3683     if (o == -2) return -3;
3684     if (o == -1) return -2;
3685     break;
3686   case DM_POLYTOPE_QUADRILATERAL:
3687     if (o == -4) return -2;
3688     if (o == -3) return -1;
3689     if (o == -2) return -4;
3690     if (o == -1) return -3;
3691     break;
3692   default:
3693     return o;
3694   }
3695   return o;
3696 }
3697 
3698 /* Converts an orientation o in the previous scheme used in Plex to the current numbering */
3699 PetscInt DMPolytopeConvertOldOrientation_Internal(DMPolytopeType ct, PetscInt o)
3700 {
3701   switch (ct) {
3702   case DM_POLYTOPE_SEGMENT:
3703     if ((o == -2) || (o == 1)) return -1;
3704     if (o == -1) return 0;
3705     break;
3706   case DM_POLYTOPE_TRIANGLE:
3707     if (o == -3) return -2;
3708     if (o == -2) return -1;
3709     if (o == -1) return -3;
3710     break;
3711   case DM_POLYTOPE_QUADRILATERAL:
3712     if (o == -4) return -2;
3713     if (o == -3) return -1;
3714     if (o == -2) return -4;
3715     if (o == -1) return -3;
3716     break;
3717   default:
3718     return o;
3719   }
3720   return o;
3721 }
3722 
3723 /* Takes in a mesh whose orientations are in the previous scheme and converts them all to the current numbering */
3724 PetscErrorCode DMPlexConvertOldOrientations_Internal(DM dm)
3725 {
3726   PetscInt pStart, pEnd, p;
3727 
3728   PetscFunctionBegin;
3729   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3730   for (p = pStart; p < pEnd; ++p) {
3731     const PetscInt *cone, *ornt;
3732     PetscInt        coneSize, c;
3733 
3734     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
3735     PetscCall(DMPlexGetCone(dm, p, &cone));
3736     PetscCall(DMPlexGetConeOrientation(dm, p, &ornt));
3737     for (c = 0; c < coneSize; ++c) {
3738       DMPolytopeType ct;
3739       const PetscInt o = ornt[c];
3740 
3741       PetscCall(DMPlexGetCellType(dm, cone[c], &ct));
3742       switch (ct) {
3743       case DM_POLYTOPE_SEGMENT:
3744         if ((o == -2) || (o == 1)) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3745         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, 0));
3746         break;
3747       case DM_POLYTOPE_TRIANGLE:
3748         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3749         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3750         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3751         break;
3752       case DM_POLYTOPE_QUADRILATERAL:
3753         if (o == -4) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3754         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3755         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -4));
3756         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3757         break;
3758       default:
3759         break;
3760       }
3761     }
3762   }
3763   PetscFunctionReturn(PETSC_SUCCESS);
3764 }
3765 
3766 static inline PetscErrorCode DMPlexGetTransitiveClosure_Hot_Private(DM dm, PetscInt p, PetscBool useCone, PetscInt *size, const PetscInt *arr[], const PetscInt *ornt[])
3767 {
3768   DM_Plex *mesh = (DM_Plex *)dm->data;
3769 
3770   PetscFunctionBeginHot;
3771   if (PetscDefined(USE_DEBUG) || mesh->tr) {
3772     if (useCone) {
3773       PetscCall(DMPlexGetConeSize(dm, p, size));
3774       PetscCall(DMPlexGetOrientedCone(dm, p, arr, ornt));
3775     } else {
3776       PetscCall(DMPlexGetSupportSize(dm, p, size));
3777       PetscCall(DMPlexGetSupport(dm, p, arr));
3778     }
3779   } else {
3780     if (useCone) {
3781       const PetscSection s   = mesh->coneSection;
3782       const PetscInt     ps  = p - s->pStart;
3783       const PetscInt     off = s->atlasOff[ps];
3784 
3785       *size = s->atlasDof[ps];
3786       *arr  = mesh->cones + off;
3787       *ornt = mesh->coneOrientations + off;
3788     } else {
3789       const PetscSection s   = mesh->supportSection;
3790       const PetscInt     ps  = p - s->pStart;
3791       const PetscInt     off = s->atlasOff[ps];
3792 
3793       *size = s->atlasDof[ps];
3794       *arr  = mesh->supports + off;
3795     }
3796   }
3797   PetscFunctionReturn(PETSC_SUCCESS);
3798 }
3799 
3800 static inline PetscErrorCode DMPlexRestoreTransitiveClosure_Hot_Private(DM dm, PetscInt p, PetscBool useCone, PetscInt *size, const PetscInt *arr[], const PetscInt *ornt[])
3801 {
3802   DM_Plex *mesh = (DM_Plex *)dm->data;
3803 
3804   PetscFunctionBeginHot;
3805   if (PetscDefined(USE_DEBUG) || mesh->tr) {
3806     if (useCone) PetscCall(DMPlexRestoreOrientedCone(dm, p, arr, ornt));
3807   }
3808   PetscFunctionReturn(PETSC_SUCCESS);
3809 }
3810 
3811 static PetscErrorCode DMPlexGetTransitiveClosure_Depth1_Private(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3812 {
3813   DMPolytopeType  ct = DM_POLYTOPE_UNKNOWN;
3814   PetscInt       *closure;
3815   const PetscInt *tmp = NULL, *tmpO = NULL;
3816   PetscInt        off = 0, tmpSize, t;
3817 
3818   PetscFunctionBeginHot;
3819   if (ornt) {
3820     PetscCall(DMPlexGetCellType(dm, p, &ct));
3821     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;
3822   }
3823   if (*points) {
3824     closure = *points;
3825   } else {
3826     PetscInt maxConeSize, maxSupportSize;
3827     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3828     PetscCall(DMGetWorkArray(dm, 2 * (PetscMax(maxConeSize, maxSupportSize) + 1), MPIU_INT, &closure));
3829   }
3830   PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, p, useCone, &tmpSize, &tmp, &tmpO));
3831   if (ct == DM_POLYTOPE_UNKNOWN) {
3832     closure[off++] = p;
3833     closure[off++] = 0;
3834     for (t = 0; t < tmpSize; ++t) {
3835       closure[off++] = tmp[t];
3836       closure[off++] = tmpO ? tmpO[t] : 0;
3837     }
3838   } else {
3839     const PetscInt *arr = DMPolytopeTypeGetArrangment(ct, ornt);
3840 
3841     /* We assume that cells with a valid type have faces with a valid type */
3842     closure[off++] = p;
3843     closure[off++] = ornt;
3844     for (t = 0; t < tmpSize; ++t) {
3845       DMPolytopeType ft;
3846 
3847       PetscCall(DMPlexGetCellType(dm, tmp[t], &ft));
3848       closure[off++] = tmp[arr[t]];
3849       closure[off++] = tmpO ? DMPolytopeTypeComposeOrientation(ft, ornt, tmpO[t]) : 0;
3850     }
3851   }
3852   PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, p, useCone, &tmpSize, &tmp, &tmpO));
3853   if (numPoints) *numPoints = tmpSize + 1;
3854   if (points) *points = closure;
3855   PetscFunctionReturn(PETSC_SUCCESS);
3856 }
3857 
3858 /* We need a special tensor version because we want to allow duplicate points in the endcaps for hybrid cells */
3859 static PetscErrorCode DMPlexTransitiveClosure_Tensor_Internal(DM dm, PetscInt point, DMPolytopeType ct, PetscInt o, PetscBool useCone, PetscInt *numPoints, PetscInt **points)
3860 {
3861   const PetscInt *arr = DMPolytopeTypeGetArrangment(ct, o);
3862   const PetscInt *cone, *ornt;
3863   PetscInt       *pts, *closure = NULL;
3864   DMPolytopeType  ft;
3865   PetscInt        maxConeSize, maxSupportSize, coneSeries, supportSeries, maxSize;
3866   PetscInt        dim, coneSize, c, d, clSize, cl;
3867 
3868   PetscFunctionBeginHot;
3869   PetscCall(DMGetDimension(dm, &dim));
3870   PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, point, PETSC_TRUE, &coneSize, &cone, &ornt));
3871   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3872   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, dim + 1) - 1) / (maxConeSize - 1)) : dim + 1;
3873   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, dim + 1) - 1) / (maxSupportSize - 1)) : dim + 1;
3874   maxSize       = PetscMax(coneSeries, supportSeries);
3875   if (*points) {
3876     pts = *points;
3877   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &pts));
3878   c        = 0;
3879   pts[c++] = point;
3880   pts[c++] = o;
3881   PetscCall(DMPlexGetCellType(dm, cone[arr[0 * 2 + 0]], &ft));
3882   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[0 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[0 * 2 + 1], ornt[0]), useCone, &clSize, &closure));
3883   for (cl = 0; cl < clSize * 2; cl += 2) {
3884     pts[c++] = closure[cl];
3885     pts[c++] = closure[cl + 1];
3886   }
3887   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[1 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[1 * 2 + 1], ornt[1]), useCone, &clSize, &closure));
3888   for (cl = 0; cl < clSize * 2; cl += 2) {
3889     pts[c++] = closure[cl];
3890     pts[c++] = closure[cl + 1];
3891   }
3892   PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[0], useCone, &clSize, &closure));
3893   for (d = 2; d < coneSize; ++d) {
3894     PetscCall(DMPlexGetCellType(dm, cone[arr[d * 2 + 0]], &ft));
3895     pts[c++] = cone[arr[d * 2 + 0]];
3896     pts[c++] = DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]);
3897   }
3898   PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, point, PETSC_TRUE, &coneSize, &cone, &ornt));
3899   if (dim >= 3) {
3900     for (d = 2; d < coneSize; ++d) {
3901       const PetscInt  fpoint = cone[arr[d * 2 + 0]];
3902       const PetscInt *fcone, *fornt;
3903       PetscInt        fconeSize, fc, i;
3904 
3905       PetscCall(DMPlexGetCellType(dm, fpoint, &ft));
3906       const PetscInt *farr = DMPolytopeTypeGetArrangment(ft, DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]));
3907       PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, fpoint, PETSC_TRUE, &fconeSize, &fcone, &fornt));
3908       for (fc = 0; fc < fconeSize; ++fc) {
3909         const PetscInt cp = fcone[farr[fc * 2 + 0]];
3910         const PetscInt co = farr[fc * 2 + 1];
3911 
3912         for (i = 0; i < c; i += 2)
3913           if (pts[i] == cp) break;
3914         if (i == c) {
3915           PetscCall(DMPlexGetCellType(dm, cp, &ft));
3916           pts[c++] = cp;
3917           pts[c++] = DMPolytopeTypeComposeOrientation(ft, co, fornt[farr[fc * 2 + 0]]);
3918         }
3919       }
3920       PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, fpoint, PETSC_TRUE, &fconeSize, &fcone, &fornt));
3921     }
3922   }
3923   *numPoints = c / 2;
3924   *points    = pts;
3925   PetscFunctionReturn(PETSC_SUCCESS);
3926 }
3927 
3928 PetscErrorCode DMPlexGetTransitiveClosure_Internal(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3929 {
3930   DMPolytopeType ct;
3931   PetscInt      *closure, *fifo;
3932   PetscInt       closureSize = 0, fifoStart = 0, fifoSize = 0;
3933   PetscInt       maxConeSize, maxSupportSize, coneSeries, supportSeries;
3934   PetscInt       depth, maxSize;
3935 
3936   PetscFunctionBeginHot;
3937   PetscCall(DMPlexGetDepth(dm, &depth));
3938   if (depth == 1) {
3939     PetscCall(DMPlexGetTransitiveClosure_Depth1_Private(dm, p, ornt, useCone, numPoints, points));
3940     PetscFunctionReturn(PETSC_SUCCESS);
3941   }
3942   PetscCall(DMPlexGetCellType(dm, p, &ct));
3943   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;
3944   if (DMPolytopeTypeIsHybrid(ct) && ct != DM_POLYTOPE_POINT_PRISM_TENSOR) {
3945     PetscCall(DMPlexTransitiveClosure_Tensor_Internal(dm, p, ct, ornt, useCone, numPoints, points));
3946     PetscFunctionReturn(PETSC_SUCCESS);
3947   }
3948   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3949   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, depth + 1) - 1) / (maxConeSize - 1)) : depth + 1;
3950   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, depth + 1) - 1) / (maxSupportSize - 1)) : depth + 1;
3951   maxSize       = PetscMax(coneSeries, supportSeries);
3952   PetscCall(DMGetWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
3953   if (*points) {
3954     closure = *points;
3955   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &closure));
3956   closure[closureSize++] = p;
3957   closure[closureSize++] = ornt;
3958   fifo[fifoSize++]       = p;
3959   fifo[fifoSize++]       = ornt;
3960   fifo[fifoSize++]       = ct;
3961   /* Should kick out early when depth is reached, rather than checking all vertices for empty cones */
3962   while (fifoSize - fifoStart) {
3963     const PetscInt       q    = fifo[fifoStart++];
3964     const PetscInt       o    = fifo[fifoStart++];
3965     const DMPolytopeType qt   = (DMPolytopeType)fifo[fifoStart++];
3966     const PetscInt      *qarr = DMPolytopeTypeGetArrangment(qt, o);
3967     const PetscInt      *tmp, *tmpO = NULL;
3968     PetscInt             tmpSize, t;
3969 
3970     if (PetscDefined(USE_DEBUG)) {
3971       PetscInt nO = DMPolytopeTypeGetNumArrangments(qt) / 2;
3972       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);
3973     }
3974     PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, q, useCone, &tmpSize, &tmp, &tmpO));
3975     for (t = 0; t < tmpSize; ++t) {
3976       const PetscInt ip = useCone && qarr ? qarr[t * 2] : t;
3977       const PetscInt io = useCone && qarr ? qarr[t * 2 + 1] : 0;
3978       const PetscInt cp = tmp[ip];
3979       PetscCall(DMPlexGetCellType(dm, cp, &ct));
3980       const PetscInt co = tmpO ? DMPolytopeTypeComposeOrientation(ct, io, tmpO[ip]) : 0;
3981       PetscInt       c;
3982 
3983       /* Check for duplicate */
3984       for (c = 0; c < closureSize; c += 2) {
3985         if (closure[c] == cp) break;
3986       }
3987       if (c == closureSize) {
3988         closure[closureSize++] = cp;
3989         closure[closureSize++] = co;
3990         fifo[fifoSize++]       = cp;
3991         fifo[fifoSize++]       = co;
3992         fifo[fifoSize++]       = ct;
3993       }
3994     }
3995     PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, q, useCone, &tmpSize, &tmp, &tmpO));
3996   }
3997   PetscCall(DMRestoreWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
3998   if (numPoints) *numPoints = closureSize / 2;
3999   if (points) *points = closure;
4000   PetscFunctionReturn(PETSC_SUCCESS);
4001 }
4002 
4003 /*@C
4004   DMPlexGetTransitiveClosure - Return the points on the transitive closure of the in-edges or out-edges for this point in the DAG
4005 
4006   Not Collective
4007 
4008   Input Parameters:
4009 + dm      - The `DMPLEX`
4010 . p       - The mesh point
4011 - useCone - `PETSC_TRUE` for the closure, otherwise return the star
4012 
4013   Input/Output Parameter:
4014 . points - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...];
4015            if `NULL` on input, internal storage will be returned, otherwise the provided array is used
4016 
4017   Output Parameter:
4018 . numPoints - The number of points in the closure, so points[] is of size 2*`numPoints`
4019 
4020   Level: beginner
4021 
4022   Note:
4023   If using internal storage (points is `NULL` on input), each call overwrites the last output.
4024 
4025   Fortran Notes:
4026   The `numPoints` argument is not present in the Fortran binding since it is internal to the array.
4027 
4028 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
4029 @*/
4030 PetscErrorCode DMPlexGetTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
4031 {
4032   PetscFunctionBeginHot;
4033   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4034   if (numPoints) PetscAssertPointer(numPoints, 4);
4035   if (points) PetscAssertPointer(points, 5);
4036   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, p, 0, useCone, numPoints, points));
4037   PetscFunctionReturn(PETSC_SUCCESS);
4038 }
4039 
4040 /*@C
4041   DMPlexRestoreTransitiveClosure - Restore the array of points on the transitive closure of the in-edges or out-edges for this point in the DAG
4042 
4043   Not Collective
4044 
4045   Input Parameters:
4046 + dm        - The `DMPLEX`
4047 . p         - The mesh point
4048 . useCone   - `PETSC_TRUE` for the closure, otherwise return the star
4049 . numPoints - The number of points in the closure, so points[] is of size 2*`numPoints`
4050 - points    - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...]
4051 
4052   Level: beginner
4053 
4054   Note:
4055   If not using internal storage (points is not `NULL` on input), this call is unnecessary
4056 
4057 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
4058 @*/
4059 PetscErrorCode DMPlexRestoreTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
4060 {
4061   PetscFunctionBeginHot;
4062   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4063   if (numPoints) *numPoints = 0;
4064   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, points));
4065   PetscFunctionReturn(PETSC_SUCCESS);
4066 }
4067 
4068 /*@
4069   DMPlexGetMaxSizes - Return the maximum number of in-edges (cone) and out-edges (support) for any point in the DAG
4070 
4071   Not Collective
4072 
4073   Input Parameter:
4074 . dm - The `DMPLEX`
4075 
4076   Output Parameters:
4077 + maxConeSize    - The maximum number of in-edges
4078 - maxSupportSize - The maximum number of out-edges
4079 
4080   Level: beginner
4081 
4082 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
4083 @*/
4084 PetscErrorCode DMPlexGetMaxSizes(DM dm, PetscInt *maxConeSize, PetscInt *maxSupportSize)
4085 {
4086   DM_Plex *mesh = (DM_Plex *)dm->data;
4087 
4088   PetscFunctionBegin;
4089   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4090   if (maxConeSize) PetscCall(PetscSectionGetMaxDof(mesh->coneSection, maxConeSize));
4091   if (maxSupportSize) PetscCall(PetscSectionGetMaxDof(mesh->supportSection, maxSupportSize));
4092   PetscFunctionReturn(PETSC_SUCCESS);
4093 }
4094 
4095 PetscErrorCode DMSetUp_Plex(DM dm)
4096 {
4097   DM_Plex *mesh = (DM_Plex *)dm->data;
4098   PetscInt size, maxSupportSize;
4099 
4100   PetscFunctionBegin;
4101   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4102   PetscCall(PetscSectionSetUp(mesh->coneSection));
4103   PetscCall(PetscSectionGetStorageSize(mesh->coneSection, &size));
4104   PetscCall(PetscMalloc1(size, &mesh->cones));
4105   PetscCall(PetscCalloc1(size, &mesh->coneOrientations));
4106   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
4107   if (maxSupportSize) {
4108     PetscCall(PetscSectionSetUp(mesh->supportSection));
4109     PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &size));
4110     PetscCall(PetscMalloc1(size, &mesh->supports));
4111   }
4112   PetscFunctionReturn(PETSC_SUCCESS);
4113 }
4114 
4115 PetscErrorCode DMCreateSubDM_Plex(DM dm, PetscInt numFields, const PetscInt fields[], IS *is, DM *subdm)
4116 {
4117   PetscFunctionBegin;
4118   if (subdm) PetscCall(DMClone(dm, subdm));
4119   PetscCall(DMCreateSectionSubDM(dm, numFields, fields, is, subdm));
4120   if (subdm) (*subdm)->useNatural = dm->useNatural;
4121   if (dm->useNatural && dm->sfMigration) {
4122     PetscSF sfNatural;
4123 
4124     (*subdm)->sfMigration = dm->sfMigration;
4125     PetscCall(PetscObjectReference((PetscObject)dm->sfMigration));
4126     PetscCall(DMPlexCreateGlobalToNaturalSF(*subdm, NULL, (*subdm)->sfMigration, &sfNatural));
4127     (*subdm)->sfNatural = sfNatural;
4128   }
4129   PetscFunctionReturn(PETSC_SUCCESS);
4130 }
4131 
4132 PetscErrorCode DMCreateSuperDM_Plex(DM dms[], PetscInt len, IS **is, DM *superdm)
4133 {
4134   PetscInt i = 0;
4135 
4136   PetscFunctionBegin;
4137   PetscCall(DMClone(dms[0], superdm));
4138   PetscCall(DMCreateSectionSuperDM(dms, len, is, superdm));
4139   (*superdm)->useNatural = PETSC_FALSE;
4140   for (i = 0; i < len; i++) {
4141     if (dms[i]->useNatural && dms[i]->sfMigration) {
4142       PetscSF sfNatural;
4143 
4144       (*superdm)->sfMigration = dms[i]->sfMigration;
4145       PetscCall(PetscObjectReference((PetscObject)dms[i]->sfMigration));
4146       (*superdm)->useNatural = PETSC_TRUE;
4147       PetscCall(DMPlexCreateGlobalToNaturalSF(*superdm, NULL, (*superdm)->sfMigration, &sfNatural));
4148       (*superdm)->sfNatural = sfNatural;
4149       break;
4150     }
4151   }
4152   PetscFunctionReturn(PETSC_SUCCESS);
4153 }
4154 
4155 /*@
4156   DMPlexSymmetrize - Create support (out-edge) information from cone (in-edge) information
4157 
4158   Not Collective
4159 
4160   Input Parameter:
4161 . dm - The `DMPLEX`
4162 
4163   Level: beginner
4164 
4165   Note:
4166   This should be called after all calls to `DMPlexSetCone()`
4167 
4168 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMPlexSetCone()`
4169 @*/
4170 PetscErrorCode DMPlexSymmetrize(DM dm)
4171 {
4172   DM_Plex  *mesh = (DM_Plex *)dm->data;
4173   PetscInt *offsets;
4174   PetscInt  supportSize;
4175   PetscInt  pStart, pEnd, p;
4176 
4177   PetscFunctionBegin;
4178   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4179   PetscCheck(!mesh->supports, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONGSTATE, "Supports were already setup in this DMPlex");
4180   PetscCall(PetscLogEventBegin(DMPLEX_Symmetrize, dm, 0, 0, 0));
4181   /* Calculate support sizes */
4182   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4183   for (p = pStart; p < pEnd; ++p) {
4184     PetscInt dof, off, c;
4185 
4186     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
4187     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
4188     for (c = off; c < off + dof; ++c) PetscCall(PetscSectionAddDof(mesh->supportSection, mesh->cones[c], 1));
4189   }
4190   PetscCall(PetscSectionSetUp(mesh->supportSection));
4191   /* Calculate supports */
4192   PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &supportSize));
4193   PetscCall(PetscMalloc1(supportSize, &mesh->supports));
4194   PetscCall(PetscCalloc1(pEnd - pStart, &offsets));
4195   for (p = pStart; p < pEnd; ++p) {
4196     PetscInt dof, off, c;
4197 
4198     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
4199     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
4200     for (c = off; c < off + dof; ++c) {
4201       const PetscInt q = mesh->cones[c];
4202       PetscInt       offS;
4203 
4204       PetscCall(PetscSectionGetOffset(mesh->supportSection, q, &offS));
4205 
4206       mesh->supports[offS + offsets[q]] = p;
4207       ++offsets[q];
4208     }
4209   }
4210   PetscCall(PetscFree(offsets));
4211   PetscCall(PetscLogEventEnd(DMPLEX_Symmetrize, dm, 0, 0, 0));
4212   PetscFunctionReturn(PETSC_SUCCESS);
4213 }
4214 
4215 static PetscErrorCode DMPlexCreateDepthStratum(DM dm, DMLabel label, PetscInt depth, PetscInt pStart, PetscInt pEnd)
4216 {
4217   IS stratumIS;
4218 
4219   PetscFunctionBegin;
4220   if (pStart >= pEnd) PetscFunctionReturn(PETSC_SUCCESS);
4221   if (PetscDefined(USE_DEBUG)) {
4222     PetscInt  qStart, qEnd, numLevels, level;
4223     PetscBool overlap = PETSC_FALSE;
4224     PetscCall(DMLabelGetNumValues(label, &numLevels));
4225     for (level = 0; level < numLevels; level++) {
4226       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4227       if ((pStart >= qStart && pStart < qEnd) || (pEnd > qStart && pEnd <= qEnd)) {
4228         overlap = PETSC_TRUE;
4229         break;
4230       }
4231     }
4232     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);
4233   }
4234   PetscCall(ISCreateStride(PETSC_COMM_SELF, pEnd - pStart, pStart, 1, &stratumIS));
4235   PetscCall(DMLabelSetStratumIS(label, depth, stratumIS));
4236   PetscCall(ISDestroy(&stratumIS));
4237   PetscFunctionReturn(PETSC_SUCCESS);
4238 }
4239 
4240 static PetscErrorCode DMPlexStratify_CellType_Private(DM dm, DMLabel label)
4241 {
4242   PetscInt *pMin, *pMax;
4243   PetscInt  pStart, pEnd;
4244   PetscInt  dmin = PETSC_MAX_INT, dmax = PETSC_MIN_INT;
4245 
4246   PetscFunctionBegin;
4247   {
4248     DMLabel label2;
4249 
4250     PetscCall(DMPlexGetCellTypeLabel(dm, &label2));
4251     PetscCall(PetscObjectViewFromOptions((PetscObject)label2, NULL, "-ct_view"));
4252   }
4253   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4254   for (PetscInt p = pStart; p < pEnd; ++p) {
4255     DMPolytopeType ct;
4256 
4257     PetscCall(DMPlexGetCellType(dm, p, &ct));
4258     dmin = PetscMin(DMPolytopeTypeGetDim(ct), dmin);
4259     dmax = PetscMax(DMPolytopeTypeGetDim(ct), dmax);
4260   }
4261   PetscCall(PetscMalloc2(dmax + 1, &pMin, dmax + 1, &pMax));
4262   for (PetscInt d = dmin; d <= dmax; ++d) {
4263     pMin[d] = PETSC_MAX_INT;
4264     pMax[d] = PETSC_MIN_INT;
4265   }
4266   for (PetscInt p = pStart; p < pEnd; ++p) {
4267     DMPolytopeType ct;
4268     PetscInt       d;
4269 
4270     PetscCall(DMPlexGetCellType(dm, p, &ct));
4271     d       = DMPolytopeTypeGetDim(ct);
4272     pMin[d] = PetscMin(p, pMin[d]);
4273     pMax[d] = PetscMax(p, pMax[d]);
4274   }
4275   for (PetscInt d = dmin; d <= dmax; ++d) {
4276     if (pMin[d] > pMax[d]) continue;
4277     PetscCall(DMPlexCreateDepthStratum(dm, label, d, pMin[d], pMax[d] + 1));
4278   }
4279   PetscCall(PetscFree2(pMin, pMax));
4280   PetscFunctionReturn(PETSC_SUCCESS);
4281 }
4282 
4283 static PetscErrorCode DMPlexStratify_Topological_Private(DM dm, DMLabel label)
4284 {
4285   PetscInt pStart, pEnd;
4286   PetscInt numRoots = 0, numLeaves = 0;
4287 
4288   PetscFunctionBegin;
4289   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4290   {
4291     /* Initialize roots and count leaves */
4292     PetscInt sMin = PETSC_MAX_INT;
4293     PetscInt sMax = PETSC_MIN_INT;
4294     PetscInt coneSize, supportSize;
4295 
4296     for (PetscInt p = pStart; p < pEnd; ++p) {
4297       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4298       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
4299       if (!coneSize && supportSize) {
4300         sMin = PetscMin(p, sMin);
4301         sMax = PetscMax(p, sMax);
4302         ++numRoots;
4303       } else if (!supportSize && coneSize) {
4304         ++numLeaves;
4305       } else if (!supportSize && !coneSize) {
4306         /* Isolated points */
4307         sMin = PetscMin(p, sMin);
4308         sMax = PetscMax(p, sMax);
4309       }
4310     }
4311     PetscCall(DMPlexCreateDepthStratum(dm, label, 0, sMin, sMax + 1));
4312   }
4313 
4314   if (numRoots + numLeaves == (pEnd - pStart)) {
4315     PetscInt sMin = PETSC_MAX_INT;
4316     PetscInt sMax = PETSC_MIN_INT;
4317     PetscInt coneSize, supportSize;
4318 
4319     for (PetscInt p = pStart; p < pEnd; ++p) {
4320       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4321       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
4322       if (!supportSize && coneSize) {
4323         sMin = PetscMin(p, sMin);
4324         sMax = PetscMax(p, sMax);
4325       }
4326     }
4327     PetscCall(DMPlexCreateDepthStratum(dm, label, 1, sMin, sMax + 1));
4328   } else {
4329     PetscInt level = 0;
4330     PetscInt qStart, qEnd;
4331 
4332     PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4333     while (qEnd > qStart) {
4334       PetscInt sMin = PETSC_MAX_INT;
4335       PetscInt sMax = PETSC_MIN_INT;
4336 
4337       for (PetscInt q = qStart; q < qEnd; ++q) {
4338         const PetscInt *support;
4339         PetscInt        supportSize;
4340 
4341         PetscCall(DMPlexGetSupportSize(dm, q, &supportSize));
4342         PetscCall(DMPlexGetSupport(dm, q, &support));
4343         for (PetscInt s = 0; s < supportSize; ++s) {
4344           sMin = PetscMin(support[s], sMin);
4345           sMax = PetscMax(support[s], sMax);
4346         }
4347       }
4348       PetscCall(DMLabelGetNumValues(label, &level));
4349       PetscCall(DMPlexCreateDepthStratum(dm, label, level, sMin, sMax + 1));
4350       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4351     }
4352   }
4353   PetscFunctionReturn(PETSC_SUCCESS);
4354 }
4355 
4356 /*@
4357   DMPlexStratify - Computes the strata for all points in the `DMPLEX`
4358 
4359   Collective
4360 
4361   Input Parameter:
4362 . dm - The `DMPLEX`
4363 
4364   Level: beginner
4365 
4366   Notes:
4367   The strata group all points of the same grade, and this function calculates the strata. This
4368   grade can be seen as the height (or depth) of the point in the DAG.
4369 
4370   The DAG for most topologies is a graded poset (https://en.wikipedia.org/wiki/Graded_poset), and
4371   can be illustrated by a Hasse Diagram (https://en.wikipedia.org/wiki/Hasse_diagram).
4372   Concretely, `DMPlexStratify()` creates a new label named "depth" containing the depth in the DAG of each point. For cell-vertex
4373   meshes, vertices are depth 0 and cells are depth 1. For fully interpolated meshes, depth 0 for vertices, 1 for edges, and so on
4374   until cells have depth equal to the dimension of the mesh. The depth label can be accessed through `DMPlexGetDepthLabel()` or `DMPlexGetDepthStratum()`, or
4375   manually via `DMGetLabel()`.  The height is defined implicitly by height = maxDimension - depth, and can be accessed
4376   via `DMPlexGetHeightStratum()`.  For example, cells have height 0 and faces have height 1.
4377 
4378   The depth of a point is calculated by executing a breadth-first search (BFS) on the DAG. This could produce surprising results
4379   if run on a partially interpolated mesh, meaning one that had some edges and faces, but not others. For example, suppose that
4380   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
4381   to interpolate only that one (e0), so that
4382 .vb
4383   cone(c0) = {e0, v2}
4384   cone(e0) = {v0, v1}
4385 .ve
4386   If `DMPlexStratify()` is run on this mesh, it will give depths
4387 .vb
4388    depth 0 = {v0, v1, v2}
4389    depth 1 = {e0, c0}
4390 .ve
4391   where the triangle has been given depth 1, instead of 2, because it is reachable from vertex v2.
4392 
4393   `DMPlexStratify()` should be called after all calls to `DMPlexSymmetrize()`
4394 
4395 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexComputeCellTypes()`
4396 @*/
4397 PetscErrorCode DMPlexStratify(DM dm)
4398 {
4399   DM_Plex  *mesh = (DM_Plex *)dm->data;
4400   DMLabel   label;
4401   PetscBool flg = PETSC_FALSE;
4402 
4403   PetscFunctionBegin;
4404   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4405   PetscCall(PetscLogEventBegin(DMPLEX_Stratify, dm, 0, 0, 0));
4406 
4407   // Create depth label
4408   PetscCall(DMCreateLabel(dm, "depth"));
4409   PetscCall(DMPlexGetDepthLabel(dm, &label));
4410 
4411   PetscCall(PetscOptionsGetBool(NULL, dm->hdr.prefix, "-dm_plex_stratify_celltype", &flg, NULL));
4412   if (flg) PetscCall(DMPlexStratify_CellType_Private(dm, label));
4413   else PetscCall(DMPlexStratify_Topological_Private(dm, label));
4414 
4415   { /* just in case there is an empty process */
4416     PetscInt numValues, maxValues = 0, v;
4417 
4418     PetscCall(DMLabelGetNumValues(label, &numValues));
4419     PetscCall(MPIU_Allreduce(&numValues, &maxValues, 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
4420     for (v = numValues; v < maxValues; v++) PetscCall(DMLabelAddStratum(label, v));
4421   }
4422   PetscCall(PetscObjectStateGet((PetscObject)label, &mesh->depthState));
4423   PetscCall(PetscLogEventEnd(DMPLEX_Stratify, dm, 0, 0, 0));
4424   PetscFunctionReturn(PETSC_SUCCESS);
4425 }
4426 
4427 PetscErrorCode DMPlexComputeCellType_Internal(DM dm, PetscInt p, PetscInt pdepth, DMPolytopeType *pt)
4428 {
4429   DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4430   PetscInt       dim, depth, pheight, coneSize;
4431 
4432   PetscFunctionBeginHot;
4433   PetscCall(DMGetDimension(dm, &dim));
4434   PetscCall(DMPlexGetDepth(dm, &depth));
4435   PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4436   pheight = depth - pdepth;
4437   if (depth <= 1) {
4438     switch (pdepth) {
4439     case 0:
4440       ct = DM_POLYTOPE_POINT;
4441       break;
4442     case 1:
4443       switch (coneSize) {
4444       case 2:
4445         ct = DM_POLYTOPE_SEGMENT;
4446         break;
4447       case 3:
4448         ct = DM_POLYTOPE_TRIANGLE;
4449         break;
4450       case 4:
4451         switch (dim) {
4452         case 2:
4453           ct = DM_POLYTOPE_QUADRILATERAL;
4454           break;
4455         case 3:
4456           ct = DM_POLYTOPE_TETRAHEDRON;
4457           break;
4458         default:
4459           break;
4460         }
4461         break;
4462       case 5:
4463         ct = DM_POLYTOPE_PYRAMID;
4464         break;
4465       case 6:
4466         ct = DM_POLYTOPE_TRI_PRISM_TENSOR;
4467         break;
4468       case 8:
4469         ct = DM_POLYTOPE_HEXAHEDRON;
4470         break;
4471       default:
4472         break;
4473       }
4474     }
4475   } else {
4476     if (pdepth == 0) {
4477       ct = DM_POLYTOPE_POINT;
4478     } else if (pheight == 0) {
4479       switch (dim) {
4480       case 1:
4481         switch (coneSize) {
4482         case 2:
4483           ct = DM_POLYTOPE_SEGMENT;
4484           break;
4485         default:
4486           break;
4487         }
4488         break;
4489       case 2:
4490         switch (coneSize) {
4491         case 3:
4492           ct = DM_POLYTOPE_TRIANGLE;
4493           break;
4494         case 4:
4495           ct = DM_POLYTOPE_QUADRILATERAL;
4496           break;
4497         default:
4498           break;
4499         }
4500         break;
4501       case 3:
4502         switch (coneSize) {
4503         case 4:
4504           ct = DM_POLYTOPE_TETRAHEDRON;
4505           break;
4506         case 5: {
4507           const PetscInt *cone;
4508           PetscInt        faceConeSize;
4509 
4510           PetscCall(DMPlexGetCone(dm, p, &cone));
4511           PetscCall(DMPlexGetConeSize(dm, cone[0], &faceConeSize));
4512           switch (faceConeSize) {
4513           case 3:
4514             ct = DM_POLYTOPE_TRI_PRISM_TENSOR;
4515             break;
4516           case 4:
4517             ct = DM_POLYTOPE_PYRAMID;
4518             break;
4519           }
4520         } break;
4521         case 6:
4522           ct = DM_POLYTOPE_HEXAHEDRON;
4523           break;
4524         default:
4525           break;
4526         }
4527         break;
4528       default:
4529         break;
4530       }
4531     } else if (pheight > 0) {
4532       switch (coneSize) {
4533       case 2:
4534         ct = DM_POLYTOPE_SEGMENT;
4535         break;
4536       case 3:
4537         ct = DM_POLYTOPE_TRIANGLE;
4538         break;
4539       case 4:
4540         ct = DM_POLYTOPE_QUADRILATERAL;
4541         break;
4542       default:
4543         break;
4544       }
4545     }
4546   }
4547   *pt = ct;
4548   PetscFunctionReturn(PETSC_SUCCESS);
4549 }
4550 
4551 /*@
4552   DMPlexComputeCellTypes - Infer the polytope type of every cell using its dimension and cone size.
4553 
4554   Collective
4555 
4556   Input Parameter:
4557 . dm - The `DMPLEX`
4558 
4559   Level: developer
4560 
4561   Note:
4562   This function is normally called automatically when a cell type is requested. It creates an
4563   internal `DMLabel` named "celltype" which can be directly accessed using `DMGetLabel()`. A user may disable
4564   automatic creation by creating the label manually, using `DMCreateLabel`(dm, "celltype").
4565 
4566   `DMPlexComputeCellTypes()` should be called after all calls to `DMPlexSymmetrize()` and `DMPlexStratify()`
4567 
4568 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexStratify()`, `DMGetLabel()`, `DMCreateLabel()`
4569 @*/
4570 PetscErrorCode DMPlexComputeCellTypes(DM dm)
4571 {
4572   DM_Plex *mesh;
4573   DMLabel  ctLabel;
4574   PetscInt pStart, pEnd, p;
4575 
4576   PetscFunctionBegin;
4577   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4578   mesh = (DM_Plex *)dm->data;
4579   PetscCall(DMCreateLabel(dm, "celltype"));
4580   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
4581   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4582   PetscCall(PetscFree(mesh->cellTypes));
4583   PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
4584   for (p = pStart; p < pEnd; ++p) {
4585     DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4586     PetscInt       pdepth;
4587 
4588     PetscCall(DMPlexGetPointDepth(dm, p, &pdepth));
4589     PetscCall(DMPlexComputeCellType_Internal(dm, p, pdepth, &ct));
4590     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);
4591     PetscCall(DMLabelSetValue(ctLabel, p, ct));
4592     mesh->cellTypes[p - pStart].value_as_uint8 = ct;
4593   }
4594   PetscCall(PetscObjectStateGet((PetscObject)ctLabel, &mesh->celltypeState));
4595   PetscCall(PetscObjectViewFromOptions((PetscObject)ctLabel, NULL, "-dm_plex_celltypes_view"));
4596   PetscFunctionReturn(PETSC_SUCCESS);
4597 }
4598 
4599 /*@C
4600   DMPlexGetJoin - Get an array for the join of the set of points
4601 
4602   Not Collective
4603 
4604   Input Parameters:
4605 + dm        - The `DMPLEX` object
4606 . numPoints - The number of input points for the join
4607 - points    - The input points
4608 
4609   Output Parameters:
4610 + numCoveredPoints - The number of points in the join
4611 - coveredPoints    - The points in the join
4612 
4613   Level: intermediate
4614 
4615   Note:
4616   Currently, this is restricted to a single level join
4617 
4618   Fortran Notes:
4619   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4620 
4621 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4622 @*/
4623 PetscErrorCode DMPlexGetJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4624 {
4625   DM_Plex  *mesh = (DM_Plex *)dm->data;
4626   PetscInt *join[2];
4627   PetscInt  joinSize, i = 0;
4628   PetscInt  dof, off, p, c, m;
4629   PetscInt  maxSupportSize;
4630 
4631   PetscFunctionBegin;
4632   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4633   PetscAssertPointer(points, 3);
4634   PetscAssertPointer(numCoveredPoints, 4);
4635   PetscAssertPointer(coveredPoints, 5);
4636   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
4637   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[0]));
4638   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[1]));
4639   /* Copy in support of first point */
4640   PetscCall(PetscSectionGetDof(mesh->supportSection, points[0], &dof));
4641   PetscCall(PetscSectionGetOffset(mesh->supportSection, points[0], &off));
4642   for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = mesh->supports[off + joinSize];
4643   /* Check each successive support */
4644   for (p = 1; p < numPoints; ++p) {
4645     PetscInt newJoinSize = 0;
4646 
4647     PetscCall(PetscSectionGetDof(mesh->supportSection, points[p], &dof));
4648     PetscCall(PetscSectionGetOffset(mesh->supportSection, points[p], &off));
4649     for (c = 0; c < dof; ++c) {
4650       const PetscInt point = mesh->supports[off + c];
4651 
4652       for (m = 0; m < joinSize; ++m) {
4653         if (point == join[i][m]) {
4654           join[1 - i][newJoinSize++] = point;
4655           break;
4656         }
4657       }
4658     }
4659     joinSize = newJoinSize;
4660     i        = 1 - i;
4661   }
4662   *numCoveredPoints = joinSize;
4663   *coveredPoints    = join[i];
4664   PetscCall(DMRestoreWorkArray(dm, maxSupportSize, MPIU_INT, &join[1 - i]));
4665   PetscFunctionReturn(PETSC_SUCCESS);
4666 }
4667 
4668 /*@C
4669   DMPlexRestoreJoin - Restore an array for the join of the set of points
4670 
4671   Not Collective
4672 
4673   Input Parameters:
4674 + dm        - The `DMPLEX` object
4675 . numPoints - The number of input points for the join
4676 - points    - The input points
4677 
4678   Output Parameters:
4679 + numCoveredPoints - The number of points in the join
4680 - coveredPoints    - The points in the join
4681 
4682   Level: intermediate
4683 
4684   Fortran Notes:
4685   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4686 
4687 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetJoin()`, `DMPlexGetFullJoin()`, `DMPlexGetMeet()`
4688 @*/
4689 PetscErrorCode DMPlexRestoreJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4690 {
4691   PetscFunctionBegin;
4692   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4693   if (points) PetscAssertPointer(points, 3);
4694   if (numCoveredPoints) PetscAssertPointer(numCoveredPoints, 4);
4695   PetscAssertPointer(coveredPoints, 5);
4696   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
4697   if (numCoveredPoints) *numCoveredPoints = 0;
4698   PetscFunctionReturn(PETSC_SUCCESS);
4699 }
4700 
4701 /*@C
4702   DMPlexGetFullJoin - Get an array for the join of the set of points
4703 
4704   Not Collective
4705 
4706   Input Parameters:
4707 + dm        - The `DMPLEX` object
4708 . numPoints - The number of input points for the join
4709 - points    - The input points
4710 
4711   Output Parameters:
4712 + numCoveredPoints - The number of points in the join
4713 - coveredPoints    - The points in the join
4714 
4715   Level: intermediate
4716 
4717   Fortran Notes:
4718   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4719 
4720 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetJoin()`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4721 @*/
4722 PetscErrorCode DMPlexGetFullJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4723 {
4724   PetscInt *offsets, **closures;
4725   PetscInt *join[2];
4726   PetscInt  depth = 0, maxSize, joinSize = 0, i = 0;
4727   PetscInt  p, d, c, m, ms;
4728 
4729   PetscFunctionBegin;
4730   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4731   PetscAssertPointer(points, 3);
4732   PetscAssertPointer(numCoveredPoints, 4);
4733   PetscAssertPointer(coveredPoints, 5);
4734 
4735   PetscCall(DMPlexGetDepth(dm, &depth));
4736   PetscCall(PetscCalloc1(numPoints, &closures));
4737   PetscCall(DMGetWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
4738   PetscCall(DMPlexGetMaxSizes(dm, NULL, &ms));
4739   maxSize = (ms > 1) ? ((PetscPowInt(ms, depth + 1) - 1) / (ms - 1)) : depth + 1;
4740   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[0]));
4741   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[1]));
4742 
4743   for (p = 0; p < numPoints; ++p) {
4744     PetscInt closureSize;
4745 
4746     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_FALSE, &closureSize, &closures[p]));
4747 
4748     offsets[p * (depth + 2) + 0] = 0;
4749     for (d = 0; d < depth + 1; ++d) {
4750       PetscInt pStart, pEnd, i;
4751 
4752       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
4753       for (i = offsets[p * (depth + 2) + d]; i < closureSize; ++i) {
4754         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
4755           offsets[p * (depth + 2) + d + 1] = i;
4756           break;
4757         }
4758       }
4759       if (i == closureSize) offsets[p * (depth + 2) + d + 1] = i;
4760     }
4761     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);
4762   }
4763   for (d = 0; d < depth + 1; ++d) {
4764     PetscInt dof;
4765 
4766     /* Copy in support of first point */
4767     dof = offsets[d + 1] - offsets[d];
4768     for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = closures[0][(offsets[d] + joinSize) * 2];
4769     /* Check each successive cone */
4770     for (p = 1; p < numPoints && joinSize; ++p) {
4771       PetscInt newJoinSize = 0;
4772 
4773       dof = offsets[p * (depth + 2) + d + 1] - offsets[p * (depth + 2) + d];
4774       for (c = 0; c < dof; ++c) {
4775         const PetscInt point = closures[p][(offsets[p * (depth + 2) + d] + c) * 2];
4776 
4777         for (m = 0; m < joinSize; ++m) {
4778           if (point == join[i][m]) {
4779             join[1 - i][newJoinSize++] = point;
4780             break;
4781           }
4782         }
4783       }
4784       joinSize = newJoinSize;
4785       i        = 1 - i;
4786     }
4787     if (joinSize) break;
4788   }
4789   *numCoveredPoints = joinSize;
4790   *coveredPoints    = join[i];
4791   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_FALSE, NULL, &closures[p]));
4792   PetscCall(PetscFree(closures));
4793   PetscCall(DMRestoreWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
4794   PetscCall(DMRestoreWorkArray(dm, ms, MPIU_INT, &join[1 - i]));
4795   PetscFunctionReturn(PETSC_SUCCESS);
4796 }
4797 
4798 /*@C
4799   DMPlexGetMeet - Get an array for the meet of the set of points
4800 
4801   Not Collective
4802 
4803   Input Parameters:
4804 + dm        - The `DMPLEX` object
4805 . numPoints - The number of input points for the meet
4806 - points    - The input points
4807 
4808   Output Parameters:
4809 + numCoveringPoints - The number of points in the meet
4810 - coveringPoints    - The points in the meet
4811 
4812   Level: intermediate
4813 
4814   Note:
4815   Currently, this is restricted to a single level meet
4816 
4817   Fortran Notes:
4818   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4819 
4820 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
4821 @*/
4822 PetscErrorCode DMPlexGetMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveringPoints, const PetscInt **coveringPoints)
4823 {
4824   DM_Plex  *mesh = (DM_Plex *)dm->data;
4825   PetscInt *meet[2];
4826   PetscInt  meetSize, i = 0;
4827   PetscInt  dof, off, p, c, m;
4828   PetscInt  maxConeSize;
4829 
4830   PetscFunctionBegin;
4831   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4832   PetscAssertPointer(points, 3);
4833   PetscAssertPointer(numCoveringPoints, 4);
4834   PetscAssertPointer(coveringPoints, 5);
4835   PetscCall(PetscSectionGetMaxDof(mesh->coneSection, &maxConeSize));
4836   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[0]));
4837   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[1]));
4838   /* Copy in cone of first point */
4839   PetscCall(PetscSectionGetDof(mesh->coneSection, points[0], &dof));
4840   PetscCall(PetscSectionGetOffset(mesh->coneSection, points[0], &off));
4841   for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = mesh->cones[off + meetSize];
4842   /* Check each successive cone */
4843   for (p = 1; p < numPoints; ++p) {
4844     PetscInt newMeetSize = 0;
4845 
4846     PetscCall(PetscSectionGetDof(mesh->coneSection, points[p], &dof));
4847     PetscCall(PetscSectionGetOffset(mesh->coneSection, points[p], &off));
4848     for (c = 0; c < dof; ++c) {
4849       const PetscInt point = mesh->cones[off + c];
4850 
4851       for (m = 0; m < meetSize; ++m) {
4852         if (point == meet[i][m]) {
4853           meet[1 - i][newMeetSize++] = point;
4854           break;
4855         }
4856       }
4857     }
4858     meetSize = newMeetSize;
4859     i        = 1 - i;
4860   }
4861   *numCoveringPoints = meetSize;
4862   *coveringPoints    = meet[i];
4863   PetscCall(DMRestoreWorkArray(dm, maxConeSize, MPIU_INT, &meet[1 - i]));
4864   PetscFunctionReturn(PETSC_SUCCESS);
4865 }
4866 
4867 /*@C
4868   DMPlexRestoreMeet - Restore an array for the meet of the set of points
4869 
4870   Not Collective
4871 
4872   Input Parameters:
4873 + dm        - The `DMPLEX` object
4874 . numPoints - The number of input points for the meet
4875 - points    - The input points
4876 
4877   Output Parameters:
4878 + numCoveredPoints - The number of points in the meet
4879 - coveredPoints    - The points in the meet
4880 
4881   Level: intermediate
4882 
4883   Fortran Notes:
4884   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4885 
4886 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetMeet()`, `DMPlexGetFullMeet()`, `DMPlexGetJoin()`
4887 @*/
4888 PetscErrorCode DMPlexRestoreMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4889 {
4890   PetscFunctionBegin;
4891   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4892   if (points) PetscAssertPointer(points, 3);
4893   if (numCoveredPoints) PetscAssertPointer(numCoveredPoints, 4);
4894   PetscAssertPointer(coveredPoints, 5);
4895   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
4896   if (numCoveredPoints) *numCoveredPoints = 0;
4897   PetscFunctionReturn(PETSC_SUCCESS);
4898 }
4899 
4900 /*@C
4901   DMPlexGetFullMeet - Get an array for the meet of the set of points
4902 
4903   Not Collective
4904 
4905   Input Parameters:
4906 + dm        - The `DMPLEX` object
4907 . numPoints - The number of input points for the meet
4908 - points    - The input points
4909 
4910   Output Parameters:
4911 + numCoveredPoints - The number of points in the meet
4912 - coveredPoints    - The points in the meet
4913 
4914   Level: intermediate
4915 
4916   Fortran Notes:
4917   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4918 
4919 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetMeet()`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
4920 @*/
4921 PetscErrorCode DMPlexGetFullMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4922 {
4923   PetscInt *offsets, **closures;
4924   PetscInt *meet[2];
4925   PetscInt  height = 0, maxSize, meetSize = 0, i = 0;
4926   PetscInt  p, h, c, m, mc;
4927 
4928   PetscFunctionBegin;
4929   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4930   PetscAssertPointer(points, 3);
4931   PetscAssertPointer(numCoveredPoints, 4);
4932   PetscAssertPointer(coveredPoints, 5);
4933 
4934   PetscCall(DMPlexGetDepth(dm, &height));
4935   PetscCall(PetscMalloc1(numPoints, &closures));
4936   PetscCall(DMGetWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
4937   PetscCall(DMPlexGetMaxSizes(dm, &mc, NULL));
4938   maxSize = (mc > 1) ? ((PetscPowInt(mc, height + 1) - 1) / (mc - 1)) : height + 1;
4939   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[0]));
4940   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[1]));
4941 
4942   for (p = 0; p < numPoints; ++p) {
4943     PetscInt closureSize;
4944 
4945     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_TRUE, &closureSize, &closures[p]));
4946 
4947     offsets[p * (height + 2) + 0] = 0;
4948     for (h = 0; h < height + 1; ++h) {
4949       PetscInt pStart, pEnd, i;
4950 
4951       PetscCall(DMPlexGetHeightStratum(dm, h, &pStart, &pEnd));
4952       for (i = offsets[p * (height + 2) + h]; i < closureSize; ++i) {
4953         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
4954           offsets[p * (height + 2) + h + 1] = i;
4955           break;
4956         }
4957       }
4958       if (i == closureSize) offsets[p * (height + 2) + h + 1] = i;
4959     }
4960     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);
4961   }
4962   for (h = 0; h < height + 1; ++h) {
4963     PetscInt dof;
4964 
4965     /* Copy in cone of first point */
4966     dof = offsets[h + 1] - offsets[h];
4967     for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = closures[0][(offsets[h] + meetSize) * 2];
4968     /* Check each successive cone */
4969     for (p = 1; p < numPoints && meetSize; ++p) {
4970       PetscInt newMeetSize = 0;
4971 
4972       dof = offsets[p * (height + 2) + h + 1] - offsets[p * (height + 2) + h];
4973       for (c = 0; c < dof; ++c) {
4974         const PetscInt point = closures[p][(offsets[p * (height + 2) + h] + c) * 2];
4975 
4976         for (m = 0; m < meetSize; ++m) {
4977           if (point == meet[i][m]) {
4978             meet[1 - i][newMeetSize++] = point;
4979             break;
4980           }
4981         }
4982       }
4983       meetSize = newMeetSize;
4984       i        = 1 - i;
4985     }
4986     if (meetSize) break;
4987   }
4988   *numCoveredPoints = meetSize;
4989   *coveredPoints    = meet[i];
4990   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_TRUE, NULL, &closures[p]));
4991   PetscCall(PetscFree(closures));
4992   PetscCall(DMRestoreWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
4993   PetscCall(DMRestoreWorkArray(dm, mc, MPIU_INT, &meet[1 - i]));
4994   PetscFunctionReturn(PETSC_SUCCESS);
4995 }
4996 
4997 /*@C
4998   DMPlexEqual - Determine if two `DM` have the same topology
4999 
5000   Not Collective
5001 
5002   Input Parameters:
5003 + dmA - A `DMPLEX` object
5004 - dmB - A `DMPLEX` object
5005 
5006   Output Parameter:
5007 . equal - `PETSC_TRUE` if the topologies are identical
5008 
5009   Level: intermediate
5010 
5011   Note:
5012   We are not solving graph isomorphism, so we do not permute.
5013 
5014 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCone()`
5015 @*/
5016 PetscErrorCode DMPlexEqual(DM dmA, DM dmB, PetscBool *equal)
5017 {
5018   PetscInt depth, depthB, pStart, pEnd, pStartB, pEndB, p;
5019 
5020   PetscFunctionBegin;
5021   PetscValidHeaderSpecific(dmA, DM_CLASSID, 1);
5022   PetscValidHeaderSpecific(dmB, DM_CLASSID, 2);
5023   PetscAssertPointer(equal, 3);
5024 
5025   *equal = PETSC_FALSE;
5026   PetscCall(DMPlexGetDepth(dmA, &depth));
5027   PetscCall(DMPlexGetDepth(dmB, &depthB));
5028   if (depth != depthB) PetscFunctionReturn(PETSC_SUCCESS);
5029   PetscCall(DMPlexGetChart(dmA, &pStart, &pEnd));
5030   PetscCall(DMPlexGetChart(dmB, &pStartB, &pEndB));
5031   if ((pStart != pStartB) || (pEnd != pEndB)) PetscFunctionReturn(PETSC_SUCCESS);
5032   for (p = pStart; p < pEnd; ++p) {
5033     const PetscInt *cone, *coneB, *ornt, *orntB, *support, *supportB;
5034     PetscInt        coneSize, coneSizeB, c, supportSize, supportSizeB, s;
5035 
5036     PetscCall(DMPlexGetConeSize(dmA, p, &coneSize));
5037     PetscCall(DMPlexGetCone(dmA, p, &cone));
5038     PetscCall(DMPlexGetConeOrientation(dmA, p, &ornt));
5039     PetscCall(DMPlexGetConeSize(dmB, p, &coneSizeB));
5040     PetscCall(DMPlexGetCone(dmB, p, &coneB));
5041     PetscCall(DMPlexGetConeOrientation(dmB, p, &orntB));
5042     if (coneSize != coneSizeB) PetscFunctionReturn(PETSC_SUCCESS);
5043     for (c = 0; c < coneSize; ++c) {
5044       if (cone[c] != coneB[c]) PetscFunctionReturn(PETSC_SUCCESS);
5045       if (ornt[c] != orntB[c]) PetscFunctionReturn(PETSC_SUCCESS);
5046     }
5047     PetscCall(DMPlexGetSupportSize(dmA, p, &supportSize));
5048     PetscCall(DMPlexGetSupport(dmA, p, &support));
5049     PetscCall(DMPlexGetSupportSize(dmB, p, &supportSizeB));
5050     PetscCall(DMPlexGetSupport(dmB, p, &supportB));
5051     if (supportSize != supportSizeB) PetscFunctionReturn(PETSC_SUCCESS);
5052     for (s = 0; s < supportSize; ++s) {
5053       if (support[s] != supportB[s]) PetscFunctionReturn(PETSC_SUCCESS);
5054     }
5055   }
5056   *equal = PETSC_TRUE;
5057   PetscFunctionReturn(PETSC_SUCCESS);
5058 }
5059 
5060 /*@C
5061   DMPlexGetNumFaceVertices - Returns the number of vertices on a face
5062 
5063   Not Collective
5064 
5065   Input Parameters:
5066 + dm         - The `DMPLEX`
5067 . cellDim    - The cell dimension
5068 - numCorners - The number of vertices on a cell
5069 
5070   Output Parameter:
5071 . numFaceVertices - The number of vertices on a face
5072 
5073   Level: developer
5074 
5075   Note:
5076   Of course this can only work for a restricted set of symmetric shapes
5077 
5078 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCone()`
5079 @*/
5080 PetscErrorCode DMPlexGetNumFaceVertices(DM dm, PetscInt cellDim, PetscInt numCorners, PetscInt *numFaceVertices)
5081 {
5082   MPI_Comm comm;
5083 
5084   PetscFunctionBegin;
5085   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
5086   PetscAssertPointer(numFaceVertices, 4);
5087   switch (cellDim) {
5088   case 0:
5089     *numFaceVertices = 0;
5090     break;
5091   case 1:
5092     *numFaceVertices = 1;
5093     break;
5094   case 2:
5095     switch (numCorners) {
5096     case 3:                 /* triangle */
5097       *numFaceVertices = 2; /* Edge has 2 vertices */
5098       break;
5099     case 4:                 /* quadrilateral */
5100       *numFaceVertices = 2; /* Edge has 2 vertices */
5101       break;
5102     case 6:                 /* quadratic triangle, tri and quad cohesive Lagrange cells */
5103       *numFaceVertices = 3; /* Edge has 3 vertices */
5104       break;
5105     case 9:                 /* quadratic quadrilateral, quadratic quad cohesive Lagrange cells */
5106       *numFaceVertices = 3; /* Edge has 3 vertices */
5107       break;
5108     default:
5109       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
5110     }
5111     break;
5112   case 3:
5113     switch (numCorners) {
5114     case 4:                 /* tetradehdron */
5115       *numFaceVertices = 3; /* Face has 3 vertices */
5116       break;
5117     case 6:                 /* tet cohesive cells */
5118       *numFaceVertices = 4; /* Face has 4 vertices */
5119       break;
5120     case 8:                 /* hexahedron */
5121       *numFaceVertices = 4; /* Face has 4 vertices */
5122       break;
5123     case 9:                 /* tet cohesive Lagrange cells */
5124       *numFaceVertices = 6; /* Face has 6 vertices */
5125       break;
5126     case 10:                /* quadratic tetrahedron */
5127       *numFaceVertices = 6; /* Face has 6 vertices */
5128       break;
5129     case 12:                /* hex cohesive Lagrange cells */
5130       *numFaceVertices = 6; /* Face has 6 vertices */
5131       break;
5132     case 18:                /* quadratic tet cohesive Lagrange cells */
5133       *numFaceVertices = 6; /* Face has 6 vertices */
5134       break;
5135     case 27:                /* quadratic hexahedron, quadratic hex cohesive Lagrange cells */
5136       *numFaceVertices = 9; /* Face has 9 vertices */
5137       break;
5138     default:
5139       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
5140     }
5141     break;
5142   default:
5143     SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid cell dimension %" PetscInt_FMT, cellDim);
5144   }
5145   PetscFunctionReturn(PETSC_SUCCESS);
5146 }
5147 
5148 /*@
5149   DMPlexGetDepthLabel - Get the `DMLabel` recording the depth of each point
5150 
5151   Not Collective
5152 
5153   Input Parameter:
5154 . dm - The `DMPLEX` object
5155 
5156   Output Parameter:
5157 . depthLabel - The `DMLabel` recording point depth
5158 
5159   Level: developer
5160 
5161 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepth()`, `DMPlexGetHeightStratum()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`,
5162 @*/
5163 PetscErrorCode DMPlexGetDepthLabel(DM dm, DMLabel *depthLabel)
5164 {
5165   PetscFunctionBegin;
5166   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5167   PetscAssertPointer(depthLabel, 2);
5168   *depthLabel = dm->depthLabel;
5169   PetscFunctionReturn(PETSC_SUCCESS);
5170 }
5171 
5172 /*@
5173   DMPlexGetDepth - Get the depth of the DAG representing this mesh
5174 
5175   Not Collective
5176 
5177   Input Parameter:
5178 . dm - The `DMPLEX` object
5179 
5180   Output Parameter:
5181 . depth - The number of strata (breadth first levels) in the DAG
5182 
5183   Level: developer
5184 
5185   Notes:
5186   This returns maximum of point depths over all points, i.e. maximum value of the label returned by `DMPlexGetDepthLabel()`.
5187 
5188   The point depth is described more in detail in `DMPlexGetDepthStratum()`.
5189 
5190   An empty mesh gives -1.
5191 
5192 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepthLabel()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`
5193 @*/
5194 PetscErrorCode DMPlexGetDepth(DM dm, PetscInt *depth)
5195 {
5196   DM_Plex *mesh = (DM_Plex *)dm->data;
5197   DMLabel  label;
5198   PetscInt d = 0;
5199 
5200   PetscFunctionBegin;
5201   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5202   PetscAssertPointer(depth, 2);
5203   if (mesh->tr) {
5204     PetscCall(DMPlexTransformGetDepth(mesh->tr, depth));
5205   } else {
5206     PetscCall(DMPlexGetDepthLabel(dm, &label));
5207     if (label) PetscCall(DMLabelGetNumValues(label, &d));
5208     *depth = d - 1;
5209   }
5210   PetscFunctionReturn(PETSC_SUCCESS);
5211 }
5212 
5213 /*@
5214   DMPlexGetDepthStratum - Get the bounds [`start`, `end`) for all points at a certain depth.
5215 
5216   Not Collective
5217 
5218   Input Parameters:
5219 + dm    - The `DMPLEX` object
5220 - depth - The requested depth
5221 
5222   Output Parameters:
5223 + start - The first point at this `depth`
5224 - end   - One beyond the last point at this `depth`
5225 
5226   Level: developer
5227 
5228   Notes:
5229   Depth indexing is related to topological dimension.  Depth stratum 0 contains the lowest topological dimension points,
5230   often "vertices".  If the mesh is "interpolated" (see `DMPlexInterpolate()`), then depth stratum 1 contains the next
5231   higher dimension, e.g., "edges".
5232 
5233 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetHeightStratum()`, `DMPlexGetCellTypeStratum()`, `DMPlexGetDepth()`, `DMPlexGetDepthLabel()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`, `DMPlexInterpolate()`
5234 @*/
5235 PetscErrorCode DMPlexGetDepthStratum(DM dm, PetscInt depth, PetscInt *start, PetscInt *end)
5236 {
5237   DM_Plex *mesh = (DM_Plex *)dm->data;
5238   DMLabel  label;
5239   PetscInt pStart, pEnd;
5240 
5241   PetscFunctionBegin;
5242   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5243   if (start) {
5244     PetscAssertPointer(start, 3);
5245     *start = 0;
5246   }
5247   if (end) {
5248     PetscAssertPointer(end, 4);
5249     *end = 0;
5250   }
5251   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
5252   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
5253   if (depth < 0) {
5254     if (start) *start = pStart;
5255     if (end) *end = pEnd;
5256     PetscFunctionReturn(PETSC_SUCCESS);
5257   }
5258   if (mesh->tr) {
5259     PetscCall(DMPlexTransformGetDepthStratum(mesh->tr, depth, start, end));
5260   } else {
5261     PetscCall(DMPlexGetDepthLabel(dm, &label));
5262     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named depth was found");
5263     PetscCall(DMLabelGetStratumBounds(label, depth, start, end));
5264   }
5265   PetscFunctionReturn(PETSC_SUCCESS);
5266 }
5267 
5268 /*@
5269   DMPlexGetHeightStratum - Get the bounds [`start`, `end`) for all points at a certain height.
5270 
5271   Not Collective
5272 
5273   Input Parameters:
5274 + dm     - The `DMPLEX` object
5275 - height - The requested height
5276 
5277   Output Parameters:
5278 + start - The first point at this `height`
5279 - end   - One beyond the last point at this `height`
5280 
5281   Level: developer
5282 
5283   Notes:
5284   Height indexing is related to topological codimension.  Height stratum 0 contains the highest topological dimension
5285   points, often called "cells" or "elements".  If the mesh is "interpolated" (see `DMPlexInterpolate()`), then height
5286   stratum 1 contains the boundary of these "cells", often called "faces" or "facets".
5287 
5288 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepthStratum()`, `DMPlexGetCellTypeStratum()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
5289 @*/
5290 PetscErrorCode DMPlexGetHeightStratum(DM dm, PetscInt height, PetscInt *start, PetscInt *end)
5291 {
5292   DMLabel  label;
5293   PetscInt depth, pStart, pEnd;
5294 
5295   PetscFunctionBegin;
5296   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5297   if (start) {
5298     PetscAssertPointer(start, 3);
5299     *start = 0;
5300   }
5301   if (end) {
5302     PetscAssertPointer(end, 4);
5303     *end = 0;
5304   }
5305   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
5306   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
5307   if (height < 0) {
5308     if (start) *start = pStart;
5309     if (end) *end = pEnd;
5310     PetscFunctionReturn(PETSC_SUCCESS);
5311   }
5312   PetscCall(DMPlexGetDepthLabel(dm, &label));
5313   if (label) PetscCall(DMLabelGetNumValues(label, &depth));
5314   else PetscCall(DMGetDimension(dm, &depth));
5315   PetscCheck(depth >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Depth not yet computed");
5316   PetscCall(DMPlexGetDepthStratum(dm, depth - 1 - height, start, end));
5317   PetscFunctionReturn(PETSC_SUCCESS);
5318 }
5319 
5320 /*@
5321   DMPlexGetPointDepth - Get the `depth` of a given point
5322 
5323   Not Collective
5324 
5325   Input Parameters:
5326 + dm    - The `DMPLEX` object
5327 - point - The point
5328 
5329   Output Parameter:
5330 . depth - The depth of the `point`
5331 
5332   Level: intermediate
5333 
5334 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
5335 @*/
5336 PetscErrorCode DMPlexGetPointDepth(DM dm, PetscInt point, PetscInt *depth)
5337 {
5338   PetscFunctionBegin;
5339   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5340   PetscAssertPointer(depth, 3);
5341   PetscCall(DMLabelGetValue(dm->depthLabel, point, depth));
5342   PetscFunctionReturn(PETSC_SUCCESS);
5343 }
5344 
5345 /*@
5346   DMPlexGetPointHeight - Get the `height` of a given point
5347 
5348   Not Collective
5349 
5350   Input Parameters:
5351 + dm    - The `DMPLEX` object
5352 - point - The point
5353 
5354   Output Parameter:
5355 . height - The height of the `point`
5356 
5357   Level: intermediate
5358 
5359 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointDepth()`
5360 @*/
5361 PetscErrorCode DMPlexGetPointHeight(DM dm, PetscInt point, PetscInt *height)
5362 {
5363   PetscInt n, pDepth;
5364 
5365   PetscFunctionBegin;
5366   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5367   PetscAssertPointer(height, 3);
5368   PetscCall(DMLabelGetNumValues(dm->depthLabel, &n));
5369   PetscCall(DMLabelGetValue(dm->depthLabel, point, &pDepth));
5370   *height = n - 1 - pDepth; /* DAG depth is n-1 */
5371   PetscFunctionReturn(PETSC_SUCCESS);
5372 }
5373 
5374 /*@
5375   DMPlexGetCellTypeLabel - Get the `DMLabel` recording the polytope type of each cell
5376 
5377   Not Collective
5378 
5379   Input Parameter:
5380 . dm - The `DMPLEX` object
5381 
5382   Output Parameter:
5383 . celltypeLabel - The `DMLabel` recording cell polytope type
5384 
5385   Level: developer
5386 
5387   Note:
5388   This function will trigger automatica computation of cell types. This can be disabled by calling
5389   `DMCreateLabel`(dm, "celltype") beforehand.
5390 
5391 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMCreateLabel()`
5392 @*/
5393 PetscErrorCode DMPlexGetCellTypeLabel(DM dm, DMLabel *celltypeLabel)
5394 {
5395   PetscFunctionBegin;
5396   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5397   PetscAssertPointer(celltypeLabel, 2);
5398   if (!dm->celltypeLabel) PetscCall(DMPlexComputeCellTypes(dm));
5399   *celltypeLabel = dm->celltypeLabel;
5400   PetscFunctionReturn(PETSC_SUCCESS);
5401 }
5402 
5403 /*@
5404   DMPlexGetCellType - Get the polytope type of a given cell
5405 
5406   Not Collective
5407 
5408   Input Parameters:
5409 + dm   - The `DMPLEX` object
5410 - cell - The cell
5411 
5412   Output Parameter:
5413 . celltype - The polytope type of the cell
5414 
5415   Level: intermediate
5416 
5417 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPolytopeType`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`
5418 @*/
5419 PetscErrorCode DMPlexGetCellType(DM dm, PetscInt cell, DMPolytopeType *celltype)
5420 {
5421   DM_Plex *mesh = (DM_Plex *)dm->data;
5422   DMLabel  label;
5423   PetscInt ct;
5424 
5425   PetscFunctionBegin;
5426   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5427   PetscAssertPointer(celltype, 3);
5428   if (mesh->tr) {
5429     PetscCall(DMPlexTransformGetCellType(mesh->tr, cell, celltype));
5430   } else {
5431     PetscInt pStart, pEnd;
5432 
5433     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, NULL));
5434     if (!mesh->cellTypes) { /* XXX remove? optimize? */
5435       PetscCall(PetscSectionGetChart(mesh->coneSection, NULL, &pEnd));
5436       PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
5437       PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5438       for (PetscInt p = pStart; p < pEnd; p++) {
5439         PetscCall(DMLabelGetValue(label, p, &ct));
5440         mesh->cellTypes[p - pStart].value_as_uint8 = (DMPolytopeType)ct;
5441       }
5442     }
5443     *celltype = (DMPolytopeType)mesh->cellTypes[cell - pStart].value_as_uint8;
5444     if (PetscDefined(USE_DEBUG)) {
5445       PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5446       PetscCall(DMLabelGetValue(label, cell, &ct));
5447       PetscCheck(ct >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Cell %" PetscInt_FMT " has not been assigned a cell type", cell);
5448       PetscCheck(ct == (PetscInt)*celltype, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid cellType for %" PetscInt_FMT ": %d != %" PetscInt_FMT, cell, (int)*celltype, ct);
5449     }
5450   }
5451   PetscFunctionReturn(PETSC_SUCCESS);
5452 }
5453 
5454 /*@
5455   DMPlexSetCellType - Set the polytope type of a given cell
5456 
5457   Not Collective
5458 
5459   Input Parameters:
5460 + dm       - The `DMPLEX` object
5461 . cell     - The cell
5462 - celltype - The polytope type of the cell
5463 
5464   Level: advanced
5465 
5466   Note:
5467   By default, cell types will be automatically computed using `DMPlexComputeCellTypes()` before this function
5468   is executed. This function will override the computed type. However, if automatic classification will not succeed
5469   and a user wants to manually specify all types, the classification must be disabled by calling
5470   DMCreateLabel(dm, "celltype") before getting or setting any cell types.
5471 
5472 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexComputeCellTypes()`, `DMCreateLabel()`
5473 @*/
5474 PetscErrorCode DMPlexSetCellType(DM dm, PetscInt cell, DMPolytopeType celltype)
5475 {
5476   DM_Plex *mesh = (DM_Plex *)dm->data;
5477   DMLabel  label;
5478   PetscInt pStart, pEnd;
5479 
5480   PetscFunctionBegin;
5481   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5482   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
5483   PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5484   PetscCall(DMLabelSetValue(label, cell, celltype));
5485   if (!mesh->cellTypes) PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
5486   mesh->cellTypes[cell - pStart].value_as_uint8 = celltype;
5487   PetscFunctionReturn(PETSC_SUCCESS);
5488 }
5489 
5490 PetscErrorCode DMCreateCoordinateDM_Plex(DM dm, DM *cdm)
5491 {
5492   PetscSection section, s;
5493   Mat          m;
5494   PetscInt     maxHeight;
5495   const char  *prefix;
5496 
5497   PetscFunctionBegin;
5498   PetscCall(DMClone(dm, cdm));
5499   PetscCall(PetscObjectGetOptionsPrefix((PetscObject)dm, &prefix));
5500   PetscCall(PetscObjectSetOptionsPrefix((PetscObject)*cdm, prefix));
5501   PetscCall(PetscObjectAppendOptionsPrefix((PetscObject)*cdm, "cdm_"));
5502   PetscCall(DMPlexGetMaxProjectionHeight(dm, &maxHeight));
5503   PetscCall(DMPlexSetMaxProjectionHeight(*cdm, maxHeight));
5504   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
5505   PetscCall(DMSetLocalSection(*cdm, section));
5506   PetscCall(PetscSectionDestroy(&section));
5507   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &s));
5508   PetscCall(MatCreate(PETSC_COMM_SELF, &m));
5509   PetscCall(DMSetDefaultConstraints(*cdm, s, m, NULL));
5510   PetscCall(PetscSectionDestroy(&s));
5511   PetscCall(MatDestroy(&m));
5512 
5513   PetscCall(DMSetNumFields(*cdm, 1));
5514   PetscCall(DMCreateDS(*cdm));
5515   (*cdm)->cloneOpts = PETSC_TRUE;
5516   if (dm->setfromoptionscalled) PetscCall(DMSetFromOptions(*cdm));
5517   PetscFunctionReturn(PETSC_SUCCESS);
5518 }
5519 
5520 PetscErrorCode DMCreateCoordinateField_Plex(DM dm, DMField *field)
5521 {
5522   Vec coordsLocal, cellCoordsLocal;
5523   DM  coordsDM, cellCoordsDM;
5524 
5525   PetscFunctionBegin;
5526   *field = NULL;
5527   PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
5528   PetscCall(DMGetCoordinateDM(dm, &coordsDM));
5529   PetscCall(DMGetCellCoordinatesLocal(dm, &cellCoordsLocal));
5530   PetscCall(DMGetCellCoordinateDM(dm, &cellCoordsDM));
5531   if (coordsLocal && coordsDM) {
5532     if (cellCoordsLocal && cellCoordsDM) PetscCall(DMFieldCreateDSWithDG(coordsDM, cellCoordsDM, 0, coordsLocal, cellCoordsLocal, field));
5533     else PetscCall(DMFieldCreateDS(coordsDM, 0, coordsLocal, field));
5534   }
5535   PetscFunctionReturn(PETSC_SUCCESS);
5536 }
5537 
5538 /*@C
5539   DMPlexGetConeSection - Return a section which describes the layout of cone data
5540 
5541   Not Collective
5542 
5543   Input Parameter:
5544 . dm - The `DMPLEX` object
5545 
5546   Output Parameter:
5547 . section - The `PetscSection` object
5548 
5549   Level: developer
5550 
5551 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSupportSection()`, `DMPlexGetCones()`, `DMPlexGetConeOrientations()`, `PetscSection`
5552 @*/
5553 PetscErrorCode DMPlexGetConeSection(DM dm, PetscSection *section)
5554 {
5555   DM_Plex *mesh = (DM_Plex *)dm->data;
5556 
5557   PetscFunctionBegin;
5558   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5559   if (section) *section = mesh->coneSection;
5560   PetscFunctionReturn(PETSC_SUCCESS);
5561 }
5562 
5563 /*@C
5564   DMPlexGetSupportSection - Return a section which describes the layout of support data
5565 
5566   Not Collective
5567 
5568   Input Parameter:
5569 . dm - The `DMPLEX` object
5570 
5571   Output Parameter:
5572 . section - The `PetscSection` object
5573 
5574   Level: developer
5575 
5576 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `PetscSection`
5577 @*/
5578 PetscErrorCode DMPlexGetSupportSection(DM dm, PetscSection *section)
5579 {
5580   DM_Plex *mesh = (DM_Plex *)dm->data;
5581 
5582   PetscFunctionBegin;
5583   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5584   if (section) *section = mesh->supportSection;
5585   PetscFunctionReturn(PETSC_SUCCESS);
5586 }
5587 
5588 /*@C
5589   DMPlexGetCones - Return cone data
5590 
5591   Not Collective
5592 
5593   Input Parameter:
5594 . dm - The `DMPLEX` object
5595 
5596   Output Parameter:
5597 . cones - The cone for each point
5598 
5599   Level: developer
5600 
5601 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`
5602 @*/
5603 PetscErrorCode DMPlexGetCones(DM dm, PetscInt *cones[])
5604 {
5605   DM_Plex *mesh = (DM_Plex *)dm->data;
5606 
5607   PetscFunctionBegin;
5608   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5609   if (cones) *cones = mesh->cones;
5610   PetscFunctionReturn(PETSC_SUCCESS);
5611 }
5612 
5613 /*@C
5614   DMPlexGetConeOrientations - Return cone orientation data
5615 
5616   Not Collective
5617 
5618   Input Parameter:
5619 . dm - The `DMPLEX` object
5620 
5621   Output Parameter:
5622 . coneOrientations - The array of cone orientations for all points
5623 
5624   Level: developer
5625 
5626   Notes:
5627   The `PetscSection` returned by `DMPlexGetConeSection()` partitions coneOrientations into cone orientations of particular points as returned by `DMPlexGetConeOrientation()`.
5628 
5629   The meaning of coneOrientations values is detailed in `DMPlexGetConeOrientation()`.
5630 
5631 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `DMPlexGetConeOrientation()`, `PetscSection`
5632 @*/
5633 PetscErrorCode DMPlexGetConeOrientations(DM dm, PetscInt *coneOrientations[])
5634 {
5635   DM_Plex *mesh = (DM_Plex *)dm->data;
5636 
5637   PetscFunctionBegin;
5638   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5639   if (coneOrientations) *coneOrientations = mesh->coneOrientations;
5640   PetscFunctionReturn(PETSC_SUCCESS);
5641 }
5642 
5643 /******************************** FEM Support **********************************/
5644 
5645 PetscErrorCode DMPlexGetAllCells_Internal(DM plex, IS *cellIS)
5646 {
5647   PetscInt depth;
5648 
5649   PetscFunctionBegin;
5650   PetscCall(DMPlexGetDepth(plex, &depth));
5651   PetscCall(DMGetStratumIS(plex, "dim", depth, cellIS));
5652   if (!*cellIS) PetscCall(DMGetStratumIS(plex, "depth", depth, cellIS));
5653   PetscFunctionReturn(PETSC_SUCCESS);
5654 }
5655 
5656 PetscErrorCode DMPlexGetAllFaces_Internal(DM plex, IS *faceIS)
5657 {
5658   PetscInt depth;
5659 
5660   PetscFunctionBegin;
5661   PetscCall(DMPlexGetDepth(plex, &depth));
5662   PetscCall(DMGetStratumIS(plex, "dim", depth - 1, faceIS));
5663   if (!*faceIS) PetscCall(DMGetStratumIS(plex, "depth", depth - 1, faceIS));
5664   PetscFunctionReturn(PETSC_SUCCESS);
5665 }
5666 
5667 /*
5668  Returns number of components and tensor degree for the field.  For interpolated meshes, line should be a point
5669  representing a line in the section.
5670 */
5671 static PetscErrorCode PetscSectionFieldGetTensorDegree_Private(DM dm, PetscSection section, PetscInt field, PetscInt line, PetscInt *Nc, PetscInt *k, PetscBool *continuous)
5672 {
5673   PetscObject  obj;
5674   PetscClassId id;
5675   PetscFE      fe = NULL;
5676 
5677   PetscFunctionBeginHot;
5678   PetscCall(PetscSectionGetFieldComponents(section, field, Nc));
5679   PetscCall(DMGetField(dm, field, NULL, &obj));
5680   PetscCall(PetscObjectGetClassId(obj, &id));
5681   if (id == PETSCFE_CLASSID) fe = (PetscFE)obj;
5682 
5683   if (!fe) {
5684     /* Assume the full interpolated mesh is in the chart; lines in particular */
5685     /* An order k SEM disc has k-1 dofs on an edge */
5686     PetscCall(PetscSectionGetFieldDof(section, line, field, k));
5687     *k = *k / *Nc + 1;
5688   } else {
5689     PetscInt       dual_space_size, dim;
5690     PetscDualSpace dual_space;
5691     PetscCall(DMGetDimension(dm, &dim));
5692     PetscCall(PetscFEGetDualSpace(fe, &dual_space));
5693     PetscCall(PetscDualSpaceGetDimension(dual_space, &dual_space_size));
5694     *k = (PetscInt)PetscCeilReal(PetscPowReal(dual_space_size / *Nc, 1.0 / dim)) - 1;
5695     PetscCall(PetscDualSpaceLagrangeGetContinuity(dual_space, continuous));
5696   }
5697   PetscFunctionReturn(PETSC_SUCCESS);
5698 }
5699 
5700 /*@
5701 
5702   DMPlexSetClosurePermutationTensor - Create a permutation from the default (BFS) point ordering in the closure, to a
5703   lexicographic ordering over the tensor product cell (i.e., line, quad, hex, etc.), and set this permutation in the
5704   section provided (or the section of the `DM`).
5705 
5706   Input Parameters:
5707 + dm      - The `DM`
5708 . point   - Either a cell (highest dim point) or an edge (dim 1 point), or `PETSC_DETERMINE`
5709 - section - The `PetscSection` to reorder, or `NULL` for the default section
5710 
5711   Example:
5712   A typical interpolated single-quad mesh might order points as
5713 .vb
5714   [c0, v1, v2, v3, v4, e5, e6, e7, e8]
5715 
5716   v4 -- e6 -- v3
5717   |           |
5718   e7    c0    e8
5719   |           |
5720   v1 -- e5 -- v2
5721 .ve
5722 
5723   (There is no significance to the ordering described here.)  The default section for a Q3 quad might typically assign
5724   dofs in the order of points, e.g.,
5725 .vb
5726     c0 -> [0,1,2,3]
5727     v1 -> [4]
5728     ...
5729     e5 -> [8, 9]
5730 .ve
5731 
5732   which corresponds to the dofs
5733 .vb
5734     6   10  11  7
5735     13  2   3   15
5736     12  0   1   14
5737     4   8   9   5
5738 .ve
5739 
5740   The closure in BFS ordering works through height strata (cells, edges, vertices) to produce the ordering
5741 .vb
5742   0 1 2 3 8 9 14 15 11 10 13 12 4 5 7 6
5743 .ve
5744 
5745   After calling DMPlexSetClosurePermutationTensor(), the closure will be ordered lexicographically,
5746 .vb
5747    4 8 9 5 12 0 1 14 13 2 3 15 6 10 11 7
5748 .ve
5749 
5750   Level: developer
5751 
5752   Notes:
5753   The point is used to determine the number of dofs/field on an edge. For SEM, this is related to the polynomial
5754   degree of the basis.
5755 
5756   This is required to run with libCEED.
5757 
5758 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMGetLocalSection()`, `PetscSectionSetClosurePermutation()`, `DMSetGlobalSection()`
5759 @*/
5760 PetscErrorCode DMPlexSetClosurePermutationTensor(DM dm, PetscInt point, PetscSection section)
5761 {
5762   DMLabel   label;
5763   PetscInt  dim, depth = -1, eStart = -1, Nf;
5764   PetscBool continuous = PETSC_TRUE;
5765 
5766   PetscFunctionBegin;
5767   PetscCall(DMGetDimension(dm, &dim));
5768   if (dim < 1) PetscFunctionReturn(PETSC_SUCCESS);
5769   if (point < 0) {
5770     PetscInt sStart, sEnd;
5771 
5772     PetscCall(DMPlexGetDepthStratum(dm, 1, &sStart, &sEnd));
5773     point = sEnd - sStart ? sStart : point;
5774   }
5775   PetscCall(DMPlexGetDepthLabel(dm, &label));
5776   if (point >= 0) PetscCall(DMLabelGetValue(label, point, &depth));
5777   if (!section) PetscCall(DMGetLocalSection(dm, &section));
5778   if (depth == 1) {
5779     eStart = point;
5780   } else if (depth == dim) {
5781     const PetscInt *cone;
5782 
5783     PetscCall(DMPlexGetCone(dm, point, &cone));
5784     if (dim == 2) eStart = cone[0];
5785     else if (dim == 3) {
5786       const PetscInt *cone2;
5787       PetscCall(DMPlexGetCone(dm, cone[0], &cone2));
5788       eStart = cone2[0];
5789     } 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);
5790   } 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);
5791 
5792   PetscCall(PetscSectionGetNumFields(section, &Nf));
5793   for (PetscInt d = 1; d <= dim; d++) {
5794     PetscInt  k, f, Nc, c, i, j, size = 0, offset = 0, foffset = 0;
5795     PetscInt *perm;
5796 
5797     for (f = 0; f < Nf; ++f) {
5798       PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous));
5799       size += PetscPowInt(k + 1, d) * Nc;
5800     }
5801     PetscCall(PetscMalloc1(size, &perm));
5802     for (f = 0; f < Nf; ++f) {
5803       switch (d) {
5804       case 1:
5805         PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous));
5806         /*
5807          Original ordering is [ edge of length k-1; vtx0; vtx1 ]
5808          We want              [ vtx0; edge of length k-1; vtx1 ]
5809          */
5810         if (continuous) {
5811           for (c = 0; c < Nc; c++, offset++) perm[offset] = (k - 1) * Nc + c + foffset;
5812           for (i = 0; i < k - 1; i++)
5813             for (c = 0; c < Nc; c++, offset++) perm[offset] = i * Nc + c + foffset;
5814           for (c = 0; c < Nc; c++, offset++) perm[offset] = k * Nc + c + foffset;
5815           foffset = offset;
5816         } else {
5817           for (i = offset; i < size; i++) perm[i] = i - offset + foffset;
5818           foffset = offset = size;
5819         }
5820         break;
5821       case 2:
5822         /* The original quad closure is oriented clockwise, {f, e_b, e_r, e_t, e_l, v_lb, v_rb, v_tr, v_tl} */
5823         PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous));
5824         /* The SEM order is
5825 
5826          v_lb, {e_b}, v_rb,
5827          e^{(k-1)-i}_l, {f^{i*(k-1)}}, e^i_r,
5828          v_lt, reverse {e_t}, v_rt
5829          */
5830         if (continuous) {
5831           const PetscInt of   = 0;
5832           const PetscInt oeb  = of + PetscSqr(k - 1);
5833           const PetscInt oer  = oeb + (k - 1);
5834           const PetscInt oet  = oer + (k - 1);
5835           const PetscInt oel  = oet + (k - 1);
5836           const PetscInt ovlb = oel + (k - 1);
5837           const PetscInt ovrb = ovlb + 1;
5838           const PetscInt ovrt = ovrb + 1;
5839           const PetscInt ovlt = ovrt + 1;
5840           PetscInt       o;
5841 
5842           /* bottom */
5843           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlb * Nc + c + foffset;
5844           for (o = oeb; o < oer; ++o)
5845             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5846           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrb * Nc + c + foffset;
5847           /* middle */
5848           for (i = 0; i < k - 1; ++i) {
5849             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oel + (k - 2) - i) * Nc + c + foffset;
5850             for (o = of + (k - 1) * i; o < of + (k - 1) * (i + 1); ++o)
5851               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5852             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oer + i) * Nc + c + foffset;
5853           }
5854           /* top */
5855           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlt * Nc + c + foffset;
5856           for (o = oel - 1; o >= oet; --o)
5857             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5858           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrt * Nc + c + foffset;
5859           foffset = offset;
5860         } else {
5861           for (i = offset; i < size; i++) perm[i] = i - offset + foffset;
5862           foffset = offset = size;
5863         }
5864         break;
5865       case 3:
5866         /* The original hex closure is
5867 
5868          {c,
5869          f_b, f_t, f_f, f_b, f_r, f_l,
5870          e_bl, e_bb, e_br, e_bf,  e_tf, e_tr, e_tb, e_tl,  e_rf, e_lf, e_lb, e_rb,
5871          v_blf, v_blb, v_brb, v_brf, v_tlf, v_trf, v_trb, v_tlb}
5872          */
5873         PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous));
5874         /* The SEM order is
5875          Bottom Slice
5876          v_blf, {e^{(k-1)-n}_bf}, v_brf,
5877          e^{i}_bl, f^{n*(k-1)+(k-1)-i}_b, e^{(k-1)-i}_br,
5878          v_blb, {e_bb}, v_brb,
5879 
5880          Middle Slice (j)
5881          {e^{(k-1)-j}_lf}, {f^{j*(k-1)+n}_f}, e^j_rf,
5882          f^{i*(k-1)+j}_l, {c^{(j*(k-1) + i)*(k-1)+n}_t}, f^{j*(k-1)+i}_r,
5883          e^j_lb, {f^{j*(k-1)+(k-1)-n}_b}, e^{(k-1)-j}_rb,
5884 
5885          Top Slice
5886          v_tlf, {e_tf}, v_trf,
5887          e^{(k-1)-i}_tl, {f^{i*(k-1)}_t}, e^{i}_tr,
5888          v_tlb, {e^{(k-1)-n}_tb}, v_trb,
5889          */
5890         if (continuous) {
5891           const PetscInt oc    = 0;
5892           const PetscInt ofb   = oc + PetscSqr(k - 1) * (k - 1);
5893           const PetscInt oft   = ofb + PetscSqr(k - 1);
5894           const PetscInt off   = oft + PetscSqr(k - 1);
5895           const PetscInt ofk   = off + PetscSqr(k - 1);
5896           const PetscInt ofr   = ofk + PetscSqr(k - 1);
5897           const PetscInt ofl   = ofr + PetscSqr(k - 1);
5898           const PetscInt oebl  = ofl + PetscSqr(k - 1);
5899           const PetscInt oebb  = oebl + (k - 1);
5900           const PetscInt oebr  = oebb + (k - 1);
5901           const PetscInt oebf  = oebr + (k - 1);
5902           const PetscInt oetf  = oebf + (k - 1);
5903           const PetscInt oetr  = oetf + (k - 1);
5904           const PetscInt oetb  = oetr + (k - 1);
5905           const PetscInt oetl  = oetb + (k - 1);
5906           const PetscInt oerf  = oetl + (k - 1);
5907           const PetscInt oelf  = oerf + (k - 1);
5908           const PetscInt oelb  = oelf + (k - 1);
5909           const PetscInt oerb  = oelb + (k - 1);
5910           const PetscInt ovblf = oerb + (k - 1);
5911           const PetscInt ovblb = ovblf + 1;
5912           const PetscInt ovbrb = ovblb + 1;
5913           const PetscInt ovbrf = ovbrb + 1;
5914           const PetscInt ovtlf = ovbrf + 1;
5915           const PetscInt ovtrf = ovtlf + 1;
5916           const PetscInt ovtrb = ovtrf + 1;
5917           const PetscInt ovtlb = ovtrb + 1;
5918           PetscInt       o, n;
5919 
5920           /* Bottom Slice */
5921           /*   bottom */
5922           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblf * Nc + c + foffset;
5923           for (o = oetf - 1; o >= oebf; --o)
5924             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5925           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrf * Nc + c + foffset;
5926           /*   middle */
5927           for (i = 0; i < k - 1; ++i) {
5928             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebl + i) * Nc + c + foffset;
5929             for (n = 0; n < k - 1; ++n) {
5930               o = ofb + n * (k - 1) + i;
5931               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5932             }
5933             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebr + (k - 2) - i) * Nc + c + foffset;
5934           }
5935           /*   top */
5936           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblb * Nc + c + foffset;
5937           for (o = oebb; o < oebr; ++o)
5938             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5939           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrb * Nc + c + foffset;
5940 
5941           /* Middle Slice */
5942           for (j = 0; j < k - 1; ++j) {
5943             /*   bottom */
5944             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelf + (k - 2) - j) * Nc + c + foffset;
5945             for (o = off + j * (k - 1); o < off + (j + 1) * (k - 1); ++o)
5946               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5947             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerf + j) * Nc + c + foffset;
5948             /*   middle */
5949             for (i = 0; i < k - 1; ++i) {
5950               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofl + i * (k - 1) + j) * Nc + c + foffset;
5951               for (n = 0; n < k - 1; ++n)
5952                 for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oc + (j * (k - 1) + i) * (k - 1) + n) * Nc + c + foffset;
5953               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofr + j * (k - 1) + i) * Nc + c + foffset;
5954             }
5955             /*   top */
5956             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelb + j) * Nc + c + foffset;
5957             for (o = ofk + j * (k - 1) + (k - 2); o >= ofk + j * (k - 1); --o)
5958               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5959             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerb + (k - 2) - j) * Nc + c + foffset;
5960           }
5961 
5962           /* Top Slice */
5963           /*   bottom */
5964           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlf * Nc + c + foffset;
5965           for (o = oetf; o < oetr; ++o)
5966             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5967           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrf * Nc + c + foffset;
5968           /*   middle */
5969           for (i = 0; i < k - 1; ++i) {
5970             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetl + (k - 2) - i) * Nc + c + foffset;
5971             for (n = 0; n < k - 1; ++n)
5972               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oft + i * (k - 1) + n) * Nc + c + foffset;
5973             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetr + i) * Nc + c + foffset;
5974           }
5975           /*   top */
5976           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlb * Nc + c + foffset;
5977           for (o = oetl - 1; o >= oetb; --o)
5978             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5979           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrb * Nc + c + foffset;
5980 
5981           foffset = offset;
5982         } else {
5983           for (i = offset; i < size; i++) perm[i] = i - offset + foffset;
5984           foffset = offset = size;
5985         }
5986         break;
5987       default:
5988         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "No spectral ordering for dimension %" PetscInt_FMT, d);
5989       }
5990     }
5991     PetscCheck(offset == size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Number of permutation entries %" PetscInt_FMT " != %" PetscInt_FMT, offset, size);
5992     /* Check permutation */
5993     {
5994       PetscInt *check;
5995 
5996       PetscCall(PetscMalloc1(size, &check));
5997       for (i = 0; i < size; ++i) {
5998         check[i] = -1;
5999         PetscCheck(perm[i] >= 0 && perm[i] < size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid permutation index p[%" PetscInt_FMT "] = %" PetscInt_FMT, i, perm[i]);
6000       }
6001       for (i = 0; i < size; ++i) check[perm[i]] = i;
6002       for (i = 0; i < size; ++i) PetscCheck(check[i] >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Missing permutation index %" PetscInt_FMT, i);
6003       PetscCall(PetscFree(check));
6004     }
6005     PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size, PETSC_OWN_POINTER, perm));
6006     if (d == dim) { // Add permutation for localized (in case this is a coordinate DM)
6007       PetscInt *loc_perm;
6008       PetscCall(PetscMalloc1(size * 2, &loc_perm));
6009       for (PetscInt i = 0; i < size; i++) {
6010         loc_perm[i]        = perm[i];
6011         loc_perm[size + i] = size + perm[i];
6012       }
6013       PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size * 2, PETSC_OWN_POINTER, loc_perm));
6014     }
6015   }
6016   PetscFunctionReturn(PETSC_SUCCESS);
6017 }
6018 
6019 PetscErrorCode DMPlexGetPointDualSpaceFEM(DM dm, PetscInt point, PetscInt field, PetscDualSpace *dspace)
6020 {
6021   PetscDS  prob;
6022   PetscInt depth, Nf, h;
6023   DMLabel  label;
6024 
6025   PetscFunctionBeginHot;
6026   PetscCall(DMGetDS(dm, &prob));
6027   Nf      = prob->Nf;
6028   label   = dm->depthLabel;
6029   *dspace = NULL;
6030   if (field < Nf) {
6031     PetscObject disc = prob->disc[field];
6032 
6033     if (disc->classid == PETSCFE_CLASSID) {
6034       PetscDualSpace dsp;
6035 
6036       PetscCall(PetscFEGetDualSpace((PetscFE)disc, &dsp));
6037       PetscCall(DMLabelGetNumValues(label, &depth));
6038       PetscCall(DMLabelGetValue(label, point, &h));
6039       h = depth - 1 - h;
6040       if (h) {
6041         PetscCall(PetscDualSpaceGetHeightSubspace(dsp, h, dspace));
6042       } else {
6043         *dspace = dsp;
6044       }
6045     }
6046   }
6047   PetscFunctionReturn(PETSC_SUCCESS);
6048 }
6049 
6050 static inline PetscErrorCode DMPlexVecGetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6051 {
6052   PetscScalar       *array;
6053   const PetscScalar *vArray;
6054   const PetscInt    *cone, *coneO;
6055   PetscInt           pStart, pEnd, p, numPoints, size = 0, offset = 0;
6056 
6057   PetscFunctionBeginHot;
6058   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6059   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
6060   PetscCall(DMPlexGetCone(dm, point, &cone));
6061   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
6062   if (!values || !*values) {
6063     if ((point >= pStart) && (point < pEnd)) {
6064       PetscInt dof;
6065 
6066       PetscCall(PetscSectionGetDof(section, point, &dof));
6067       size += dof;
6068     }
6069     for (p = 0; p < numPoints; ++p) {
6070       const PetscInt cp = cone[p];
6071       PetscInt       dof;
6072 
6073       if ((cp < pStart) || (cp >= pEnd)) continue;
6074       PetscCall(PetscSectionGetDof(section, cp, &dof));
6075       size += dof;
6076     }
6077     if (!values) {
6078       if (csize) *csize = size;
6079       PetscFunctionReturn(PETSC_SUCCESS);
6080     }
6081     PetscCall(DMGetWorkArray(dm, size, MPIU_SCALAR, &array));
6082   } else {
6083     array = *values;
6084   }
6085   size = 0;
6086   PetscCall(VecGetArrayRead(v, &vArray));
6087   if ((point >= pStart) && (point < pEnd)) {
6088     PetscInt           dof, off, d;
6089     const PetscScalar *varr;
6090 
6091     PetscCall(PetscSectionGetDof(section, point, &dof));
6092     PetscCall(PetscSectionGetOffset(section, point, &off));
6093     varr = &vArray[off];
6094     for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
6095     size += dof;
6096   }
6097   for (p = 0; p < numPoints; ++p) {
6098     const PetscInt     cp = cone[p];
6099     PetscInt           o  = coneO[p];
6100     PetscInt           dof, off, d;
6101     const PetscScalar *varr;
6102 
6103     if ((cp < pStart) || (cp >= pEnd)) continue;
6104     PetscCall(PetscSectionGetDof(section, cp, &dof));
6105     PetscCall(PetscSectionGetOffset(section, cp, &off));
6106     varr = &vArray[off];
6107     if (o >= 0) {
6108       for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
6109     } else {
6110       for (d = dof - 1; d >= 0; --d, ++offset) array[offset] = varr[d];
6111     }
6112     size += dof;
6113   }
6114   PetscCall(VecRestoreArrayRead(v, &vArray));
6115   if (!*values) {
6116     if (csize) *csize = size;
6117     *values = array;
6118   } else {
6119     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
6120     *csize = size;
6121   }
6122   PetscFunctionReturn(PETSC_SUCCESS);
6123 }
6124 
6125 /* Compress out points not in the section */
6126 static inline PetscErrorCode CompressPoints_Private(PetscSection section, PetscInt *numPoints, PetscInt points[])
6127 {
6128   const PetscInt np = *numPoints;
6129   PetscInt       pStart, pEnd, p, q;
6130 
6131   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6132   for (p = 0, q = 0; p < np; ++p) {
6133     const PetscInt r = points[p * 2];
6134     if ((r >= pStart) && (r < pEnd)) {
6135       points[q * 2]     = r;
6136       points[q * 2 + 1] = points[p * 2 + 1];
6137       ++q;
6138     }
6139   }
6140   *numPoints = q;
6141   return PETSC_SUCCESS;
6142 }
6143 
6144 /* Compressed closure does not apply closure permutation */
6145 PetscErrorCode DMPlexGetCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt ornt, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
6146 {
6147   const PetscInt *cla = NULL;
6148   PetscInt        np, *pts = NULL;
6149 
6150   PetscFunctionBeginHot;
6151   PetscCall(PetscSectionGetClosureIndex(section, (PetscObject)dm, clSec, clPoints));
6152   if (!ornt && *clPoints) {
6153     PetscInt dof, off;
6154 
6155     PetscCall(PetscSectionGetDof(*clSec, point, &dof));
6156     PetscCall(PetscSectionGetOffset(*clSec, point, &off));
6157     PetscCall(ISGetIndices(*clPoints, &cla));
6158     np  = dof / 2;
6159     pts = (PetscInt *)&cla[off];
6160   } else {
6161     PetscCall(DMPlexGetTransitiveClosure_Internal(dm, point, ornt, PETSC_TRUE, &np, &pts));
6162     PetscCall(CompressPoints_Private(section, &np, pts));
6163   }
6164   *numPoints = np;
6165   *points    = pts;
6166   *clp       = cla;
6167   PetscFunctionReturn(PETSC_SUCCESS);
6168 }
6169 
6170 PetscErrorCode DMPlexRestoreCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
6171 {
6172   PetscFunctionBeginHot;
6173   if (!*clPoints) {
6174     PetscCall(DMPlexRestoreTransitiveClosure(dm, point, PETSC_TRUE, numPoints, points));
6175   } else {
6176     PetscCall(ISRestoreIndices(*clPoints, clp));
6177   }
6178   *numPoints = 0;
6179   *points    = NULL;
6180   *clSec     = NULL;
6181   *clPoints  = NULL;
6182   *clp       = NULL;
6183   PetscFunctionReturn(PETSC_SUCCESS);
6184 }
6185 
6186 static inline PetscErrorCode DMPlexVecGetClosure_Static(DM dm, PetscSection section, PetscInt numPoints, const PetscInt points[], const PetscInt clperm[], const PetscScalar vArray[], PetscInt *size, PetscScalar array[])
6187 {
6188   PetscInt            offset = 0, p;
6189   const PetscInt    **perms  = NULL;
6190   const PetscScalar **flips  = NULL;
6191 
6192   PetscFunctionBeginHot;
6193   *size = 0;
6194   PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
6195   for (p = 0; p < numPoints; p++) {
6196     const PetscInt     point = points[2 * p];
6197     const PetscInt    *perm  = perms ? perms[p] : NULL;
6198     const PetscScalar *flip  = flips ? flips[p] : NULL;
6199     PetscInt           dof, off, d;
6200     const PetscScalar *varr;
6201 
6202     PetscCall(PetscSectionGetDof(section, point, &dof));
6203     PetscCall(PetscSectionGetOffset(section, point, &off));
6204     varr = &vArray[off];
6205     if (clperm) {
6206       if (perm) {
6207         for (d = 0; d < dof; d++) array[clperm[offset + perm[d]]] = varr[d];
6208       } else {
6209         for (d = 0; d < dof; d++) array[clperm[offset + d]] = varr[d];
6210       }
6211       if (flip) {
6212         for (d = 0; d < dof; d++) array[clperm[offset + d]] *= flip[d];
6213       }
6214     } else {
6215       if (perm) {
6216         for (d = 0; d < dof; d++) array[offset + perm[d]] = varr[d];
6217       } else {
6218         for (d = 0; d < dof; d++) array[offset + d] = varr[d];
6219       }
6220       if (flip) {
6221         for (d = 0; d < dof; d++) array[offset + d] *= flip[d];
6222       }
6223     }
6224     offset += dof;
6225   }
6226   PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
6227   *size = offset;
6228   PetscFunctionReturn(PETSC_SUCCESS);
6229 }
6230 
6231 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[])
6232 {
6233   PetscInt offset = 0, f;
6234 
6235   PetscFunctionBeginHot;
6236   *size = 0;
6237   for (f = 0; f < numFields; ++f) {
6238     PetscInt            p;
6239     const PetscInt    **perms = NULL;
6240     const PetscScalar **flips = NULL;
6241 
6242     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6243     for (p = 0; p < numPoints; p++) {
6244       const PetscInt     point = points[2 * p];
6245       PetscInt           fdof, foff, b;
6246       const PetscScalar *varr;
6247       const PetscInt    *perm = perms ? perms[p] : NULL;
6248       const PetscScalar *flip = flips ? flips[p] : NULL;
6249 
6250       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6251       PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6252       varr = &vArray[foff];
6253       if (clperm) {
6254         if (perm) {
6255           for (b = 0; b < fdof; b++) array[clperm[offset + perm[b]]] = varr[b];
6256         } else {
6257           for (b = 0; b < fdof; b++) array[clperm[offset + b]] = varr[b];
6258         }
6259         if (flip) {
6260           for (b = 0; b < fdof; b++) array[clperm[offset + b]] *= flip[b];
6261         }
6262       } else {
6263         if (perm) {
6264           for (b = 0; b < fdof; b++) array[offset + perm[b]] = varr[b];
6265         } else {
6266           for (b = 0; b < fdof; b++) array[offset + b] = varr[b];
6267         }
6268         if (flip) {
6269           for (b = 0; b < fdof; b++) array[offset + b] *= flip[b];
6270         }
6271       }
6272       offset += fdof;
6273     }
6274     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6275   }
6276   *size = offset;
6277   PetscFunctionReturn(PETSC_SUCCESS);
6278 }
6279 
6280 PetscErrorCode DMPlexVecGetOrientedClosure_Internal(DM dm, PetscSection section, PetscBool useClPerm, Vec v, PetscInt point, PetscInt ornt, PetscInt *csize, PetscScalar *values[])
6281 {
6282   PetscSection    clSection;
6283   IS              clPoints;
6284   PetscInt       *points = NULL;
6285   const PetscInt *clp, *perm = NULL;
6286   PetscInt        depth, numFields, numPoints, asize;
6287 
6288   PetscFunctionBeginHot;
6289   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6290   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6291   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6292   PetscValidHeaderSpecific(v, VEC_CLASSID, 4);
6293   PetscCall(DMPlexGetDepth(dm, &depth));
6294   PetscCall(PetscSectionGetNumFields(section, &numFields));
6295   if (depth == 1 && numFields < 2) {
6296     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6297     PetscFunctionReturn(PETSC_SUCCESS);
6298   }
6299   /* Get points */
6300   PetscCall(DMPlexGetCompressedClosure(dm, section, point, ornt, &numPoints, &points, &clSection, &clPoints, &clp));
6301   /* Get sizes */
6302   asize = 0;
6303   for (PetscInt p = 0; p < numPoints * 2; p += 2) {
6304     PetscInt dof;
6305     PetscCall(PetscSectionGetDof(section, points[p], &dof));
6306     asize += dof;
6307   }
6308   if (values) {
6309     const PetscScalar *vArray;
6310     PetscInt           size;
6311 
6312     if (*values) {
6313       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);
6314     } else PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, values));
6315     if (useClPerm) PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, asize, &perm));
6316     PetscCall(VecGetArrayRead(v, &vArray));
6317     /* Get values */
6318     if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, numPoints, points, numFields, perm, vArray, &size, *values));
6319     else PetscCall(DMPlexVecGetClosure_Static(dm, section, numPoints, points, perm, vArray, &size, *values));
6320     PetscCheck(asize == size, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Section size %" PetscInt_FMT " does not match Vec closure size %" PetscInt_FMT, asize, size);
6321     /* Cleanup array */
6322     PetscCall(VecRestoreArrayRead(v, &vArray));
6323   }
6324   if (csize) *csize = asize;
6325   /* Cleanup points */
6326   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6327   PetscFunctionReturn(PETSC_SUCCESS);
6328 }
6329 
6330 /*@C
6331   DMPlexVecGetClosure - Get an array of the values on the closure of 'point'
6332 
6333   Not collective
6334 
6335   Input Parameters:
6336 + dm      - The `DM`
6337 . section - The section describing the layout in `v`, or `NULL` to use the default section
6338 . v       - The local vector
6339 - point   - The point in the `DM`
6340 
6341   Input/Output Parameters:
6342 + csize  - The size of the input values array, or `NULL`; on output the number of values in the closure
6343 - values - An array to use for the values, or `NULL` to have it allocated automatically;
6344            if the user provided `NULL`, it is a borrowed array and should not be freed
6345 
6346   Level: intermediate
6347 
6348   Notes:
6349   `DMPlexVecGetClosure()`/`DMPlexVecRestoreClosure()` only allocates the values array if it set to `NULL` in the
6350   calling function. This is because `DMPlexVecGetClosure()` is typically called in the inner loop of a `Vec` or `Mat`
6351   assembly function, and a user may already have allocated storage for this operation.
6352 
6353   A typical use could be
6354 .vb
6355    values = NULL;
6356    PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
6357    for (cl = 0; cl < clSize; ++cl) {
6358      <Compute on closure>
6359    }
6360    PetscCall(DMPlexVecRestoreClosure(dm, NULL, v, p, &clSize, &values));
6361 .ve
6362   or
6363 .vb
6364    PetscMalloc1(clMaxSize, &values);
6365    for (p = pStart; p < pEnd; ++p) {
6366      clSize = clMaxSize;
6367      PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
6368      for (cl = 0; cl < clSize; ++cl) {
6369        <Compute on closure>
6370      }
6371    }
6372    PetscFree(values);
6373 .ve
6374 
6375   Fortran Notes:
6376   The `csize` argument is not present in the Fortran binding since it is internal to the array.
6377 
6378 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecRestoreClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6379 @*/
6380 PetscErrorCode DMPlexVecGetClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6381 {
6382   PetscFunctionBeginHot;
6383   PetscCall(DMPlexVecGetOrientedClosure_Internal(dm, section, PETSC_TRUE, v, point, 0, csize, values));
6384   PetscFunctionReturn(PETSC_SUCCESS);
6385 }
6386 
6387 PetscErrorCode DMPlexVecGetClosureAtDepth_Internal(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt depth, PetscInt *csize, PetscScalar *values[])
6388 {
6389   DMLabel            depthLabel;
6390   PetscSection       clSection;
6391   IS                 clPoints;
6392   PetscScalar       *array;
6393   const PetscScalar *vArray;
6394   PetscInt          *points = NULL;
6395   const PetscInt    *clp, *perm = NULL;
6396   PetscInt           mdepth, numFields, numPoints, Np = 0, p, clsize, size;
6397 
6398   PetscFunctionBeginHot;
6399   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6400   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6401   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6402   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6403   PetscCall(DMPlexGetDepth(dm, &mdepth));
6404   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
6405   PetscCall(PetscSectionGetNumFields(section, &numFields));
6406   if (mdepth == 1 && numFields < 2) {
6407     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6408     PetscFunctionReturn(PETSC_SUCCESS);
6409   }
6410   /* Get points */
6411   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
6412   for (clsize = 0, p = 0; p < Np; p++) {
6413     PetscInt dof;
6414     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
6415     clsize += dof;
6416   }
6417   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &perm));
6418   /* Filter points */
6419   for (p = 0; p < numPoints * 2; p += 2) {
6420     PetscInt dep;
6421 
6422     PetscCall(DMLabelGetValue(depthLabel, points[p], &dep));
6423     if (dep != depth) continue;
6424     points[Np * 2 + 0] = points[p];
6425     points[Np * 2 + 1] = points[p + 1];
6426     ++Np;
6427   }
6428   /* Get array */
6429   if (!values || !*values) {
6430     PetscInt asize = 0, dof;
6431 
6432     for (p = 0; p < Np * 2; p += 2) {
6433       PetscCall(PetscSectionGetDof(section, points[p], &dof));
6434       asize += dof;
6435     }
6436     if (!values) {
6437       PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6438       if (csize) *csize = asize;
6439       PetscFunctionReturn(PETSC_SUCCESS);
6440     }
6441     PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, &array));
6442   } else {
6443     array = *values;
6444   }
6445   PetscCall(VecGetArrayRead(v, &vArray));
6446   /* Get values */
6447   if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, Np, points, numFields, perm, vArray, &size, array));
6448   else PetscCall(DMPlexVecGetClosure_Static(dm, section, Np, points, perm, vArray, &size, array));
6449   /* Cleanup points */
6450   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6451   /* Cleanup array */
6452   PetscCall(VecRestoreArrayRead(v, &vArray));
6453   if (!*values) {
6454     if (csize) *csize = size;
6455     *values = array;
6456   } else {
6457     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
6458     *csize = size;
6459   }
6460   PetscFunctionReturn(PETSC_SUCCESS);
6461 }
6462 
6463 /*@C
6464   DMPlexVecRestoreClosure - Restore the array of the values on the closure of 'point'
6465 
6466   Not collective
6467 
6468   Input Parameters:
6469 + dm      - The `DM`
6470 . section - The section describing the layout in `v`, or `NULL` to use the default section
6471 . v       - The local vector
6472 . point   - The point in the `DM`
6473 . csize   - The number of values in the closure, or `NULL`
6474 - values  - The array of values, which is a borrowed array and should not be freed
6475 
6476   Level: intermediate
6477 
6478   Note:
6479   The array values are discarded and not copied back into `v`. In order to copy values back to `v`, use `DMPlexVecSetClosure()`
6480 
6481   Fortran Notes:
6482   The `csize` argument is not present in the Fortran binding since it is internal to the array.
6483 
6484 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6485 @*/
6486 PetscErrorCode DMPlexVecRestoreClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6487 {
6488   PetscInt size = 0;
6489 
6490   PetscFunctionBegin;
6491   /* Should work without recalculating size */
6492   PetscCall(DMRestoreWorkArray(dm, size, MPIU_SCALAR, (void *)values));
6493   *values = NULL;
6494   PetscFunctionReturn(PETSC_SUCCESS);
6495 }
6496 
6497 static inline void add(PetscScalar *x, PetscScalar y)
6498 {
6499   *x += y;
6500 }
6501 static inline void insert(PetscScalar *x, PetscScalar y)
6502 {
6503   *x = y;
6504 }
6505 
6506 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[])
6507 {
6508   PetscInt        cdof;  /* The number of constraints on this point */
6509   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6510   PetscScalar    *a;
6511   PetscInt        off, cind = 0, k;
6512 
6513   PetscFunctionBegin;
6514   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6515   PetscCall(PetscSectionGetOffset(section, point, &off));
6516   a = &array[off];
6517   if (!cdof || setBC) {
6518     if (clperm) {
6519       if (perm) {
6520         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6521       } else {
6522         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6523       }
6524     } else {
6525       if (perm) {
6526         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6527       } else {
6528         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6529       }
6530     }
6531   } else {
6532     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6533     if (clperm) {
6534       if (perm) {
6535         for (k = 0; k < dof; ++k) {
6536           if ((cind < cdof) && (k == cdofs[cind])) {
6537             ++cind;
6538             continue;
6539           }
6540           fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6541         }
6542       } else {
6543         for (k = 0; k < dof; ++k) {
6544           if ((cind < cdof) && (k == cdofs[cind])) {
6545             ++cind;
6546             continue;
6547           }
6548           fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6549         }
6550       }
6551     } else {
6552       if (perm) {
6553         for (k = 0; k < dof; ++k) {
6554           if ((cind < cdof) && (k == cdofs[cind])) {
6555             ++cind;
6556             continue;
6557           }
6558           fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6559         }
6560       } else {
6561         for (k = 0; k < dof; ++k) {
6562           if ((cind < cdof) && (k == cdofs[cind])) {
6563             ++cind;
6564             continue;
6565           }
6566           fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6567         }
6568       }
6569     }
6570   }
6571   PetscFunctionReturn(PETSC_SUCCESS);
6572 }
6573 
6574 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[])
6575 {
6576   PetscInt        cdof;  /* The number of constraints on this point */
6577   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6578   PetscScalar    *a;
6579   PetscInt        off, cind = 0, k;
6580 
6581   PetscFunctionBegin;
6582   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6583   PetscCall(PetscSectionGetOffset(section, point, &off));
6584   a = &array[off];
6585   if (cdof) {
6586     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6587     if (clperm) {
6588       if (perm) {
6589         for (k = 0; k < dof; ++k) {
6590           if ((cind < cdof) && (k == cdofs[cind])) {
6591             fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6592             cind++;
6593           }
6594         }
6595       } else {
6596         for (k = 0; k < dof; ++k) {
6597           if ((cind < cdof) && (k == cdofs[cind])) {
6598             fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6599             cind++;
6600           }
6601         }
6602       }
6603     } else {
6604       if (perm) {
6605         for (k = 0; k < dof; ++k) {
6606           if ((cind < cdof) && (k == cdofs[cind])) {
6607             fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6608             cind++;
6609           }
6610         }
6611       } else {
6612         for (k = 0; k < dof; ++k) {
6613           if ((cind < cdof) && (k == cdofs[cind])) {
6614             fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6615             cind++;
6616           }
6617         }
6618       }
6619     }
6620   }
6621   PetscFunctionReturn(PETSC_SUCCESS);
6622 }
6623 
6624 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[])
6625 {
6626   PetscScalar    *a;
6627   PetscInt        fdof, foff, fcdof, foffset = *offset;
6628   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6629   PetscInt        cind = 0, b;
6630 
6631   PetscFunctionBegin;
6632   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6633   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6634   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6635   a = &array[foff];
6636   if (!fcdof || setBC) {
6637     if (clperm) {
6638       if (perm) {
6639         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6640       } else {
6641         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6642       }
6643     } else {
6644       if (perm) {
6645         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6646       } else {
6647         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6648       }
6649     }
6650   } else {
6651     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6652     if (clperm) {
6653       if (perm) {
6654         for (b = 0; b < fdof; b++) {
6655           if ((cind < fcdof) && (b == fcdofs[cind])) {
6656             ++cind;
6657             continue;
6658           }
6659           fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6660         }
6661       } else {
6662         for (b = 0; b < fdof; b++) {
6663           if ((cind < fcdof) && (b == fcdofs[cind])) {
6664             ++cind;
6665             continue;
6666           }
6667           fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6668         }
6669       }
6670     } else {
6671       if (perm) {
6672         for (b = 0; b < fdof; b++) {
6673           if ((cind < fcdof) && (b == fcdofs[cind])) {
6674             ++cind;
6675             continue;
6676           }
6677           fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6678         }
6679       } else {
6680         for (b = 0; b < fdof; b++) {
6681           if ((cind < fcdof) && (b == fcdofs[cind])) {
6682             ++cind;
6683             continue;
6684           }
6685           fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6686         }
6687       }
6688     }
6689   }
6690   *offset += fdof;
6691   PetscFunctionReturn(PETSC_SUCCESS);
6692 }
6693 
6694 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[])
6695 {
6696   PetscScalar    *a;
6697   PetscInt        fdof, foff, fcdof, foffset = *offset;
6698   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6699   PetscInt        Nc, cind = 0, ncind = 0, b;
6700   PetscBool       ncSet, fcSet;
6701 
6702   PetscFunctionBegin;
6703   PetscCall(PetscSectionGetFieldComponents(section, f, &Nc));
6704   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6705   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6706   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6707   a = &array[foff];
6708   if (fcdof) {
6709     /* We just override fcdof and fcdofs with Ncc and comps */
6710     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6711     if (clperm) {
6712       if (perm) {
6713         if (comps) {
6714           for (b = 0; b < fdof; b++) {
6715             ncSet = fcSet = PETSC_FALSE;
6716             if (b % Nc == comps[ncind]) {
6717               ncind = (ncind + 1) % Ncc;
6718               ncSet = PETSC_TRUE;
6719             }
6720             if ((cind < fcdof) && (b == fcdofs[cind])) {
6721               ++cind;
6722               fcSet = PETSC_TRUE;
6723             }
6724             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6725           }
6726         } else {
6727           for (b = 0; b < fdof; b++) {
6728             if ((cind < fcdof) && (b == fcdofs[cind])) {
6729               fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6730               ++cind;
6731             }
6732           }
6733         }
6734       } else {
6735         if (comps) {
6736           for (b = 0; b < fdof; b++) {
6737             ncSet = fcSet = PETSC_FALSE;
6738             if (b % Nc == comps[ncind]) {
6739               ncind = (ncind + 1) % Ncc;
6740               ncSet = PETSC_TRUE;
6741             }
6742             if ((cind < fcdof) && (b == fcdofs[cind])) {
6743               ++cind;
6744               fcSet = PETSC_TRUE;
6745             }
6746             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6747           }
6748         } else {
6749           for (b = 0; b < fdof; b++) {
6750             if ((cind < fcdof) && (b == fcdofs[cind])) {
6751               fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6752               ++cind;
6753             }
6754           }
6755         }
6756       }
6757     } else {
6758       if (perm) {
6759         if (comps) {
6760           for (b = 0; b < fdof; b++) {
6761             ncSet = fcSet = PETSC_FALSE;
6762             if (b % Nc == comps[ncind]) {
6763               ncind = (ncind + 1) % Ncc;
6764               ncSet = PETSC_TRUE;
6765             }
6766             if ((cind < fcdof) && (b == fcdofs[cind])) {
6767               ++cind;
6768               fcSet = PETSC_TRUE;
6769             }
6770             if (ncSet && fcSet) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6771           }
6772         } else {
6773           for (b = 0; b < fdof; b++) {
6774             if ((cind < fcdof) && (b == fcdofs[cind])) {
6775               fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6776               ++cind;
6777             }
6778           }
6779         }
6780       } else {
6781         if (comps) {
6782           for (b = 0; b < fdof; b++) {
6783             ncSet = fcSet = PETSC_FALSE;
6784             if (b % Nc == comps[ncind]) {
6785               ncind = (ncind + 1) % Ncc;
6786               ncSet = PETSC_TRUE;
6787             }
6788             if ((cind < fcdof) && (b == fcdofs[cind])) {
6789               ++cind;
6790               fcSet = PETSC_TRUE;
6791             }
6792             if (ncSet && fcSet) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6793           }
6794         } else {
6795           for (b = 0; b < fdof; b++) {
6796             if ((cind < fcdof) && (b == fcdofs[cind])) {
6797               fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6798               ++cind;
6799             }
6800           }
6801         }
6802       }
6803     }
6804   }
6805   *offset += fdof;
6806   PetscFunctionReturn(PETSC_SUCCESS);
6807 }
6808 
6809 static inline PetscErrorCode DMPlexVecSetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6810 {
6811   PetscScalar    *array;
6812   const PetscInt *cone, *coneO;
6813   PetscInt        pStart, pEnd, p, numPoints, off, dof;
6814 
6815   PetscFunctionBeginHot;
6816   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6817   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
6818   PetscCall(DMPlexGetCone(dm, point, &cone));
6819   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
6820   PetscCall(VecGetArray(v, &array));
6821   for (p = 0, off = 0; p <= numPoints; ++p, off += dof) {
6822     const PetscInt cp = !p ? point : cone[p - 1];
6823     const PetscInt o  = !p ? 0 : coneO[p - 1];
6824 
6825     if ((cp < pStart) || (cp >= pEnd)) {
6826       dof = 0;
6827       continue;
6828     }
6829     PetscCall(PetscSectionGetDof(section, cp, &dof));
6830     /* ADD_VALUES */
6831     {
6832       const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6833       PetscScalar    *a;
6834       PetscInt        cdof, coff, cind = 0, k;
6835 
6836       PetscCall(PetscSectionGetConstraintDof(section, cp, &cdof));
6837       PetscCall(PetscSectionGetOffset(section, cp, &coff));
6838       a = &array[coff];
6839       if (!cdof) {
6840         if (o >= 0) {
6841           for (k = 0; k < dof; ++k) a[k] += values[off + k];
6842         } else {
6843           for (k = 0; k < dof; ++k) a[k] += values[off + dof - k - 1];
6844         }
6845       } else {
6846         PetscCall(PetscSectionGetConstraintIndices(section, cp, &cdofs));
6847         if (o >= 0) {
6848           for (k = 0; k < dof; ++k) {
6849             if ((cind < cdof) && (k == cdofs[cind])) {
6850               ++cind;
6851               continue;
6852             }
6853             a[k] += values[off + k];
6854           }
6855         } else {
6856           for (k = 0; k < dof; ++k) {
6857             if ((cind < cdof) && (k == cdofs[cind])) {
6858               ++cind;
6859               continue;
6860             }
6861             a[k] += values[off + dof - k - 1];
6862           }
6863         }
6864       }
6865     }
6866   }
6867   PetscCall(VecRestoreArray(v, &array));
6868   PetscFunctionReturn(PETSC_SUCCESS);
6869 }
6870 
6871 /*@C
6872   DMPlexVecSetClosure - Set an array of the values on the closure of `point`
6873 
6874   Not collective
6875 
6876   Input Parameters:
6877 + dm      - The `DM`
6878 . section - The section describing the layout in `v`, or `NULL` to use the default section
6879 . v       - The local vector
6880 . point   - The point in the `DM`
6881 . values  - The array of values
6882 - mode    - The insert mode. One of `INSERT_ALL_VALUES`, `ADD_ALL_VALUES`, `INSERT_VALUES`, `ADD_VALUES`, `INSERT_BC_VALUES`, and `ADD_BC_VALUES`,
6883          where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions.
6884 
6885   Level: intermediate
6886 
6887 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`
6888 @*/
6889 PetscErrorCode DMPlexVecSetClosure(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6890 {
6891   PetscSection    clSection;
6892   IS              clPoints;
6893   PetscScalar    *array;
6894   PetscInt       *points = NULL;
6895   const PetscInt *clp, *clperm = NULL;
6896   PetscInt        depth, numFields, numPoints, p, clsize;
6897 
6898   PetscFunctionBeginHot;
6899   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6900   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6901   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6902   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6903   PetscCall(DMPlexGetDepth(dm, &depth));
6904   PetscCall(PetscSectionGetNumFields(section, &numFields));
6905   if (depth == 1 && numFields < 2 && mode == ADD_VALUES) {
6906     PetscCall(DMPlexVecSetClosure_Depth1_Static(dm, section, v, point, values, mode));
6907     PetscFunctionReturn(PETSC_SUCCESS);
6908   }
6909   /* Get points */
6910   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
6911   for (clsize = 0, p = 0; p < numPoints; p++) {
6912     PetscInt dof;
6913     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
6914     clsize += dof;
6915   }
6916   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
6917   /* Get array */
6918   PetscCall(VecGetArray(v, &array));
6919   /* Get values */
6920   if (numFields > 0) {
6921     PetscInt offset = 0, f;
6922     for (f = 0; f < numFields; ++f) {
6923       const PetscInt    **perms = NULL;
6924       const PetscScalar **flips = NULL;
6925 
6926       PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6927       switch (mode) {
6928       case INSERT_VALUES:
6929         for (p = 0; p < numPoints; p++) {
6930           const PetscInt     point = points[2 * p];
6931           const PetscInt    *perm  = perms ? perms[p] : NULL;
6932           const PetscScalar *flip  = flips ? flips[p] : NULL;
6933           PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, clperm, values, &offset, array));
6934         }
6935         break;
6936       case INSERT_ALL_VALUES:
6937         for (p = 0; p < numPoints; p++) {
6938           const PetscInt     point = points[2 * p];
6939           const PetscInt    *perm  = perms ? perms[p] : NULL;
6940           const PetscScalar *flip  = flips ? flips[p] : NULL;
6941           PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, clperm, values, &offset, array));
6942         }
6943         break;
6944       case INSERT_BC_VALUES:
6945         for (p = 0; p < numPoints; p++) {
6946           const PetscInt     point = points[2 * p];
6947           const PetscInt    *perm  = perms ? perms[p] : NULL;
6948           const PetscScalar *flip  = flips ? flips[p] : NULL;
6949           PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, insert, clperm, values, &offset, array));
6950         }
6951         break;
6952       case ADD_VALUES:
6953         for (p = 0; p < numPoints; p++) {
6954           const PetscInt     point = points[2 * p];
6955           const PetscInt    *perm  = perms ? perms[p] : NULL;
6956           const PetscScalar *flip  = flips ? flips[p] : NULL;
6957           PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, clperm, values, &offset, array));
6958         }
6959         break;
6960       case ADD_ALL_VALUES:
6961         for (p = 0; p < numPoints; p++) {
6962           const PetscInt     point = points[2 * p];
6963           const PetscInt    *perm  = perms ? perms[p] : NULL;
6964           const PetscScalar *flip  = flips ? flips[p] : NULL;
6965           PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, clperm, values, &offset, array));
6966         }
6967         break;
6968       case ADD_BC_VALUES:
6969         for (p = 0; p < numPoints; p++) {
6970           const PetscInt     point = points[2 * p];
6971           const PetscInt    *perm  = perms ? perms[p] : NULL;
6972           const PetscScalar *flip  = flips ? flips[p] : NULL;
6973           PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, add, clperm, values, &offset, array));
6974         }
6975         break;
6976       default:
6977         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6978       }
6979       PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6980     }
6981   } else {
6982     PetscInt            dof, off;
6983     const PetscInt    **perms = NULL;
6984     const PetscScalar **flips = NULL;
6985 
6986     PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
6987     switch (mode) {
6988     case INSERT_VALUES:
6989       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6990         const PetscInt     point = points[2 * p];
6991         const PetscInt    *perm  = perms ? perms[p] : NULL;
6992         const PetscScalar *flip  = flips ? flips[p] : NULL;
6993         PetscCall(PetscSectionGetDof(section, point, &dof));
6994         PetscCall(updatePoint_private(section, point, dof, insert, PETSC_FALSE, perm, flip, clperm, values, off, array));
6995       }
6996       break;
6997     case INSERT_ALL_VALUES:
6998       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6999         const PetscInt     point = points[2 * p];
7000         const PetscInt    *perm  = perms ? perms[p] : NULL;
7001         const PetscScalar *flip  = flips ? flips[p] : NULL;
7002         PetscCall(PetscSectionGetDof(section, point, &dof));
7003         PetscCall(updatePoint_private(section, point, dof, insert, PETSC_TRUE, perm, flip, clperm, values, off, array));
7004       }
7005       break;
7006     case INSERT_BC_VALUES:
7007       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7008         const PetscInt     point = points[2 * p];
7009         const PetscInt    *perm  = perms ? perms[p] : NULL;
7010         const PetscScalar *flip  = flips ? flips[p] : NULL;
7011         PetscCall(PetscSectionGetDof(section, point, &dof));
7012         PetscCall(updatePointBC_private(section, point, dof, insert, perm, flip, clperm, values, off, array));
7013       }
7014       break;
7015     case ADD_VALUES:
7016       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7017         const PetscInt     point = points[2 * p];
7018         const PetscInt    *perm  = perms ? perms[p] : NULL;
7019         const PetscScalar *flip  = flips ? flips[p] : NULL;
7020         PetscCall(PetscSectionGetDof(section, point, &dof));
7021         PetscCall(updatePoint_private(section, point, dof, add, PETSC_FALSE, perm, flip, clperm, values, off, array));
7022       }
7023       break;
7024     case ADD_ALL_VALUES:
7025       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7026         const PetscInt     point = points[2 * p];
7027         const PetscInt    *perm  = perms ? perms[p] : NULL;
7028         const PetscScalar *flip  = flips ? flips[p] : NULL;
7029         PetscCall(PetscSectionGetDof(section, point, &dof));
7030         PetscCall(updatePoint_private(section, point, dof, add, PETSC_TRUE, perm, flip, clperm, values, off, array));
7031       }
7032       break;
7033     case ADD_BC_VALUES:
7034       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7035         const PetscInt     point = points[2 * p];
7036         const PetscInt    *perm  = perms ? perms[p] : NULL;
7037         const PetscScalar *flip  = flips ? flips[p] : NULL;
7038         PetscCall(PetscSectionGetDof(section, point, &dof));
7039         PetscCall(updatePointBC_private(section, point, dof, add, perm, flip, clperm, values, off, array));
7040       }
7041       break;
7042     default:
7043       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
7044     }
7045     PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
7046   }
7047   /* Cleanup points */
7048   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
7049   /* Cleanup array */
7050   PetscCall(VecRestoreArray(v, &array));
7051   PetscFunctionReturn(PETSC_SUCCESS);
7052 }
7053 
7054 /* Check whether the given point is in the label. If not, update the offset to skip this point */
7055 static inline PetscErrorCode CheckPoint_Private(DMLabel label, PetscInt labelId, PetscSection section, PetscInt point, PetscInt f, PetscInt *offset, PetscBool *contains)
7056 {
7057   PetscFunctionBegin;
7058   *contains = PETSC_TRUE;
7059   if (label) {
7060     PetscInt fdof;
7061 
7062     PetscCall(DMLabelStratumHasPoint(label, labelId, point, contains));
7063     if (!*contains) {
7064       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7065       *offset += fdof;
7066       PetscFunctionReturn(PETSC_SUCCESS);
7067     }
7068   }
7069   PetscFunctionReturn(PETSC_SUCCESS);
7070 }
7071 
7072 /* Unlike DMPlexVecSetClosure(), this uses plex-native closure permutation, not a user-specified permutation such as DMPlexSetClosurePermutationTensor(). */
7073 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)
7074 {
7075   PetscSection    clSection;
7076   IS              clPoints;
7077   PetscScalar    *array;
7078   PetscInt       *points = NULL;
7079   const PetscInt *clp;
7080   PetscInt        numFields, numPoints, p;
7081   PetscInt        offset = 0, f;
7082 
7083   PetscFunctionBeginHot;
7084   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7085   if (!section) PetscCall(DMGetLocalSection(dm, &section));
7086   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7087   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
7088   PetscCall(PetscSectionGetNumFields(section, &numFields));
7089   /* Get points */
7090   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
7091   /* Get array */
7092   PetscCall(VecGetArray(v, &array));
7093   /* Get values */
7094   for (f = 0; f < numFields; ++f) {
7095     const PetscInt    **perms = NULL;
7096     const PetscScalar **flips = NULL;
7097     PetscBool           contains;
7098 
7099     if (!fieldActive[f]) {
7100       for (p = 0; p < numPoints * 2; p += 2) {
7101         PetscInt fdof;
7102         PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
7103         offset += fdof;
7104       }
7105       continue;
7106     }
7107     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7108     switch (mode) {
7109     case INSERT_VALUES:
7110       for (p = 0; p < numPoints; p++) {
7111         const PetscInt     point = points[2 * p];
7112         const PetscInt    *perm  = perms ? perms[p] : NULL;
7113         const PetscScalar *flip  = flips ? flips[p] : NULL;
7114         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7115         if (!contains) continue;
7116         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, NULL, values, &offset, array));
7117       }
7118       break;
7119     case INSERT_ALL_VALUES:
7120       for (p = 0; p < numPoints; p++) {
7121         const PetscInt     point = points[2 * p];
7122         const PetscInt    *perm  = perms ? perms[p] : NULL;
7123         const PetscScalar *flip  = flips ? flips[p] : NULL;
7124         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7125         if (!contains) continue;
7126         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, NULL, values, &offset, array));
7127       }
7128       break;
7129     case INSERT_BC_VALUES:
7130       for (p = 0; p < numPoints; p++) {
7131         const PetscInt     point = points[2 * p];
7132         const PetscInt    *perm  = perms ? perms[p] : NULL;
7133         const PetscScalar *flip  = flips ? flips[p] : NULL;
7134         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7135         if (!contains) continue;
7136         PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, Ncc, comps, insert, NULL, values, &offset, array));
7137       }
7138       break;
7139     case ADD_VALUES:
7140       for (p = 0; p < numPoints; p++) {
7141         const PetscInt     point = points[2 * p];
7142         const PetscInt    *perm  = perms ? perms[p] : NULL;
7143         const PetscScalar *flip  = flips ? flips[p] : NULL;
7144         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7145         if (!contains) continue;
7146         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, NULL, values, &offset, array));
7147       }
7148       break;
7149     case ADD_ALL_VALUES:
7150       for (p = 0; p < numPoints; p++) {
7151         const PetscInt     point = points[2 * p];
7152         const PetscInt    *perm  = perms ? perms[p] : NULL;
7153         const PetscScalar *flip  = flips ? flips[p] : NULL;
7154         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7155         if (!contains) continue;
7156         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, NULL, values, &offset, array));
7157       }
7158       break;
7159     default:
7160       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
7161     }
7162     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7163   }
7164   /* Cleanup points */
7165   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
7166   /* Cleanup array */
7167   PetscCall(VecRestoreArray(v, &array));
7168   PetscFunctionReturn(PETSC_SUCCESS);
7169 }
7170 
7171 static PetscErrorCode DMPlexPrintMatSetValues(PetscViewer viewer, Mat A, PetscInt point, PetscInt numRIndices, const PetscInt rindices[], PetscInt numCIndices, const PetscInt cindices[], const PetscScalar values[])
7172 {
7173   PetscMPIInt rank;
7174   PetscInt    i, j;
7175 
7176   PetscFunctionBegin;
7177   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7178   PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat for point %" PetscInt_FMT "\n", rank, point));
7179   for (i = 0; i < numRIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat row indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, rindices[i]));
7180   for (i = 0; i < numCIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat col indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, cindices[i]));
7181   numCIndices = numCIndices ? numCIndices : numRIndices;
7182   if (!values) PetscFunctionReturn(PETSC_SUCCESS);
7183   for (i = 0; i < numRIndices; i++) {
7184     PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]", rank));
7185     for (j = 0; j < numCIndices; j++) {
7186 #if defined(PETSC_USE_COMPLEX)
7187       PetscCall(PetscViewerASCIIPrintf(viewer, " (%g,%g)", (double)PetscRealPart(values[i * numCIndices + j]), (double)PetscImaginaryPart(values[i * numCIndices + j])));
7188 #else
7189       PetscCall(PetscViewerASCIIPrintf(viewer, " %g", (double)values[i * numCIndices + j]));
7190 #endif
7191     }
7192     PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
7193   }
7194   PetscFunctionReturn(PETSC_SUCCESS);
7195 }
7196 
7197 /*
7198   DMPlexGetIndicesPoint_Internal - Add the indices for dofs on a point to an index array
7199 
7200   Input Parameters:
7201 + section - The section for this data layout
7202 . islocal - Is the section (and thus indices being requested) local or global?
7203 . point   - The point contributing dofs with these indices
7204 . off     - The global offset of this point
7205 . loff    - The local offset of each field
7206 . setBC   - The flag determining whether to include indices of boundary values
7207 . perm    - A permutation of the dofs on this point, or NULL
7208 - indperm - A permutation of the entire indices array, or NULL
7209 
7210   Output Parameter:
7211 . indices - Indices for dofs on this point
7212 
7213   Level: developer
7214 
7215   Note: The indices could be local or global, depending on the value of 'off'.
7216 */
7217 PetscErrorCode DMPlexGetIndicesPoint_Internal(PetscSection section, PetscBool islocal, PetscInt point, PetscInt off, PetscInt *loff, PetscBool setBC, const PetscInt perm[], const PetscInt indperm[], PetscInt indices[])
7218 {
7219   PetscInt        dof;   /* The number of unknowns on this point */
7220   PetscInt        cdof;  /* The number of constraints on this point */
7221   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
7222   PetscInt        cind = 0, k;
7223 
7224   PetscFunctionBegin;
7225   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
7226   PetscCall(PetscSectionGetDof(section, point, &dof));
7227   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
7228   if (!cdof || setBC) {
7229     for (k = 0; k < dof; ++k) {
7230       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
7231       const PetscInt ind    = indperm ? indperm[preind] : preind;
7232 
7233       indices[ind] = off + k;
7234     }
7235   } else {
7236     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
7237     for (k = 0; k < dof; ++k) {
7238       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
7239       const PetscInt ind    = indperm ? indperm[preind] : preind;
7240 
7241       if ((cind < cdof) && (k == cdofs[cind])) {
7242         /* Insert check for returning constrained indices */
7243         indices[ind] = -(off + k + 1);
7244         ++cind;
7245       } else {
7246         indices[ind] = off + k - (islocal ? 0 : cind);
7247       }
7248     }
7249   }
7250   *loff += dof;
7251   PetscFunctionReturn(PETSC_SUCCESS);
7252 }
7253 
7254 /*
7255  DMPlexGetIndicesPointFields_Internal - gets section indices for a point in its canonical ordering.
7256 
7257  Input Parameters:
7258 + section - a section (global or local)
7259 - islocal - `PETSC_TRUE` if requesting local indices (i.e., section is local); `PETSC_FALSE` for global
7260 . point - point within section
7261 . off - The offset of this point in the (local or global) indexed space - should match islocal and (usually) the section
7262 . foffs - array of length numFields containing the offset in canonical point ordering (the location in indices) of each field
7263 . setBC - identify constrained (boundary condition) points via involution.
7264 . perms - perms[f][permsoff][:] is a permutation of dofs within each field
7265 . permsoff - offset
7266 - indperm - index permutation
7267 
7268  Output Parameter:
7269 . foffs - each entry is incremented by the number of (unconstrained if setBC=FALSE) dofs in that field
7270 . indices - array to hold indices (as defined by section) of each dof associated with point
7271 
7272  Notes:
7273  If section is local and setBC=true, there is no distinction between constrained and unconstrained dofs.
7274  If section is local and setBC=false, the indices for constrained points are the involution -(i+1) of their position
7275  in the local vector.
7276 
7277  If section is global and setBC=false, the indices for constrained points are negative (and their value is not
7278  significant).  It is invalid to call with a global section and setBC=true.
7279 
7280  Developer Note:
7281  The section is only used for field layout, so islocal is technically a statement about the offset (off).  At some point
7282  in the future, global sections may have fields set, in which case we could pass the global section and obtain the
7283  offset could be obtained from the section instead of passing it explicitly as we do now.
7284 
7285  Example:
7286  Suppose a point contains one field with three components, and for which the unconstrained indices are {10, 11, 12}.
7287  When the middle component is constrained, we get the array {10, -12, 12} for (islocal=TRUE, setBC=FALSE).
7288  Note that -12 is the involution of 11, so the user can involute negative indices to recover local indices.
7289  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.
7290 
7291  Level: developer
7292 */
7293 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[])
7294 {
7295   PetscInt numFields, foff, f;
7296 
7297   PetscFunctionBegin;
7298   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
7299   PetscCall(PetscSectionGetNumFields(section, &numFields));
7300   for (f = 0, foff = 0; f < numFields; ++f) {
7301     PetscInt        fdof, cfdof;
7302     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7303     PetscInt        cind = 0, b;
7304     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7305 
7306     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7307     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7308     if (!cfdof || setBC) {
7309       for (b = 0; b < fdof; ++b) {
7310         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7311         const PetscInt ind    = indperm ? indperm[preind] : preind;
7312 
7313         indices[ind] = off + foff + b;
7314       }
7315     } else {
7316       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7317       for (b = 0; b < fdof; ++b) {
7318         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7319         const PetscInt ind    = indperm ? indperm[preind] : preind;
7320 
7321         if ((cind < cfdof) && (b == fcdofs[cind])) {
7322           indices[ind] = -(off + foff + b + 1);
7323           ++cind;
7324         } else {
7325           indices[ind] = off + foff + b - (islocal ? 0 : cind);
7326         }
7327       }
7328     }
7329     foff += (setBC || islocal ? fdof : (fdof - cfdof));
7330     foffs[f] += fdof;
7331   }
7332   PetscFunctionReturn(PETSC_SUCCESS);
7333 }
7334 
7335 /*
7336   This version believes the globalSection offsets for each field, rather than just the point offset
7337 
7338  . foffs - The offset into 'indices' for each field, since it is segregated by field
7339 
7340  Notes:
7341  The semantics of this function relate to that of setBC=FALSE in DMPlexGetIndicesPointFields_Internal.
7342  Since this function uses global indices, setBC=TRUE would be invalid, so no such argument exists.
7343 */
7344 static PetscErrorCode DMPlexGetIndicesPointFieldsSplit_Internal(PetscSection section, PetscSection globalSection, PetscInt point, PetscInt foffs[], const PetscInt ***perms, PetscInt permsoff, const PetscInt indperm[], PetscInt indices[])
7345 {
7346   PetscInt numFields, foff, f;
7347 
7348   PetscFunctionBegin;
7349   PetscCall(PetscSectionGetNumFields(section, &numFields));
7350   for (f = 0; f < numFields; ++f) {
7351     PetscInt        fdof, cfdof;
7352     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7353     PetscInt        cind = 0, b;
7354     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7355 
7356     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7357     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7358     PetscCall(PetscSectionGetFieldOffset(globalSection, point, f, &foff));
7359     if (!cfdof) {
7360       for (b = 0; b < fdof; ++b) {
7361         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7362         const PetscInt ind    = indperm ? indperm[preind] : preind;
7363 
7364         indices[ind] = foff + b;
7365       }
7366     } else {
7367       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7368       for (b = 0; b < fdof; ++b) {
7369         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7370         const PetscInt ind    = indperm ? indperm[preind] : preind;
7371 
7372         if ((cind < cfdof) && (b == fcdofs[cind])) {
7373           indices[ind] = -(foff + b + 1);
7374           ++cind;
7375         } else {
7376           indices[ind] = foff + b - cind;
7377         }
7378       }
7379     }
7380     foffs[f] += fdof;
7381   }
7382   PetscFunctionReturn(PETSC_SUCCESS);
7383 }
7384 
7385 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)
7386 {
7387   Mat             cMat;
7388   PetscSection    aSec, cSec;
7389   IS              aIS;
7390   PetscInt        aStart = -1, aEnd = -1;
7391   const PetscInt *anchors;
7392   PetscInt        numFields, f, p, q, newP = 0;
7393   PetscInt        newNumPoints = 0, newNumIndices = 0;
7394   PetscInt       *newPoints, *indices, *newIndices;
7395   PetscInt        maxAnchor, maxDof;
7396   PetscInt        newOffsets[32];
7397   PetscInt       *pointMatOffsets[32];
7398   PetscInt       *newPointOffsets[32];
7399   PetscScalar    *pointMat[32];
7400   PetscScalar    *newValues      = NULL, *tmpValues;
7401   PetscBool       anyConstrained = PETSC_FALSE;
7402 
7403   PetscFunctionBegin;
7404   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7405   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7406   PetscCall(PetscSectionGetNumFields(section, &numFields));
7407 
7408   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
7409   /* if there are point-to-point constraints */
7410   if (aSec) {
7411     PetscCall(PetscArrayzero(newOffsets, 32));
7412     PetscCall(ISGetIndices(aIS, &anchors));
7413     PetscCall(PetscSectionGetChart(aSec, &aStart, &aEnd));
7414     /* figure out how many points are going to be in the new element matrix
7415      * (we allow double counting, because it's all just going to be summed
7416      * into the global matrix anyway) */
7417     for (p = 0; p < 2 * numPoints; p += 2) {
7418       PetscInt b    = points[p];
7419       PetscInt bDof = 0, bSecDof;
7420 
7421       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7422       if (!bSecDof) continue;
7423       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7424       if (bDof) {
7425         /* this point is constrained */
7426         /* it is going to be replaced by its anchors */
7427         PetscInt bOff, q;
7428 
7429         anyConstrained = PETSC_TRUE;
7430         newNumPoints += bDof;
7431         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7432         for (q = 0; q < bDof; q++) {
7433           PetscInt a = anchors[bOff + q];
7434           PetscInt aDof;
7435 
7436           PetscCall(PetscSectionGetDof(section, a, &aDof));
7437           newNumIndices += aDof;
7438           for (f = 0; f < numFields; ++f) {
7439             PetscInt fDof;
7440 
7441             PetscCall(PetscSectionGetFieldDof(section, a, f, &fDof));
7442             newOffsets[f + 1] += fDof;
7443           }
7444         }
7445       } else {
7446         /* this point is not constrained */
7447         newNumPoints++;
7448         newNumIndices += bSecDof;
7449         for (f = 0; f < numFields; ++f) {
7450           PetscInt fDof;
7451 
7452           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7453           newOffsets[f + 1] += fDof;
7454         }
7455       }
7456     }
7457   }
7458   if (!anyConstrained) {
7459     if (outNumPoints) *outNumPoints = 0;
7460     if (outNumIndices) *outNumIndices = 0;
7461     if (outPoints) *outPoints = NULL;
7462     if (outValues) *outValues = NULL;
7463     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7464     PetscFunctionReturn(PETSC_SUCCESS);
7465   }
7466 
7467   if (outNumPoints) *outNumPoints = newNumPoints;
7468   if (outNumIndices) *outNumIndices = newNumIndices;
7469 
7470   for (f = 0; f < numFields; ++f) newOffsets[f + 1] += newOffsets[f];
7471 
7472   if (!outPoints && !outValues) {
7473     if (offsets) {
7474       for (f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7475     }
7476     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7477     PetscFunctionReturn(PETSC_SUCCESS);
7478   }
7479 
7480   PetscCheck(!numFields || newOffsets[numFields] == newNumIndices, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, newOffsets[numFields], newNumIndices);
7481 
7482   PetscCall(DMGetDefaultConstraints(dm, &cSec, &cMat, NULL));
7483 
7484   /* workspaces */
7485   if (numFields) {
7486     for (f = 0; f < numFields; f++) {
7487       PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[f]));
7488       PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[f]));
7489     }
7490   } else {
7491     PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[0]));
7492     PetscCall(DMGetWorkArray(dm, numPoints, MPIU_INT, &newPointOffsets[0]));
7493   }
7494 
7495   /* get workspaces for the point-to-point matrices */
7496   if (numFields) {
7497     PetscInt totalOffset, totalMatOffset;
7498 
7499     for (p = 0; p < numPoints; p++) {
7500       PetscInt b    = points[2 * p];
7501       PetscInt bDof = 0, bSecDof;
7502 
7503       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7504       if (!bSecDof) {
7505         for (f = 0; f < numFields; f++) {
7506           newPointOffsets[f][p + 1] = 0;
7507           pointMatOffsets[f][p + 1] = 0;
7508         }
7509         continue;
7510       }
7511       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7512       if (bDof) {
7513         for (f = 0; f < numFields; f++) {
7514           PetscInt fDof, q, bOff, allFDof = 0;
7515 
7516           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7517           PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7518           for (q = 0; q < bDof; q++) {
7519             PetscInt a = anchors[bOff + q];
7520             PetscInt aFDof;
7521 
7522             PetscCall(PetscSectionGetFieldDof(section, a, f, &aFDof));
7523             allFDof += aFDof;
7524           }
7525           newPointOffsets[f][p + 1] = allFDof;
7526           pointMatOffsets[f][p + 1] = fDof * allFDof;
7527         }
7528       } else {
7529         for (f = 0; f < numFields; f++) {
7530           PetscInt fDof;
7531 
7532           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7533           newPointOffsets[f][p + 1] = fDof;
7534           pointMatOffsets[f][p + 1] = 0;
7535         }
7536       }
7537     }
7538     for (f = 0, totalOffset = 0, totalMatOffset = 0; f < numFields; f++) {
7539       newPointOffsets[f][0] = totalOffset;
7540       pointMatOffsets[f][0] = totalMatOffset;
7541       for (p = 0; p < numPoints; p++) {
7542         newPointOffsets[f][p + 1] += newPointOffsets[f][p];
7543         pointMatOffsets[f][p + 1] += pointMatOffsets[f][p];
7544       }
7545       totalOffset    = newPointOffsets[f][numPoints];
7546       totalMatOffset = pointMatOffsets[f][numPoints];
7547       PetscCall(DMGetWorkArray(dm, pointMatOffsets[f][numPoints], MPIU_SCALAR, &pointMat[f]));
7548     }
7549   } else {
7550     for (p = 0; p < numPoints; p++) {
7551       PetscInt b    = points[2 * p];
7552       PetscInt bDof = 0, bSecDof;
7553 
7554       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7555       if (!bSecDof) {
7556         newPointOffsets[0][p + 1] = 0;
7557         pointMatOffsets[0][p + 1] = 0;
7558         continue;
7559       }
7560       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7561       if (bDof) {
7562         PetscInt bOff, q, allDof = 0;
7563 
7564         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7565         for (q = 0; q < bDof; q++) {
7566           PetscInt a = anchors[bOff + q], aDof;
7567 
7568           PetscCall(PetscSectionGetDof(section, a, &aDof));
7569           allDof += aDof;
7570         }
7571         newPointOffsets[0][p + 1] = allDof;
7572         pointMatOffsets[0][p + 1] = bSecDof * allDof;
7573       } else {
7574         newPointOffsets[0][p + 1] = bSecDof;
7575         pointMatOffsets[0][p + 1] = 0;
7576       }
7577     }
7578     newPointOffsets[0][0] = 0;
7579     pointMatOffsets[0][0] = 0;
7580     for (p = 0; p < numPoints; p++) {
7581       newPointOffsets[0][p + 1] += newPointOffsets[0][p];
7582       pointMatOffsets[0][p + 1] += pointMatOffsets[0][p];
7583     }
7584     PetscCall(DMGetWorkArray(dm, pointMatOffsets[0][numPoints], MPIU_SCALAR, &pointMat[0]));
7585   }
7586 
7587   /* output arrays */
7588   PetscCall(DMGetWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7589 
7590   /* get the point-to-point matrices; construct newPoints */
7591   PetscCall(PetscSectionGetMaxDof(aSec, &maxAnchor));
7592   PetscCall(PetscSectionGetMaxDof(section, &maxDof));
7593   PetscCall(DMGetWorkArray(dm, maxDof, MPIU_INT, &indices));
7594   PetscCall(DMGetWorkArray(dm, maxAnchor * maxDof, MPIU_INT, &newIndices));
7595   if (numFields) {
7596     for (p = 0, newP = 0; p < numPoints; p++) {
7597       PetscInt b    = points[2 * p];
7598       PetscInt o    = points[2 * p + 1];
7599       PetscInt bDof = 0, bSecDof;
7600 
7601       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7602       if (!bSecDof) continue;
7603       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7604       if (bDof) {
7605         PetscInt fStart[32], fEnd[32], fAnchorStart[32], fAnchorEnd[32], bOff, q;
7606 
7607         fStart[0] = 0;
7608         fEnd[0]   = 0;
7609         for (f = 0; f < numFields; f++) {
7610           PetscInt fDof;
7611 
7612           PetscCall(PetscSectionGetFieldDof(cSec, b, f, &fDof));
7613           fStart[f + 1] = fStart[f] + fDof;
7614           fEnd[f + 1]   = fStart[f + 1];
7615         }
7616         PetscCall(PetscSectionGetOffset(cSec, b, &bOff));
7617         PetscCall(DMPlexGetIndicesPointFields_Internal(cSec, PETSC_TRUE, b, bOff, fEnd, PETSC_TRUE, perms, p, NULL, indices));
7618 
7619         fAnchorStart[0] = 0;
7620         fAnchorEnd[0]   = 0;
7621         for (f = 0; f < numFields; f++) {
7622           PetscInt fDof = newPointOffsets[f][p + 1] - newPointOffsets[f][p];
7623 
7624           fAnchorStart[f + 1] = fAnchorStart[f] + fDof;
7625           fAnchorEnd[f + 1]   = fAnchorStart[f + 1];
7626         }
7627         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7628         for (q = 0; q < bDof; q++) {
7629           PetscInt a = anchors[bOff + q], aOff;
7630 
7631           /* we take the orientation of ap into account in the order that we constructed the indices above: the newly added points have no orientation */
7632           newPoints[2 * (newP + q)]     = a;
7633           newPoints[2 * (newP + q) + 1] = 0;
7634           PetscCall(PetscSectionGetOffset(section, a, &aOff));
7635           PetscCall(DMPlexGetIndicesPointFields_Internal(section, PETSC_TRUE, a, aOff, fAnchorEnd, PETSC_TRUE, NULL, -1, NULL, newIndices));
7636         }
7637         newP += bDof;
7638 
7639         if (outValues) {
7640           /* get the point-to-point submatrix */
7641           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]));
7642         }
7643       } else {
7644         newPoints[2 * newP]     = b;
7645         newPoints[2 * newP + 1] = o;
7646         newP++;
7647       }
7648     }
7649   } else {
7650     for (p = 0; p < numPoints; p++) {
7651       PetscInt b    = points[2 * p];
7652       PetscInt o    = points[2 * p + 1];
7653       PetscInt bDof = 0, bSecDof;
7654 
7655       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7656       if (!bSecDof) continue;
7657       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7658       if (bDof) {
7659         PetscInt bEnd = 0, bAnchorEnd = 0, bOff;
7660 
7661         PetscCall(PetscSectionGetOffset(cSec, b, &bOff));
7662         PetscCall(DMPlexGetIndicesPoint_Internal(cSec, PETSC_TRUE, b, bOff, &bEnd, PETSC_TRUE, (perms && perms[0]) ? perms[0][p] : NULL, NULL, indices));
7663 
7664         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7665         for (q = 0; q < bDof; q++) {
7666           PetscInt a = anchors[bOff + q], aOff;
7667 
7668           /* we take the orientation of ap into account in the order that we constructed the indices above: the newly added points have no orientation */
7669 
7670           newPoints[2 * (newP + q)]     = a;
7671           newPoints[2 * (newP + q) + 1] = 0;
7672           PetscCall(PetscSectionGetOffset(section, a, &aOff));
7673           PetscCall(DMPlexGetIndicesPoint_Internal(section, PETSC_TRUE, a, aOff, &bAnchorEnd, PETSC_TRUE, NULL, NULL, newIndices));
7674         }
7675         newP += bDof;
7676 
7677         /* get the point-to-point submatrix */
7678         if (outValues) PetscCall(MatGetValues(cMat, bEnd, indices, bAnchorEnd, newIndices, pointMat[0] + pointMatOffsets[0][p]));
7679       } else {
7680         newPoints[2 * newP]     = b;
7681         newPoints[2 * newP + 1] = o;
7682         newP++;
7683       }
7684     }
7685   }
7686 
7687   if (outValues) {
7688     PetscCall(DMGetWorkArray(dm, newNumIndices * numIndices, MPIU_SCALAR, &tmpValues));
7689     PetscCall(PetscArrayzero(tmpValues, newNumIndices * numIndices));
7690     /* multiply constraints on the right */
7691     if (numFields) {
7692       for (f = 0; f < numFields; f++) {
7693         PetscInt oldOff = offsets[f];
7694 
7695         for (p = 0; p < numPoints; p++) {
7696           PetscInt cStart = newPointOffsets[f][p];
7697           PetscInt b      = points[2 * p];
7698           PetscInt c, r, k;
7699           PetscInt dof;
7700 
7701           PetscCall(PetscSectionGetFieldDof(section, b, f, &dof));
7702           if (!dof) continue;
7703           if (pointMatOffsets[f][p] < pointMatOffsets[f][p + 1]) {
7704             PetscInt           nCols = newPointOffsets[f][p + 1] - cStart;
7705             const PetscScalar *mat   = pointMat[f] + pointMatOffsets[f][p];
7706 
7707             for (r = 0; r < numIndices; r++) {
7708               for (c = 0; c < nCols; c++) {
7709                 for (k = 0; k < dof; k++) tmpValues[r * newNumIndices + cStart + c] += values[r * numIndices + oldOff + k] * mat[k * nCols + c];
7710               }
7711             }
7712           } else {
7713             /* copy this column as is */
7714             for (r = 0; r < numIndices; r++) {
7715               for (c = 0; c < dof; c++) tmpValues[r * newNumIndices + cStart + c] = values[r * numIndices + oldOff + c];
7716             }
7717           }
7718           oldOff += dof;
7719         }
7720       }
7721     } else {
7722       PetscInt oldOff = 0;
7723       for (p = 0; p < numPoints; p++) {
7724         PetscInt cStart = newPointOffsets[0][p];
7725         PetscInt b      = points[2 * p];
7726         PetscInt c, r, k;
7727         PetscInt dof;
7728 
7729         PetscCall(PetscSectionGetDof(section, b, &dof));
7730         if (!dof) continue;
7731         if (pointMatOffsets[0][p] < pointMatOffsets[0][p + 1]) {
7732           PetscInt           nCols = newPointOffsets[0][p + 1] - cStart;
7733           const PetscScalar *mat   = pointMat[0] + pointMatOffsets[0][p];
7734 
7735           for (r = 0; r < numIndices; r++) {
7736             for (c = 0; c < nCols; c++) {
7737               for (k = 0; k < dof; k++) tmpValues[r * newNumIndices + cStart + c] += mat[k * nCols + c] * values[r * numIndices + oldOff + k];
7738             }
7739           }
7740         } else {
7741           /* copy this column as is */
7742           for (r = 0; r < numIndices; r++) {
7743             for (c = 0; c < dof; c++) tmpValues[r * newNumIndices + cStart + c] = values[r * numIndices + oldOff + c];
7744           }
7745         }
7746         oldOff += dof;
7747       }
7748     }
7749 
7750     if (multiplyLeft) {
7751       PetscCall(DMGetWorkArray(dm, newNumIndices * newNumIndices, MPIU_SCALAR, &newValues));
7752       PetscCall(PetscArrayzero(newValues, newNumIndices * newNumIndices));
7753       /* multiply constraints transpose on the left */
7754       if (numFields) {
7755         for (f = 0; f < numFields; f++) {
7756           PetscInt oldOff = offsets[f];
7757 
7758           for (p = 0; p < numPoints; p++) {
7759             PetscInt rStart = newPointOffsets[f][p];
7760             PetscInt b      = points[2 * p];
7761             PetscInt c, r, k;
7762             PetscInt dof;
7763 
7764             PetscCall(PetscSectionGetFieldDof(section, b, f, &dof));
7765             if (pointMatOffsets[f][p] < pointMatOffsets[f][p + 1]) {
7766               PetscInt                          nRows = newPointOffsets[f][p + 1] - rStart;
7767               const PetscScalar *PETSC_RESTRICT mat   = pointMat[f] + pointMatOffsets[f][p];
7768 
7769               for (r = 0; r < nRows; r++) {
7770                 for (c = 0; c < newNumIndices; c++) {
7771                   for (k = 0; k < dof; k++) newValues[(rStart + r) * newNumIndices + c] += mat[k * nRows + r] * tmpValues[(oldOff + k) * newNumIndices + c];
7772                 }
7773               }
7774             } else {
7775               /* copy this row as is */
7776               for (r = 0; r < dof; r++) {
7777                 for (c = 0; c < newNumIndices; c++) newValues[(rStart + r) * newNumIndices + c] = tmpValues[(oldOff + r) * newNumIndices + c];
7778               }
7779             }
7780             oldOff += dof;
7781           }
7782         }
7783       } else {
7784         PetscInt oldOff = 0;
7785 
7786         for (p = 0; p < numPoints; p++) {
7787           PetscInt rStart = newPointOffsets[0][p];
7788           PetscInt b      = points[2 * p];
7789           PetscInt c, r, k;
7790           PetscInt dof;
7791 
7792           PetscCall(PetscSectionGetDof(section, b, &dof));
7793           if (pointMatOffsets[0][p] < pointMatOffsets[0][p + 1]) {
7794             PetscInt                          nRows = newPointOffsets[0][p + 1] - rStart;
7795             const PetscScalar *PETSC_RESTRICT mat   = pointMat[0] + pointMatOffsets[0][p];
7796 
7797             for (r = 0; r < nRows; r++) {
7798               for (c = 0; c < newNumIndices; c++) {
7799                 for (k = 0; k < dof; k++) newValues[(rStart + r) * newNumIndices + c] += mat[k * nRows + r] * tmpValues[(oldOff + k) * newNumIndices + c];
7800               }
7801             }
7802           } else {
7803             /* copy this row as is */
7804             for (r = 0; r < dof; r++) {
7805               for (c = 0; c < newNumIndices; c++) newValues[(rStart + r) * newNumIndices + c] = tmpValues[(oldOff + r) * newNumIndices + c];
7806             }
7807           }
7808           oldOff += dof;
7809         }
7810       }
7811 
7812       PetscCall(DMRestoreWorkArray(dm, newNumIndices * numIndices, MPIU_SCALAR, &tmpValues));
7813     } else {
7814       newValues = tmpValues;
7815     }
7816   }
7817 
7818   /* clean up */
7819   PetscCall(DMRestoreWorkArray(dm, maxDof, MPIU_INT, &indices));
7820   PetscCall(DMRestoreWorkArray(dm, maxAnchor * maxDof, MPIU_INT, &newIndices));
7821 
7822   if (numFields) {
7823     for (f = 0; f < numFields; f++) {
7824       PetscCall(DMRestoreWorkArray(dm, pointMatOffsets[f][numPoints], MPIU_SCALAR, &pointMat[f]));
7825       PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[f]));
7826       PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[f]));
7827     }
7828   } else {
7829     PetscCall(DMRestoreWorkArray(dm, pointMatOffsets[0][numPoints], MPIU_SCALAR, &pointMat[0]));
7830     PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[0]));
7831     PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[0]));
7832   }
7833   PetscCall(ISRestoreIndices(aIS, &anchors));
7834 
7835   /* output */
7836   if (outPoints) {
7837     *outPoints = newPoints;
7838   } else {
7839     PetscCall(DMRestoreWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7840   }
7841   if (outValues) *outValues = newValues;
7842   for (f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7843   PetscFunctionReturn(PETSC_SUCCESS);
7844 }
7845 
7846 /*@C
7847   DMPlexGetClosureIndices - Gets the global dof indices associated with the closure of the given point within the provided sections.
7848 
7849   Not collective
7850 
7851   Input Parameters:
7852 + dm         - The `DM`
7853 . section    - The `PetscSection` describing the points (a local section)
7854 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
7855 . point      - The point defining the closure
7856 - useClPerm  - Use the closure point permutation if available
7857 
7858   Output Parameters:
7859 + numIndices - The number of dof indices in the closure of point with the input sections
7860 . indices    - The dof indices
7861 . outOffsets - Array to write the field offsets into, or `NULL`
7862 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or `NULL`
7863 
7864   Level: advanced
7865 
7866   Notes:
7867   Must call `DMPlexRestoreClosureIndices()` to free allocated memory
7868 
7869   If `idxSection` is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
7870   of those indices is not significant.  If `idxSection` is local, the constrained dofs will yield the involution -(idx+1)
7871   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
7872   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when `idxSection` == section, otherwise global
7873   indices (with the above semantics) are implied.
7874 
7875 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`,
7876           `PetscSection`, `DMGetGlobalSection()`
7877 @*/
7878 PetscErrorCode DMPlexGetClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
7879 {
7880   /* Closure ordering */
7881   PetscSection    clSection;
7882   IS              clPoints;
7883   const PetscInt *clp;
7884   PetscInt       *points;
7885   const PetscInt *clperm = NULL;
7886   /* Dof permutation and sign flips */
7887   const PetscInt    **perms[32] = {NULL};
7888   const PetscScalar **flips[32] = {NULL};
7889   PetscScalar        *valCopy   = NULL;
7890   /* Hanging node constraints */
7891   PetscInt    *pointsC = NULL;
7892   PetscScalar *valuesC = NULL;
7893   PetscInt     NclC, NiC;
7894 
7895   PetscInt *idx;
7896   PetscInt  Nf, Ncl, Ni = 0, offsets[32], p, f;
7897   PetscBool isLocal = (section == idxSection) ? PETSC_TRUE : PETSC_FALSE;
7898 
7899   PetscFunctionBeginHot;
7900   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7901   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7902   PetscValidHeaderSpecific(idxSection, PETSC_SECTION_CLASSID, 3);
7903   if (numIndices) PetscAssertPointer(numIndices, 6);
7904   if (indices) PetscAssertPointer(indices, 7);
7905   if (outOffsets) PetscAssertPointer(outOffsets, 8);
7906   if (values) PetscAssertPointer(values, 9);
7907   PetscCall(PetscSectionGetNumFields(section, &Nf));
7908   PetscCheck(Nf <= 31, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", Nf);
7909   PetscCall(PetscArrayzero(offsets, 32));
7910   /* 1) Get points in closure */
7911   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &Ncl, &points, &clSection, &clPoints, &clp));
7912   if (useClPerm) {
7913     PetscInt depth, clsize;
7914     PetscCall(DMPlexGetPointDepth(dm, point, &depth));
7915     for (clsize = 0, p = 0; p < Ncl; p++) {
7916       PetscInt dof;
7917       PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
7918       clsize += dof;
7919     }
7920     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
7921   }
7922   /* 2) Get number of indices on these points and field offsets from section */
7923   for (p = 0; p < Ncl * 2; p += 2) {
7924     PetscInt dof, fdof;
7925 
7926     PetscCall(PetscSectionGetDof(section, points[p], &dof));
7927     for (f = 0; f < Nf; ++f) {
7928       PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
7929       offsets[f + 1] += fdof;
7930     }
7931     Ni += dof;
7932   }
7933   for (f = 1; f < Nf; ++f) offsets[f + 1] += offsets[f];
7934   PetscCheck(!Nf || offsets[Nf] == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, offsets[Nf], Ni);
7935   /* 3) Get symmetries and sign flips. Apply sign flips to values if passed in (only works for square values matrix) */
7936   for (f = 0; f < PetscMax(1, Nf); ++f) {
7937     if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7938     else PetscCall(PetscSectionGetPointSyms(section, Ncl, points, &perms[f], &flips[f]));
7939     /* may need to apply sign changes to the element matrix */
7940     if (values && flips[f]) {
7941       PetscInt foffset = offsets[f];
7942 
7943       for (p = 0; p < Ncl; ++p) {
7944         PetscInt           pnt  = points[2 * p], fdof;
7945         const PetscScalar *flip = flips[f] ? flips[f][p] : NULL;
7946 
7947         if (!Nf) PetscCall(PetscSectionGetDof(section, pnt, &fdof));
7948         else PetscCall(PetscSectionGetFieldDof(section, pnt, f, &fdof));
7949         if (flip) {
7950           PetscInt i, j, k;
7951 
7952           if (!valCopy) {
7953             PetscCall(DMGetWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
7954             for (j = 0; j < Ni * Ni; ++j) valCopy[j] = (*values)[j];
7955             *values = valCopy;
7956           }
7957           for (i = 0; i < fdof; ++i) {
7958             PetscScalar fval = flip[i];
7959 
7960             for (k = 0; k < Ni; ++k) {
7961               valCopy[Ni * (foffset + i) + k] *= fval;
7962               valCopy[Ni * k + (foffset + i)] *= fval;
7963             }
7964           }
7965         }
7966         foffset += fdof;
7967       }
7968     }
7969   }
7970   /* 4) Apply hanging node constraints. Get new symmetries and replace all storage with constrained storage */
7971   PetscCall(DMPlexAnchorsModifyMat(dm, section, Ncl, Ni, points, perms, values ? *values : NULL, &NclC, &NiC, &pointsC, values ? &valuesC : NULL, offsets, PETSC_TRUE));
7972   if (NclC) {
7973     if (valCopy) PetscCall(DMRestoreWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
7974     for (f = 0; f < PetscMax(1, Nf); ++f) {
7975       if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7976       else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
7977     }
7978     for (f = 0; f < PetscMax(1, Nf); ++f) {
7979       if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, NclC, pointsC, &perms[f], &flips[f]));
7980       else PetscCall(PetscSectionGetPointSyms(section, NclC, pointsC, &perms[f], &flips[f]));
7981     }
7982     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7983     Ncl    = NclC;
7984     Ni     = NiC;
7985     points = pointsC;
7986     if (values) *values = valuesC;
7987   }
7988   /* 5) Calculate indices */
7989   PetscCall(DMGetWorkArray(dm, Ni, MPIU_INT, &idx));
7990   if (Nf) {
7991     PetscInt  idxOff;
7992     PetscBool useFieldOffsets;
7993 
7994     if (outOffsets) {
7995       for (f = 0; f <= Nf; f++) outOffsets[f] = offsets[f];
7996     }
7997     PetscCall(PetscSectionGetUseFieldOffsets(idxSection, &useFieldOffsets));
7998     if (useFieldOffsets) {
7999       for (p = 0; p < Ncl; ++p) {
8000         const PetscInt pnt = points[p * 2];
8001 
8002         PetscCall(DMPlexGetIndicesPointFieldsSplit_Internal(section, idxSection, pnt, offsets, perms, p, clperm, idx));
8003       }
8004     } else {
8005       for (p = 0; p < Ncl; ++p) {
8006         const PetscInt pnt = points[p * 2];
8007 
8008         PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
8009         /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
8010          * not (at the time of this writing) have fields set. They probably should, in which case we would pass the
8011          * global section. */
8012         PetscCall(DMPlexGetIndicesPointFields_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, offsets, PETSC_FALSE, perms, p, clperm, idx));
8013       }
8014     }
8015   } else {
8016     PetscInt off = 0, idxOff;
8017 
8018     for (p = 0; p < Ncl; ++p) {
8019       const PetscInt  pnt  = points[p * 2];
8020       const PetscInt *perm = perms[0] ? perms[0][p] : NULL;
8021 
8022       PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
8023       /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
8024        * not (at the time of this writing) have fields set. They probably should, in which case we would pass the global section. */
8025       PetscCall(DMPlexGetIndicesPoint_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, &off, PETSC_FALSE, perm, clperm, idx));
8026     }
8027   }
8028   /* 6) Cleanup */
8029   for (f = 0; f < PetscMax(1, Nf); ++f) {
8030     if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
8031     else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
8032   }
8033   if (NclC) {
8034     PetscCall(DMRestoreWorkArray(dm, NclC * 2, MPIU_INT, &pointsC));
8035   } else {
8036     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
8037   }
8038 
8039   if (numIndices) *numIndices = Ni;
8040   if (indices) *indices = idx;
8041   PetscFunctionReturn(PETSC_SUCCESS);
8042 }
8043 
8044 /*@C
8045   DMPlexRestoreClosureIndices - Restores the global dof indices associated with the closure of the given point within the provided sections.
8046 
8047   Not collective
8048 
8049   Input Parameters:
8050 + dm         - The `DM`
8051 . section    - The `PetscSection` describing the points (a local section)
8052 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
8053 . point      - The point defining the closure
8054 - useClPerm  - Use the closure point permutation if available
8055 
8056   Output Parameters:
8057 + numIndices - The number of dof indices in the closure of point with the input sections
8058 . indices    - The dof indices
8059 . outOffsets - Array to write the field offsets into, or `NULL`
8060 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or `NULL`
8061 
8062   Level: advanced
8063 
8064   Notes:
8065   If values were modified, the user is responsible for calling `DMRestoreWorkArray`(dm, 0, `MPIU_SCALAR`, &values).
8066 
8067   If idxSection is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
8068   of those indices is not significant.  If idxSection is local, the constrained dofs will yield the involution -(idx+1)
8069   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
8070   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when idxSection == section, otherwise global
8071   indices (with the above semantics) are implied.
8072 
8073 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`, `DMGetGlobalSection()`
8074 @*/
8075 PetscErrorCode DMPlexRestoreClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
8076 {
8077   PetscFunctionBegin;
8078   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8079   PetscAssertPointer(indices, 7);
8080   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, indices));
8081   PetscFunctionReturn(PETSC_SUCCESS);
8082 }
8083 
8084 PetscErrorCode DMPlexMatSetClosure_Internal(DM dm, PetscSection section, PetscSection globalSection, PetscBool useClPerm, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8085 {
8086   DM_Plex           *mesh = (DM_Plex *)dm->data;
8087   PetscInt          *indices;
8088   PetscInt           numIndices;
8089   const PetscScalar *valuesOrig = values;
8090   PetscErrorCode     ierr;
8091 
8092   PetscFunctionBegin;
8093   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8094   if (!section) PetscCall(DMGetLocalSection(dm, &section));
8095   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
8096   if (!globalSection) PetscCall(DMGetGlobalSection(dm, &globalSection));
8097   PetscValidHeaderSpecific(globalSection, PETSC_SECTION_CLASSID, 3);
8098   PetscValidHeaderSpecific(A, MAT_CLASSID, 5);
8099 
8100   PetscCall(DMPlexGetClosureIndices(dm, section, globalSection, point, useClPerm, &numIndices, &indices, NULL, (PetscScalar **)&values));
8101 
8102   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndices, indices, 0, NULL, values));
8103   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8104   ierr = MatSetValues(A, numIndices, indices, numIndices, indices, values, mode);
8105   if (ierr) {
8106     PetscMPIInt rank;
8107 
8108     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8109     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8110     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndices, indices, 0, NULL, values));
8111     PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
8112     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
8113     SETERRQ(PetscObjectComm((PetscObject)dm), ierr, "Not possible to set matrix values");
8114   }
8115   if (mesh->printFEM > 1) {
8116     PetscInt i;
8117     PetscCall(PetscPrintf(PETSC_COMM_SELF, "  Indices:"));
8118     for (i = 0; i < numIndices; ++i) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, indices[i]));
8119     PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8120   }
8121 
8122   PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
8123   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
8124   PetscFunctionReturn(PETSC_SUCCESS);
8125 }
8126 
8127 /*@C
8128   DMPlexMatSetClosure - Set an array of the values on the closure of 'point'
8129 
8130   Not collective
8131 
8132   Input Parameters:
8133 + dm            - The `DM`
8134 . section       - The section describing the layout in `v`, or `NULL` to use the default section
8135 . globalSection - The section describing the layout in `v`, or `NULL` to use the default global section
8136 . A             - The matrix
8137 . point         - The point in the `DM`
8138 . values        - The array of values
8139 - mode          - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
8140 
8141   Level: intermediate
8142 
8143 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosureGeneral()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
8144 @*/
8145 PetscErrorCode DMPlexMatSetClosure(DM dm, PetscSection section, PetscSection globalSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8146 {
8147   PetscFunctionBegin;
8148   PetscCall(DMPlexMatSetClosure_Internal(dm, section, globalSection, PETSC_TRUE, A, point, values, mode));
8149   PetscFunctionReturn(PETSC_SUCCESS);
8150 }
8151 
8152 /*@C
8153   DMPlexMatSetClosureGeneral - Set an array of the values on the closure of 'point' using a different row and column section
8154 
8155   Not collective
8156 
8157   Input Parameters:
8158 + dmRow            - The `DM` for the row fields
8159 . sectionRow       - The section describing the layout, or `NULL` to use the default section in `dmRow`
8160 . useRowPerm       - The flag to use the closure permutation of the `dmRow` if available
8161 . globalSectionRow - The section describing the layout, or `NULL` to use the default global section in `dmRow`
8162 . dmCol            - The `DM` for the column fields
8163 . sectionCol       - The section describing the layout, or `NULL` to use the default section in `dmCol`
8164 . useColPerm       - The flag to use the closure permutation of the `dmCol` if available
8165 . globalSectionCol - The section describing the layout, or `NULL` to use the default global section in `dmCol`
8166 . A                - The matrix
8167 . point            - The point in the `DM`
8168 . values           - The array of values
8169 - mode             - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
8170 
8171   Level: intermediate
8172 
8173 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosure()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
8174 @*/
8175 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)
8176 {
8177   DM_Plex           *mesh = (DM_Plex *)dmRow->data;
8178   PetscInt          *indicesRow, *indicesCol;
8179   PetscInt           numIndicesRow, numIndicesCol;
8180   const PetscScalar *valuesOrig = values;
8181   PetscErrorCode     ierr;
8182 
8183   PetscFunctionBegin;
8184   PetscValidHeaderSpecific(dmRow, DM_CLASSID, 1);
8185   if (!sectionRow) PetscCall(DMGetLocalSection(dmRow, &sectionRow));
8186   PetscValidHeaderSpecific(sectionRow, PETSC_SECTION_CLASSID, 2);
8187   if (!globalSectionRow) PetscCall(DMGetGlobalSection(dmRow, &globalSectionRow));
8188   PetscValidHeaderSpecific(globalSectionRow, PETSC_SECTION_CLASSID, 3);
8189   PetscValidHeaderSpecific(dmCol, DM_CLASSID, 5);
8190   if (!sectionCol) PetscCall(DMGetLocalSection(dmCol, &sectionCol));
8191   PetscValidHeaderSpecific(sectionCol, PETSC_SECTION_CLASSID, 6);
8192   if (!globalSectionCol) PetscCall(DMGetGlobalSection(dmCol, &globalSectionCol));
8193   PetscValidHeaderSpecific(globalSectionCol, PETSC_SECTION_CLASSID, 7);
8194   PetscValidHeaderSpecific(A, MAT_CLASSID, 9);
8195 
8196   PetscCall(DMPlexGetClosureIndices(dmRow, sectionRow, globalSectionRow, point, useRowPerm, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&values));
8197   PetscCall(DMPlexGetClosureIndices(dmCol, sectionCol, globalSectionCol, point, useColPerm, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&values));
8198 
8199   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
8200   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8201   ierr = MatSetValues(A, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values, mode);
8202   if (ierr) {
8203     PetscMPIInt rank;
8204 
8205     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8206     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8207     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
8208     PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&values));
8209     PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesRow, NULL, (PetscScalar **)&values));
8210     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &values));
8211   }
8212 
8213   PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, useRowPerm, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&values));
8214   PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, useColPerm, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&values));
8215   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &values));
8216   PetscFunctionReturn(PETSC_SUCCESS);
8217 }
8218 
8219 PetscErrorCode DMPlexMatSetClosureRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8220 {
8221   DM_Plex        *mesh    = (DM_Plex *)dmf->data;
8222   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
8223   PetscInt       *cpoints = NULL;
8224   PetscInt       *findices, *cindices;
8225   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
8226   PetscInt        foffsets[32], coffsets[32];
8227   DMPolytopeType  ct;
8228   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
8229   PetscErrorCode  ierr;
8230 
8231   PetscFunctionBegin;
8232   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
8233   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
8234   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
8235   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
8236   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
8237   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
8238   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
8239   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
8240   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
8241   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
8242   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
8243   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
8244   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
8245   PetscCall(PetscArrayzero(foffsets, 32));
8246   PetscCall(PetscArrayzero(coffsets, 32));
8247   /* Column indices */
8248   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8249   maxFPoints = numCPoints;
8250   /* Compress out points not in the section */
8251   /*   TODO: Squeeze out points with 0 dof as well */
8252   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
8253   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
8254     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
8255       cpoints[q * 2]     = cpoints[p];
8256       cpoints[q * 2 + 1] = cpoints[p + 1];
8257       ++q;
8258     }
8259   }
8260   numCPoints = q;
8261   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
8262     PetscInt fdof;
8263 
8264     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
8265     if (!dof) continue;
8266     for (f = 0; f < numFields; ++f) {
8267       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
8268       coffsets[f + 1] += fdof;
8269     }
8270     numCIndices += dof;
8271   }
8272   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
8273   /* Row indices */
8274   PetscCall(DMPlexGetCellType(dmc, point, &ct));
8275   {
8276     DMPlexTransform tr;
8277     DMPolytopeType *rct;
8278     PetscInt       *rsize, *rcone, *rornt, Nt;
8279 
8280     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8281     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8282     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8283     numSubcells = rsize[Nt - 1];
8284     PetscCall(DMPlexTransformDestroy(&tr));
8285   }
8286   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8287   for (r = 0, q = 0; r < numSubcells; ++r) {
8288     /* TODO Map from coarse to fine cells */
8289     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8290     /* Compress out points not in the section */
8291     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8292     for (p = 0; p < numFPoints * 2; p += 2) {
8293       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8294         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8295         if (!dof) continue;
8296         for (s = 0; s < q; ++s)
8297           if (fpoints[p] == ftotpoints[s * 2]) break;
8298         if (s < q) continue;
8299         ftotpoints[q * 2]     = fpoints[p];
8300         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8301         ++q;
8302       }
8303     }
8304     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8305   }
8306   numFPoints = q;
8307   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8308     PetscInt fdof;
8309 
8310     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8311     if (!dof) continue;
8312     for (f = 0; f < numFields; ++f) {
8313       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8314       foffsets[f + 1] += fdof;
8315     }
8316     numFIndices += dof;
8317   }
8318   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8319 
8320   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8321   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8322   PetscCall(DMGetWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8323   PetscCall(DMGetWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8324   if (numFields) {
8325     const PetscInt **permsF[32] = {NULL};
8326     const PetscInt **permsC[32] = {NULL};
8327 
8328     for (f = 0; f < numFields; f++) {
8329       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8330       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8331     }
8332     for (p = 0; p < numFPoints; p++) {
8333       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8334       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8335     }
8336     for (p = 0; p < numCPoints; p++) {
8337       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8338       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8339     }
8340     for (f = 0; f < numFields; f++) {
8341       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8342       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8343     }
8344   } else {
8345     const PetscInt **permsF = NULL;
8346     const PetscInt **permsC = NULL;
8347 
8348     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8349     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8350     for (p = 0, off = 0; p < numFPoints; p++) {
8351       const PetscInt *perm = permsF ? permsF[p] : NULL;
8352 
8353       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8354       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8355     }
8356     for (p = 0, off = 0; p < numCPoints; p++) {
8357       const PetscInt *perm = permsC ? permsC[p] : NULL;
8358 
8359       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8360       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8361     }
8362     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8363     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8364   }
8365   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8366   /* TODO: flips */
8367   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8368   ierr = MatSetValues(A, numFIndices, findices, numCIndices, cindices, values, mode);
8369   if (ierr) {
8370     PetscMPIInt rank;
8371 
8372     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8373     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8374     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8375     PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8376     PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8377   }
8378   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8379   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8380   PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8381   PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8382   PetscFunctionReturn(PETSC_SUCCESS);
8383 }
8384 
8385 PetscErrorCode DMPlexMatGetClosureIndicesRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, PetscInt point, PetscInt cindices[], PetscInt findices[])
8386 {
8387   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
8388   PetscInt       *cpoints = NULL;
8389   PetscInt        foffsets[32], coffsets[32];
8390   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
8391   DMPolytopeType  ct;
8392   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
8393 
8394   PetscFunctionBegin;
8395   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
8396   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
8397   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
8398   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
8399   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
8400   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
8401   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
8402   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
8403   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
8404   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
8405   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
8406   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
8407   PetscCall(PetscArrayzero(foffsets, 32));
8408   PetscCall(PetscArrayzero(coffsets, 32));
8409   /* Column indices */
8410   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8411   maxFPoints = numCPoints;
8412   /* Compress out points not in the section */
8413   /*   TODO: Squeeze out points with 0 dof as well */
8414   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
8415   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
8416     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
8417       cpoints[q * 2]     = cpoints[p];
8418       cpoints[q * 2 + 1] = cpoints[p + 1];
8419       ++q;
8420     }
8421   }
8422   numCPoints = q;
8423   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
8424     PetscInt fdof;
8425 
8426     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
8427     if (!dof) continue;
8428     for (f = 0; f < numFields; ++f) {
8429       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
8430       coffsets[f + 1] += fdof;
8431     }
8432     numCIndices += dof;
8433   }
8434   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
8435   /* Row indices */
8436   PetscCall(DMPlexGetCellType(dmc, point, &ct));
8437   {
8438     DMPlexTransform tr;
8439     DMPolytopeType *rct;
8440     PetscInt       *rsize, *rcone, *rornt, Nt;
8441 
8442     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8443     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8444     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8445     numSubcells = rsize[Nt - 1];
8446     PetscCall(DMPlexTransformDestroy(&tr));
8447   }
8448   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8449   for (r = 0, q = 0; r < numSubcells; ++r) {
8450     /* TODO Map from coarse to fine cells */
8451     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8452     /* Compress out points not in the section */
8453     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8454     for (p = 0; p < numFPoints * 2; p += 2) {
8455       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8456         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8457         if (!dof) continue;
8458         for (s = 0; s < q; ++s)
8459           if (fpoints[p] == ftotpoints[s * 2]) break;
8460         if (s < q) continue;
8461         ftotpoints[q * 2]     = fpoints[p];
8462         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8463         ++q;
8464       }
8465     }
8466     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8467   }
8468   numFPoints = q;
8469   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8470     PetscInt fdof;
8471 
8472     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8473     if (!dof) continue;
8474     for (f = 0; f < numFields; ++f) {
8475       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8476       foffsets[f + 1] += fdof;
8477     }
8478     numFIndices += dof;
8479   }
8480   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8481 
8482   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8483   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8484   if (numFields) {
8485     const PetscInt **permsF[32] = {NULL};
8486     const PetscInt **permsC[32] = {NULL};
8487 
8488     for (f = 0; f < numFields; f++) {
8489       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8490       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8491     }
8492     for (p = 0; p < numFPoints; p++) {
8493       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8494       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8495     }
8496     for (p = 0; p < numCPoints; p++) {
8497       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8498       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8499     }
8500     for (f = 0; f < numFields; f++) {
8501       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8502       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8503     }
8504   } else {
8505     const PetscInt **permsF = NULL;
8506     const PetscInt **permsC = NULL;
8507 
8508     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8509     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8510     for (p = 0, off = 0; p < numFPoints; p++) {
8511       const PetscInt *perm = permsF ? permsF[p] : NULL;
8512 
8513       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8514       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8515     }
8516     for (p = 0, off = 0; p < numCPoints; p++) {
8517       const PetscInt *perm = permsC ? permsC[p] : NULL;
8518 
8519       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8520       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8521     }
8522     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8523     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8524   }
8525   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8526   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8527   PetscFunctionReturn(PETSC_SUCCESS);
8528 }
8529 
8530 /*@C
8531   DMPlexGetVTKCellHeight - Returns the height in the DAG used to determine which points are cells (normally 0)
8532 
8533   Input Parameter:
8534 . dm - The `DMPLEX` object
8535 
8536   Output Parameter:
8537 . cellHeight - The height of a cell
8538 
8539   Level: developer
8540 
8541 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetVTKCellHeight()`
8542 @*/
8543 PetscErrorCode DMPlexGetVTKCellHeight(DM dm, PetscInt *cellHeight)
8544 {
8545   DM_Plex *mesh = (DM_Plex *)dm->data;
8546 
8547   PetscFunctionBegin;
8548   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8549   PetscAssertPointer(cellHeight, 2);
8550   *cellHeight = mesh->vtkCellHeight;
8551   PetscFunctionReturn(PETSC_SUCCESS);
8552 }
8553 
8554 /*@C
8555   DMPlexSetVTKCellHeight - Sets the height in the DAG used to determine which points are cells (normally 0)
8556 
8557   Input Parameters:
8558 + dm         - The `DMPLEX` object
8559 - cellHeight - The height of a cell
8560 
8561   Level: developer
8562 
8563 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetVTKCellHeight()`
8564 @*/
8565 PetscErrorCode DMPlexSetVTKCellHeight(DM dm, PetscInt cellHeight)
8566 {
8567   DM_Plex *mesh = (DM_Plex *)dm->data;
8568 
8569   PetscFunctionBegin;
8570   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8571   mesh->vtkCellHeight = cellHeight;
8572   PetscFunctionReturn(PETSC_SUCCESS);
8573 }
8574 
8575 /*@
8576   DMPlexGetCellTypeStratum - Get the range of cells of a given celltype
8577 
8578   Input Parameters:
8579 + dm - The `DMPLEX` object
8580 - ct - The `DMPolytopeType` of the cell
8581 
8582   Output Parameters:
8583 + start - The first cell of this type, or `NULL`
8584 - end   - The upper bound on this celltype, or `NULL`
8585 
8586   Level: advanced
8587 
8588 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexConstructGhostCells()`, `DMPlexGetDepthStratum()`, `DMPlexGetHeightStratum()`
8589 @*/
8590 PetscErrorCode DMPlexGetCellTypeStratum(DM dm, DMPolytopeType ct, PetscInt *start, PetscInt *end)
8591 {
8592   DM_Plex *mesh = (DM_Plex *)dm->data;
8593   DMLabel  label;
8594   PetscInt pStart, pEnd;
8595 
8596   PetscFunctionBegin;
8597   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8598   if (start) {
8599     PetscAssertPointer(start, 3);
8600     *start = 0;
8601   }
8602   if (end) {
8603     PetscAssertPointer(end, 4);
8604     *end = 0;
8605   }
8606   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8607   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
8608   if (mesh->tr) {
8609     PetscCall(DMPlexTransformGetCellTypeStratum(mesh->tr, ct, start, end));
8610   } else {
8611     PetscCall(DMPlexGetCellTypeLabel(dm, &label));
8612     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named celltype was found");
8613     PetscCall(DMLabelGetStratumBounds(label, ct, start, end));
8614   }
8615   PetscFunctionReturn(PETSC_SUCCESS);
8616 }
8617 
8618 PetscErrorCode DMPlexCreateNumbering_Plex(DM dm, PetscInt pStart, PetscInt pEnd, PetscInt shift, PetscInt *globalSize, PetscSF sf, IS *numbering)
8619 {
8620   PetscSection section, globalSection;
8621   PetscInt    *numbers, p;
8622 
8623   PetscFunctionBegin;
8624   if (PetscDefined(USE_DEBUG)) PetscCall(DMPlexCheckPointSF(dm, sf, PETSC_TRUE));
8625   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
8626   PetscCall(PetscSectionSetChart(section, pStart, pEnd));
8627   for (p = pStart; p < pEnd; ++p) PetscCall(PetscSectionSetDof(section, p, 1));
8628   PetscCall(PetscSectionSetUp(section));
8629   PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_FALSE, PETSC_FALSE, &globalSection));
8630   PetscCall(PetscMalloc1(pEnd - pStart, &numbers));
8631   for (p = pStart; p < pEnd; ++p) {
8632     PetscCall(PetscSectionGetOffset(globalSection, p, &numbers[p - pStart]));
8633     if (numbers[p - pStart] < 0) numbers[p - pStart] -= shift;
8634     else numbers[p - pStart] += shift;
8635   }
8636   PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), pEnd - pStart, numbers, PETSC_OWN_POINTER, numbering));
8637   if (globalSize) {
8638     PetscLayout layout;
8639     PetscCall(PetscSectionGetPointLayout(PetscObjectComm((PetscObject)dm), globalSection, &layout));
8640     PetscCall(PetscLayoutGetSize(layout, globalSize));
8641     PetscCall(PetscLayoutDestroy(&layout));
8642   }
8643   PetscCall(PetscSectionDestroy(&section));
8644   PetscCall(PetscSectionDestroy(&globalSection));
8645   PetscFunctionReturn(PETSC_SUCCESS);
8646 }
8647 
8648 PetscErrorCode DMPlexCreateCellNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalCellNumbers)
8649 {
8650   PetscInt cellHeight, cStart, cEnd;
8651 
8652   PetscFunctionBegin;
8653   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8654   if (includeHybrid) PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8655   else PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd));
8656   PetscCall(DMPlexCreateNumbering_Plex(dm, cStart, cEnd, 0, NULL, dm->sf, globalCellNumbers));
8657   PetscFunctionReturn(PETSC_SUCCESS);
8658 }
8659 
8660 /*@
8661   DMPlexGetCellNumbering - Get a global cell numbering for all cells on this process
8662 
8663   Input Parameter:
8664 . dm - The `DMPLEX` object
8665 
8666   Output Parameter:
8667 . globalCellNumbers - Global cell numbers for all cells on this process
8668 
8669   Level: developer
8670 
8671 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetVertexNumbering()`
8672 @*/
8673 PetscErrorCode DMPlexGetCellNumbering(DM dm, IS *globalCellNumbers)
8674 {
8675   DM_Plex *mesh = (DM_Plex *)dm->data;
8676 
8677   PetscFunctionBegin;
8678   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8679   if (!mesh->globalCellNumbers) PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_FALSE, &mesh->globalCellNumbers));
8680   *globalCellNumbers = mesh->globalCellNumbers;
8681   PetscFunctionReturn(PETSC_SUCCESS);
8682 }
8683 
8684 PetscErrorCode DMPlexCreateVertexNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalVertexNumbers)
8685 {
8686   PetscInt vStart, vEnd;
8687 
8688   PetscFunctionBegin;
8689   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8690   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8691   PetscCall(DMPlexCreateNumbering_Plex(dm, vStart, vEnd, 0, NULL, dm->sf, globalVertexNumbers));
8692   PetscFunctionReturn(PETSC_SUCCESS);
8693 }
8694 
8695 /*@
8696   DMPlexGetVertexNumbering - Get a global vertex numbering for all vertices on this process
8697 
8698   Input Parameter:
8699 . dm - The `DMPLEX` object
8700 
8701   Output Parameter:
8702 . globalVertexNumbers - Global vertex numbers for all vertices on this process
8703 
8704   Level: developer
8705 
8706 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
8707 @*/
8708 PetscErrorCode DMPlexGetVertexNumbering(DM dm, IS *globalVertexNumbers)
8709 {
8710   DM_Plex *mesh = (DM_Plex *)dm->data;
8711 
8712   PetscFunctionBegin;
8713   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8714   if (!mesh->globalVertexNumbers) PetscCall(DMPlexCreateVertexNumbering_Internal(dm, PETSC_FALSE, &mesh->globalVertexNumbers));
8715   *globalVertexNumbers = mesh->globalVertexNumbers;
8716   PetscFunctionReturn(PETSC_SUCCESS);
8717 }
8718 
8719 /*@
8720   DMPlexCreatePointNumbering - Create a global numbering for all points.
8721 
8722   Collective
8723 
8724   Input Parameter:
8725 . dm - The `DMPLEX` object
8726 
8727   Output Parameter:
8728 . globalPointNumbers - Global numbers for all points on this process
8729 
8730   Level: developer
8731 
8732   Notes:
8733   The point numbering `IS` is parallel, with local portion indexed by local points (see `DMGetLocalSection()`). The global
8734   points are taken as stratified, with each MPI rank owning a contiguous subset of each stratum. In the IS, owned points
8735   will have their non-negative value while points owned by different ranks will be involuted -(idx+1). As an example,
8736   consider a parallel mesh in which the first two elements and first two vertices are owned by rank 0.
8737 
8738   The partitioned mesh is
8739   ```
8740   (2)--0--(3)--1--(4)    (1)--0--(2)
8741   ```
8742   and its global numbering is
8743   ```
8744   (3)--0--(4)--1--(5)--2--(6)
8745   ```
8746   Then the global numbering is provided as
8747   ```
8748   [0] Number of indices in set 5
8749   [0] 0 0
8750   [0] 1 1
8751   [0] 2 3
8752   [0] 3 4
8753   [0] 4 -6
8754   [1] Number of indices in set 3
8755   [1] 0 2
8756   [1] 1 5
8757   [1] 2 6
8758   ```
8759 
8760 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
8761 @*/
8762 PetscErrorCode DMPlexCreatePointNumbering(DM dm, IS *globalPointNumbers)
8763 {
8764   IS        nums[4];
8765   PetscInt  depths[4], gdepths[4], starts[4];
8766   PetscInt  depth, d, shift = 0;
8767   PetscBool empty = PETSC_FALSE;
8768 
8769   PetscFunctionBegin;
8770   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8771   PetscCall(DMPlexGetDepth(dm, &depth));
8772   // For unstratified meshes use dim instead of depth
8773   if (depth < 0) PetscCall(DMGetDimension(dm, &depth));
8774   // If any stratum is empty, we must mark all empty
8775   for (d = 0; d <= depth; ++d) {
8776     PetscInt end;
8777 
8778     depths[d] = depth - d;
8779     PetscCall(DMPlexGetDepthStratum(dm, depths[d], &starts[d], &end));
8780     if (!(starts[d] - end)) empty = PETSC_TRUE;
8781   }
8782   if (empty)
8783     for (d = 0; d <= depth; ++d) {
8784       depths[d] = -1;
8785       starts[d] = -1;
8786     }
8787   else PetscCall(PetscSortIntWithArray(depth + 1, starts, depths));
8788   PetscCall(MPIU_Allreduce(depths, gdepths, depth + 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
8789   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]);
8790   // Note here that 'shift' is collective, so that the numbering is stratified by depth
8791   for (d = 0; d <= depth; ++d) {
8792     PetscInt pStart, pEnd, gsize;
8793 
8794     PetscCall(DMPlexGetDepthStratum(dm, gdepths[d], &pStart, &pEnd));
8795     PetscCall(DMPlexCreateNumbering_Plex(dm, pStart, pEnd, shift, &gsize, dm->sf, &nums[d]));
8796     shift += gsize;
8797   }
8798   PetscCall(ISConcatenate(PETSC_COMM_SELF, depth + 1, nums, globalPointNumbers));
8799   for (d = 0; d <= depth; ++d) PetscCall(ISDestroy(&nums[d]));
8800   PetscFunctionReturn(PETSC_SUCCESS);
8801 }
8802 
8803 /*@
8804   DMPlexCreateRankField - Create a cell field whose value is the rank of the owner
8805 
8806   Input Parameter:
8807 . dm - The `DMPLEX` object
8808 
8809   Output Parameter:
8810 . ranks - The rank field
8811 
8812   Options Database Key:
8813 . -dm_partition_view - Adds the rank field into the `DM` output from `-dm_view` using the same viewer
8814 
8815   Level: intermediate
8816 
8817 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`
8818 @*/
8819 PetscErrorCode DMPlexCreateRankField(DM dm, Vec *ranks)
8820 {
8821   DM             rdm;
8822   PetscFE        fe;
8823   PetscScalar   *r;
8824   PetscMPIInt    rank;
8825   DMPolytopeType ct;
8826   PetscInt       dim, cStart, cEnd, c;
8827   PetscBool      simplex;
8828 
8829   PetscFunctionBeginUser;
8830   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8831   PetscAssertPointer(ranks, 2);
8832   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
8833   PetscCall(DMClone(dm, &rdm));
8834   PetscCall(DMGetDimension(rdm, &dim));
8835   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
8836   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
8837   simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct) + 1 ? PETSC_TRUE : PETSC_FALSE;
8838   PetscCall(PetscFECreateDefault(PETSC_COMM_SELF, dim, 1, simplex, "PETSc___rank_", -1, &fe));
8839   PetscCall(PetscObjectSetName((PetscObject)fe, "rank"));
8840   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
8841   PetscCall(PetscFEDestroy(&fe));
8842   PetscCall(DMCreateDS(rdm));
8843   PetscCall(DMCreateGlobalVector(rdm, ranks));
8844   PetscCall(PetscObjectSetName((PetscObject)*ranks, "partition"));
8845   PetscCall(VecGetArray(*ranks, &r));
8846   for (c = cStart; c < cEnd; ++c) {
8847     PetscScalar *lr;
8848 
8849     PetscCall(DMPlexPointGlobalRef(rdm, c, r, &lr));
8850     if (lr) *lr = rank;
8851   }
8852   PetscCall(VecRestoreArray(*ranks, &r));
8853   PetscCall(DMDestroy(&rdm));
8854   PetscFunctionReturn(PETSC_SUCCESS);
8855 }
8856 
8857 /*@
8858   DMPlexCreateLabelField - Create a cell field whose value is the label value for that cell
8859 
8860   Input Parameters:
8861 + dm    - The `DMPLEX`
8862 - label - The `DMLabel`
8863 
8864   Output Parameter:
8865 . val - The label value field
8866 
8867   Options Database Key:
8868 . -dm_label_view - Adds the label value field into the `DM` output from `-dm_view` using the same viewer
8869 
8870   Level: intermediate
8871 
8872 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`
8873 @*/
8874 PetscErrorCode DMPlexCreateLabelField(DM dm, DMLabel label, Vec *val)
8875 {
8876   DM           rdm;
8877   PetscFE      fe;
8878   PetscScalar *v;
8879   PetscInt     dim, cStart, cEnd, c;
8880 
8881   PetscFunctionBeginUser;
8882   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8883   PetscAssertPointer(label, 2);
8884   PetscAssertPointer(val, 3);
8885   PetscCall(DMClone(dm, &rdm));
8886   PetscCall(DMGetDimension(rdm, &dim));
8887   PetscCall(PetscFECreateDefault(PetscObjectComm((PetscObject)rdm), dim, 1, PETSC_TRUE, "PETSc___label_value_", -1, &fe));
8888   PetscCall(PetscObjectSetName((PetscObject)fe, "label_value"));
8889   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
8890   PetscCall(PetscFEDestroy(&fe));
8891   PetscCall(DMCreateDS(rdm));
8892   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
8893   PetscCall(DMCreateGlobalVector(rdm, val));
8894   PetscCall(PetscObjectSetName((PetscObject)*val, "label_value"));
8895   PetscCall(VecGetArray(*val, &v));
8896   for (c = cStart; c < cEnd; ++c) {
8897     PetscScalar *lv;
8898     PetscInt     cval;
8899 
8900     PetscCall(DMPlexPointGlobalRef(rdm, c, v, &lv));
8901     PetscCall(DMLabelGetValue(label, c, &cval));
8902     *lv = cval;
8903   }
8904   PetscCall(VecRestoreArray(*val, &v));
8905   PetscCall(DMDestroy(&rdm));
8906   PetscFunctionReturn(PETSC_SUCCESS);
8907 }
8908 
8909 /*@
8910   DMPlexCheckSymmetry - Check that the adjacency information in the mesh is symmetric.
8911 
8912   Input Parameter:
8913 . dm - The `DMPLEX` object
8914 
8915   Level: developer
8916 
8917   Notes:
8918   This is a useful diagnostic when creating meshes programmatically.
8919 
8920   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
8921 
8922 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
8923 @*/
8924 PetscErrorCode DMPlexCheckSymmetry(DM dm)
8925 {
8926   PetscSection    coneSection, supportSection;
8927   const PetscInt *cone, *support;
8928   PetscInt        coneSize, c, supportSize, s;
8929   PetscInt        pStart, pEnd, p, pp, csize, ssize;
8930   PetscBool       storagecheck = PETSC_TRUE;
8931 
8932   PetscFunctionBegin;
8933   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8934   PetscCall(DMViewFromOptions(dm, NULL, "-sym_dm_view"));
8935   PetscCall(DMPlexGetConeSection(dm, &coneSection));
8936   PetscCall(DMPlexGetSupportSection(dm, &supportSection));
8937   /* Check that point p is found in the support of its cone points, and vice versa */
8938   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8939   for (p = pStart; p < pEnd; ++p) {
8940     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
8941     PetscCall(DMPlexGetCone(dm, p, &cone));
8942     for (c = 0; c < coneSize; ++c) {
8943       PetscBool dup = PETSC_FALSE;
8944       PetscInt  d;
8945       for (d = c - 1; d >= 0; --d) {
8946         if (cone[c] == cone[d]) {
8947           dup = PETSC_TRUE;
8948           break;
8949         }
8950       }
8951       PetscCall(DMPlexGetSupportSize(dm, cone[c], &supportSize));
8952       PetscCall(DMPlexGetSupport(dm, cone[c], &support));
8953       for (s = 0; s < supportSize; ++s) {
8954         if (support[s] == p) break;
8955       }
8956       if ((s >= supportSize) || (dup && (support[s + 1] != p))) {
8957         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", p));
8958         for (s = 0; s < coneSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[s]));
8959         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8960         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", cone[c]));
8961         for (s = 0; s < supportSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[s]));
8962         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8963         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]);
8964         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in support of cone point %" PetscInt_FMT, p, cone[c]);
8965       }
8966     }
8967     PetscCall(DMPlexGetTreeParent(dm, p, &pp, NULL));
8968     if (p != pp) {
8969       storagecheck = PETSC_FALSE;
8970       continue;
8971     }
8972     PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
8973     PetscCall(DMPlexGetSupport(dm, p, &support));
8974     for (s = 0; s < supportSize; ++s) {
8975       PetscCall(DMPlexGetConeSize(dm, support[s], &coneSize));
8976       PetscCall(DMPlexGetCone(dm, support[s], &cone));
8977       for (c = 0; c < coneSize; ++c) {
8978         PetscCall(DMPlexGetTreeParent(dm, cone[c], &pp, NULL));
8979         if (cone[c] != pp) {
8980           c = 0;
8981           break;
8982         }
8983         if (cone[c] == p) break;
8984       }
8985       if (c >= coneSize) {
8986         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", p));
8987         for (c = 0; c < supportSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[c]));
8988         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8989         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", support[s]));
8990         for (c = 0; c < coneSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[c]));
8991         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8992         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in cone of support point %" PetscInt_FMT, p, support[s]);
8993       }
8994     }
8995   }
8996   if (storagecheck) {
8997     PetscCall(PetscSectionGetStorageSize(coneSection, &csize));
8998     PetscCall(PetscSectionGetStorageSize(supportSection, &ssize));
8999     PetscCheck(csize == ssize, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Total cone size %" PetscInt_FMT " != Total support size %" PetscInt_FMT, csize, ssize);
9000   }
9001   PetscFunctionReturn(PETSC_SUCCESS);
9002 }
9003 
9004 /*
9005   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.
9006 */
9007 static PetscErrorCode DMPlexCellUnsplitVertices_Private(DM dm, PetscInt c, DMPolytopeType ct, PetscInt *unsplit)
9008 {
9009   DMPolytopeType  cct;
9010   PetscInt        ptpoints[4];
9011   const PetscInt *cone, *ccone, *ptcone;
9012   PetscInt        coneSize, cp, cconeSize, ccp, npt = 0, pt;
9013 
9014   PetscFunctionBegin;
9015   *unsplit = 0;
9016   switch (ct) {
9017   case DM_POLYTOPE_POINT_PRISM_TENSOR:
9018     ptpoints[npt++] = c;
9019     break;
9020   case DM_POLYTOPE_SEG_PRISM_TENSOR:
9021     PetscCall(DMPlexGetCone(dm, c, &cone));
9022     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9023     for (cp = 0; cp < coneSize; ++cp) {
9024       PetscCall(DMPlexGetCellType(dm, cone[cp], &cct));
9025       if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) ptpoints[npt++] = cone[cp];
9026     }
9027     break;
9028   case DM_POLYTOPE_TRI_PRISM_TENSOR:
9029   case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9030     PetscCall(DMPlexGetCone(dm, c, &cone));
9031     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9032     for (cp = 0; cp < coneSize; ++cp) {
9033       PetscCall(DMPlexGetCone(dm, cone[cp], &ccone));
9034       PetscCall(DMPlexGetConeSize(dm, cone[cp], &cconeSize));
9035       for (ccp = 0; ccp < cconeSize; ++ccp) {
9036         PetscCall(DMPlexGetCellType(dm, ccone[ccp], &cct));
9037         if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) {
9038           PetscInt p;
9039           for (p = 0; p < npt; ++p)
9040             if (ptpoints[p] == ccone[ccp]) break;
9041           if (p == npt) ptpoints[npt++] = ccone[ccp];
9042         }
9043       }
9044     }
9045     break;
9046   default:
9047     break;
9048   }
9049   for (pt = 0; pt < npt; ++pt) {
9050     PetscCall(DMPlexGetCone(dm, ptpoints[pt], &ptcone));
9051     if (ptcone[0] == ptcone[1]) ++(*unsplit);
9052   }
9053   PetscFunctionReturn(PETSC_SUCCESS);
9054 }
9055 
9056 /*@
9057   DMPlexCheckSkeleton - Check that each cell has the correct number of vertices
9058 
9059   Input Parameters:
9060 + dm         - The `DMPLEX` object
9061 - cellHeight - Normally 0
9062 
9063   Level: developer
9064 
9065   Notes:
9066   This is a useful diagnostic when creating meshes programmatically.
9067   Currently applicable only to homogeneous simplex or tensor meshes.
9068 
9069   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9070 
9071 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9072 @*/
9073 PetscErrorCode DMPlexCheckSkeleton(DM dm, PetscInt cellHeight)
9074 {
9075   DMPlexInterpolatedFlag interp;
9076   DMPolytopeType         ct;
9077   PetscInt               vStart, vEnd, cStart, cEnd, c;
9078 
9079   PetscFunctionBegin;
9080   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9081   PetscCall(DMPlexIsInterpolated(dm, &interp));
9082   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9083   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
9084   for (c = cStart; c < cEnd; ++c) {
9085     PetscInt *closure = NULL;
9086     PetscInt  coneSize, closureSize, cl, Nv = 0;
9087 
9088     PetscCall(DMPlexGetCellType(dm, c, &ct));
9089     PetscCheck((PetscInt)ct >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell %" PetscInt_FMT " has no cell type", c);
9090     if (ct == DM_POLYTOPE_UNKNOWN) continue;
9091     if (interp == DMPLEX_INTERPOLATED_FULL) {
9092       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9093       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));
9094     }
9095     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9096     for (cl = 0; cl < closureSize * 2; cl += 2) {
9097       const PetscInt p = closure[cl];
9098       if ((p >= vStart) && (p < vEnd)) ++Nv;
9099     }
9100     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9101     /* Special Case: Tensor faces with identified vertices */
9102     if (Nv < DMPolytopeTypeGetNumVertices(ct)) {
9103       PetscInt unsplit;
9104 
9105       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9106       if (Nv + unsplit == DMPolytopeTypeGetNumVertices(ct)) continue;
9107     }
9108     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));
9109   }
9110   PetscFunctionReturn(PETSC_SUCCESS);
9111 }
9112 
9113 /*@
9114   DMPlexCheckFaces - Check that the faces of each cell give a vertex order this is consistent with what we expect from the cell type
9115 
9116   Collective
9117 
9118   Input Parameters:
9119 + dm         - The `DMPLEX` object
9120 - cellHeight - Normally 0
9121 
9122   Level: developer
9123 
9124   Notes:
9125   This is a useful diagnostic when creating meshes programmatically.
9126   This routine is only relevant for meshes that are fully interpolated across all ranks.
9127   It will error out if a partially interpolated mesh is given on some rank.
9128   It will do nothing for locally uninterpolated mesh (as there is nothing to check).
9129 
9130   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9131 
9132 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMPlexGetVTKCellHeight()`, `DMSetFromOptions()`
9133 @*/
9134 PetscErrorCode DMPlexCheckFaces(DM dm, PetscInt cellHeight)
9135 {
9136   PetscInt               dim, depth, vStart, vEnd, cStart, cEnd, c, h;
9137   DMPlexInterpolatedFlag interpEnum;
9138 
9139   PetscFunctionBegin;
9140   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9141   PetscCall(DMPlexIsInterpolatedCollective(dm, &interpEnum));
9142   if (interpEnum == DMPLEX_INTERPOLATED_NONE) PetscFunctionReturn(PETSC_SUCCESS);
9143   if (interpEnum != DMPLEX_INTERPOLATED_FULL) {
9144     PetscCall(PetscPrintf(PetscObjectComm((PetscObject)dm), "DMPlexCheckFaces() warning: Mesh is only partially interpolated, this is currently not supported"));
9145     PetscFunctionReturn(PETSC_SUCCESS);
9146   }
9147 
9148   PetscCall(DMGetDimension(dm, &dim));
9149   PetscCall(DMPlexGetDepth(dm, &depth));
9150   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
9151   for (h = cellHeight; h < PetscMin(depth, dim); ++h) {
9152     PetscCall(DMPlexGetHeightStratum(dm, h, &cStart, &cEnd));
9153     for (c = cStart; c < cEnd; ++c) {
9154       const PetscInt       *cone, *ornt, *faceSizes, *faces;
9155       const DMPolytopeType *faceTypes;
9156       DMPolytopeType        ct;
9157       PetscInt              numFaces, coneSize, f;
9158       PetscInt             *closure = NULL, closureSize, cl, numCorners = 0, fOff = 0, unsplit;
9159 
9160       PetscCall(DMPlexGetCellType(dm, c, &ct));
9161       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9162       if (unsplit) continue;
9163       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9164       PetscCall(DMPlexGetCone(dm, c, &cone));
9165       PetscCall(DMPlexGetConeOrientation(dm, c, &ornt));
9166       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9167       for (cl = 0; cl < closureSize * 2; cl += 2) {
9168         const PetscInt p = closure[cl];
9169         if ((p >= vStart) && (p < vEnd)) closure[numCorners++] = p;
9170       }
9171       PetscCall(DMPlexGetRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
9172       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);
9173       for (f = 0; f < numFaces; ++f) {
9174         DMPolytopeType fct;
9175         PetscInt      *fclosure = NULL, fclosureSize, cl, fnumCorners = 0, v;
9176 
9177         PetscCall(DMPlexGetCellType(dm, cone[f], &fct));
9178         PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[f], ornt[f], PETSC_TRUE, &fclosureSize, &fclosure));
9179         for (cl = 0; cl < fclosureSize * 2; cl += 2) {
9180           const PetscInt p = fclosure[cl];
9181           if ((p >= vStart) && (p < vEnd)) fclosure[fnumCorners++] = p;
9182         }
9183         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]);
9184         for (v = 0; v < fnumCorners; ++v) {
9185           if (fclosure[v] != faces[fOff + v]) {
9186             PetscInt v1;
9187 
9188             PetscCall(PetscPrintf(PETSC_COMM_SELF, "face closure:"));
9189             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, fclosure[v1]));
9190             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\ncell face:"));
9191             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, faces[fOff + v1]));
9192             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9193             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]);
9194           }
9195         }
9196         PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[f], PETSC_TRUE, &fclosureSize, &fclosure));
9197         fOff += faceSizes[f];
9198       }
9199       PetscCall(DMPlexRestoreRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
9200       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9201     }
9202   }
9203   PetscFunctionReturn(PETSC_SUCCESS);
9204 }
9205 
9206 /*@
9207   DMPlexCheckGeometry - Check the geometry of mesh cells
9208 
9209   Input Parameter:
9210 . dm - The `DMPLEX` object
9211 
9212   Level: developer
9213 
9214   Notes:
9215   This is a useful diagnostic when creating meshes programmatically.
9216 
9217   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9218 
9219 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9220 @*/
9221 PetscErrorCode DMPlexCheckGeometry(DM dm)
9222 {
9223   Vec       coordinates;
9224   PetscReal detJ, J[9], refVol = 1.0;
9225   PetscReal vol;
9226   PetscInt  dim, depth, dE, d, cStart, cEnd, c;
9227 
9228   PetscFunctionBegin;
9229   PetscCall(DMGetDimension(dm, &dim));
9230   PetscCall(DMGetCoordinateDim(dm, &dE));
9231   if (dim != dE) PetscFunctionReturn(PETSC_SUCCESS);
9232   PetscCall(DMPlexGetDepth(dm, &depth));
9233   for (d = 0; d < dim; ++d) refVol *= 2.0;
9234   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
9235   /* Make sure local coordinates are created, because that step is collective */
9236   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
9237   if (!coordinates) PetscFunctionReturn(PETSC_SUCCESS);
9238   for (c = cStart; c < cEnd; ++c) {
9239     DMPolytopeType ct;
9240     PetscInt       unsplit;
9241     PetscBool      ignoreZeroVol = PETSC_FALSE;
9242 
9243     PetscCall(DMPlexGetCellType(dm, c, &ct));
9244     switch (ct) {
9245     case DM_POLYTOPE_SEG_PRISM_TENSOR:
9246     case DM_POLYTOPE_TRI_PRISM_TENSOR:
9247     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9248       ignoreZeroVol = PETSC_TRUE;
9249       break;
9250     default:
9251       break;
9252     }
9253     switch (ct) {
9254     case DM_POLYTOPE_TRI_PRISM:
9255     case DM_POLYTOPE_TRI_PRISM_TENSOR:
9256     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9257     case DM_POLYTOPE_PYRAMID:
9258       continue;
9259     default:
9260       break;
9261     }
9262     PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9263     if (unsplit) continue;
9264     PetscCall(DMPlexComputeCellGeometryFEM(dm, c, NULL, NULL, J, NULL, &detJ));
9265     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);
9266     PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FEM Volume %g\n", c, (double)(detJ * refVol)));
9267     /* This should work with periodicity since DG coordinates should be used */
9268     if (depth > 1) {
9269       PetscCall(DMPlexComputeCellGeometryFVM(dm, c, &vol, NULL, NULL));
9270       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);
9271       PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FVM Volume %g\n", c, (double)vol));
9272     }
9273   }
9274   PetscFunctionReturn(PETSC_SUCCESS);
9275 }
9276 
9277 /*@
9278   DMPlexCheckPointSF - Check that several necessary conditions are met for the point `PetscSF` of this plex.
9279 
9280   Collective
9281 
9282   Input Parameters:
9283 + dm              - The `DMPLEX` object
9284 . pointSF         - The `PetscSF`, or `NULL` for `PointSF` attached to `DM`
9285 - allowExtraRoots - Flag to allow extra points not present in the `DM`
9286 
9287   Level: developer
9288 
9289   Notes:
9290   This is mainly intended for debugging/testing purposes.
9291 
9292   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9293 
9294   Extra roots can come from periodic cuts, where additional points appear on the boundary
9295 
9296 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMGetPointSF()`, `DMSetFromOptions()`
9297 @*/
9298 PetscErrorCode DMPlexCheckPointSF(DM dm, PetscSF pointSF, PetscBool allowExtraRoots)
9299 {
9300   PetscInt           l, nleaves, nroots, overlap;
9301   const PetscInt    *locals;
9302   const PetscSFNode *remotes;
9303   PetscBool          distributed;
9304   MPI_Comm           comm;
9305   PetscMPIInt        rank;
9306 
9307   PetscFunctionBegin;
9308   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9309   if (pointSF) PetscValidHeaderSpecific(pointSF, PETSCSF_CLASSID, 2);
9310   else pointSF = dm->sf;
9311   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9312   PetscCheck(pointSF, comm, PETSC_ERR_ARG_WRONGSTATE, "DMPlex must have Point SF attached");
9313   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9314   {
9315     PetscMPIInt mpiFlag;
9316 
9317     PetscCallMPI(MPI_Comm_compare(comm, PetscObjectComm((PetscObject)pointSF), &mpiFlag));
9318     PetscCheck(mpiFlag == MPI_CONGRUENT || mpiFlag == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "DM and Point SF have different communicators (flag %d)", mpiFlag);
9319   }
9320   PetscCall(PetscSFGetGraph(pointSF, &nroots, &nleaves, &locals, &remotes));
9321   PetscCall(DMPlexIsDistributed(dm, &distributed));
9322   if (!distributed) {
9323     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);
9324     PetscFunctionReturn(PETSC_SUCCESS);
9325   }
9326   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);
9327   PetscCall(DMPlexGetOverlap(dm, &overlap));
9328 
9329   /* Check SF graph is compatible with DMPlex chart */
9330   {
9331     PetscInt pStart, pEnd, maxLeaf;
9332 
9333     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
9334     PetscCall(PetscSFGetLeafRange(pointSF, NULL, &maxLeaf));
9335     PetscCheck(allowExtraRoots || pEnd - pStart == nroots, PETSC_COMM_SELF, PETSC_ERR_PLIB, "pEnd - pStart = %" PetscInt_FMT " != nroots = %" PetscInt_FMT, pEnd - pStart, nroots);
9336     PetscCheck(maxLeaf < pEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "maxLeaf = %" PetscInt_FMT " >= pEnd = %" PetscInt_FMT, maxLeaf, pEnd);
9337   }
9338 
9339   /* Check Point SF has no local points referenced */
9340   for (l = 0; l < nleaves; l++) {
9341     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);
9342   }
9343 
9344   /* Check there are no cells in interface */
9345   if (!overlap) {
9346     PetscInt cellHeight, cStart, cEnd;
9347 
9348     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9349     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9350     for (l = 0; l < nleaves; ++l) {
9351       const PetscInt point = locals ? locals[l] : l;
9352 
9353       PetscCheck(point < cStart || point >= cEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " which is a cell", point);
9354     }
9355   }
9356 
9357   /* If some point is in interface, then all its cone points must be also in interface (either as leaves or roots) */
9358   {
9359     const PetscInt *rootdegree;
9360 
9361     PetscCall(PetscSFComputeDegreeBegin(pointSF, &rootdegree));
9362     PetscCall(PetscSFComputeDegreeEnd(pointSF, &rootdegree));
9363     for (l = 0; l < nleaves; ++l) {
9364       const PetscInt  point = locals ? locals[l] : l;
9365       const PetscInt *cone;
9366       PetscInt        coneSize, c, idx;
9367 
9368       PetscCall(DMPlexGetConeSize(dm, point, &coneSize));
9369       PetscCall(DMPlexGetCone(dm, point, &cone));
9370       for (c = 0; c < coneSize; ++c) {
9371         if (!rootdegree[cone[c]]) {
9372           if (locals) {
9373             PetscCall(PetscFindInt(cone[c], nleaves, locals, &idx));
9374           } else {
9375             idx = (cone[c] < nleaves) ? cone[c] : -1;
9376           }
9377           PetscCheck(idx >= 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " but not %" PetscInt_FMT " from its cone", point, cone[c]);
9378         }
9379       }
9380     }
9381   }
9382   PetscFunctionReturn(PETSC_SUCCESS);
9383 }
9384 
9385 /*@
9386   DMPlexCheck - Perform various checks of `DMPLEX` sanity
9387 
9388   Input Parameter:
9389 . dm - The `DMPLEX` object
9390 
9391   Level: developer
9392 
9393   Notes:
9394   This is a useful diagnostic when creating meshes programmatically.
9395 
9396   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9397 
9398   Currently does not include `DMPlexCheckCellShape()`.
9399 
9400 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9401 @*/
9402 PetscErrorCode DMPlexCheck(DM dm)
9403 {
9404   PetscInt cellHeight;
9405 
9406   PetscFunctionBegin;
9407   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9408   PetscCall(DMPlexCheckSymmetry(dm));
9409   PetscCall(DMPlexCheckSkeleton(dm, cellHeight));
9410   PetscCall(DMPlexCheckFaces(dm, cellHeight));
9411   PetscCall(DMPlexCheckGeometry(dm));
9412   PetscCall(DMPlexCheckPointSF(dm, NULL, PETSC_FALSE));
9413   PetscCall(DMPlexCheckInterfaceCones(dm));
9414   PetscFunctionReturn(PETSC_SUCCESS);
9415 }
9416 
9417 typedef struct cell_stats {
9418   PetscReal min, max, sum, squaresum;
9419   PetscInt  count;
9420 } cell_stats_t;
9421 
9422 static void MPIAPI cell_stats_reduce(void *a, void *b, int *len, MPI_Datatype *datatype)
9423 {
9424   PetscInt i, N = *len;
9425 
9426   for (i = 0; i < N; i++) {
9427     cell_stats_t *A = (cell_stats_t *)a;
9428     cell_stats_t *B = (cell_stats_t *)b;
9429 
9430     B->min = PetscMin(A->min, B->min);
9431     B->max = PetscMax(A->max, B->max);
9432     B->sum += A->sum;
9433     B->squaresum += A->squaresum;
9434     B->count += A->count;
9435   }
9436 }
9437 
9438 /*@
9439   DMPlexCheckCellShape - Checks the Jacobian of the mapping from reference to real cells and computes some minimal statistics.
9440 
9441   Collective
9442 
9443   Input Parameters:
9444 + dm        - The `DMPLEX` object
9445 . output    - If true, statistics will be displayed on `stdout`
9446 - condLimit - Display all cells above this condition number, or `PETSC_DETERMINE` for no cell output
9447 
9448   Level: developer
9449 
9450   Notes:
9451   This is mainly intended for debugging/testing purposes.
9452 
9453   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9454 
9455 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexComputeOrthogonalQuality()`
9456 @*/
9457 PetscErrorCode DMPlexCheckCellShape(DM dm, PetscBool output, PetscReal condLimit)
9458 {
9459   DM           dmCoarse;
9460   cell_stats_t stats, globalStats;
9461   MPI_Comm     comm = PetscObjectComm((PetscObject)dm);
9462   PetscReal   *J, *invJ, min = 0, max = 0, mean = 0, stdev = 0;
9463   PetscReal    limit = condLimit > 0 ? condLimit : PETSC_MAX_REAL;
9464   PetscInt     cdim, cStart, cEnd, c, eStart, eEnd, count = 0;
9465   PetscMPIInt  rank, size;
9466 
9467   PetscFunctionBegin;
9468   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9469   stats.min = PETSC_MAX_REAL;
9470   stats.max = PETSC_MIN_REAL;
9471   stats.sum = stats.squaresum = 0.;
9472   stats.count                 = 0;
9473 
9474   PetscCallMPI(MPI_Comm_size(comm, &size));
9475   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9476   PetscCall(DMGetCoordinateDim(dm, &cdim));
9477   PetscCall(PetscMalloc2(PetscSqr(cdim), &J, PetscSqr(cdim), &invJ));
9478   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
9479   PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
9480   for (c = cStart; c < cEnd; c++) {
9481     PetscInt  i;
9482     PetscReal frobJ = 0., frobInvJ = 0., cond2, cond, detJ;
9483 
9484     PetscCall(DMPlexComputeCellGeometryAffineFEM(dm, c, NULL, J, invJ, &detJ));
9485     PetscCheck(detJ >= 0.0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Mesh cell %" PetscInt_FMT " is inverted", c);
9486     for (i = 0; i < PetscSqr(cdim); ++i) {
9487       frobJ += J[i] * J[i];
9488       frobInvJ += invJ[i] * invJ[i];
9489     }
9490     cond2 = frobJ * frobInvJ;
9491     cond  = PetscSqrtReal(cond2);
9492 
9493     stats.min = PetscMin(stats.min, cond);
9494     stats.max = PetscMax(stats.max, cond);
9495     stats.sum += cond;
9496     stats.squaresum += cond2;
9497     stats.count++;
9498     if (output && cond > limit) {
9499       PetscSection coordSection;
9500       Vec          coordsLocal;
9501       PetscScalar *coords = NULL;
9502       PetscInt     Nv, d, clSize, cl, *closure = NULL;
9503 
9504       PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
9505       PetscCall(DMGetCoordinateSection(dm, &coordSection));
9506       PetscCall(DMPlexVecGetClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9507       PetscCall(PetscSynchronizedPrintf(comm, "[%d] Cell %" PetscInt_FMT " cond %g\n", rank, c, (double)cond));
9508       for (i = 0; i < Nv / cdim; ++i) {
9509         PetscCall(PetscSynchronizedPrintf(comm, "  Vertex %" PetscInt_FMT ": (", i));
9510         for (d = 0; d < cdim; ++d) {
9511           if (d > 0) PetscCall(PetscSynchronizedPrintf(comm, ", "));
9512           PetscCall(PetscSynchronizedPrintf(comm, "%g", (double)PetscRealPart(coords[i * cdim + d])));
9513         }
9514         PetscCall(PetscSynchronizedPrintf(comm, ")\n"));
9515       }
9516       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9517       for (cl = 0; cl < clSize * 2; cl += 2) {
9518         const PetscInt edge = closure[cl];
9519 
9520         if ((edge >= eStart) && (edge < eEnd)) {
9521           PetscReal len;
9522 
9523           PetscCall(DMPlexComputeCellGeometryFVM(dm, edge, &len, NULL, NULL));
9524           PetscCall(PetscSynchronizedPrintf(comm, "  Edge %" PetscInt_FMT ": length %g\n", edge, (double)len));
9525         }
9526       }
9527       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9528       PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9529     }
9530   }
9531   if (output) PetscCall(PetscSynchronizedFlush(comm, NULL));
9532 
9533   if (size > 1) {
9534     PetscMPIInt  blockLengths[2] = {4, 1};
9535     MPI_Aint     blockOffsets[2] = {offsetof(cell_stats_t, min), offsetof(cell_stats_t, count)};
9536     MPI_Datatype blockTypes[2]   = {MPIU_REAL, MPIU_INT}, statType;
9537     MPI_Op       statReduce;
9538 
9539     PetscCallMPI(MPI_Type_create_struct(2, blockLengths, blockOffsets, blockTypes, &statType));
9540     PetscCallMPI(MPI_Type_commit(&statType));
9541     PetscCallMPI(MPI_Op_create(cell_stats_reduce, PETSC_TRUE, &statReduce));
9542     PetscCallMPI(MPI_Reduce(&stats, &globalStats, 1, statType, statReduce, 0, comm));
9543     PetscCallMPI(MPI_Op_free(&statReduce));
9544     PetscCallMPI(MPI_Type_free(&statType));
9545   } else {
9546     PetscCall(PetscArraycpy(&globalStats, &stats, 1));
9547   }
9548   if (rank == 0) {
9549     count = globalStats.count;
9550     min   = globalStats.min;
9551     max   = globalStats.max;
9552     mean  = globalStats.sum / globalStats.count;
9553     stdev = globalStats.count > 1 ? PetscSqrtReal(PetscMax((globalStats.squaresum - globalStats.count * mean * mean) / (globalStats.count - 1), 0)) : 0.0;
9554   }
9555 
9556   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));
9557   PetscCall(PetscFree2(J, invJ));
9558 
9559   PetscCall(DMGetCoarseDM(dm, &dmCoarse));
9560   if (dmCoarse) {
9561     PetscBool isplex;
9562 
9563     PetscCall(PetscObjectTypeCompare((PetscObject)dmCoarse, DMPLEX, &isplex));
9564     if (isplex) PetscCall(DMPlexCheckCellShape(dmCoarse, output, condLimit));
9565   }
9566   PetscFunctionReturn(PETSC_SUCCESS);
9567 }
9568 
9569 /*@
9570   DMPlexComputeOrthogonalQuality - Compute cell-wise orthogonal quality mesh statistic. Optionally tags all cells with
9571   orthogonal quality below given tolerance.
9572 
9573   Collective
9574 
9575   Input Parameters:
9576 + dm   - The `DMPLEX` object
9577 . fv   - Optional `PetscFV` object for pre-computed cell/face centroid information
9578 - atol - [0, 1] Absolute tolerance for tagging cells.
9579 
9580   Output Parameters:
9581 + OrthQual      - `Vec` containing orthogonal quality per cell
9582 - OrthQualLabel - `DMLabel` tagging cells below atol with `DM_ADAPT_REFINE`
9583 
9584   Options Database Keys:
9585 + -dm_plex_orthogonal_quality_label_view - view OrthQualLabel if label is requested. Currently only `PETSCVIEWERASCII` is supported.
9586 - -dm_plex_orthogonal_quality_vec_view   - view OrthQual vector.
9587 
9588   Level: intermediate
9589 
9590   Notes:
9591   Orthogonal quality is given by the following formula\:
9592 
9593   $ \min \left[ \frac{A_i \cdot f_i}{\|A_i\| \|f_i\|} , \frac{A_i \cdot c_i}{\|A_i\| \|c_i\|} \right]$
9594 
9595   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
9596   is the vector from the current cells centroid to the centroid of its i'th neighbor (which shares a face with the
9597   current cell). This computes the vector similarity between each cell face and its corresponding neighbor centroid by
9598   calculating the cosine of the angle between these vectors.
9599 
9600   Orthogonal quality ranges from 1 (best) to 0 (worst).
9601 
9602   This routine is mainly useful for FVM, however is not restricted to only FVM. The `PetscFV` object is optionally used to check for
9603   pre-computed FVM cell data, but if it is not passed in then this data will be computed.
9604 
9605   Cells are tagged if they have an orthogonal quality less than or equal to the absolute tolerance.
9606 
9607 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCheckCellShape()`, `DMCreateLabel()`, `PetscFV`, `DMLabel`, `Vec`
9608 @*/
9609 PetscErrorCode DMPlexComputeOrthogonalQuality(DM dm, PetscFV fv, PetscReal atol, Vec *OrthQual, DMLabel *OrthQualLabel)
9610 {
9611   PetscInt               nc, cellHeight, cStart, cEnd, cell, cellIter = 0;
9612   PetscInt              *idx;
9613   PetscScalar           *oqVals;
9614   const PetscScalar     *cellGeomArr, *faceGeomArr;
9615   PetscReal             *ci, *fi, *Ai;
9616   MPI_Comm               comm;
9617   Vec                    cellgeom, facegeom;
9618   DM                     dmFace, dmCell;
9619   IS                     glob;
9620   ISLocalToGlobalMapping ltog;
9621   PetscViewer            vwr;
9622 
9623   PetscFunctionBegin;
9624   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9625   if (fv) PetscValidHeaderSpecific(fv, PETSCFV_CLASSID, 2);
9626   PetscAssertPointer(OrthQual, 4);
9627   PetscCheck(atol >= 0.0 && atol <= 1.0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Absolute tolerance %g not in [0,1]", (double)atol);
9628   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9629   PetscCall(DMGetDimension(dm, &nc));
9630   PetscCheck(nc >= 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must have dimension >= 2 (current %" PetscInt_FMT ")", nc);
9631   {
9632     DMPlexInterpolatedFlag interpFlag;
9633 
9634     PetscCall(DMPlexIsInterpolated(dm, &interpFlag));
9635     if (interpFlag != DMPLEX_INTERPOLATED_FULL) {
9636       PetscMPIInt rank;
9637 
9638       PetscCallMPI(MPI_Comm_rank(comm, &rank));
9639       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must be fully interpolated, DM on rank %d is not fully interpolated", rank);
9640     }
9641   }
9642   if (OrthQualLabel) {
9643     PetscAssertPointer(OrthQualLabel, 5);
9644     PetscCall(DMCreateLabel(dm, "Orthogonal_Quality"));
9645     PetscCall(DMGetLabel(dm, "Orthogonal_Quality", OrthQualLabel));
9646   } else {
9647     *OrthQualLabel = NULL;
9648   }
9649   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9650   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9651   PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_TRUE, &glob));
9652   PetscCall(ISLocalToGlobalMappingCreateIS(glob, &ltog));
9653   PetscCall(ISLocalToGlobalMappingSetType(ltog, ISLOCALTOGLOBALMAPPINGHASH));
9654   PetscCall(VecCreate(comm, OrthQual));
9655   PetscCall(VecSetType(*OrthQual, VECSTANDARD));
9656   PetscCall(VecSetSizes(*OrthQual, cEnd - cStart, PETSC_DETERMINE));
9657   PetscCall(VecSetLocalToGlobalMapping(*OrthQual, ltog));
9658   PetscCall(VecSetUp(*OrthQual));
9659   PetscCall(ISDestroy(&glob));
9660   PetscCall(ISLocalToGlobalMappingDestroy(&ltog));
9661   PetscCall(DMPlexGetDataFVM(dm, fv, &cellgeom, &facegeom, NULL));
9662   PetscCall(VecGetArrayRead(cellgeom, &cellGeomArr));
9663   PetscCall(VecGetArrayRead(facegeom, &faceGeomArr));
9664   PetscCall(VecGetDM(cellgeom, &dmCell));
9665   PetscCall(VecGetDM(facegeom, &dmFace));
9666   PetscCall(PetscMalloc5(cEnd - cStart, &idx, cEnd - cStart, &oqVals, nc, &ci, nc, &fi, nc, &Ai));
9667   for (cell = cStart; cell < cEnd; cellIter++, cell++) {
9668     PetscInt         cellneigh, cellneighiter = 0, adjSize = PETSC_DETERMINE;
9669     PetscInt         cellarr[2], *adj = NULL;
9670     PetscScalar     *cArr, *fArr;
9671     PetscReal        minvalc = 1.0, minvalf = 1.0;
9672     PetscFVCellGeom *cg;
9673 
9674     idx[cellIter] = cell - cStart;
9675     cellarr[0]    = cell;
9676     /* Make indexing into cellGeom easier */
9677     PetscCall(DMPlexPointLocalRead(dmCell, cell, cellGeomArr, &cg));
9678     PetscCall(DMPlexGetAdjacency_Internal(dm, cell, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &adjSize, &adj));
9679     /* Technically 1 too big, but easier than fiddling with empty adjacency array */
9680     PetscCall(PetscCalloc2(adjSize, &cArr, adjSize, &fArr));
9681     for (cellneigh = 0; cellneigh < adjSize; cellneighiter++, cellneigh++) {
9682       PetscInt         i;
9683       const PetscInt   neigh  = adj[cellneigh];
9684       PetscReal        normci = 0, normfi = 0, normai = 0;
9685       PetscFVCellGeom *cgneigh;
9686       PetscFVFaceGeom *fg;
9687 
9688       /* Don't count ourselves in the neighbor list */
9689       if (neigh == cell) continue;
9690       PetscCall(DMPlexPointLocalRead(dmCell, neigh, cellGeomArr, &cgneigh));
9691       cellarr[1] = neigh;
9692       {
9693         PetscInt        numcovpts;
9694         const PetscInt *covpts;
9695 
9696         PetscCall(DMPlexGetMeet(dm, 2, cellarr, &numcovpts, &covpts));
9697         PetscCall(DMPlexPointLocalRead(dmFace, covpts[0], faceGeomArr, &fg));
9698         PetscCall(DMPlexRestoreMeet(dm, 2, cellarr, &numcovpts, &covpts));
9699       }
9700 
9701       /* Compute c_i, f_i and their norms */
9702       for (i = 0; i < nc; i++) {
9703         ci[i] = cgneigh->centroid[i] - cg->centroid[i];
9704         fi[i] = fg->centroid[i] - cg->centroid[i];
9705         Ai[i] = fg->normal[i];
9706         normci += PetscPowReal(ci[i], 2);
9707         normfi += PetscPowReal(fi[i], 2);
9708         normai += PetscPowReal(Ai[i], 2);
9709       }
9710       normci = PetscSqrtReal(normci);
9711       normfi = PetscSqrtReal(normfi);
9712       normai = PetscSqrtReal(normai);
9713 
9714       /* Normalize and compute for each face-cell-normal pair */
9715       for (i = 0; i < nc; i++) {
9716         ci[i] = ci[i] / normci;
9717         fi[i] = fi[i] / normfi;
9718         Ai[i] = Ai[i] / normai;
9719         /* PetscAbs because I don't know if normals are guaranteed to point out */
9720         cArr[cellneighiter] += PetscAbs(Ai[i] * ci[i]);
9721         fArr[cellneighiter] += PetscAbs(Ai[i] * fi[i]);
9722       }
9723       if (PetscRealPart(cArr[cellneighiter]) < minvalc) minvalc = PetscRealPart(cArr[cellneighiter]);
9724       if (PetscRealPart(fArr[cellneighiter]) < minvalf) minvalf = PetscRealPart(fArr[cellneighiter]);
9725     }
9726     PetscCall(PetscFree(adj));
9727     PetscCall(PetscFree2(cArr, fArr));
9728     /* Defer to cell if they're equal */
9729     oqVals[cellIter] = PetscMin(minvalf, minvalc);
9730     if (OrthQualLabel) {
9731       if (PetscRealPart(oqVals[cellIter]) <= atol) PetscCall(DMLabelSetValue(*OrthQualLabel, cell, DM_ADAPT_REFINE));
9732     }
9733   }
9734   PetscCall(VecSetValuesLocal(*OrthQual, cEnd - cStart, idx, oqVals, INSERT_VALUES));
9735   PetscCall(VecAssemblyBegin(*OrthQual));
9736   PetscCall(VecAssemblyEnd(*OrthQual));
9737   PetscCall(VecRestoreArrayRead(cellgeom, &cellGeomArr));
9738   PetscCall(VecRestoreArrayRead(facegeom, &faceGeomArr));
9739   PetscCall(PetscOptionsGetViewer(comm, NULL, NULL, "-dm_plex_orthogonal_quality_label_view", &vwr, NULL, NULL));
9740   if (OrthQualLabel) {
9741     if (vwr) PetscCall(DMLabelView(*OrthQualLabel, vwr));
9742   }
9743   PetscCall(PetscFree5(idx, oqVals, ci, fi, Ai));
9744   PetscCall(PetscOptionsRestoreViewer(&vwr));
9745   PetscCall(VecViewFromOptions(*OrthQual, NULL, "-dm_plex_orthogonal_quality_vec_view"));
9746   PetscFunctionReturn(PETSC_SUCCESS);
9747 }
9748 
9749 /* this is here instead of DMGetOutputDM because output DM still has constraints in the local indices that affect
9750  * interpolator construction */
9751 static PetscErrorCode DMGetFullDM(DM dm, DM *odm)
9752 {
9753   PetscSection section, newSection, gsection;
9754   PetscSF      sf;
9755   PetscBool    hasConstraints, ghasConstraints;
9756 
9757   PetscFunctionBegin;
9758   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9759   PetscAssertPointer(odm, 2);
9760   PetscCall(DMGetLocalSection(dm, &section));
9761   PetscCall(PetscSectionHasConstraints(section, &hasConstraints));
9762   PetscCall(MPIU_Allreduce(&hasConstraints, &ghasConstraints, 1, MPIU_BOOL, MPI_LOR, PetscObjectComm((PetscObject)dm)));
9763   if (!ghasConstraints) {
9764     PetscCall(PetscObjectReference((PetscObject)dm));
9765     *odm = dm;
9766     PetscFunctionReturn(PETSC_SUCCESS);
9767   }
9768   PetscCall(DMClone(dm, odm));
9769   PetscCall(DMCopyFields(dm, *odm));
9770   PetscCall(DMGetLocalSection(*odm, &newSection));
9771   PetscCall(DMGetPointSF(*odm, &sf));
9772   PetscCall(PetscSectionCreateGlobalSection(newSection, sf, PETSC_TRUE, PETSC_FALSE, &gsection));
9773   PetscCall(DMSetGlobalSection(*odm, gsection));
9774   PetscCall(PetscSectionDestroy(&gsection));
9775   PetscFunctionReturn(PETSC_SUCCESS);
9776 }
9777 
9778 static PetscErrorCode DMCreateAffineInterpolationCorrection_Plex(DM dmc, DM dmf, Vec *shift)
9779 {
9780   DM        dmco, dmfo;
9781   Mat       interpo;
9782   Vec       rscale;
9783   Vec       cglobalo, clocal;
9784   Vec       fglobal, fglobalo, flocal;
9785   PetscBool regular;
9786 
9787   PetscFunctionBegin;
9788   PetscCall(DMGetFullDM(dmc, &dmco));
9789   PetscCall(DMGetFullDM(dmf, &dmfo));
9790   PetscCall(DMSetCoarseDM(dmfo, dmco));
9791   PetscCall(DMPlexGetRegularRefinement(dmf, &regular));
9792   PetscCall(DMPlexSetRegularRefinement(dmfo, regular));
9793   PetscCall(DMCreateInterpolation(dmco, dmfo, &interpo, &rscale));
9794   PetscCall(DMCreateGlobalVector(dmco, &cglobalo));
9795   PetscCall(DMCreateLocalVector(dmc, &clocal));
9796   PetscCall(VecSet(cglobalo, 0.));
9797   PetscCall(VecSet(clocal, 0.));
9798   PetscCall(DMCreateGlobalVector(dmf, &fglobal));
9799   PetscCall(DMCreateGlobalVector(dmfo, &fglobalo));
9800   PetscCall(DMCreateLocalVector(dmf, &flocal));
9801   PetscCall(VecSet(fglobal, 0.));
9802   PetscCall(VecSet(fglobalo, 0.));
9803   PetscCall(VecSet(flocal, 0.));
9804   PetscCall(DMPlexInsertBoundaryValues(dmc, PETSC_TRUE, clocal, 0., NULL, NULL, NULL));
9805   PetscCall(DMLocalToGlobalBegin(dmco, clocal, INSERT_VALUES, cglobalo));
9806   PetscCall(DMLocalToGlobalEnd(dmco, clocal, INSERT_VALUES, cglobalo));
9807   PetscCall(MatMult(interpo, cglobalo, fglobalo));
9808   PetscCall(DMGlobalToLocalBegin(dmfo, fglobalo, INSERT_VALUES, flocal));
9809   PetscCall(DMGlobalToLocalEnd(dmfo, fglobalo, INSERT_VALUES, flocal));
9810   PetscCall(DMLocalToGlobalBegin(dmf, flocal, INSERT_VALUES, fglobal));
9811   PetscCall(DMLocalToGlobalEnd(dmf, flocal, INSERT_VALUES, fglobal));
9812   *shift = fglobal;
9813   PetscCall(VecDestroy(&flocal));
9814   PetscCall(VecDestroy(&fglobalo));
9815   PetscCall(VecDestroy(&clocal));
9816   PetscCall(VecDestroy(&cglobalo));
9817   PetscCall(VecDestroy(&rscale));
9818   PetscCall(MatDestroy(&interpo));
9819   PetscCall(DMDestroy(&dmfo));
9820   PetscCall(DMDestroy(&dmco));
9821   PetscFunctionReturn(PETSC_SUCCESS);
9822 }
9823 
9824 PETSC_INTERN PetscErrorCode DMInterpolateSolution_Plex(DM coarse, DM fine, Mat interp, Vec coarseSol, Vec fineSol)
9825 {
9826   PetscObject shifto;
9827   Vec         shift;
9828 
9829   PetscFunctionBegin;
9830   if (!interp) {
9831     Vec rscale;
9832 
9833     PetscCall(DMCreateInterpolation(coarse, fine, &interp, &rscale));
9834     PetscCall(VecDestroy(&rscale));
9835   } else {
9836     PetscCall(PetscObjectReference((PetscObject)interp));
9837   }
9838   PetscCall(PetscObjectQuery((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", &shifto));
9839   if (!shifto) {
9840     PetscCall(DMCreateAffineInterpolationCorrection_Plex(coarse, fine, &shift));
9841     PetscCall(PetscObjectCompose((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", (PetscObject)shift));
9842     shifto = (PetscObject)shift;
9843     PetscCall(VecDestroy(&shift));
9844   }
9845   shift = (Vec)shifto;
9846   PetscCall(MatInterpolate(interp, coarseSol, fineSol));
9847   PetscCall(VecAXPY(fineSol, 1.0, shift));
9848   PetscCall(MatDestroy(&interp));
9849   PetscFunctionReturn(PETSC_SUCCESS);
9850 }
9851 
9852 /* Pointwise interpolation
9853      Just code FEM for now
9854      u^f = I u^c
9855      sum_k u^f_k phi^f_k = I sum_j u^c_j phi^c_j
9856      u^f_i = sum_j psi^f_i I phi^c_j u^c_j
9857      I_{ij} = psi^f_i phi^c_j
9858 */
9859 PetscErrorCode DMCreateInterpolation_Plex(DM dmCoarse, DM dmFine, Mat *interpolation, Vec *scaling)
9860 {
9861   PetscSection gsc, gsf;
9862   PetscInt     m, n;
9863   void        *ctx;
9864   DM           cdm;
9865   PetscBool    regular, ismatis, isRefined = dmCoarse->data == dmFine->data ? PETSC_FALSE : PETSC_TRUE;
9866 
9867   PetscFunctionBegin;
9868   PetscCall(DMGetGlobalSection(dmFine, &gsf));
9869   PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
9870   PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
9871   PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
9872 
9873   PetscCall(PetscStrcmp(dmCoarse->mattype, MATIS, &ismatis));
9874   PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), interpolation));
9875   PetscCall(MatSetSizes(*interpolation, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
9876   PetscCall(MatSetType(*interpolation, ismatis ? MATAIJ : dmCoarse->mattype));
9877   PetscCall(DMGetApplicationContext(dmFine, &ctx));
9878 
9879   PetscCall(DMGetCoarseDM(dmFine, &cdm));
9880   PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
9881   if (!isRefined || (regular && cdm == dmCoarse)) PetscCall(DMPlexComputeInterpolatorNested(dmCoarse, dmFine, isRefined, *interpolation, ctx));
9882   else PetscCall(DMPlexComputeInterpolatorGeneral(dmCoarse, dmFine, *interpolation, ctx));
9883   PetscCall(MatViewFromOptions(*interpolation, NULL, "-interp_mat_view"));
9884   if (scaling) {
9885     /* Use naive scaling */
9886     PetscCall(DMCreateInterpolationScale(dmCoarse, dmFine, *interpolation, scaling));
9887   }
9888   PetscFunctionReturn(PETSC_SUCCESS);
9889 }
9890 
9891 PetscErrorCode DMCreateInjection_Plex(DM dmCoarse, DM dmFine, Mat *mat)
9892 {
9893   VecScatter ctx;
9894 
9895   PetscFunctionBegin;
9896   PetscCall(DMPlexComputeInjectorFEM(dmCoarse, dmFine, &ctx, NULL));
9897   PetscCall(MatCreateScatter(PetscObjectComm((PetscObject)ctx), ctx, mat));
9898   PetscCall(VecScatterDestroy(&ctx));
9899   PetscFunctionReturn(PETSC_SUCCESS);
9900 }
9901 
9902 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[])
9903 {
9904   const PetscInt Nc = uOff[1] - uOff[0];
9905   PetscInt       c;
9906   for (c = 0; c < Nc; ++c) g0[c * Nc + c] = 1.0;
9907 }
9908 
9909 PetscErrorCode DMCreateMassMatrixLumped_Plex(DM dm, Vec *mass)
9910 {
9911   DM           dmc;
9912   PetscDS      ds;
9913   Vec          ones, locmass;
9914   IS           cellIS;
9915   PetscFormKey key;
9916   PetscInt     depth;
9917 
9918   PetscFunctionBegin;
9919   PetscCall(DMClone(dm, &dmc));
9920   PetscCall(DMCopyDisc(dm, dmc));
9921   PetscCall(DMGetDS(dmc, &ds));
9922   PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
9923   PetscCall(DMCreateGlobalVector(dmc, mass));
9924   PetscCall(DMGetLocalVector(dmc, &ones));
9925   PetscCall(DMGetLocalVector(dmc, &locmass));
9926   PetscCall(DMPlexGetDepth(dmc, &depth));
9927   PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
9928   PetscCall(VecSet(locmass, 0.0));
9929   PetscCall(VecSet(ones, 1.0));
9930   key.label = NULL;
9931   key.value = 0;
9932   key.field = 0;
9933   key.part  = 0;
9934   PetscCall(DMPlexComputeJacobian_Action_Internal(dmc, key, cellIS, 0.0, 0.0, ones, NULL, ones, locmass, NULL));
9935   PetscCall(ISDestroy(&cellIS));
9936   PetscCall(VecSet(*mass, 0.0));
9937   PetscCall(DMLocalToGlobalBegin(dmc, locmass, ADD_VALUES, *mass));
9938   PetscCall(DMLocalToGlobalEnd(dmc, locmass, ADD_VALUES, *mass));
9939   PetscCall(DMRestoreLocalVector(dmc, &ones));
9940   PetscCall(DMRestoreLocalVector(dmc, &locmass));
9941   PetscCall(DMDestroy(&dmc));
9942   PetscFunctionReturn(PETSC_SUCCESS);
9943 }
9944 
9945 PetscErrorCode DMCreateMassMatrix_Plex(DM dmCoarse, DM dmFine, Mat *mass)
9946 {
9947   PetscSection gsc, gsf;
9948   PetscInt     m, n;
9949   void        *ctx;
9950   DM           cdm;
9951   PetscBool    regular;
9952 
9953   PetscFunctionBegin;
9954   if (dmFine == dmCoarse) {
9955     DM            dmc;
9956     PetscDS       ds;
9957     PetscWeakForm wf;
9958     Vec           u;
9959     IS            cellIS;
9960     PetscFormKey  key;
9961     PetscInt      depth;
9962 
9963     PetscCall(DMClone(dmFine, &dmc));
9964     PetscCall(DMCopyDisc(dmFine, dmc));
9965     PetscCall(DMGetDS(dmc, &ds));
9966     PetscCall(PetscDSGetWeakForm(ds, &wf));
9967     PetscCall(PetscWeakFormClear(wf));
9968     PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
9969     PetscCall(DMCreateMatrix(dmc, mass));
9970     PetscCall(DMGetLocalVector(dmc, &u));
9971     PetscCall(DMPlexGetDepth(dmc, &depth));
9972     PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
9973     PetscCall(MatZeroEntries(*mass));
9974     key.label = NULL;
9975     key.value = 0;
9976     key.field = 0;
9977     key.part  = 0;
9978     PetscCall(DMPlexComputeJacobian_Internal(dmc, key, cellIS, 0.0, 0.0, u, NULL, *mass, *mass, NULL));
9979     PetscCall(ISDestroy(&cellIS));
9980     PetscCall(DMRestoreLocalVector(dmc, &u));
9981     PetscCall(DMDestroy(&dmc));
9982   } else {
9983     PetscCall(DMGetGlobalSection(dmFine, &gsf));
9984     PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
9985     PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
9986     PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
9987 
9988     PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), mass));
9989     PetscCall(MatSetSizes(*mass, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
9990     PetscCall(MatSetType(*mass, dmCoarse->mattype));
9991     PetscCall(DMGetApplicationContext(dmFine, &ctx));
9992 
9993     PetscCall(DMGetCoarseDM(dmFine, &cdm));
9994     PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
9995     if (regular && cdm == dmCoarse) PetscCall(DMPlexComputeMassMatrixNested(dmCoarse, dmFine, *mass, ctx));
9996     else PetscCall(DMPlexComputeMassMatrixGeneral(dmCoarse, dmFine, *mass, ctx));
9997   }
9998   PetscCall(MatViewFromOptions(*mass, NULL, "-mass_mat_view"));
9999   PetscFunctionReturn(PETSC_SUCCESS);
10000 }
10001 
10002 /*@
10003   DMPlexGetRegularRefinement - Get the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
10004 
10005   Input Parameter:
10006 . dm - The `DMPLEX` object
10007 
10008   Output Parameter:
10009 . regular - The flag
10010 
10011   Level: intermediate
10012 
10013 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetRegularRefinement()`
10014 @*/
10015 PetscErrorCode DMPlexGetRegularRefinement(DM dm, PetscBool *regular)
10016 {
10017   PetscFunctionBegin;
10018   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10019   PetscAssertPointer(regular, 2);
10020   *regular = ((DM_Plex *)dm->data)->regularRefinement;
10021   PetscFunctionReturn(PETSC_SUCCESS);
10022 }
10023 
10024 /*@
10025   DMPlexSetRegularRefinement - Set the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
10026 
10027   Input Parameters:
10028 + dm      - The `DMPLEX` object
10029 - regular - The flag
10030 
10031   Level: intermediate
10032 
10033 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetRegularRefinement()`
10034 @*/
10035 PetscErrorCode DMPlexSetRegularRefinement(DM dm, PetscBool regular)
10036 {
10037   PetscFunctionBegin;
10038   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10039   ((DM_Plex *)dm->data)->regularRefinement = regular;
10040   PetscFunctionReturn(PETSC_SUCCESS);
10041 }
10042 
10043 /*@
10044   DMPlexGetAnchors - Get the layout of the anchor (point-to-point) constraints.  Typically, the user will not have to
10045   call DMPlexGetAnchors() directly: if there are anchors, then `DMPlexGetAnchors()` is called during `DMGetDefaultConstraints()`.
10046 
10047   Not Collective
10048 
10049   Input Parameter:
10050 . dm - The `DMPLEX` object
10051 
10052   Output Parameters:
10053 + anchorSection - If not `NULL`, set to the section describing which points anchor the constrained points.
10054 - anchorIS      - If not `NULL`, set to the list of anchors indexed by `anchorSection`
10055 
10056   Level: intermediate
10057 
10058 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`, `IS`, `PetscSection`
10059 @*/
10060 PetscErrorCode DMPlexGetAnchors(DM dm, PetscSection *anchorSection, IS *anchorIS)
10061 {
10062   DM_Plex *plex = (DM_Plex *)dm->data;
10063 
10064   PetscFunctionBegin;
10065   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10066   if (!plex->anchorSection && !plex->anchorIS && plex->createanchors) PetscCall((*plex->createanchors)(dm));
10067   if (anchorSection) *anchorSection = plex->anchorSection;
10068   if (anchorIS) *anchorIS = plex->anchorIS;
10069   PetscFunctionReturn(PETSC_SUCCESS);
10070 }
10071 
10072 /*@
10073   DMPlexSetAnchors - Set the layout of the local anchor (point-to-point) constraints.
10074 
10075   Collective
10076 
10077   Input Parameters:
10078 + dm            - The `DMPLEX` object
10079 . anchorSection - The section that describes the mapping from constrained points to the anchor points listed in anchorIS.
10080                   Must have a local communicator (`PETSC_COMM_SELF` or derivative).
10081 - anchorIS      - The list of all anchor points.  Must have a local communicator (`PETSC_COMM_SELF` or derivative).
10082 
10083   Level: intermediate
10084 
10085   Notes:
10086   Unlike boundary conditions, when a point's degrees of freedom in a section are constrained to
10087   an outside value, the anchor constraints set a point's degrees of freedom to be a linear
10088   combination of other points' degrees of freedom.
10089 
10090   After specifying the layout of constraints with `DMPlexSetAnchors()`, one specifies the constraints by calling
10091   `DMGetDefaultConstraints()` and filling in the entries in the constraint matrix.
10092 
10093   The reference counts of `anchorSection` and `anchorIS` are incremented.
10094 
10095 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`
10096 @*/
10097 PetscErrorCode DMPlexSetAnchors(DM dm, PetscSection anchorSection, IS anchorIS)
10098 {
10099   DM_Plex    *plex = (DM_Plex *)dm->data;
10100   PetscMPIInt result;
10101 
10102   PetscFunctionBegin;
10103   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10104   if (anchorSection) {
10105     PetscValidHeaderSpecific(anchorSection, PETSC_SECTION_CLASSID, 2);
10106     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorSection), &result));
10107     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor section must have local communicator");
10108   }
10109   if (anchorIS) {
10110     PetscValidHeaderSpecific(anchorIS, IS_CLASSID, 3);
10111     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorIS), &result));
10112     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor IS must have local communicator");
10113   }
10114 
10115   PetscCall(PetscObjectReference((PetscObject)anchorSection));
10116   PetscCall(PetscSectionDestroy(&plex->anchorSection));
10117   plex->anchorSection = anchorSection;
10118 
10119   PetscCall(PetscObjectReference((PetscObject)anchorIS));
10120   PetscCall(ISDestroy(&plex->anchorIS));
10121   plex->anchorIS = anchorIS;
10122 
10123   if (PetscUnlikelyDebug(anchorIS && anchorSection)) {
10124     PetscInt        size, a, pStart, pEnd;
10125     const PetscInt *anchors;
10126 
10127     PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
10128     PetscCall(ISGetLocalSize(anchorIS, &size));
10129     PetscCall(ISGetIndices(anchorIS, &anchors));
10130     for (a = 0; a < size; a++) {
10131       PetscInt p;
10132 
10133       p = anchors[a];
10134       if (p >= pStart && p < pEnd) {
10135         PetscInt dof;
10136 
10137         PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
10138         if (dof) {
10139           PetscCall(ISRestoreIndices(anchorIS, &anchors));
10140           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Point %" PetscInt_FMT " cannot be constrained and an anchor", p);
10141         }
10142       }
10143     }
10144     PetscCall(ISRestoreIndices(anchorIS, &anchors));
10145   }
10146   /* reset the generic constraints */
10147   PetscCall(DMSetDefaultConstraints(dm, NULL, NULL, NULL));
10148   PetscFunctionReturn(PETSC_SUCCESS);
10149 }
10150 
10151 static PetscErrorCode DMPlexCreateConstraintSection_Anchors(DM dm, PetscSection section, PetscSection *cSec)
10152 {
10153   PetscSection anchorSection;
10154   PetscInt     pStart, pEnd, sStart, sEnd, p, dof, numFields, f;
10155 
10156   PetscFunctionBegin;
10157   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10158   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
10159   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, cSec));
10160   PetscCall(PetscSectionGetNumFields(section, &numFields));
10161   if (numFields) {
10162     PetscInt f;
10163     PetscCall(PetscSectionSetNumFields(*cSec, numFields));
10164 
10165     for (f = 0; f < numFields; f++) {
10166       PetscInt numComp;
10167 
10168       PetscCall(PetscSectionGetFieldComponents(section, f, &numComp));
10169       PetscCall(PetscSectionSetFieldComponents(*cSec, f, numComp));
10170     }
10171   }
10172   PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
10173   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
10174   pStart = PetscMax(pStart, sStart);
10175   pEnd   = PetscMin(pEnd, sEnd);
10176   pEnd   = PetscMax(pStart, pEnd);
10177   PetscCall(PetscSectionSetChart(*cSec, pStart, pEnd));
10178   for (p = pStart; p < pEnd; p++) {
10179     PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
10180     if (dof) {
10181       PetscCall(PetscSectionGetDof(section, p, &dof));
10182       PetscCall(PetscSectionSetDof(*cSec, p, dof));
10183       for (f = 0; f < numFields; f++) {
10184         PetscCall(PetscSectionGetFieldDof(section, p, f, &dof));
10185         PetscCall(PetscSectionSetFieldDof(*cSec, p, f, dof));
10186       }
10187     }
10188   }
10189   PetscCall(PetscSectionSetUp(*cSec));
10190   PetscCall(PetscObjectSetName((PetscObject)*cSec, "Constraint Section"));
10191   PetscFunctionReturn(PETSC_SUCCESS);
10192 }
10193 
10194 static PetscErrorCode DMPlexCreateConstraintMatrix_Anchors(DM dm, PetscSection section, PetscSection cSec, Mat *cMat)
10195 {
10196   PetscSection    aSec;
10197   PetscInt        pStart, pEnd, p, sStart, sEnd, dof, aDof, aOff, off, nnz, annz, m, n, q, a, offset, *i, *j;
10198   const PetscInt *anchors;
10199   PetscInt        numFields, f;
10200   IS              aIS;
10201   MatType         mtype;
10202   PetscBool       iscuda, iskokkos;
10203 
10204   PetscFunctionBegin;
10205   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10206   PetscCall(PetscSectionGetStorageSize(cSec, &m));
10207   PetscCall(PetscSectionGetStorageSize(section, &n));
10208   PetscCall(MatCreate(PETSC_COMM_SELF, cMat));
10209   PetscCall(MatSetSizes(*cMat, m, n, m, n));
10210   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJCUSPARSE, &iscuda));
10211   if (!iscuda) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJCUSPARSE, &iscuda));
10212   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJKOKKOS, &iskokkos));
10213   if (!iskokkos) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJKOKKOS, &iskokkos));
10214   if (iscuda) mtype = MATSEQAIJCUSPARSE;
10215   else if (iskokkos) mtype = MATSEQAIJKOKKOS;
10216   else mtype = MATSEQAIJ;
10217   PetscCall(MatSetType(*cMat, mtype));
10218   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
10219   PetscCall(ISGetIndices(aIS, &anchors));
10220   /* cSec will be a subset of aSec and section */
10221   PetscCall(PetscSectionGetChart(cSec, &pStart, &pEnd));
10222   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
10223   PetscCall(PetscMalloc1(m + 1, &i));
10224   i[0] = 0;
10225   PetscCall(PetscSectionGetNumFields(section, &numFields));
10226   for (p = pStart; p < pEnd; p++) {
10227     PetscInt rDof, rOff, r;
10228 
10229     PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10230     if (!rDof) continue;
10231     PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10232     if (numFields) {
10233       for (f = 0; f < numFields; f++) {
10234         annz = 0;
10235         for (r = 0; r < rDof; r++) {
10236           a = anchors[rOff + r];
10237           if (a < sStart || a >= sEnd) continue;
10238           PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
10239           annz += aDof;
10240         }
10241         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
10242         PetscCall(PetscSectionGetFieldOffset(cSec, p, f, &off));
10243         for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
10244       }
10245     } else {
10246       annz = 0;
10247       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10248       for (q = 0; q < dof; q++) {
10249         a = anchors[rOff + q];
10250         if (a < sStart || a >= sEnd) continue;
10251         PetscCall(PetscSectionGetDof(section, a, &aDof));
10252         annz += aDof;
10253       }
10254       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10255       PetscCall(PetscSectionGetOffset(cSec, p, &off));
10256       for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
10257     }
10258   }
10259   nnz = i[m];
10260   PetscCall(PetscMalloc1(nnz, &j));
10261   offset = 0;
10262   for (p = pStart; p < pEnd; p++) {
10263     if (numFields) {
10264       for (f = 0; f < numFields; f++) {
10265         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
10266         for (q = 0; q < dof; q++) {
10267           PetscInt rDof, rOff, r;
10268           PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10269           PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10270           for (r = 0; r < rDof; r++) {
10271             PetscInt s;
10272 
10273             a = anchors[rOff + r];
10274             if (a < sStart || a >= sEnd) continue;
10275             PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
10276             PetscCall(PetscSectionGetFieldOffset(section, a, f, &aOff));
10277             for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
10278           }
10279         }
10280       }
10281     } else {
10282       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10283       for (q = 0; q < dof; q++) {
10284         PetscInt rDof, rOff, r;
10285         PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10286         PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10287         for (r = 0; r < rDof; r++) {
10288           PetscInt s;
10289 
10290           a = anchors[rOff + r];
10291           if (a < sStart || a >= sEnd) continue;
10292           PetscCall(PetscSectionGetDof(section, a, &aDof));
10293           PetscCall(PetscSectionGetOffset(section, a, &aOff));
10294           for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
10295         }
10296       }
10297     }
10298   }
10299   PetscCall(MatSeqAIJSetPreallocationCSR(*cMat, i, j, NULL));
10300   PetscCall(PetscFree(i));
10301   PetscCall(PetscFree(j));
10302   PetscCall(ISRestoreIndices(aIS, &anchors));
10303   PetscFunctionReturn(PETSC_SUCCESS);
10304 }
10305 
10306 PetscErrorCode DMCreateDefaultConstraints_Plex(DM dm)
10307 {
10308   DM_Plex     *plex = (DM_Plex *)dm->data;
10309   PetscSection anchorSection, section, cSec;
10310   Mat          cMat;
10311 
10312   PetscFunctionBegin;
10313   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10314   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
10315   if (anchorSection) {
10316     PetscInt Nf;
10317 
10318     PetscCall(DMGetLocalSection(dm, &section));
10319     PetscCall(DMPlexCreateConstraintSection_Anchors(dm, section, &cSec));
10320     PetscCall(DMPlexCreateConstraintMatrix_Anchors(dm, section, cSec, &cMat));
10321     PetscCall(DMGetNumFields(dm, &Nf));
10322     if (Nf && plex->computeanchormatrix) PetscCall((*plex->computeanchormatrix)(dm, section, cSec, cMat));
10323     PetscCall(DMSetDefaultConstraints(dm, cSec, cMat, NULL));
10324     PetscCall(PetscSectionDestroy(&cSec));
10325     PetscCall(MatDestroy(&cMat));
10326   }
10327   PetscFunctionReturn(PETSC_SUCCESS);
10328 }
10329 
10330 PetscErrorCode DMCreateSubDomainDM_Plex(DM dm, DMLabel label, PetscInt value, IS *is, DM *subdm)
10331 {
10332   IS           subis;
10333   PetscSection section, subsection;
10334 
10335   PetscFunctionBegin;
10336   PetscCall(DMGetLocalSection(dm, &section));
10337   PetscCheck(section, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set default section for DM before splitting subdomain");
10338   PetscCheck(subdm, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set output subDM for splitting subdomain");
10339   /* Create subdomain */
10340   PetscCall(DMPlexFilter(dm, label, value, subdm));
10341   /* Create submodel */
10342   PetscCall(DMPlexGetSubpointIS(*subdm, &subis));
10343   PetscCall(PetscSectionCreateSubmeshSection(section, subis, &subsection));
10344   PetscCall(DMSetLocalSection(*subdm, subsection));
10345   PetscCall(PetscSectionDestroy(&subsection));
10346   PetscCall(DMCopyDisc(dm, *subdm));
10347   /* Create map from submodel to global model */
10348   if (is) {
10349     PetscSection    sectionGlobal, subsectionGlobal;
10350     IS              spIS;
10351     const PetscInt *spmap;
10352     PetscInt       *subIndices;
10353     PetscInt        subSize = 0, subOff = 0, pStart, pEnd, p;
10354     PetscInt        Nf, f, bs = -1, bsLocal[2], bsMinMax[2];
10355 
10356     PetscCall(DMPlexGetSubpointIS(*subdm, &spIS));
10357     PetscCall(ISGetIndices(spIS, &spmap));
10358     PetscCall(PetscSectionGetNumFields(section, &Nf));
10359     PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
10360     PetscCall(DMGetGlobalSection(*subdm, &subsectionGlobal));
10361     PetscCall(PetscSectionGetChart(subsection, &pStart, &pEnd));
10362     for (p = pStart; p < pEnd; ++p) {
10363       PetscInt gdof, pSubSize = 0;
10364 
10365       PetscCall(PetscSectionGetDof(sectionGlobal, p, &gdof));
10366       if (gdof > 0) {
10367         for (f = 0; f < Nf; ++f) {
10368           PetscInt fdof, fcdof;
10369 
10370           PetscCall(PetscSectionGetFieldDof(subsection, p, f, &fdof));
10371           PetscCall(PetscSectionGetFieldConstraintDof(subsection, p, f, &fcdof));
10372           pSubSize += fdof - fcdof;
10373         }
10374         subSize += pSubSize;
10375         if (pSubSize) {
10376           if (bs < 0) {
10377             bs = pSubSize;
10378           } else if (bs != pSubSize) {
10379             /* Layout does not admit a pointwise block size */
10380             bs = 1;
10381           }
10382         }
10383       }
10384     }
10385     /* Must have same blocksize on all procs (some might have no points) */
10386     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs;
10387     bsLocal[1] = bs;
10388     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
10389     if (bsMinMax[0] != bsMinMax[1]) {
10390       bs = 1;
10391     } else {
10392       bs = bsMinMax[0];
10393     }
10394     PetscCall(PetscMalloc1(subSize, &subIndices));
10395     for (p = pStart; p < pEnd; ++p) {
10396       PetscInt gdof, goff;
10397 
10398       PetscCall(PetscSectionGetDof(subsectionGlobal, p, &gdof));
10399       if (gdof > 0) {
10400         const PetscInt point = spmap[p];
10401 
10402         PetscCall(PetscSectionGetOffset(sectionGlobal, point, &goff));
10403         for (f = 0; f < Nf; ++f) {
10404           PetscInt fdof, fcdof, fc, f2, poff = 0;
10405 
10406           /* Can get rid of this loop by storing field information in the global section */
10407           for (f2 = 0; f2 < f; ++f2) {
10408             PetscCall(PetscSectionGetFieldDof(section, p, f2, &fdof));
10409             PetscCall(PetscSectionGetFieldConstraintDof(section, p, f2, &fcdof));
10410             poff += fdof - fcdof;
10411           }
10412           PetscCall(PetscSectionGetFieldDof(section, p, f, &fdof));
10413           PetscCall(PetscSectionGetFieldConstraintDof(section, p, f, &fcdof));
10414           for (fc = 0; fc < fdof - fcdof; ++fc, ++subOff) subIndices[subOff] = goff + poff + fc;
10415         }
10416       }
10417     }
10418     PetscCall(ISRestoreIndices(spIS, &spmap));
10419     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), subSize, subIndices, PETSC_OWN_POINTER, is));
10420     if (bs > 1) {
10421       /* We need to check that the block size does not come from non-contiguous fields */
10422       PetscInt i, j, set = 1;
10423       for (i = 0; i < subSize; i += bs) {
10424         for (j = 0; j < bs; ++j) {
10425           if (subIndices[i + j] != subIndices[i] + j) {
10426             set = 0;
10427             break;
10428           }
10429         }
10430       }
10431       if (set) PetscCall(ISSetBlockSize(*is, bs));
10432     }
10433     /* Attach nullspace */
10434     for (f = 0; f < Nf; ++f) {
10435       (*subdm)->nullspaceConstructors[f] = dm->nullspaceConstructors[f];
10436       if ((*subdm)->nullspaceConstructors[f]) break;
10437     }
10438     if (f < Nf) {
10439       MatNullSpace nullSpace;
10440       PetscCall((*(*subdm)->nullspaceConstructors[f])(*subdm, f, f, &nullSpace));
10441 
10442       PetscCall(PetscObjectCompose((PetscObject)*is, "nullspace", (PetscObject)nullSpace));
10443       PetscCall(MatNullSpaceDestroy(&nullSpace));
10444     }
10445   }
10446   PetscFunctionReturn(PETSC_SUCCESS);
10447 }
10448 
10449 /*@
10450   DMPlexMonitorThroughput - Report the cell throughput of FE integration
10451 
10452   Input Parameters:
10453 + dm    - The `DM`
10454 - dummy - unused argument
10455 
10456   Options Database Key:
10457 . -dm_plex_monitor_throughput - Activate the monitor
10458 
10459   Level: developer
10460 
10461 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexCreate()`
10462 @*/
10463 PetscErrorCode DMPlexMonitorThroughput(DM dm, void *dummy)
10464 {
10465   PetscLogHandler default_handler;
10466 
10467   PetscFunctionBegin;
10468   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10469   PetscCall(PetscLogGetDefaultHandler(&default_handler));
10470   if (default_handler) {
10471     PetscLogEvent      event;
10472     PetscEventPerfInfo eventInfo;
10473     PetscReal          cellRate, flopRate;
10474     PetscInt           cStart, cEnd, Nf, N;
10475     const char        *name;
10476 
10477     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
10478     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
10479     PetscCall(DMGetNumFields(dm, &Nf));
10480     PetscCall(PetscLogEventGetId("DMPlexResidualFE", &event));
10481     PetscCall(PetscLogEventGetPerfInfo(PETSC_DEFAULT, event, &eventInfo));
10482     N        = (cEnd - cStart) * Nf * eventInfo.count;
10483     flopRate = eventInfo.flops / eventInfo.time;
10484     cellRate = N / eventInfo.time;
10485     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)));
10486   } else {
10487     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.");
10488   }
10489   PetscFunctionReturn(PETSC_SUCCESS);
10490 }
10491