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