xref: /petsc/src/dm/impls/plex/plex.c (revision 9b529ac9d3e6c2bd81b4bb2dab3f698fdbcd19f2)
1 #include <petsc/private/dmpleximpl.h> /*I      "petscdmplex.h"   I*/
2 #include <petsc/private/dmlabelimpl.h>
3 #include <petsc/private/isimpl.h>
4 #include <petsc/private/vecimpl.h>
5 #include <petsc/private/glvisvecimpl.h>
6 #include <petscsf.h>
7 #include <petscds.h>
8 #include <petscdraw.h>
9 #include <petscdmfield.h>
10 #include <petscdmplextransform.h>
11 #include <petscblaslapack.h>
12 
13 /* Logging support */
14 PetscLogEvent DMPLEX_Interpolate, DMPLEX_Partition, DMPLEX_Distribute, DMPLEX_DistributeCones, DMPLEX_DistributeLabels, DMPLEX_DistributeSF, DMPLEX_DistributeOverlap, DMPLEX_DistributeField, DMPLEX_DistributeData, DMPLEX_Migrate, DMPLEX_InterpolateSF, DMPLEX_GlobalToNaturalBegin, DMPLEX_GlobalToNaturalEnd, DMPLEX_NaturalToGlobalBegin, DMPLEX_NaturalToGlobalEnd, DMPLEX_Stratify, DMPLEX_Symmetrize, DMPLEX_Preallocate, DMPLEX_ResidualFEM, DMPLEX_JacobianFEM, DMPLEX_InterpolatorFEM, DMPLEX_InjectorFEM, DMPLEX_IntegralFEM, DMPLEX_CreateGmsh, DMPLEX_RebalanceSharedPoints, DMPLEX_PartSelf, DMPLEX_PartLabelInvert, DMPLEX_PartLabelCreateSF, DMPLEX_PartStratSF, DMPLEX_CreatePointSF, DMPLEX_LocatePoints, DMPLEX_TopologyView, DMPLEX_LabelsView, DMPLEX_CoordinatesView, DMPLEX_SectionView, DMPLEX_GlobalVectorView, DMPLEX_LocalVectorView, DMPLEX_TopologyLoad, DMPLEX_LabelsLoad, DMPLEX_CoordinatesLoad, DMPLEX_SectionLoad, DMPLEX_GlobalVectorLoad, DMPLEX_LocalVectorLoad;
15 PetscLogEvent DMPLEX_RebalBuildGraph, DMPLEX_RebalRewriteSF, DMPLEX_RebalGatherGraph, DMPLEX_RebalPartition, DMPLEX_RebalScatterPart, DMPLEX_Generate, DMPLEX_Transform, DMPLEX_GetLocalOffsets, DMPLEX_Uninterpolate;
16 
17 PetscBool  Plexcite       = PETSC_FALSE;
18 const char PlexCitation[] = "@article{LangeMitchellKnepleyGorman2015,\n"
19                             "title     = {Efficient mesh management in {Firedrake} using {PETSc-DMPlex}},\n"
20                             "author    = {Michael Lange and Lawrence Mitchell and Matthew G. Knepley and Gerard J. Gorman},\n"
21                             "journal   = {SIAM Journal on Scientific Computing},\n"
22                             "volume    = {38},\n"
23                             "number    = {5},\n"
24                             "pages     = {S143--S155},\n"
25                             "eprint    = {http://arxiv.org/abs/1506.07749},\n"
26                             "doi       = {10.1137/15M1026092},\n"
27                             "year      = {2016},\n"
28                             "petsc_uses={DMPlex},\n}\n";
29 
30 PETSC_EXTERN PetscErrorCode VecView_MPI(Vec, PetscViewer);
31 
32 /*@
33   DMPlexIsSimplex - Is the first cell in this mesh a simplex?
34 
35   Input Parameter:
36 . dm - The `DMPLEX` object
37 
38   Output Parameter:
39 . simplex - Flag checking for a simplex
40 
41   Level: intermediate
42 
43   Note:
44   This just gives the first range of cells found. If the mesh has several cell types, it will only give the first.
45   If the mesh has no cells, this returns `PETSC_FALSE`.
46 
47 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSimplexOrBoxCells()`, `DMPlexGetCellType()`, `DMPlexGetHeightStratum()`, `DMPolytopeTypeGetNumVertices()`
48 @*/
49 PetscErrorCode DMPlexIsSimplex(DM dm, PetscBool *simplex)
50 {
51   DMPolytopeType ct;
52   PetscInt       cStart, cEnd;
53 
54   PetscFunctionBegin;
55   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
56   if (cEnd <= cStart) {
57     *simplex = PETSC_FALSE;
58     PetscFunctionReturn(PETSC_SUCCESS);
59   }
60   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
61   *simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct) + 1 ? PETSC_TRUE : PETSC_FALSE;
62   PetscFunctionReturn(PETSC_SUCCESS);
63 }
64 
65 /*@
66   DMPlexGetSimplexOrBoxCells - Get the range of cells which are neither prisms nor ghost FV cells
67 
68   Input Parameters:
69 + dm     - The `DMPLEX` object
70 - height - The cell height in the Plex, 0 is the default
71 
72   Output Parameters:
73 + cStart - The first "normal" cell
74 - cEnd   - The upper bound on "normal" cells
75 
76   Level: developer
77 
78   Note:
79   This function requires that tensor cells are ordered last.
80 
81 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexConstructGhostCells()`, `DMPlexGetCellTypeStratum()`
82 @*/
83 PetscErrorCode DMPlexGetSimplexOrBoxCells(DM dm, PetscInt height, PetscInt *cStart, PetscInt *cEnd)
84 {
85   DMLabel         ctLabel;
86   IS              valueIS;
87   const PetscInt *ctypes;
88   PetscBool       found = PETSC_FALSE;
89   PetscInt        Nct, cS = PETSC_INT_MAX, cE = 0;
90 
91   PetscFunctionBegin;
92   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
93   PetscCall(DMLabelGetValueIS(ctLabel, &valueIS));
94   PetscCall(ISGetLocalSize(valueIS, &Nct));
95   PetscCall(ISGetIndices(valueIS, &ctypes));
96   for (PetscInt t = 0; t < Nct; ++t) {
97     const DMPolytopeType ct = (DMPolytopeType)ctypes[t];
98     PetscInt             ctS, ctE, ht;
99 
100     if (ct == DM_POLYTOPE_UNKNOWN) {
101       // If any cells are not typed, just use all cells
102       PetscCall(DMPlexGetHeightStratum(dm, PetscMax(height, 0), cStart, cEnd));
103       break;
104     }
105     if (DMPolytopeTypeIsHybrid(ct) || ct == DM_POLYTOPE_FV_GHOST) continue;
106     PetscCall(DMLabelGetStratumBounds(ctLabel, ct, &ctS, &ctE));
107     if (ctS >= ctE) continue;
108     // Check that a point has the right height
109     PetscCall(DMPlexGetPointHeight(dm, ctS, &ht));
110     if (ht != height) continue;
111     cS    = PetscMin(cS, ctS);
112     cE    = PetscMax(cE, ctE);
113     found = PETSC_TRUE;
114   }
115   if (!Nct || !found) cS = cE = 0;
116   PetscCall(ISDestroy(&valueIS));
117   // Reset label for fast lookup
118   PetscCall(DMLabelMakeAllInvalid_Internal(ctLabel));
119   if (cStart) *cStart = cS;
120   if (cEnd) *cEnd = cE;
121   PetscFunctionReturn(PETSC_SUCCESS);
122 }
123 
124 PetscErrorCode DMPlexGetFieldTypes_Internal(DM dm, PetscSection section, PetscInt field, PetscInt *types, PetscInt **ssStart, PetscInt **ssEnd, PetscViewerVTKFieldType **sft)
125 {
126   PetscInt                 cdim, pStart, pEnd, vStart, vEnd, cStart, cEnd, c, depth, cellHeight, t;
127   PetscInt                *sStart, *sEnd;
128   PetscViewerVTKFieldType *ft;
129   PetscInt                 vcdof[DM_NUM_POLYTOPES + 1], globalvcdof[DM_NUM_POLYTOPES + 1];
130   DMLabel                  depthLabel, ctLabel;
131 
132   PetscFunctionBegin;
133   /* the vcdof and globalvcdof are sized to allow every polytope type and simple vertex at DM_NUM_POLYTOPES */
134   PetscCall(PetscArrayzero(vcdof, DM_NUM_POLYTOPES + 1));
135   PetscCall(DMGetCoordinateDim(dm, &cdim));
136   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
137   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
138   if (field >= 0) {
139     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, vStart, field, &vcdof[DM_NUM_POLYTOPES]));
140   } else {
141     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetDof(section, vStart, &vcdof[DM_NUM_POLYTOPES]));
142   }
143 
144   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
145   PetscCall(DMPlexGetDepth(dm, &depth));
146   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
147   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
148   for (c = 0; c < DM_NUM_POLYTOPES; ++c) {
149     const DMPolytopeType ict = (DMPolytopeType)c;
150     PetscInt             dep;
151 
152     if (ict == DM_POLYTOPE_FV_GHOST) continue;
153     PetscCall(DMLabelGetStratumBounds(ctLabel, ict, &cStart, &cEnd));
154     if (pStart >= 0) {
155       PetscCall(DMLabelGetValue(depthLabel, cStart, &dep));
156       if (dep != depth - cellHeight) continue;
157     }
158     if (field >= 0) {
159       if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, cStart, field, &vcdof[c]));
160     } else {
161       if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetDof(section, cStart, &vcdof[c]));
162     }
163   }
164 
165   PetscCallMPI(MPIU_Allreduce(vcdof, globalvcdof, DM_NUM_POLYTOPES + 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
166   *types = 0;
167 
168   for (c = 0; c < DM_NUM_POLYTOPES + 1; ++c) {
169     if (globalvcdof[c]) ++(*types);
170   }
171 
172   PetscCall(PetscMalloc3(*types, &sStart, *types, &sEnd, *types, &ft));
173   t = 0;
174   if (globalvcdof[DM_NUM_POLYTOPES]) {
175     sStart[t] = vStart;
176     sEnd[t]   = vEnd;
177     ft[t]     = (globalvcdof[t] == cdim) ? PETSC_VTK_POINT_VECTOR_FIELD : PETSC_VTK_POINT_FIELD;
178     ++t;
179   }
180 
181   for (c = 0; c < DM_NUM_POLYTOPES; ++c) {
182     if (globalvcdof[c]) {
183       const DMPolytopeType ict = (DMPolytopeType)c;
184 
185       PetscCall(DMLabelGetStratumBounds(ctLabel, ict, &cStart, &cEnd));
186       sStart[t] = cStart;
187       sEnd[t]   = cEnd;
188       ft[t]     = (globalvcdof[c] == cdim) ? PETSC_VTK_CELL_VECTOR_FIELD : PETSC_VTK_CELL_FIELD;
189       ++t;
190     }
191   }
192 
193   if (!*types) {
194     if (field >= 0) {
195       const char *fieldname;
196 
197       PetscCall(PetscSectionGetFieldName(section, field, &fieldname));
198       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section field %" PetscInt_FMT " \"%s\"\n", field, fieldname));
199     } else {
200       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section\n"));
201     }
202   }
203 
204   *ssStart = sStart;
205   *ssEnd   = sEnd;
206   *sft     = ft;
207   PetscFunctionReturn(PETSC_SUCCESS);
208 }
209 
210 PetscErrorCode DMPlexRestoreFieldTypes_Internal(DM dm, PetscSection section, PetscInt field, PetscInt *types, PetscInt **sStart, PetscInt **sEnd, PetscViewerVTKFieldType **ft)
211 {
212   PetscFunctionBegin;
213   PetscCall(PetscFree3(*sStart, *sEnd, *ft));
214   PetscFunctionReturn(PETSC_SUCCESS);
215 }
216 
217 PetscErrorCode DMPlexGetFieldType_Internal(DM dm, PetscSection section, PetscInt field, PetscInt *sStart, PetscInt *sEnd, PetscViewerVTKFieldType *ft)
218 {
219   PetscInt cdim, pStart, pEnd, vStart, vEnd, cStart, cEnd;
220   PetscInt vcdof[2] = {0, 0}, globalvcdof[2];
221 
222   PetscFunctionBegin;
223   *ft = PETSC_VTK_INVALID;
224   PetscCall(DMGetCoordinateDim(dm, &cdim));
225   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
226   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
227   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
228   if (field >= 0) {
229     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, vStart, field, &vcdof[0]));
230     if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, cStart, field, &vcdof[1]));
231   } else {
232     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetDof(section, vStart, &vcdof[0]));
233     if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetDof(section, cStart, &vcdof[1]));
234   }
235   PetscCallMPI(MPIU_Allreduce(vcdof, globalvcdof, 2, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
236   if (globalvcdof[0]) {
237     *sStart = vStart;
238     *sEnd   = vEnd;
239     if (globalvcdof[0] == cdim) *ft = PETSC_VTK_POINT_VECTOR_FIELD;
240     else *ft = PETSC_VTK_POINT_FIELD;
241   } else if (globalvcdof[1]) {
242     *sStart = cStart;
243     *sEnd   = cEnd;
244     if (globalvcdof[1] == cdim) *ft = PETSC_VTK_CELL_VECTOR_FIELD;
245     else *ft = PETSC_VTK_CELL_FIELD;
246   } else {
247     if (field >= 0) {
248       const char *fieldname;
249 
250       PetscCall(PetscSectionGetFieldName(section, field, &fieldname));
251       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section field %" PetscInt_FMT " \"%s\"\n", field, fieldname));
252     } else {
253       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section\n"));
254     }
255   }
256   PetscFunctionReturn(PETSC_SUCCESS);
257 }
258 
259 /*@
260   DMPlexVecView1D - Plot many 1D solutions on the same line graph
261 
262   Collective
263 
264   Input Parameters:
265 + dm     - The `DMPLEX` object
266 . n      - The number of vectors
267 . u      - The array of local vectors
268 - viewer - The `PetscViewer`
269 
270   Level: advanced
271 
272 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `VecViewFromOptions()`, `VecView()`
273 @*/
274 PetscErrorCode DMPlexVecView1D(DM dm, PetscInt n, Vec u[], PetscViewer viewer)
275 {
276   PetscDS            ds;
277   PetscDraw          draw = NULL;
278   PetscDrawLG        lg;
279   Vec                coordinates;
280   const PetscScalar *coords, **sol;
281   PetscReal         *vals;
282   PetscInt          *Nc;
283   PetscInt           Nf, f, c, Nl, l, i, vStart, vEnd, v;
284   char             **names;
285 
286   PetscFunctionBegin;
287   PetscCall(DMGetDS(dm, &ds));
288   PetscCall(PetscDSGetNumFields(ds, &Nf));
289   PetscCall(PetscDSGetTotalComponents(ds, &Nl));
290   PetscCall(PetscDSGetComponents(ds, &Nc));
291 
292   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
293   if (!draw) PetscFunctionReturn(PETSC_SUCCESS);
294   PetscCall(PetscDrawLGCreate(draw, n * Nl, &lg));
295 
296   PetscCall(PetscMalloc3(n, &sol, n * Nl, &names, n * Nl, &vals));
297   for (i = 0, l = 0; i < n; ++i) {
298     const char *vname;
299 
300     PetscCall(PetscObjectGetName((PetscObject)u[i], &vname));
301     for (f = 0; f < Nf; ++f) {
302       PetscObject disc;
303       const char *fname;
304       char        tmpname[PETSC_MAX_PATH_LEN];
305 
306       PetscCall(PetscDSGetDiscretization(ds, f, &disc));
307       /* TODO Create names for components */
308       for (c = 0; c < Nc[f]; ++c, ++l) {
309         PetscCall(PetscObjectGetName(disc, &fname));
310         PetscCall(PetscStrncpy(tmpname, vname, sizeof(tmpname)));
311         PetscCall(PetscStrlcat(tmpname, ":", sizeof(tmpname)));
312         PetscCall(PetscStrlcat(tmpname, fname, sizeof(tmpname)));
313         PetscCall(PetscStrallocpy(tmpname, &names[l]));
314       }
315     }
316   }
317   PetscCall(PetscDrawLGSetLegend(lg, (const char *const *)names));
318   /* Just add P_1 support for now */
319   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
320   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
321   PetscCall(VecGetArrayRead(coordinates, &coords));
322   for (i = 0; i < n; ++i) PetscCall(VecGetArrayRead(u[i], &sol[i]));
323   for (v = vStart; v < vEnd; ++v) {
324     PetscScalar *x, *svals;
325 
326     PetscCall(DMPlexPointLocalRead(dm, v, coords, &x));
327     for (i = 0; i < n; ++i) {
328       PetscCall(DMPlexPointLocalRead(dm, v, sol[i], &svals));
329       for (l = 0; l < Nl; ++l) vals[i * Nl + l] = PetscRealPart(svals[l]);
330     }
331     PetscCall(PetscDrawLGAddCommonPoint(lg, PetscRealPart(x[0]), vals));
332   }
333   PetscCall(VecRestoreArrayRead(coordinates, &coords));
334   for (i = 0; i < n; ++i) PetscCall(VecRestoreArrayRead(u[i], &sol[i]));
335   for (l = 0; l < n * Nl; ++l) PetscCall(PetscFree(names[l]));
336   PetscCall(PetscFree3(sol, names, vals));
337 
338   PetscCall(PetscDrawLGDraw(lg));
339   PetscCall(PetscDrawLGDestroy(&lg));
340   PetscFunctionReturn(PETSC_SUCCESS);
341 }
342 
343 static PetscErrorCode VecView_Plex_Local_Draw_1D(Vec u, PetscViewer viewer)
344 {
345   DM dm;
346 
347   PetscFunctionBegin;
348   PetscCall(VecGetDM(u, &dm));
349   PetscCall(DMPlexVecView1D(dm, 1, &u, viewer));
350   PetscFunctionReturn(PETSC_SUCCESS);
351 }
352 
353 static PetscErrorCode VecView_Plex_Local_Draw_2D(Vec v, PetscViewer viewer)
354 {
355   DM                 dm;
356   PetscSection       s;
357   PetscDraw          draw, popup;
358   DM                 cdm;
359   PetscSection       coordSection;
360   Vec                coordinates;
361   const PetscScalar *array;
362   PetscReal          lbound[3], ubound[3];
363   PetscReal          vbound[2], time;
364   PetscBool          flg;
365   PetscInt           dim, Nf, f, Nc, comp, vStart, vEnd, cStart, cEnd, c, N, level, step, w = 0;
366   const char        *name;
367   char               title[PETSC_MAX_PATH_LEN];
368 
369   PetscFunctionBegin;
370   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
371   PetscCall(VecGetDM(v, &dm));
372   PetscCall(DMGetCoordinateDim(dm, &dim));
373   PetscCall(DMGetLocalSection(dm, &s));
374   PetscCall(PetscSectionGetNumFields(s, &Nf));
375   PetscCall(DMGetCoarsenLevel(dm, &level));
376   PetscCall(DMGetCoordinateDM(dm, &cdm));
377   PetscCall(DMGetLocalSection(cdm, &coordSection));
378   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
379   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
380   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
381 
382   PetscCall(PetscObjectGetName((PetscObject)v, &name));
383   PetscCall(DMGetOutputSequenceNumber(dm, &step, &time));
384 
385   PetscCall(VecGetLocalSize(coordinates, &N));
386   PetscCall(DMGetBoundingBox(dm, lbound, ubound));
387   PetscCall(PetscDrawClear(draw));
388 
389   /* Could implement something like DMDASelectFields() */
390   for (f = 0; f < Nf; ++f) {
391     DM          fdm = dm;
392     Vec         fv  = v;
393     IS          fis;
394     char        prefix[PETSC_MAX_PATH_LEN];
395     const char *fname;
396 
397     PetscCall(PetscSectionGetFieldComponents(s, f, &Nc));
398     PetscCall(PetscSectionGetFieldName(s, f, &fname));
399 
400     if (v->hdr.prefix) PetscCall(PetscStrncpy(prefix, v->hdr.prefix, sizeof(prefix)));
401     else prefix[0] = '\0';
402     if (Nf > 1) {
403       PetscCall(DMCreateSubDM(dm, 1, &f, &fis, &fdm));
404       PetscCall(VecGetSubVector(v, fis, &fv));
405       PetscCall(PetscStrlcat(prefix, fname, sizeof(prefix)));
406       PetscCall(PetscStrlcat(prefix, "_", sizeof(prefix)));
407     }
408     for (comp = 0; comp < Nc; ++comp, ++w) {
409       PetscInt nmax = 2;
410 
411       PetscCall(PetscViewerDrawGetDraw(viewer, w, &draw));
412       if (Nc > 1) PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s_%" PetscInt_FMT " Step: %" PetscInt_FMT " Time: %.4g", name, fname, comp, step, (double)time));
413       else PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s Step: %" PetscInt_FMT " Time: %.4g", name, fname, step, (double)time));
414       PetscCall(PetscDrawSetTitle(draw, title));
415 
416       /* TODO Get max and min only for this component */
417       PetscCall(PetscOptionsGetRealArray(NULL, prefix, "-vec_view_bounds", vbound, &nmax, &flg));
418       if (!flg) {
419         PetscCall(VecMin(fv, NULL, &vbound[0]));
420         PetscCall(VecMax(fv, NULL, &vbound[1]));
421         if (vbound[1] <= vbound[0]) vbound[1] = vbound[0] + 1.0;
422       }
423 
424       PetscCall(PetscDrawGetPopup(draw, &popup));
425       PetscCall(PetscDrawScalePopup(popup, vbound[0], vbound[1]));
426       PetscCall(PetscDrawSetCoordinates(draw, lbound[0], lbound[1], ubound[0], ubound[1]));
427       PetscCall(VecGetArrayRead(fv, &array));
428       for (c = cStart; c < cEnd; ++c) {
429         DMPolytopeType     ct;
430         PetscScalar       *coords = NULL, *a = NULL;
431         const PetscScalar *coords_arr;
432         PetscBool          isDG;
433         PetscInt           numCoords;
434         int                color[4] = {-1, -1, -1, -1};
435 
436         PetscCall(DMPlexGetCellType(dm, c, &ct));
437         PetscCall(DMPlexPointLocalRead(fdm, c, array, &a));
438         if (a) {
439           color[0] = PetscDrawRealToColor(PetscRealPart(a[comp]), vbound[0], vbound[1]);
440           color[1] = color[2] = color[3] = color[0];
441         } else {
442           PetscScalar *vals = NULL;
443           PetscInt     numVals, va;
444 
445           PetscCall(DMPlexVecGetClosure(fdm, NULL, fv, c, &numVals, &vals));
446           if (!numVals) {
447             PetscCall(DMPlexVecRestoreClosure(fdm, NULL, fv, c, &numVals, &vals));
448             continue;
449           }
450           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);
451           switch (numVals / Nc) {
452           case 1: /* P1 Clamped Segment Prism */
453           case 2: /* P1 Segment Prism, P2 Clamped Segment Prism */
454             PetscCheck(ct == DM_POLYTOPE_SEG_PRISM_TENSOR, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell should be a tensor segment, but it is a %s", DMPolytopeTypes[ct]);
455             for (va = 0; va < numVals / Nc; ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp]), vbound[0], vbound[1]);
456             break;
457           case 3: /* P1 Triangle */
458           case 4: /* P1 Quadrangle */
459             PetscCheck(ct == DM_POLYTOPE_TRIANGLE || ct == DM_POLYTOPE_QUADRILATERAL || ct == DM_POLYTOPE_SEG_PRISM_TENSOR, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell should be a triangle or quad, but it is a %s", DMPolytopeTypes[ct]);
460             for (va = 0; va < numVals / Nc; ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp]), vbound[0], vbound[1]);
461             break;
462           case 6: /* P2 Triangle */
463           case 8: /* P2 Quadrangle */
464             PetscCheck(ct == DM_POLYTOPE_TRIANGLE || ct == DM_POLYTOPE_QUADRILATERAL || ct == DM_POLYTOPE_SEG_PRISM_TENSOR, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell should be a triangle or quad, but it is a %s", DMPolytopeTypes[ct]);
465             for (va = 0; va < numVals / (Nc * 2); ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp + numVals / (Nc * 2)]), vbound[0], vbound[1]);
466             break;
467           default:
468             SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of values for cell closure %" PetscInt_FMT " cannot be handled", numVals / Nc);
469           }
470           PetscCall(DMPlexVecRestoreClosure(fdm, NULL, fv, c, &numVals, &vals));
471         }
472         PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
473         switch (numCoords) {
474         case 6:
475         case 12: /* Localized triangle */
476           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]));
477           break;
478         case 8:
479         case 16: /* Localized quadrilateral */
480           if (ct == DM_POLYTOPE_SEG_PRISM_TENSOR) {
481             PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscMax(color[0], color[1])));
482           } else {
483             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]));
484             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]));
485           }
486           break;
487         default:
488           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells with %" PetscInt_FMT " coordinates", numCoords);
489         }
490         PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
491       }
492       PetscCall(VecRestoreArrayRead(fv, &array));
493       PetscCall(PetscDrawFlush(draw));
494       PetscCall(PetscDrawPause(draw));
495       PetscCall(PetscDrawSave(draw));
496     }
497     if (Nf > 1) {
498       PetscCall(VecRestoreSubVector(v, fis, &fv));
499       PetscCall(ISDestroy(&fis));
500       PetscCall(DMDestroy(&fdm));
501     }
502   }
503   PetscFunctionReturn(PETSC_SUCCESS);
504 }
505 
506 static PetscErrorCode VecView_Plex_Local_Draw(Vec v, PetscViewer viewer)
507 {
508   DM        dm;
509   PetscDraw draw;
510   PetscInt  dim;
511   PetscBool isnull;
512 
513   PetscFunctionBegin;
514   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
515   PetscCall(PetscDrawIsNull(draw, &isnull));
516   if (isnull) PetscFunctionReturn(PETSC_SUCCESS);
517 
518   PetscCall(VecGetDM(v, &dm));
519   PetscCall(DMGetCoordinateDim(dm, &dim));
520   switch (dim) {
521   case 1:
522     PetscCall(VecView_Plex_Local_Draw_1D(v, viewer));
523     break;
524   case 2:
525     PetscCall(VecView_Plex_Local_Draw_2D(v, viewer));
526     break;
527   default:
528     SETERRQ(PetscObjectComm((PetscObject)v), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT ". Try PETSCVIEWERGLVIS", dim);
529   }
530   PetscFunctionReturn(PETSC_SUCCESS);
531 }
532 
533 static PetscErrorCode VecView_Plex_Local_VTK(Vec v, PetscViewer viewer)
534 {
535   DM                      dm;
536   Vec                     locv;
537   const char             *name;
538   PetscSection            section;
539   PetscInt                pStart, pEnd;
540   PetscInt                numFields;
541   PetscViewerVTKFieldType ft;
542 
543   PetscFunctionBegin;
544   PetscCall(VecGetDM(v, &dm));
545   PetscCall(DMCreateLocalVector(dm, &locv)); /* VTK viewer requires exclusive ownership of the vector */
546   PetscCall(PetscObjectGetName((PetscObject)v, &name));
547   PetscCall(PetscObjectSetName((PetscObject)locv, name));
548   PetscCall(VecCopy(v, locv));
549   PetscCall(DMGetLocalSection(dm, &section));
550   PetscCall(PetscSectionGetNumFields(section, &numFields));
551   if (!numFields) {
552     PetscCall(DMPlexGetFieldType_Internal(dm, section, PETSC_DETERMINE, &pStart, &pEnd, &ft));
553     PetscCall(PetscViewerVTKAddField(viewer, (PetscObject)dm, DMPlexVTKWriteAll, PETSC_DEFAULT, ft, PETSC_TRUE, (PetscObject)locv));
554   } else {
555     PetscInt f;
556 
557     for (f = 0; f < numFields; f++) {
558       PetscCall(DMPlexGetFieldType_Internal(dm, section, f, &pStart, &pEnd, &ft));
559       if (ft == PETSC_VTK_INVALID) continue;
560       PetscCall(PetscObjectReference((PetscObject)locv));
561       PetscCall(PetscViewerVTKAddField(viewer, (PetscObject)dm, DMPlexVTKWriteAll, f, ft, PETSC_TRUE, (PetscObject)locv));
562     }
563     PetscCall(VecDestroy(&locv));
564   }
565   PetscFunctionReturn(PETSC_SUCCESS);
566 }
567 
568 PetscErrorCode VecView_Plex_Local(Vec v, PetscViewer viewer)
569 {
570   DM        dm;
571   PetscBool isvtk, ishdf5, isdraw, isglvis, iscgns;
572 
573   PetscFunctionBegin;
574   PetscCall(VecGetDM(v, &dm));
575   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
576   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
577   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
578   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
579   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
580   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
581   if (isvtk || ishdf5 || isdraw || isglvis || iscgns) {
582     PetscInt    i, numFields;
583     PetscObject fe;
584     PetscBool   fem  = PETSC_FALSE;
585     Vec         locv = v;
586     const char *name;
587     PetscInt    step;
588     PetscReal   time;
589 
590     PetscCall(DMGetNumFields(dm, &numFields));
591     for (i = 0; i < numFields; i++) {
592       PetscCall(DMGetField(dm, i, NULL, &fe));
593       if (fe->classid == PETSCFE_CLASSID) {
594         fem = PETSC_TRUE;
595         break;
596       }
597     }
598     if (fem) {
599       PetscObject isZero;
600 
601       PetscCall(DMGetLocalVector(dm, &locv));
602       PetscCall(PetscObjectGetName((PetscObject)v, &name));
603       PetscCall(PetscObjectSetName((PetscObject)locv, name));
604       PetscCall(PetscObjectQuery((PetscObject)v, "__Vec_bc_zero__", &isZero));
605       PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", isZero));
606       PetscCall(VecCopy(v, locv));
607       PetscCall(DMGetOutputSequenceNumber(dm, NULL, &time));
608       PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_TRUE, locv, time, NULL, NULL, NULL));
609     }
610     if (isvtk) {
611       PetscCall(VecView_Plex_Local_VTK(locv, viewer));
612     } else if (ishdf5) {
613 #if defined(PETSC_HAVE_HDF5)
614       PetscCall(VecView_Plex_Local_HDF5_Internal(locv, viewer));
615 #else
616       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
617 #endif
618     } else if (isdraw) {
619       PetscCall(VecView_Plex_Local_Draw(locv, viewer));
620     } else if (isglvis) {
621       PetscCall(DMGetOutputSequenceNumber(dm, &step, NULL));
622       PetscCall(PetscViewerGLVisSetSnapId(viewer, step));
623       PetscCall(VecView_GLVis(locv, viewer));
624     } else if (iscgns) {
625 #if defined(PETSC_HAVE_CGNS)
626       PetscCall(VecView_Plex_Local_CGNS(locv, viewer));
627 #else
628       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "CGNS not supported in this build.\nPlease reconfigure using --download-cgns");
629 #endif
630     }
631     if (fem) {
632       PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", NULL));
633       PetscCall(DMRestoreLocalVector(dm, &locv));
634     }
635   } else {
636     PetscBool isseq;
637 
638     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
639     if (isseq) PetscCall(VecView_Seq(v, viewer));
640     else PetscCall(VecView_MPI(v, viewer));
641   }
642   PetscFunctionReturn(PETSC_SUCCESS);
643 }
644 
645 PetscErrorCode VecView_Plex(Vec v, PetscViewer viewer)
646 {
647   DM        dm;
648   PetscBool isvtk, ishdf5, isdraw, isglvis, isexodusii, iscgns;
649 
650   PetscFunctionBegin;
651   PetscCall(VecGetDM(v, &dm));
652   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
653   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
654   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
655   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
656   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
657   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
658   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodusii));
659   if (isvtk || isdraw || isglvis || iscgns) {
660     Vec         locv;
661     PetscObject isZero;
662     const char *name;
663 
664     PetscCall(DMGetLocalVector(dm, &locv));
665     PetscCall(PetscObjectGetName((PetscObject)v, &name));
666     PetscCall(PetscObjectSetName((PetscObject)locv, name));
667     PetscCall(DMGlobalToLocalBegin(dm, v, INSERT_VALUES, locv));
668     PetscCall(DMGlobalToLocalEnd(dm, v, INSERT_VALUES, locv));
669     PetscCall(PetscObjectQuery((PetscObject)v, "__Vec_bc_zero__", &isZero));
670     PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", isZero));
671     PetscCall(VecView_Plex_Local(locv, viewer));
672     PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", NULL));
673     PetscCall(DMRestoreLocalVector(dm, &locv));
674     /* Call flush for proper logging of VecView timings */
675     if (isvtk) PetscCall(PetscViewerFlush(viewer));
676   } else if (ishdf5) {
677 #if defined(PETSC_HAVE_HDF5)
678     PetscCall(VecView_Plex_HDF5_Internal(v, viewer));
679 #else
680     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
681 #endif
682   } else if (isexodusii) {
683 #if defined(PETSC_HAVE_EXODUSII)
684     PetscCall(VecView_PlexExodusII_Internal(v, viewer));
685 #else
686     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
687 #endif
688   } else {
689     PetscBool isseq;
690 
691     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
692     if (isseq) PetscCall(VecView_Seq(v, viewer));
693     else PetscCall(VecView_MPI(v, viewer));
694   }
695   PetscFunctionReturn(PETSC_SUCCESS);
696 }
697 
698 PetscErrorCode VecView_Plex_Native(Vec originalv, PetscViewer viewer)
699 {
700   DM                dm;
701   MPI_Comm          comm;
702   PetscViewerFormat format;
703   Vec               v;
704   PetscBool         isvtk, ishdf5;
705 
706   PetscFunctionBegin;
707   PetscCall(VecGetDM(originalv, &dm));
708   PetscCall(PetscObjectGetComm((PetscObject)originalv, &comm));
709   PetscCheck(dm, comm, PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
710   PetscCall(PetscViewerGetFormat(viewer, &format));
711   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
712   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
713   if (format == PETSC_VIEWER_NATIVE) {
714     /* Natural ordering is the common case for DMDA, NATIVE means plain vector, for PLEX is the opposite */
715     /* this need a better fix */
716     if (dm->useNatural) {
717       if (dm->sfNatural) {
718         const char *vecname;
719         PetscInt    n, nroots;
720 
721         PetscCall(VecGetLocalSize(originalv, &n));
722         PetscCall(PetscSFGetGraph(dm->sfNatural, &nroots, NULL, NULL, NULL));
723         if (n == nroots) {
724           PetscCall(DMPlexCreateNaturalVector(dm, &v));
725           PetscCall(DMPlexGlobalToNaturalBegin(dm, originalv, v));
726           PetscCall(DMPlexGlobalToNaturalEnd(dm, originalv, v));
727           PetscCall(PetscObjectGetName((PetscObject)originalv, &vecname));
728           PetscCall(PetscObjectSetName((PetscObject)v, vecname));
729         } else SETERRQ(comm, PETSC_ERR_ARG_WRONG, "DM global to natural SF only handles global vectors");
730       } else SETERRQ(comm, PETSC_ERR_ARG_WRONGSTATE, "DM global to natural SF was not created");
731     } else v = originalv;
732   } else v = originalv;
733 
734   if (ishdf5) {
735 #if defined(PETSC_HAVE_HDF5)
736     PetscCall(VecView_Plex_HDF5_Native_Internal(v, viewer));
737 #else
738     SETERRQ(comm, PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
739 #endif
740   } else if (isvtk) {
741     SETERRQ(comm, PETSC_ERR_SUP, "VTK format does not support viewing in natural order. Please switch to HDF5.");
742   } else {
743     PetscBool isseq;
744 
745     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
746     if (isseq) PetscCall(VecView_Seq(v, viewer));
747     else PetscCall(VecView_MPI(v, viewer));
748   }
749   if (v != originalv) PetscCall(VecDestroy(&v));
750   PetscFunctionReturn(PETSC_SUCCESS);
751 }
752 
753 PetscErrorCode VecLoad_Plex_Local(Vec v, PetscViewer viewer)
754 {
755   DM        dm;
756   PetscBool ishdf5;
757 
758   PetscFunctionBegin;
759   PetscCall(VecGetDM(v, &dm));
760   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
761   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
762   if (ishdf5) {
763     DM          dmBC;
764     Vec         gv;
765     const char *name;
766 
767     PetscCall(DMGetOutputDM(dm, &dmBC));
768     PetscCall(DMGetGlobalVector(dmBC, &gv));
769     PetscCall(PetscObjectGetName((PetscObject)v, &name));
770     PetscCall(PetscObjectSetName((PetscObject)gv, name));
771     PetscCall(VecLoad_Default(gv, viewer));
772     PetscCall(DMGlobalToLocalBegin(dmBC, gv, INSERT_VALUES, v));
773     PetscCall(DMGlobalToLocalEnd(dmBC, gv, INSERT_VALUES, v));
774     PetscCall(DMRestoreGlobalVector(dmBC, &gv));
775   } else PetscCall(VecLoad_Default(v, viewer));
776   PetscFunctionReturn(PETSC_SUCCESS);
777 }
778 
779 PetscErrorCode VecLoad_Plex(Vec v, PetscViewer viewer)
780 {
781   DM        dm;
782   PetscBool ishdf5, isexodusii;
783 
784   PetscFunctionBegin;
785   PetscCall(VecGetDM(v, &dm));
786   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
787   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
788   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodusii));
789   if (ishdf5) {
790 #if defined(PETSC_HAVE_HDF5)
791     PetscCall(VecLoad_Plex_HDF5_Internal(v, viewer));
792 #else
793     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
794 #endif
795   } else if (isexodusii) {
796 #if defined(PETSC_HAVE_EXODUSII)
797     PetscCall(VecLoad_PlexExodusII_Internal(v, viewer));
798 #else
799     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
800 #endif
801   } else PetscCall(VecLoad_Default(v, viewer));
802   PetscFunctionReturn(PETSC_SUCCESS);
803 }
804 
805 PetscErrorCode VecLoad_Plex_Native(Vec originalv, PetscViewer viewer)
806 {
807   DM                dm;
808   PetscViewerFormat format;
809   PetscBool         ishdf5;
810 
811   PetscFunctionBegin;
812   PetscCall(VecGetDM(originalv, &dm));
813   PetscCheck(dm, PetscObjectComm((PetscObject)originalv), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
814   PetscCall(PetscViewerGetFormat(viewer, &format));
815   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
816   if (format == PETSC_VIEWER_NATIVE) {
817     if (dm->useNatural) {
818       if (dm->sfNatural) {
819         if (ishdf5) {
820 #if defined(PETSC_HAVE_HDF5)
821           Vec         v;
822           const char *vecname;
823 
824           PetscCall(DMPlexCreateNaturalVector(dm, &v));
825           PetscCall(PetscObjectGetName((PetscObject)originalv, &vecname));
826           PetscCall(PetscObjectSetName((PetscObject)v, vecname));
827           PetscCall(VecLoad_Plex_HDF5_Native_Internal(v, viewer));
828           PetscCall(DMPlexNaturalToGlobalBegin(dm, v, originalv));
829           PetscCall(DMPlexNaturalToGlobalEnd(dm, v, originalv));
830           PetscCall(VecDestroy(&v));
831 #else
832           SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
833 #endif
834         } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Reading in natural order is not supported for anything but HDF5.");
835       }
836     } else PetscCall(VecLoad_Default(originalv, viewer));
837   }
838   PetscFunctionReturn(PETSC_SUCCESS);
839 }
840 
841 PETSC_UNUSED static PetscErrorCode DMPlexView_Ascii_Geometry(DM dm, PetscViewer viewer)
842 {
843   PetscSection       coordSection;
844   Vec                coordinates;
845   DMLabel            depthLabel, celltypeLabel;
846   const char        *name[4];
847   const PetscScalar *a;
848   PetscInt           dim, pStart, pEnd, cStart, cEnd, c;
849 
850   PetscFunctionBegin;
851   PetscCall(DMGetDimension(dm, &dim));
852   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
853   PetscCall(DMGetCoordinateSection(dm, &coordSection));
854   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
855   PetscCall(DMPlexGetCellTypeLabel(dm, &celltypeLabel));
856   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
857   PetscCall(PetscSectionGetChart(coordSection, &pStart, &pEnd));
858   PetscCall(VecGetArrayRead(coordinates, &a));
859   name[0]       = "vertex";
860   name[1]       = "edge";
861   name[dim - 1] = "face";
862   name[dim]     = "cell";
863   for (c = cStart; c < cEnd; ++c) {
864     PetscInt *closure = NULL;
865     PetscInt  closureSize, cl, ct;
866 
867     PetscCall(DMLabelGetValue(celltypeLabel, c, &ct));
868     PetscCall(PetscViewerASCIIPrintf(viewer, "Geometry for cell %" PetscInt_FMT " polytope type %s:\n", c, DMPolytopeTypes[ct]));
869     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
870     PetscCall(PetscViewerASCIIPushTab(viewer));
871     for (cl = 0; cl < closureSize * 2; cl += 2) {
872       PetscInt point = closure[cl], depth, dof, off, d, p;
873 
874       if ((point < pStart) || (point >= pEnd)) continue;
875       PetscCall(PetscSectionGetDof(coordSection, point, &dof));
876       if (!dof) continue;
877       PetscCall(DMLabelGetValue(depthLabel, point, &depth));
878       PetscCall(PetscSectionGetOffset(coordSection, point, &off));
879       PetscCall(PetscViewerASCIIPrintf(viewer, "%s %" PetscInt_FMT " coords:", name[depth], point));
880       for (p = 0; p < dof / dim; ++p) {
881         PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
882         for (d = 0; d < dim; ++d) {
883           if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
884           PetscCall(PetscViewerASCIIPrintf(viewer, "%g", (double)PetscRealPart(a[off + p * dim + d])));
885         }
886         PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
887       }
888       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
889     }
890     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
891     PetscCall(PetscViewerASCIIPopTab(viewer));
892   }
893   PetscCall(VecRestoreArrayRead(coordinates, &a));
894   PetscFunctionReturn(PETSC_SUCCESS);
895 }
896 
897 typedef enum {
898   CS_CARTESIAN,
899   CS_POLAR,
900   CS_CYLINDRICAL,
901   CS_SPHERICAL
902 } CoordSystem;
903 const char *CoordSystems[] = {"cartesian", "polar", "cylindrical", "spherical", "CoordSystem", "CS_", NULL};
904 
905 static PetscErrorCode DMPlexView_Ascii_Coordinates(PetscViewer viewer, CoordSystem cs, PetscInt dim, const PetscScalar x[])
906 {
907   PetscInt i;
908 
909   PetscFunctionBegin;
910   if (dim > 3) {
911     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double)PetscRealPart(x[i])));
912   } else {
913     PetscReal coords[3], trcoords[3] = {0., 0., 0.};
914 
915     for (i = 0; i < dim; ++i) coords[i] = PetscRealPart(x[i]);
916     switch (cs) {
917     case CS_CARTESIAN:
918       for (i = 0; i < dim; ++i) trcoords[i] = coords[i];
919       break;
920     case CS_POLAR:
921       PetscCheck(dim == 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Polar coordinates are for 2 dimension, not %" PetscInt_FMT, dim);
922       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
923       trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
924       break;
925     case CS_CYLINDRICAL:
926       PetscCheck(dim == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cylindrical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
927       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
928       trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
929       trcoords[2] = coords[2];
930       break;
931     case CS_SPHERICAL:
932       PetscCheck(dim == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Spherical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
933       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]) + PetscSqr(coords[2]));
934       trcoords[1] = PetscAtan2Real(PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1])), coords[2]);
935       trcoords[2] = PetscAtan2Real(coords[1], coords[0]);
936       break;
937     }
938     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double)trcoords[i]));
939   }
940   PetscFunctionReturn(PETSC_SUCCESS);
941 }
942 
943 static PetscErrorCode DMPlexView_Ascii(DM dm, PetscViewer viewer)
944 {
945   DM_Plex          *mesh = (DM_Plex *)dm->data;
946   DM                cdm, cdmCell;
947   PetscSection      coordSection, coordSectionCell;
948   Vec               coordinates, coordinatesCell;
949   PetscViewerFormat format;
950 
951   PetscFunctionBegin;
952   PetscCall(PetscViewerGetFormat(viewer, &format));
953   if (format == PETSC_VIEWER_ASCII_INFO_DETAIL) {
954     const char *name;
955     PetscInt    dim, cellHeight, maxConeSize, maxSupportSize;
956     PetscInt    pStart, pEnd, p, numLabels, l;
957     PetscMPIInt rank, size;
958 
959     PetscCall(DMGetCoordinateDM(dm, &cdm));
960     PetscCall(DMGetCoordinateSection(dm, &coordSection));
961     PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
962     PetscCall(DMGetCellCoordinateDM(dm, &cdmCell));
963     PetscCall(DMGetCellCoordinateSection(dm, &coordSectionCell));
964     PetscCall(DMGetCellCoordinatesLocal(dm, &coordinatesCell));
965     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
966     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
967     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
968     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
969     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
970     PetscCall(DMGetDimension(dm, &dim));
971     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
972     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
973     else PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
974     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
975     PetscCall(PetscViewerASCIIPrintf(viewer, "Supports:\n"));
976     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
977     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max support size: %" PetscInt_FMT "\n", rank, maxSupportSize));
978     for (p = pStart; p < pEnd; ++p) {
979       PetscInt dof, off, s;
980 
981       PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
982       PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
983       for (s = off; s < off + dof; ++s) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d]: %" PetscInt_FMT " ----> %" PetscInt_FMT "\n", rank, p, mesh->supports[s]));
984     }
985     PetscCall(PetscViewerFlush(viewer));
986     PetscCall(PetscViewerASCIIPrintf(viewer, "Cones:\n"));
987     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max cone size: %" PetscInt_FMT "\n", rank, maxConeSize));
988     for (p = pStart; p < pEnd; ++p) {
989       PetscInt dof, off, c;
990 
991       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
992       PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
993       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]));
994     }
995     PetscCall(PetscViewerFlush(viewer));
996     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
997     if (coordSection && coordinates) {
998       CoordSystem        cs = CS_CARTESIAN;
999       const PetscScalar *array, *arrayCell = NULL;
1000       PetscInt           Nf, Nc, pvStart, pvEnd, pcStart = PETSC_INT_MAX, pcEnd = PETSC_INT_MIN, pStart, pEnd, p;
1001       PetscMPIInt        rank;
1002       const char        *name;
1003 
1004       PetscCall(PetscOptionsGetEnum(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_coord_system", CoordSystems, (PetscEnum *)&cs, NULL));
1005       PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)viewer), &rank));
1006       PetscCall(PetscSectionGetNumFields(coordSection, &Nf));
1007       PetscCheck(Nf == 1, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Coordinate section should have 1 field, not %" PetscInt_FMT, Nf);
1008       PetscCall(PetscSectionGetFieldComponents(coordSection, 0, &Nc));
1009       PetscCall(PetscSectionGetChart(coordSection, &pvStart, &pvEnd));
1010       if (coordSectionCell) PetscCall(PetscSectionGetChart(coordSectionCell, &pcStart, &pcEnd));
1011       pStart = PetscMin(pvStart, pcStart);
1012       pEnd   = PetscMax(pvEnd, pcEnd);
1013       PetscCall(PetscObjectGetName((PetscObject)coordinates, &name));
1014       PetscCall(PetscViewerASCIIPrintf(viewer, "%s with %" PetscInt_FMT " fields\n", name, Nf));
1015       PetscCall(PetscViewerASCIIPrintf(viewer, "  field 0 with %" PetscInt_FMT " components\n", Nc));
1016       if (cs != CS_CARTESIAN) PetscCall(PetscViewerASCIIPrintf(viewer, "  output coordinate system: %s\n", CoordSystems[cs]));
1017 
1018       PetscCall(VecGetArrayRead(coordinates, &array));
1019       if (coordinatesCell) PetscCall(VecGetArrayRead(coordinatesCell, &arrayCell));
1020       PetscCall(PetscViewerASCIIPushSynchronized(viewer));
1021       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "Process %d:\n", rank));
1022       for (p = pStart; p < pEnd; ++p) {
1023         PetscInt dof, off;
1024 
1025         if (p >= pvStart && p < pvEnd) {
1026           PetscCall(PetscSectionGetDof(coordSection, p, &dof));
1027           PetscCall(PetscSectionGetOffset(coordSection, p, &off));
1028           if (dof) {
1029             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dof %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
1030             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &array[off]));
1031             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
1032           }
1033         }
1034         if (cdmCell && p >= pcStart && p < pcEnd) {
1035           PetscCall(PetscSectionGetDof(coordSectionCell, p, &dof));
1036           PetscCall(PetscSectionGetOffset(coordSectionCell, p, &off));
1037           if (dof) {
1038             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dof %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
1039             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &arrayCell[off]));
1040             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
1041           }
1042         }
1043       }
1044       PetscCall(PetscViewerFlush(viewer));
1045       PetscCall(PetscViewerASCIIPopSynchronized(viewer));
1046       PetscCall(VecRestoreArrayRead(coordinates, &array));
1047       if (coordinatesCell) PetscCall(VecRestoreArrayRead(coordinatesCell, &arrayCell));
1048     }
1049     PetscCall(DMGetNumLabels(dm, &numLabels));
1050     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
1051     for (l = 0; l < numLabels; ++l) {
1052       DMLabel     label;
1053       PetscBool   isdepth;
1054       const char *name;
1055 
1056       PetscCall(DMGetLabelName(dm, l, &name));
1057       PetscCall(PetscStrcmp(name, "depth", &isdepth));
1058       if (isdepth) continue;
1059       PetscCall(DMGetLabel(dm, name, &label));
1060       PetscCall(DMLabelView(label, viewer));
1061     }
1062     if (size > 1) {
1063       PetscSF sf;
1064 
1065       PetscCall(DMGetPointSF(dm, &sf));
1066       PetscCall(PetscSFView(sf, viewer));
1067     }
1068     if (mesh->periodic.face_sfs)
1069       for (PetscInt i = 0; i < mesh->periodic.num_face_sfs; i++) PetscCall(PetscSFView(mesh->periodic.face_sfs[i], viewer));
1070     PetscCall(PetscViewerFlush(viewer));
1071   } else if (format == PETSC_VIEWER_ASCII_LATEX) {
1072     const char  *name, *color;
1073     const char  *defcolors[3]  = {"gray", "orange", "green"};
1074     const char  *deflcolors[4] = {"blue", "cyan", "red", "magenta"};
1075     char         lname[PETSC_MAX_PATH_LEN];
1076     PetscReal    scale      = 2.0;
1077     PetscReal    tikzscale  = 1.0;
1078     PetscBool    useNumbers = PETSC_TRUE, drawNumbers[4], drawColors[4], useLabels, useColors, plotEdges, drawHasse = PETSC_FALSE;
1079     double       tcoords[3];
1080     PetscScalar *coords;
1081     PetscInt     numLabels, l, numColors, numLColors, dim, d, depth, cStart, cEnd, c, vStart, vEnd, v, eStart = 0, eEnd = 0, fStart = 0, fEnd = 0, e, p, n;
1082     PetscMPIInt  rank, size;
1083     char       **names, **colors, **lcolors;
1084     PetscBool    flg, lflg;
1085     PetscBT      wp = NULL;
1086     PetscInt     pEnd, pStart;
1087 
1088     PetscCall(DMGetCoordinateDM(dm, &cdm));
1089     PetscCall(DMGetCoordinateSection(dm, &coordSection));
1090     PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1091     PetscCall(DMGetCellCoordinateDM(dm, &cdmCell));
1092     PetscCall(DMGetCellCoordinateSection(dm, &coordSectionCell));
1093     PetscCall(DMGetCellCoordinatesLocal(dm, &coordinatesCell));
1094     PetscCall(DMGetDimension(dm, &dim));
1095     PetscCall(DMPlexGetDepth(dm, &depth));
1096     PetscCall(DMGetNumLabels(dm, &numLabels));
1097     numLabels  = PetscMax(numLabels, 10);
1098     numColors  = 10;
1099     numLColors = 10;
1100     PetscCall(PetscCalloc3(numLabels, &names, numColors, &colors, numLColors, &lcolors));
1101     PetscCall(PetscOptionsGetReal(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_scale", &scale, NULL));
1102     PetscCall(PetscOptionsGetReal(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_tikzscale", &tikzscale, NULL));
1103     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_numbers", &useNumbers, NULL));
1104     for (d = 0; d < 4; ++d) drawNumbers[d] = useNumbers;
1105     for (d = 0; d < 4; ++d) drawColors[d] = PETSC_TRUE;
1106     n = 4;
1107     PetscCall(PetscOptionsGetBoolArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_numbers_depth", drawNumbers, &n, &flg));
1108     PetscCheck(!flg || n == dim + 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim + 1);
1109     n = 4;
1110     PetscCall(PetscOptionsGetBoolArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_colors_depth", drawColors, &n, &flg));
1111     PetscCheck(!flg || n == dim + 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim + 1);
1112     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_labels", names, &numLabels, &useLabels));
1113     if (!useLabels) numLabels = 0;
1114     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_colors", colors, &numColors, &useColors));
1115     if (!useColors) {
1116       numColors = 3;
1117       for (c = 0; c < numColors; ++c) PetscCall(PetscStrallocpy(defcolors[c], &colors[c]));
1118     }
1119     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_lcolors", lcolors, &numLColors, &useColors));
1120     if (!useColors) {
1121       numLColors = 4;
1122       for (c = 0; c < numLColors; ++c) PetscCall(PetscStrallocpy(deflcolors[c], &lcolors[c]));
1123     }
1124     PetscCall(PetscOptionsGetString(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_label_filter", lname, sizeof(lname), &lflg));
1125     plotEdges = (PetscBool)(depth > 1 && drawNumbers[1] && dim < 3);
1126     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_edges", &plotEdges, &flg));
1127     PetscCheck(!flg || !plotEdges || depth >= dim, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Mesh must be interpolated");
1128     if (depth < dim) plotEdges = PETSC_FALSE;
1129     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_hasse", &drawHasse, NULL));
1130 
1131     /* filter points with labelvalue != labeldefaultvalue */
1132     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
1133     PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1134     PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
1135     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1136     PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd));
1137     if (lflg) {
1138       DMLabel lbl;
1139 
1140       PetscCall(DMGetLabel(dm, lname, &lbl));
1141       if (lbl) {
1142         PetscInt val, defval;
1143 
1144         PetscCall(DMLabelGetDefaultValue(lbl, &defval));
1145         PetscCall(PetscBTCreate(pEnd - pStart, &wp));
1146         for (c = pStart; c < pEnd; c++) {
1147           PetscInt *closure = NULL;
1148           PetscInt  closureSize;
1149 
1150           PetscCall(DMLabelGetValue(lbl, c, &val));
1151           if (val == defval) continue;
1152 
1153           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1154           for (p = 0; p < closureSize * 2; p += 2) PetscCall(PetscBTSet(wp, closure[p] - pStart));
1155           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1156         }
1157       }
1158     }
1159 
1160     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1161     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
1162     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1163     PetscCall(PetscViewerASCIIPrintf(viewer, "\
1164 \\documentclass[tikz]{standalone}\n\n\
1165 \\usepackage{pgflibraryshapes}\n\
1166 \\usetikzlibrary{backgrounds}\n\
1167 \\usetikzlibrary{arrows}\n\
1168 \\begin{document}\n"));
1169     if (size > 1) {
1170       PetscCall(PetscViewerASCIIPrintf(viewer, "%s for process ", name));
1171       for (p = 0; p < size; ++p) {
1172         if (p) PetscCall(PetscViewerASCIIPrintf(viewer, (p == size - 1) ? ", and " : ", "));
1173         PetscCall(PetscViewerASCIIPrintf(viewer, "{\\textcolor{%s}%" PetscInt_FMT "}", colors[p % numColors], p));
1174       }
1175       PetscCall(PetscViewerASCIIPrintf(viewer, ".\n\n\n"));
1176     }
1177     if (drawHasse) {
1178       PetscInt maxStratum = PetscMax(vEnd - vStart, PetscMax(eEnd - eStart, PetscMax(fEnd - fStart, cEnd - cStart)));
1179 
1180       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vStart}{%" PetscInt_FMT "}\n", vStart));
1181       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vEnd}{%" PetscInt_FMT "}\n", vEnd - 1));
1182       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numVertices}{%" PetscInt_FMT "}\n", vEnd - vStart));
1183       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vShift}{%.2f}\n", 3 + (maxStratum - (vEnd - vStart)) / 2.));
1184       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eStart}{%" PetscInt_FMT "}\n", eStart));
1185       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eEnd}{%" PetscInt_FMT "}\n", eEnd - 1));
1186       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eShift}{%.2f}\n", 3 + (maxStratum - (eEnd - eStart)) / 2.));
1187       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numEdges}{%" PetscInt_FMT "}\n", eEnd - eStart));
1188       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\fStart}{%" PetscInt_FMT "}\n", fStart));
1189       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\fEnd}{%" PetscInt_FMT "}\n", fEnd - 1));
1190       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\fShift}{%.2f}\n", 3 + (maxStratum - (fEnd - fStart)) / 2.));
1191       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numFaces}{%" PetscInt_FMT "}\n", fEnd - fStart));
1192       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cStart}{%" PetscInt_FMT "}\n", cStart));
1193       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cEnd}{%" PetscInt_FMT "}\n", cEnd - 1));
1194       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numCells}{%" PetscInt_FMT "}\n", cEnd - cStart));
1195       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cShift}{%.2f}\n", 3 + (maxStratum - (cEnd - cStart)) / 2.));
1196     }
1197     PetscCall(PetscViewerASCIIPrintf(viewer, "\\begin{tikzpicture}[scale = %g,font=\\fontsize{8}{8}\\selectfont]\n", (double)tikzscale));
1198 
1199     /* Plot vertices */
1200     PetscCall(VecGetArray(coordinates, &coords));
1201     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
1202     for (v = vStart; v < vEnd; ++v) {
1203       PetscInt  off, dof, d;
1204       PetscBool isLabeled = PETSC_FALSE;
1205 
1206       if (wp && !PetscBTLookup(wp, v - pStart)) continue;
1207       PetscCall(PetscSectionGetDof(coordSection, v, &dof));
1208       PetscCall(PetscSectionGetOffset(coordSection, v, &off));
1209       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1210       PetscCheck(dof <= 3, PETSC_COMM_SELF, PETSC_ERR_PLIB, "coordSection vertex %" PetscInt_FMT " has dof %" PetscInt_FMT " > 3", v, dof);
1211       for (d = 0; d < dof; ++d) {
1212         tcoords[d] = (double)(scale * PetscRealPart(coords[off + d]));
1213         tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1214       }
1215       /* Rotate coordinates since PGF makes z point out of the page instead of up */
1216       if (dim == 3) {
1217         PetscReal tmp = tcoords[1];
1218         tcoords[1]    = tcoords[2];
1219         tcoords[2]    = -tmp;
1220       }
1221       for (d = 0; d < dof; ++d) {
1222         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1223         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)tcoords[d]));
1224       }
1225       if (drawHasse) color = colors[0 % numColors];
1226       else color = colors[rank % numColors];
1227       for (l = 0; l < numLabels; ++l) {
1228         PetscInt val;
1229         PetscCall(DMGetLabelValue(dm, names[l], v, &val));
1230         if (val >= 0) {
1231           color     = lcolors[l % numLColors];
1232           isLabeled = PETSC_TRUE;
1233           break;
1234         }
1235       }
1236       if (drawNumbers[0]) {
1237         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", v, rank, color, v));
1238       } else if (drawColors[0]) {
1239         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", v, rank, !isLabeled ? 1 : 2, color));
1240       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", v, rank));
1241     }
1242     PetscCall(VecRestoreArray(coordinates, &coords));
1243     PetscCall(PetscViewerFlush(viewer));
1244     /* Plot edges */
1245     if (plotEdges) {
1246       PetscCall(VecGetArray(coordinates, &coords));
1247       PetscCall(PetscViewerASCIIPrintf(viewer, "\\path\n"));
1248       for (e = eStart; e < eEnd; ++e) {
1249         const PetscInt *cone;
1250         PetscInt        coneSize, offA, offB, dof, d;
1251 
1252         if (wp && !PetscBTLookup(wp, e - pStart)) continue;
1253         PetscCall(DMPlexGetConeSize(dm, e, &coneSize));
1254         PetscCheck(coneSize == 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Edge %" PetscInt_FMT " cone should have two vertices, not %" PetscInt_FMT, e, coneSize);
1255         PetscCall(DMPlexGetCone(dm, e, &cone));
1256         PetscCall(PetscSectionGetDof(coordSection, cone[0], &dof));
1257         PetscCall(PetscSectionGetOffset(coordSection, cone[0], &offA));
1258         PetscCall(PetscSectionGetOffset(coordSection, cone[1], &offB));
1259         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "("));
1260         for (d = 0; d < dof; ++d) {
1261           tcoords[d] = (double)(0.5 * scale * PetscRealPart(coords[offA + d] + coords[offB + d]));
1262           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1263         }
1264         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1265         if (dim == 3) {
1266           PetscReal tmp = tcoords[1];
1267           tcoords[1]    = tcoords[2];
1268           tcoords[2]    = -tmp;
1269         }
1270         for (d = 0; d < dof; ++d) {
1271           if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1272           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)tcoords[d]));
1273         }
1274         if (drawHasse) color = colors[1 % numColors];
1275         else color = colors[rank % numColors];
1276         for (l = 0; l < numLabels; ++l) {
1277           PetscInt val;
1278           PetscCall(DMGetLabelValue(dm, names[l], e, &val));
1279           if (val >= 0) {
1280             color = lcolors[l % numLColors];
1281             break;
1282           }
1283         }
1284         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "} --\n", e, rank, color, e));
1285       }
1286       PetscCall(VecRestoreArray(coordinates, &coords));
1287       PetscCall(PetscViewerFlush(viewer));
1288       PetscCall(PetscViewerASCIIPrintf(viewer, "(0,0);\n"));
1289     }
1290     /* Plot cells */
1291     if (dim == 3 || !drawNumbers[1]) {
1292       for (e = eStart; e < eEnd; ++e) {
1293         const PetscInt *cone;
1294 
1295         if (wp && !PetscBTLookup(wp, e - pStart)) continue;
1296         color = colors[rank % numColors];
1297         for (l = 0; l < numLabels; ++l) {
1298           PetscInt val;
1299           PetscCall(DMGetLabelValue(dm, names[l], e, &val));
1300           if (val >= 0) {
1301             color = lcolors[l % numLColors];
1302             break;
1303           }
1304         }
1305         PetscCall(DMPlexGetCone(dm, e, &cone));
1306         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", color, cone[0], rank, cone[1], rank));
1307       }
1308     } else {
1309       DMPolytopeType ct;
1310 
1311       /* Drawing a 2D polygon */
1312       for (c = cStart; c < cEnd; ++c) {
1313         if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1314         PetscCall(DMPlexGetCellType(dm, c, &ct));
1315         if (DMPolytopeTypeIsHybrid(ct)) {
1316           const PetscInt *cone;
1317           PetscInt        coneSize, e;
1318 
1319           PetscCall(DMPlexGetCone(dm, c, &cone));
1320           PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
1321           for (e = 0; e < coneSize; ++e) {
1322             const PetscInt *econe;
1323 
1324             PetscCall(DMPlexGetCone(dm, cone[e], &econe));
1325             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));
1326           }
1327         } else {
1328           PetscInt *closure = NULL;
1329           PetscInt  closureSize, Nv = 0, v;
1330 
1331           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1332           for (p = 0; p < closureSize * 2; p += 2) {
1333             const PetscInt point = closure[p];
1334 
1335             if ((point >= vStart) && (point < vEnd)) closure[Nv++] = point;
1336           }
1337           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] ", colors[rank % numColors]));
1338           for (v = 0; v <= Nv; ++v) {
1339             const PetscInt vertex = closure[v % Nv];
1340 
1341             if (v > 0) {
1342               if (plotEdges) {
1343                 const PetscInt *edge;
1344                 PetscInt        endpoints[2], ne;
1345 
1346                 endpoints[0] = closure[v - 1];
1347                 endpoints[1] = vertex;
1348                 PetscCall(DMPlexGetJoin(dm, 2, endpoints, &ne, &edge));
1349                 PetscCheck(ne == 1, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Could not find edge for vertices %" PetscInt_FMT ", %" PetscInt_FMT, endpoints[0], endpoints[1]);
1350                 PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- (%" PetscInt_FMT "_%d) -- ", edge[0], rank));
1351                 PetscCall(DMPlexRestoreJoin(dm, 2, endpoints, &ne, &edge));
1352               } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- "));
1353             }
1354             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "(%" PetscInt_FMT "_%d)", vertex, rank));
1355           }
1356           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ";\n"));
1357           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1358         }
1359       }
1360     }
1361     for (c = cStart; c < cEnd; ++c) {
1362       double             ccoords[3] = {0.0, 0.0, 0.0};
1363       PetscBool          isLabeled  = PETSC_FALSE;
1364       PetscScalar       *cellCoords = NULL;
1365       const PetscScalar *array;
1366       PetscInt           numCoords, cdim, d;
1367       PetscBool          isDG;
1368 
1369       if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1370       PetscCall(DMGetCoordinateDim(dm, &cdim));
1371       PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1372       PetscCheck(!(numCoords % cdim), PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "coordinate dim %" PetscInt_FMT " does not divide numCoords %" PetscInt_FMT, cdim, numCoords);
1373       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1374       for (p = 0; p < numCoords / cdim; ++p) {
1375         for (d = 0; d < cdim; ++d) {
1376           tcoords[d] = (double)(scale * PetscRealPart(cellCoords[p * cdim + d]));
1377           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1378         }
1379         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1380         if (cdim == 3) {
1381           PetscReal tmp = tcoords[1];
1382           tcoords[1]    = tcoords[2];
1383           tcoords[2]    = -tmp;
1384         }
1385         for (d = 0; d < dim; ++d) ccoords[d] += tcoords[d];
1386       }
1387       for (d = 0; d < cdim; ++d) ccoords[d] /= (numCoords / cdim);
1388       PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1389       for (d = 0; d < cdim; ++d) {
1390         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1391         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)ccoords[d]));
1392       }
1393       if (drawHasse) color = colors[depth % numColors];
1394       else color = colors[rank % numColors];
1395       for (l = 0; l < numLabels; ++l) {
1396         PetscInt val;
1397         PetscCall(DMGetLabelValue(dm, names[l], c, &val));
1398         if (val >= 0) {
1399           color     = lcolors[l % numLColors];
1400           isLabeled = PETSC_TRUE;
1401           break;
1402         }
1403       }
1404       if (drawNumbers[dim]) {
1405         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", c, rank, color, c));
1406       } else if (drawColors[dim]) {
1407         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", c, rank, !isLabeled ? 1 : 2, color));
1408       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", c, rank));
1409     }
1410     if (drawHasse) {
1411       int height = 0;
1412 
1413       color = colors[depth % numColors];
1414       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Cells\n"));
1415       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\c in {\\cStart,...,\\cEnd}\n"));
1416       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1417       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\c_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\cShift+\\c-\\cStart,%d) {\\c};\n", rank, color, height++));
1418       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1419 
1420       if (depth > 2) {
1421         color = colors[1 % numColors];
1422         PetscCall(PetscViewerASCIIPrintf(viewer, "%% Faces\n"));
1423         PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\f in {\\fStart,...,\\fEnd}\n"));
1424         PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1425         PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\f_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\fShift+\\f-\\fStart,%d) {\\f};\n", rank, color, height++));
1426         PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1427       }
1428 
1429       color = colors[1 % numColors];
1430       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Edges\n"));
1431       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\e in {\\eStart,...,\\eEnd}\n"));
1432       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1433       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\e_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\eShift+\\e-\\eStart,%d) {\\e};\n", rank, color, height++));
1434       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1435 
1436       color = colors[0 % numColors];
1437       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Vertices\n"));
1438       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\v in {\\vStart,...,\\vEnd}\n"));
1439       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1440       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\v_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\vShift+\\v-\\vStart,%d) {\\v};\n", rank, color, height++));
1441       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1442 
1443       for (p = pStart; p < pEnd; ++p) {
1444         const PetscInt *cone;
1445         PetscInt        coneSize, cp;
1446 
1447         PetscCall(DMPlexGetCone(dm, p, &cone));
1448         PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
1449         for (cp = 0; cp < coneSize; ++cp) PetscCall(PetscViewerASCIIPrintf(viewer, "\\draw[->, shorten >=1pt] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", cone[cp], rank, p, rank));
1450       }
1451     }
1452     PetscCall(PetscViewerFlush(viewer));
1453     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
1454     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{tikzpicture}\n"));
1455     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{document}\n"));
1456     for (l = 0; l < numLabels; ++l) PetscCall(PetscFree(names[l]));
1457     for (c = 0; c < numColors; ++c) PetscCall(PetscFree(colors[c]));
1458     for (c = 0; c < numLColors; ++c) PetscCall(PetscFree(lcolors[c]));
1459     PetscCall(PetscFree3(names, colors, lcolors));
1460     PetscCall(PetscBTDestroy(&wp));
1461   } else if (format == PETSC_VIEWER_LOAD_BALANCE) {
1462     Vec                    cown, acown;
1463     VecScatter             sct;
1464     ISLocalToGlobalMapping g2l;
1465     IS                     gid, acis;
1466     MPI_Comm               comm, ncomm = MPI_COMM_NULL;
1467     MPI_Group              ggroup, ngroup;
1468     PetscScalar           *array, nid;
1469     const PetscInt        *idxs;
1470     PetscInt              *idxs2, *start, *adjacency, *work;
1471     PetscInt64             lm[3], gm[3];
1472     PetscInt               i, c, cStart, cEnd, cum, numVertices, ect, ectn, cellHeight;
1473     PetscMPIInt            d1, d2, rank;
1474 
1475     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1476     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1477 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1478     PetscCallMPI(MPI_Comm_split_type(comm, MPI_COMM_TYPE_SHARED, rank, MPI_INFO_NULL, &ncomm));
1479 #endif
1480     if (ncomm != MPI_COMM_NULL) {
1481       PetscCallMPI(MPI_Comm_group(comm, &ggroup));
1482       PetscCallMPI(MPI_Comm_group(ncomm, &ngroup));
1483       d1 = 0;
1484       PetscCallMPI(MPI_Group_translate_ranks(ngroup, 1, &d1, ggroup, &d2));
1485       nid = d2;
1486       PetscCallMPI(MPI_Group_free(&ggroup));
1487       PetscCallMPI(MPI_Group_free(&ngroup));
1488       PetscCallMPI(MPI_Comm_free(&ncomm));
1489     } else nid = 0.0;
1490 
1491     /* Get connectivity */
1492     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1493     PetscCall(DMPlexCreatePartitionerGraph(dm, cellHeight, &numVertices, &start, &adjacency, &gid));
1494 
1495     /* filter overlapped local cells */
1496     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
1497     PetscCall(ISGetIndices(gid, &idxs));
1498     PetscCall(ISGetLocalSize(gid, &cum));
1499     PetscCall(PetscMalloc1(cum, &idxs2));
1500     for (c = cStart, cum = 0; c < cEnd; c++) {
1501       if (idxs[c - cStart] < 0) continue;
1502       idxs2[cum++] = idxs[c - cStart];
1503     }
1504     PetscCall(ISRestoreIndices(gid, &idxs));
1505     PetscCheck(numVertices == cum, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Unexpected %" PetscInt_FMT " != %" PetscInt_FMT, numVertices, cum);
1506     PetscCall(ISDestroy(&gid));
1507     PetscCall(ISCreateGeneral(comm, numVertices, idxs2, PETSC_OWN_POINTER, &gid));
1508 
1509     /* support for node-aware cell locality */
1510     PetscCall(ISCreateGeneral(comm, start[numVertices], adjacency, PETSC_USE_POINTER, &acis));
1511     PetscCall(VecCreateSeq(PETSC_COMM_SELF, start[numVertices], &acown));
1512     PetscCall(VecCreateMPI(comm, numVertices, PETSC_DECIDE, &cown));
1513     PetscCall(VecGetArray(cown, &array));
1514     for (c = 0; c < numVertices; c++) array[c] = nid;
1515     PetscCall(VecRestoreArray(cown, &array));
1516     PetscCall(VecScatterCreate(cown, acis, acown, NULL, &sct));
1517     PetscCall(VecScatterBegin(sct, cown, acown, INSERT_VALUES, SCATTER_FORWARD));
1518     PetscCall(VecScatterEnd(sct, cown, acown, INSERT_VALUES, SCATTER_FORWARD));
1519     PetscCall(ISDestroy(&acis));
1520     PetscCall(VecScatterDestroy(&sct));
1521     PetscCall(VecDestroy(&cown));
1522 
1523     /* compute edgeCut */
1524     for (c = 0, cum = 0; c < numVertices; c++) cum = PetscMax(cum, start[c + 1] - start[c]);
1525     PetscCall(PetscMalloc1(cum, &work));
1526     PetscCall(ISLocalToGlobalMappingCreateIS(gid, &g2l));
1527     PetscCall(ISLocalToGlobalMappingSetType(g2l, ISLOCALTOGLOBALMAPPINGHASH));
1528     PetscCall(ISDestroy(&gid));
1529     PetscCall(VecGetArray(acown, &array));
1530     for (c = 0, ect = 0, ectn = 0; c < numVertices; c++) {
1531       PetscInt totl;
1532 
1533       totl = start[c + 1] - start[c];
1534       PetscCall(ISGlobalToLocalMappingApply(g2l, IS_GTOLM_MASK, totl, adjacency + start[c], NULL, work));
1535       for (i = 0; i < totl; i++) {
1536         if (work[i] < 0) {
1537           ect += 1;
1538           ectn += (array[i + start[c]] != nid) ? 0 : 1;
1539         }
1540       }
1541     }
1542     PetscCall(PetscFree(work));
1543     PetscCall(VecRestoreArray(acown, &array));
1544     lm[0] = numVertices > 0 ? numVertices : PETSC_INT_MAX;
1545     lm[1] = -numVertices;
1546     PetscCallMPI(MPIU_Allreduce(lm, gm, 2, MPIU_INT64, MPI_MIN, comm));
1547     PetscCall(PetscViewerASCIIPrintf(viewer, "  Cell balance: %.2f (max %" PetscInt_FMT ", min %" PetscInt_FMT, -((double)gm[1]) / ((double)gm[0]), -(PetscInt)gm[1], (PetscInt)gm[0]));
1548     lm[0] = ect;                     /* edgeCut */
1549     lm[1] = ectn;                    /* node-aware edgeCut */
1550     lm[2] = numVertices > 0 ? 0 : 1; /* empty processes */
1551     PetscCallMPI(MPIU_Allreduce(lm, gm, 3, MPIU_INT64, MPI_SUM, comm));
1552     PetscCall(PetscViewerASCIIPrintf(viewer, ", empty %" PetscInt_FMT ")\n", (PetscInt)gm[2]));
1553 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1554     PetscCall(PetscViewerASCIIPrintf(viewer, "  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n", (PetscInt)(gm[0] / 2), gm[0] ? ((double)gm[1]) / ((double)gm[0]) : 1.));
1555 #else
1556     PetscCall(PetscViewerASCIIPrintf(viewer, "  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n", (PetscInt)(gm[0] / 2), 0.0));
1557 #endif
1558     PetscCall(ISLocalToGlobalMappingDestroy(&g2l));
1559     PetscCall(PetscFree(start));
1560     PetscCall(PetscFree(adjacency));
1561     PetscCall(VecDestroy(&acown));
1562   } else {
1563     const char    *name;
1564     PetscInt      *sizes, *hybsizes, *ghostsizes;
1565     PetscInt       locDepth, depth, cellHeight, dim, d;
1566     PetscInt       pStart, pEnd, p, gcStart, gcEnd, gcNum;
1567     PetscInt       numLabels, l, maxSize = 17;
1568     DMPolytopeType ct0 = DM_POLYTOPE_UNKNOWN;
1569     MPI_Comm       comm;
1570     PetscMPIInt    size, rank;
1571 
1572     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1573     PetscCallMPI(MPI_Comm_size(comm, &size));
1574     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1575     PetscCall(DMGetDimension(dm, &dim));
1576     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1577     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1578     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
1579     else PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
1580     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
1581     PetscCall(DMPlexGetDepth(dm, &locDepth));
1582     PetscCallMPI(MPIU_Allreduce(&locDepth, &depth, 1, MPIU_INT, MPI_MAX, comm));
1583     PetscCall(DMPlexGetCellTypeStratum(dm, DM_POLYTOPE_FV_GHOST, &gcStart, &gcEnd));
1584     gcNum = gcEnd - gcStart;
1585     if (size < maxSize) PetscCall(PetscCalloc3(size, &sizes, size, &hybsizes, size, &ghostsizes));
1586     else PetscCall(PetscCalloc3(3, &sizes, 3, &hybsizes, 3, &ghostsizes));
1587     for (d = 0; d <= depth; d++) {
1588       PetscInt Nc[2] = {0, 0}, ict;
1589 
1590       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
1591       if (pStart < pEnd) PetscCall(DMPlexGetCellType(dm, pStart, &ct0));
1592       ict = ct0;
1593       PetscCallMPI(MPI_Bcast(&ict, 1, MPIU_INT, 0, comm));
1594       ct0 = (DMPolytopeType)ict;
1595       for (p = pStart; p < pEnd; ++p) {
1596         DMPolytopeType ct;
1597 
1598         PetscCall(DMPlexGetCellType(dm, p, &ct));
1599         if (ct == ct0) ++Nc[0];
1600         else ++Nc[1];
1601       }
1602       if (size < maxSize) {
1603         PetscCallMPI(MPI_Gather(&Nc[0], 1, MPIU_INT, sizes, 1, MPIU_INT, 0, comm));
1604         PetscCallMPI(MPI_Gather(&Nc[1], 1, MPIU_INT, hybsizes, 1, MPIU_INT, 0, comm));
1605         if (d == depth) PetscCallMPI(MPI_Gather(&gcNum, 1, MPIU_INT, ghostsizes, 1, MPIU_INT, 0, comm));
1606         PetscCall(PetscViewerASCIIPrintf(viewer, "  Number of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1607         for (p = 0; p < size; ++p) {
1608           if (rank == 0) {
1609             PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT, sizes[p] + hybsizes[p]));
1610             if (hybsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT ")", hybsizes[p]));
1611             if (ghostsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "]", ghostsizes[p]));
1612           }
1613         }
1614       } else {
1615         PetscInt locMinMax[2];
1616 
1617         locMinMax[0] = Nc[0] + Nc[1];
1618         locMinMax[1] = Nc[0] + Nc[1];
1619         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, sizes));
1620         locMinMax[0] = Nc[1];
1621         locMinMax[1] = Nc[1];
1622         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, hybsizes));
1623         if (d == depth) {
1624           locMinMax[0] = gcNum;
1625           locMinMax[1] = gcNum;
1626           PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, ghostsizes));
1627         }
1628         PetscCall(PetscViewerASCIIPrintf(viewer, "  Min/Max of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1629         PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT "/%" PetscInt_FMT, sizes[0], sizes[1]));
1630         if (hybsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT "/%" PetscInt_FMT ")", hybsizes[0], hybsizes[1]));
1631         if (ghostsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "/%" PetscInt_FMT "]", ghostsizes[0], ghostsizes[1]));
1632       }
1633       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
1634     }
1635     PetscCall(PetscFree3(sizes, hybsizes, ghostsizes));
1636     {
1637       const PetscReal *maxCell;
1638       const PetscReal *L;
1639       PetscBool        localized;
1640 
1641       PetscCall(DMGetPeriodicity(dm, &maxCell, NULL, &L));
1642       PetscCall(DMGetCoordinatesLocalized(dm, &localized));
1643       if (L || localized) {
1644         PetscCall(PetscViewerASCIIPrintf(viewer, "Periodic mesh"));
1645         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1646         if (L) {
1647           PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
1648           for (d = 0; d < dim; ++d) {
1649             if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1650             PetscCall(PetscViewerASCIIPrintf(viewer, "%s", L[d] > 0.0 ? "PERIODIC" : "NONE"));
1651           }
1652           PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
1653         }
1654         PetscCall(PetscViewerASCIIPrintf(viewer, " coordinates %s\n", localized ? "localized" : "not localized"));
1655         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1656       }
1657     }
1658     PetscCall(DMGetNumLabels(dm, &numLabels));
1659     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
1660     for (l = 0; l < numLabels; ++l) {
1661       DMLabel     label;
1662       const char *name;
1663       PetscInt   *values;
1664       PetscInt    numValues, v;
1665 
1666       PetscCall(DMGetLabelName(dm, l, &name));
1667       PetscCall(DMGetLabel(dm, name, &label));
1668       PetscCall(DMLabelGetNumValues(label, &numValues));
1669       PetscCall(PetscViewerASCIIPrintf(viewer, "  %s: %" PetscInt_FMT " strata with value/size (", name, numValues));
1670 
1671       { // Extract array of DMLabel values so it can be sorted
1672         IS              is_values;
1673         const PetscInt *is_values_local = NULL;
1674 
1675         PetscCall(DMLabelGetValueIS(label, &is_values));
1676         PetscCall(ISGetIndices(is_values, &is_values_local));
1677         PetscCall(PetscMalloc1(numValues, &values));
1678         PetscCall(PetscArraycpy(values, is_values_local, numValues));
1679         PetscCall(PetscSortInt(numValues, values));
1680         PetscCall(ISRestoreIndices(is_values, &is_values_local));
1681         PetscCall(ISDestroy(&is_values));
1682       }
1683       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1684       for (v = 0; v < numValues; ++v) {
1685         PetscInt size;
1686 
1687         PetscCall(DMLabelGetStratumSize(label, values[v], &size));
1688         if (v > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1689         PetscCall(PetscViewerASCIIPrintf(viewer, "%" PetscInt_FMT " (%" PetscInt_FMT ")", values[v], size));
1690       }
1691       PetscCall(PetscViewerASCIIPrintf(viewer, ")\n"));
1692       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1693       PetscCall(PetscFree(values));
1694     }
1695     {
1696       char    **labelNames;
1697       PetscInt  Nl = numLabels;
1698       PetscBool flg;
1699 
1700       PetscCall(PetscMalloc1(Nl, &labelNames));
1701       PetscCall(PetscOptionsGetStringArray(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_plex_view_labels", labelNames, &Nl, &flg));
1702       for (l = 0; l < Nl; ++l) {
1703         DMLabel label;
1704 
1705         PetscCall(DMHasLabel(dm, labelNames[l], &flg));
1706         if (flg) {
1707           PetscCall(DMGetLabel(dm, labelNames[l], &label));
1708           PetscCall(DMLabelView(label, viewer));
1709         }
1710         PetscCall(PetscFree(labelNames[l]));
1711       }
1712       PetscCall(PetscFree(labelNames));
1713     }
1714     /* If no fields are specified, people do not want to see adjacency */
1715     if (dm->Nf) {
1716       PetscInt f;
1717 
1718       for (f = 0; f < dm->Nf; ++f) {
1719         const char *name;
1720 
1721         PetscCall(PetscObjectGetName(dm->fields[f].disc, &name));
1722         if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Field %s:\n", name));
1723         PetscCall(PetscViewerASCIIPushTab(viewer));
1724         if (dm->fields[f].label) PetscCall(DMLabelView(dm->fields[f].label, viewer));
1725         if (dm->fields[f].adjacency[0]) {
1726           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM++\n"));
1727           else PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM\n"));
1728         } else {
1729           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FEM\n"));
1730           else PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FUNKY\n"));
1731         }
1732         PetscCall(PetscViewerASCIIPopTab(viewer));
1733       }
1734     }
1735     PetscCall(DMGetCoarseDM(dm, &cdm));
1736     if (cdm) {
1737       PetscCall(PetscViewerASCIIPushTab(viewer));
1738       PetscCall(PetscViewerASCIIPrintf(viewer, "Defined by transform from:\n"));
1739       PetscCall(DMPlexView_Ascii(cdm, viewer));
1740       PetscCall(PetscViewerASCIIPopTab(viewer));
1741     }
1742   }
1743   PetscFunctionReturn(PETSC_SUCCESS);
1744 }
1745 
1746 static PetscErrorCode DMPlexDrawCell(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[])
1747 {
1748   DMPolytopeType ct;
1749   PetscMPIInt    rank;
1750   PetscInt       cdim;
1751 
1752   PetscFunctionBegin;
1753   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1754   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1755   PetscCall(DMGetCoordinateDim(dm, &cdim));
1756   switch (ct) {
1757   case DM_POLYTOPE_SEGMENT:
1758   case DM_POLYTOPE_POINT_PRISM_TENSOR:
1759     switch (cdim) {
1760     case 1: {
1761       const PetscReal y  = 0.5;  /* TODO Put it in the middle of the viewport */
1762       const PetscReal dy = 0.05; /* TODO Make it a fraction of the total length */
1763 
1764       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y, PetscRealPart(coords[1]), y, PETSC_DRAW_BLACK));
1765       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y + dy, PetscRealPart(coords[0]), y - dy, PETSC_DRAW_BLACK));
1766       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[1]), y + dy, PetscRealPart(coords[1]), y - dy, PETSC_DRAW_BLACK));
1767     } break;
1768     case 2: {
1769       const PetscReal dx = (PetscRealPart(coords[3]) - PetscRealPart(coords[1]));
1770       const PetscReal dy = (PetscRealPart(coords[2]) - PetscRealPart(coords[0]));
1771       const PetscReal l  = 0.1 / PetscSqrtReal(dx * dx + dy * dy);
1772 
1773       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1774       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));
1775       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));
1776     } break;
1777     default:
1778       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of dimension %" PetscInt_FMT, cdim);
1779     }
1780     break;
1781   case DM_POLYTOPE_TRIANGLE:
1782     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));
1783     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1784     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1785     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1786     break;
1787   case DM_POLYTOPE_QUADRILATERAL:
1788     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));
1789     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));
1790     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1791     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1792     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_BLACK));
1793     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1794     break;
1795   case DM_POLYTOPE_SEG_PRISM_TENSOR:
1796     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));
1797     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));
1798     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1799     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_BLACK));
1800     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1801     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1802     break;
1803   case DM_POLYTOPE_FV_GHOST:
1804     break;
1805   default:
1806     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1807   }
1808   PetscFunctionReturn(PETSC_SUCCESS);
1809 }
1810 
1811 static PetscErrorCode DrawPolygon_Private(DM dm, PetscDraw draw, PetscInt cell, PetscInt Nv, const PetscReal refVertices[], const PetscScalar coords[], PetscInt edgeDiv, PetscReal refCoords[], PetscReal edgeCoords[])
1812 {
1813   PetscReal   centroid[2] = {0., 0.};
1814   PetscMPIInt rank;
1815   PetscMPIInt fillColor;
1816 
1817   PetscFunctionBegin;
1818   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1819   fillColor = PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2;
1820   for (PetscInt v = 0; v < Nv; ++v) {
1821     centroid[0] += PetscRealPart(coords[v * 2 + 0]) / Nv;
1822     centroid[1] += PetscRealPart(coords[v * 2 + 1]) / Nv;
1823   }
1824   for (PetscInt e = 0; e < Nv; ++e) {
1825     refCoords[0] = refVertices[e * 2 + 0];
1826     refCoords[1] = refVertices[e * 2 + 1];
1827     for (PetscInt d = 1; d <= edgeDiv; ++d) {
1828       refCoords[d * 2 + 0] = refCoords[0] + (refVertices[(e + 1) % Nv * 2 + 0] - refCoords[0]) * d / edgeDiv;
1829       refCoords[d * 2 + 1] = refCoords[1] + (refVertices[(e + 1) % Nv * 2 + 1] - refCoords[1]) * d / edgeDiv;
1830     }
1831     PetscCall(DMPlexReferenceToCoordinates(dm, cell, edgeDiv + 1, refCoords, edgeCoords));
1832     for (PetscInt d = 0; d < edgeDiv; ++d) {
1833       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));
1834       PetscCall(PetscDrawLine(draw, edgeCoords[d * 2 + 0], edgeCoords[d * 2 + 1], edgeCoords[(d + 1) * 2 + 0], edgeCoords[(d + 1) * 2 + 1], PETSC_DRAW_BLACK));
1835     }
1836   }
1837   PetscFunctionReturn(PETSC_SUCCESS);
1838 }
1839 
1840 static PetscErrorCode DMPlexDrawCellHighOrder(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[], PetscInt edgeDiv, PetscReal refCoords[], PetscReal edgeCoords[])
1841 {
1842   DMPolytopeType ct;
1843 
1844   PetscFunctionBegin;
1845   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1846   switch (ct) {
1847   case DM_POLYTOPE_TRIANGLE: {
1848     PetscReal refVertices[6] = {-1., -1., 1., -1., -1., 1.};
1849 
1850     PetscCall(DrawPolygon_Private(dm, draw, cell, 3, refVertices, coords, edgeDiv, refCoords, edgeCoords));
1851   } break;
1852   case DM_POLYTOPE_QUADRILATERAL: {
1853     PetscReal refVertices[8] = {-1., -1., 1., -1., 1., 1., -1., 1.};
1854 
1855     PetscCall(DrawPolygon_Private(dm, draw, cell, 4, refVertices, coords, edgeDiv, refCoords, edgeCoords));
1856   } break;
1857   default:
1858     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1859   }
1860   PetscFunctionReturn(PETSC_SUCCESS);
1861 }
1862 
1863 static PetscErrorCode DMPlexView_Draw(DM dm, PetscViewer viewer)
1864 {
1865   PetscDraw    draw;
1866   DM           cdm;
1867   PetscSection coordSection;
1868   Vec          coordinates;
1869   PetscReal    xyl[3], xyr[3];
1870   PetscReal   *refCoords, *edgeCoords;
1871   PetscBool    isnull, drawAffine;
1872   PetscInt     dim, vStart, vEnd, cStart, cEnd, c, cDegree, edgeDiv;
1873 
1874   PetscFunctionBegin;
1875   PetscCall(DMGetCoordinateDim(dm, &dim));
1876   PetscCheck(dim <= 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT, dim);
1877   PetscCall(DMGetCoordinateDegree_Internal(dm, &cDegree));
1878   drawAffine = cDegree > 1 ? PETSC_FALSE : PETSC_TRUE;
1879   edgeDiv    = cDegree + 1;
1880   PetscCall(PetscOptionsGetBool(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_view_draw_affine", &drawAffine, NULL));
1881   if (!drawAffine) PetscCall(PetscMalloc2((edgeDiv + 1) * dim, &refCoords, (edgeDiv + 1) * dim, &edgeCoords));
1882   PetscCall(DMGetCoordinateDM(dm, &cdm));
1883   PetscCall(DMGetLocalSection(cdm, &coordSection));
1884   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1885   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1886   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1887 
1888   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
1889   PetscCall(PetscDrawIsNull(draw, &isnull));
1890   if (isnull) PetscFunctionReturn(PETSC_SUCCESS);
1891   PetscCall(PetscDrawSetTitle(draw, "Mesh"));
1892 
1893   PetscCall(DMGetBoundingBox(dm, xyl, xyr));
1894   PetscCall(PetscDrawSetCoordinates(draw, xyl[0], xyl[1], xyr[0], xyr[1]));
1895   PetscCall(PetscDrawClear(draw));
1896 
1897   for (c = cStart; c < cEnd; ++c) {
1898     PetscScalar       *coords = NULL;
1899     const PetscScalar *coords_arr;
1900     PetscInt           numCoords;
1901     PetscBool          isDG;
1902 
1903     PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
1904     if (drawAffine) PetscCall(DMPlexDrawCell(dm, draw, c, coords));
1905     else PetscCall(DMPlexDrawCellHighOrder(dm, draw, c, coords, edgeDiv, refCoords, edgeCoords));
1906     PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
1907   }
1908   if (!drawAffine) PetscCall(PetscFree2(refCoords, edgeCoords));
1909   PetscCall(PetscDrawFlush(draw));
1910   PetscCall(PetscDrawPause(draw));
1911   PetscCall(PetscDrawSave(draw));
1912   PetscFunctionReturn(PETSC_SUCCESS);
1913 }
1914 
1915 static PetscErrorCode DMPlexCreateHighOrderSurrogate_Internal(DM dm, DM *hdm)
1916 {
1917   DM           odm = dm, rdm = dm, cdm;
1918   PetscFE      fe;
1919   PetscSpace   sp;
1920   PetscClassId id;
1921   PetscInt     degree;
1922   PetscBool    hoView = PETSC_TRUE;
1923 
1924   PetscFunctionBegin;
1925   PetscObjectOptionsBegin((PetscObject)dm);
1926   PetscCall(PetscOptionsBool("-dm_plex_high_order_view", "Subsample to view meshes with high order coordinates", "DMPlexCreateHighOrderSurrogate_Internal", hoView, &hoView, NULL));
1927   PetscOptionsEnd();
1928   PetscCall(PetscObjectReference((PetscObject)dm));
1929   *hdm = dm;
1930   if (!hoView) PetscFunctionReturn(PETSC_SUCCESS);
1931   PetscCall(DMGetCoordinateDM(dm, &cdm));
1932   PetscCall(DMGetField(cdm, 0, NULL, (PetscObject *)&fe));
1933   PetscCall(PetscObjectGetClassId((PetscObject)fe, &id));
1934   if (id != PETSCFE_CLASSID) PetscFunctionReturn(PETSC_SUCCESS);
1935   PetscCall(PetscFEGetBasisSpace(fe, &sp));
1936   PetscCall(PetscSpaceGetDegree(sp, &degree, NULL));
1937   for (PetscInt r = 0, rd = PetscCeilReal(((PetscReal)degree) / 2.); r < (PetscInt)PetscCeilReal(PetscLog2Real(degree)); ++r, rd = PetscCeilReal(((PetscReal)rd) / 2.)) {
1938     DM  cdm, rcdm;
1939     Mat In;
1940     Vec cl, rcl;
1941 
1942     PetscCall(DMRefine(odm, PetscObjectComm((PetscObject)odm), &rdm));
1943     PetscCall(DMPlexCreateCoordinateSpace(rdm, rd, PETSC_FALSE, NULL));
1944     PetscCall(PetscObjectSetName((PetscObject)rdm, "Refined Mesh with Linear Coordinates"));
1945     PetscCall(DMGetCoordinateDM(odm, &cdm));
1946     PetscCall(DMGetCoordinateDM(rdm, &rcdm));
1947     PetscCall(DMGetCoordinatesLocal(odm, &cl));
1948     PetscCall(DMGetCoordinatesLocal(rdm, &rcl));
1949     PetscCall(DMSetCoarseDM(rcdm, cdm));
1950     PetscCall(DMCreateInterpolation(cdm, rcdm, &In, NULL));
1951     PetscCall(MatMult(In, cl, rcl));
1952     PetscCall(MatDestroy(&In));
1953     PetscCall(DMSetCoordinatesLocal(rdm, rcl));
1954     PetscCall(DMDestroy(&odm));
1955     odm = rdm;
1956   }
1957   *hdm = rdm;
1958   PetscFunctionReturn(PETSC_SUCCESS);
1959 }
1960 
1961 #if defined(PETSC_HAVE_EXODUSII)
1962   #include <exodusII.h>
1963   #include <petscviewerexodusii.h>
1964 #endif
1965 
1966 PetscErrorCode DMView_Plex(DM dm, PetscViewer viewer)
1967 {
1968   PetscBool iascii, ishdf5, isvtk, isdraw, flg, isglvis, isexodus, iscgns;
1969   char      name[PETSC_MAX_PATH_LEN];
1970 
1971   PetscFunctionBegin;
1972   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1973   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1974   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &iascii));
1975   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
1976   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1977   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
1978   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
1979   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodus));
1980   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
1981   if (iascii) {
1982     PetscViewerFormat format;
1983     PetscCall(PetscViewerGetFormat(viewer, &format));
1984     if (format == PETSC_VIEWER_ASCII_GLVIS) PetscCall(DMPlexView_GLVis(dm, viewer));
1985     else PetscCall(DMPlexView_Ascii(dm, viewer));
1986   } else if (ishdf5) {
1987 #if defined(PETSC_HAVE_HDF5)
1988     PetscCall(DMPlexView_HDF5_Internal(dm, viewer));
1989 #else
1990     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1991 #endif
1992   } else if (isvtk) {
1993     PetscCall(DMPlexVTKWriteAll((PetscObject)dm, viewer));
1994   } else if (isdraw) {
1995     DM hdm;
1996 
1997     PetscCall(DMPlexCreateHighOrderSurrogate_Internal(dm, &hdm));
1998     PetscCall(DMPlexView_Draw(hdm, viewer));
1999     PetscCall(DMDestroy(&hdm));
2000   } else if (isglvis) {
2001     PetscCall(DMPlexView_GLVis(dm, viewer));
2002 #if defined(PETSC_HAVE_EXODUSII)
2003   } else if (isexodus) {
2004     /*
2005       exodusII requires that all sets be part of exactly one cell set.
2006       If the dm does not have a "Cell Sets" label defined, we create one
2007       with ID 1, containing all cells.
2008       Note that if the Cell Sets label is defined but does not cover all cells,
2009       we may still have a problem. This should probably be checked here or in the viewer;
2010     */
2011     PetscInt numCS;
2012     PetscCall(DMGetLabelSize(dm, "Cell Sets", &numCS));
2013     if (!numCS) {
2014       PetscInt cStart, cEnd, c;
2015       PetscCall(DMCreateLabel(dm, "Cell Sets"));
2016       PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
2017       for (c = cStart; c < cEnd; ++c) PetscCall(DMSetLabelValue(dm, "Cell Sets", c, 1));
2018     }
2019     PetscCall(DMView_PlexExodusII(dm, viewer));
2020 #endif
2021 #if defined(PETSC_HAVE_CGNS)
2022   } else if (iscgns) {
2023     PetscCall(DMView_PlexCGNS(dm, viewer));
2024 #endif
2025   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex writing", ((PetscObject)viewer)->type_name);
2026   /* Optionally view the partition */
2027   PetscCall(PetscOptionsHasName(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_partition_view", &flg));
2028   if (flg) {
2029     Vec ranks;
2030     PetscCall(DMPlexCreateRankField(dm, &ranks));
2031     PetscCall(VecView(ranks, viewer));
2032     PetscCall(VecDestroy(&ranks));
2033   }
2034   /* Optionally view a label */
2035   PetscCall(PetscOptionsGetString(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_label_view", name, sizeof(name), &flg));
2036   if (flg) {
2037     DMLabel label;
2038     Vec     val;
2039 
2040     PetscCall(DMGetLabel(dm, name, &label));
2041     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Label %s provided to -dm_label_view does not exist in this DM", name);
2042     PetscCall(DMPlexCreateLabelField(dm, label, &val));
2043     PetscCall(VecView(val, viewer));
2044     PetscCall(VecDestroy(&val));
2045   }
2046   PetscFunctionReturn(PETSC_SUCCESS);
2047 }
2048 
2049 /*@
2050   DMPlexTopologyView - Saves a `DMPLEX` topology into a file
2051 
2052   Collective
2053 
2054   Input Parameters:
2055 + dm     - The `DM` whose topology is to be saved
2056 - viewer - The `PetscViewer` to save it in
2057 
2058   Level: advanced
2059 
2060 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexTopologyLoad()`, `PetscViewer`
2061 @*/
2062 PetscErrorCode DMPlexTopologyView(DM dm, PetscViewer viewer)
2063 {
2064   PetscBool ishdf5;
2065 
2066   PetscFunctionBegin;
2067   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2068   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2069   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2070   PetscCall(PetscLogEventBegin(DMPLEX_TopologyView, viewer, 0, 0, 0));
2071   if (ishdf5) {
2072 #if defined(PETSC_HAVE_HDF5)
2073     PetscViewerFormat format;
2074     PetscCall(PetscViewerGetFormat(viewer, &format));
2075     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2076       IS globalPointNumbering;
2077 
2078       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
2079       PetscCall(DMPlexTopologyView_HDF5_Internal(dm, globalPointNumbering, viewer));
2080       PetscCall(ISDestroy(&globalPointNumbering));
2081     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
2082 #else
2083     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2084 #endif
2085   }
2086   PetscCall(PetscLogEventEnd(DMPLEX_TopologyView, viewer, 0, 0, 0));
2087   PetscFunctionReturn(PETSC_SUCCESS);
2088 }
2089 
2090 /*@
2091   DMPlexCoordinatesView - Saves `DMPLEX` coordinates into a file
2092 
2093   Collective
2094 
2095   Input Parameters:
2096 + dm     - The `DM` whose coordinates are to be saved
2097 - viewer - The `PetscViewer` for saving
2098 
2099   Level: advanced
2100 
2101 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexLabelsView()`, `DMPlexCoordinatesLoad()`, `PetscViewer`
2102 @*/
2103 PetscErrorCode DMPlexCoordinatesView(DM dm, PetscViewer viewer)
2104 {
2105   PetscBool ishdf5;
2106 
2107   PetscFunctionBegin;
2108   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2109   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2110   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2111   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
2112   if (ishdf5) {
2113 #if defined(PETSC_HAVE_HDF5)
2114     PetscViewerFormat format;
2115     PetscCall(PetscViewerGetFormat(viewer, &format));
2116     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2117       PetscCall(DMPlexCoordinatesView_HDF5_Internal(dm, viewer));
2118     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
2119 #else
2120     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2121 #endif
2122   }
2123   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
2124   PetscFunctionReturn(PETSC_SUCCESS);
2125 }
2126 
2127 /*@
2128   DMPlexLabelsView - Saves `DMPLEX` labels into a file
2129 
2130   Collective
2131 
2132   Input Parameters:
2133 + dm     - The `DM` whose labels are to be saved
2134 - viewer - The `PetscViewer` for saving
2135 
2136   Level: advanced
2137 
2138 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsLoad()`, `PetscViewer`
2139 @*/
2140 PetscErrorCode DMPlexLabelsView(DM dm, PetscViewer viewer)
2141 {
2142   PetscBool ishdf5;
2143 
2144   PetscFunctionBegin;
2145   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2146   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2147   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2148   PetscCall(PetscLogEventBegin(DMPLEX_LabelsView, viewer, 0, 0, 0));
2149   if (ishdf5) {
2150 #if defined(PETSC_HAVE_HDF5)
2151     IS                globalPointNumbering;
2152     PetscViewerFormat format;
2153 
2154     PetscCall(PetscViewerGetFormat(viewer, &format));
2155     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2156       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
2157       PetscCall(DMPlexLabelsView_HDF5_Internal(dm, globalPointNumbering, viewer));
2158       PetscCall(ISDestroy(&globalPointNumbering));
2159     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2160 #else
2161     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2162 #endif
2163   }
2164   PetscCall(PetscLogEventEnd(DMPLEX_LabelsView, viewer, 0, 0, 0));
2165   PetscFunctionReturn(PETSC_SUCCESS);
2166 }
2167 
2168 /*@
2169   DMPlexSectionView - Saves a section associated with a `DMPLEX`
2170 
2171   Collective
2172 
2173   Input Parameters:
2174 + dm        - The `DM` that contains the topology on which the section to be saved is defined
2175 . viewer    - The `PetscViewer` for saving
2176 - sectiondm - The `DM` that contains the section to be saved, can be `NULL`
2177 
2178   Level: advanced
2179 
2180   Notes:
2181   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.
2182 
2183   In general `dm` and `sectiondm` are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object (or in case `sectiondm` is `NULL`) if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2184 
2185 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`, `PetscSectionView()`, `DMPlexSectionLoad()`, `PetscViewer`
2186 @*/
2187 PetscErrorCode DMPlexSectionView(DM dm, PetscViewer viewer, DM sectiondm)
2188 {
2189   PetscBool ishdf5;
2190 
2191   PetscFunctionBegin;
2192   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2193   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2194   if (!sectiondm) sectiondm = dm;
2195   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2196   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2197   PetscCall(PetscLogEventBegin(DMPLEX_SectionView, viewer, 0, 0, 0));
2198   if (ishdf5) {
2199 #if defined(PETSC_HAVE_HDF5)
2200     PetscCall(DMPlexSectionView_HDF5_Internal(dm, viewer, sectiondm));
2201 #else
2202     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2203 #endif
2204   }
2205   PetscCall(PetscLogEventEnd(DMPLEX_SectionView, viewer, 0, 0, 0));
2206   PetscFunctionReturn(PETSC_SUCCESS);
2207 }
2208 
2209 /*@
2210   DMPlexGlobalVectorView - Saves a global vector
2211 
2212   Collective
2213 
2214   Input Parameters:
2215 + dm        - The `DM` that represents the topology
2216 . viewer    - The `PetscViewer` to save data with
2217 . sectiondm - The `DM` that contains the global section on which vec is defined, can be `NULL`
2218 - vec       - The global vector to be saved
2219 
2220   Level: advanced
2221 
2222   Notes:
2223   In general `dm` and `sectiondm` are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object (or in case `sectiondm` is `NULL`) if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2224 
2225   Calling sequence:
2226 .vb
2227        DMCreate(PETSC_COMM_WORLD, &dm);
2228        DMSetType(dm, DMPLEX);
2229        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2230        DMClone(dm, &sectiondm);
2231        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2232        PetscSectionCreate(PETSC_COMM_WORLD, &section);
2233        DMPlexGetChart(sectiondm, &pStart, &pEnd);
2234        PetscSectionSetChart(section, pStart, pEnd);
2235        PetscSectionSetUp(section);
2236        DMSetLocalSection(sectiondm, section);
2237        PetscSectionDestroy(&section);
2238        DMGetGlobalVector(sectiondm, &vec);
2239        PetscObjectSetName((PetscObject)vec, "vec_name");
2240        DMPlexTopologyView(dm, viewer);
2241        DMPlexSectionView(dm, viewer, sectiondm);
2242        DMPlexGlobalVectorView(dm, viewer, sectiondm, vec);
2243        DMRestoreGlobalVector(sectiondm, &vec);
2244        DMDestroy(&sectiondm);
2245        DMDestroy(&dm);
2246 .ve
2247 
2248 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexLocalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2249 @*/
2250 PetscErrorCode DMPlexGlobalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2251 {
2252   PetscBool ishdf5;
2253 
2254   PetscFunctionBegin;
2255   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2256   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2257   if (!sectiondm) sectiondm = dm;
2258   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2259   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2260   /* Check consistency */
2261   {
2262     PetscSection section;
2263     PetscBool    includesConstraints;
2264     PetscInt     m, m1;
2265 
2266     PetscCall(VecGetLocalSize(vec, &m1));
2267     PetscCall(DMGetGlobalSection(sectiondm, &section));
2268     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2269     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2270     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2271     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2272   }
2273   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2274   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorView, viewer, 0, 0, 0));
2275   if (ishdf5) {
2276 #if defined(PETSC_HAVE_HDF5)
2277     PetscCall(DMPlexGlobalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2278 #else
2279     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2280 #endif
2281   }
2282   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorView, viewer, 0, 0, 0));
2283   PetscFunctionReturn(PETSC_SUCCESS);
2284 }
2285 
2286 /*@
2287   DMPlexLocalVectorView - Saves a local vector
2288 
2289   Collective
2290 
2291   Input Parameters:
2292 + dm        - The `DM` that represents the topology
2293 . viewer    - The `PetscViewer` to save data with
2294 . sectiondm - The `DM` that contains the local section on which `vec` is defined, can be `NULL`
2295 - vec       - The local vector to be saved
2296 
2297   Level: advanced
2298 
2299   Note:
2300   In general `dm` and `sectiondm` are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object (or in case `sectiondm` is `NULL`) if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2301 
2302   Calling sequence:
2303 .vb
2304        DMCreate(PETSC_COMM_WORLD, &dm);
2305        DMSetType(dm, DMPLEX);
2306        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2307        DMClone(dm, &sectiondm);
2308        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2309        PetscSectionCreate(PETSC_COMM_WORLD, &section);
2310        DMPlexGetChart(sectiondm, &pStart, &pEnd);
2311        PetscSectionSetChart(section, pStart, pEnd);
2312        PetscSectionSetUp(section);
2313        DMSetLocalSection(sectiondm, section);
2314        DMGetLocalVector(sectiondm, &vec);
2315        PetscObjectSetName((PetscObject)vec, "vec_name");
2316        DMPlexTopologyView(dm, viewer);
2317        DMPlexSectionView(dm, viewer, sectiondm);
2318        DMPlexLocalVectorView(dm, viewer, sectiondm, vec);
2319        DMRestoreLocalVector(sectiondm, &vec);
2320        DMDestroy(&sectiondm);
2321        DMDestroy(&dm);
2322 .ve
2323 
2324 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexGlobalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2325 @*/
2326 PetscErrorCode DMPlexLocalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2327 {
2328   PetscBool ishdf5;
2329 
2330   PetscFunctionBegin;
2331   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2332   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2333   if (!sectiondm) sectiondm = dm;
2334   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2335   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2336   /* Check consistency */
2337   {
2338     PetscSection section;
2339     PetscBool    includesConstraints;
2340     PetscInt     m, m1;
2341 
2342     PetscCall(VecGetLocalSize(vec, &m1));
2343     PetscCall(DMGetLocalSection(sectiondm, &section));
2344     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2345     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2346     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2347     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2348   }
2349   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2350   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorView, viewer, 0, 0, 0));
2351   if (ishdf5) {
2352 #if defined(PETSC_HAVE_HDF5)
2353     PetscCall(DMPlexLocalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2354 #else
2355     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2356 #endif
2357   }
2358   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorView, viewer, 0, 0, 0));
2359   PetscFunctionReturn(PETSC_SUCCESS);
2360 }
2361 
2362 PetscErrorCode DMLoad_Plex(DM dm, PetscViewer viewer)
2363 {
2364   PetscBool ishdf5;
2365 
2366   PetscFunctionBegin;
2367   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2368   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2369   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2370   if (ishdf5) {
2371 #if defined(PETSC_HAVE_HDF5)
2372     PetscViewerFormat format;
2373     PetscCall(PetscViewerGetFormat(viewer, &format));
2374     if (format == PETSC_VIEWER_HDF5_XDMF || format == PETSC_VIEWER_HDF5_VIZ) {
2375       PetscCall(DMPlexLoad_HDF5_Xdmf_Internal(dm, viewer));
2376     } else if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2377       PetscCall(DMPlexLoad_HDF5_Internal(dm, viewer));
2378     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2379     PetscFunctionReturn(PETSC_SUCCESS);
2380 #else
2381     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2382 #endif
2383   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex loading", ((PetscObject)viewer)->type_name);
2384 }
2385 
2386 /*@
2387   DMPlexTopologyLoad - Loads a topology into a `DMPLEX`
2388 
2389   Collective
2390 
2391   Input Parameters:
2392 + dm     - The `DM` into which the topology is loaded
2393 - viewer - The `PetscViewer` for the saved topology
2394 
2395   Output Parameter:
2396 . 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;
2397   `NULL` if unneeded
2398 
2399   Level: advanced
2400 
2401 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2402           `PetscViewer`, `PetscSF`
2403 @*/
2404 PetscErrorCode DMPlexTopologyLoad(DM dm, PetscViewer viewer, PetscSF *globalToLocalPointSF)
2405 {
2406   PetscBool ishdf5;
2407 
2408   PetscFunctionBegin;
2409   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2410   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2411   if (globalToLocalPointSF) PetscAssertPointer(globalToLocalPointSF, 3);
2412   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2413   PetscCall(PetscLogEventBegin(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2414   if (ishdf5) {
2415 #if defined(PETSC_HAVE_HDF5)
2416     PetscViewerFormat format;
2417     PetscCall(PetscViewerGetFormat(viewer, &format));
2418     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2419       PetscCall(DMPlexTopologyLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2420     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2421 #else
2422     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2423 #endif
2424   }
2425   PetscCall(PetscLogEventEnd(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2426   PetscFunctionReturn(PETSC_SUCCESS);
2427 }
2428 
2429 /*@
2430   DMPlexCoordinatesLoad - Loads coordinates into a `DMPLEX`
2431 
2432   Collective
2433 
2434   Input Parameters:
2435 + dm                   - The `DM` into which the coordinates are loaded
2436 . viewer               - The `PetscViewer` for the saved coordinates
2437 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad()` when loading dm from viewer
2438 
2439   Level: advanced
2440 
2441 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2442           `PetscSF`, `PetscViewer`
2443 @*/
2444 PetscErrorCode DMPlexCoordinatesLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2445 {
2446   PetscBool ishdf5;
2447 
2448   PetscFunctionBegin;
2449   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2450   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2451   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2452   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2453   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesLoad, viewer, 0, 0, 0));
2454   if (ishdf5) {
2455 #if defined(PETSC_HAVE_HDF5)
2456     PetscViewerFormat format;
2457     PetscCall(PetscViewerGetFormat(viewer, &format));
2458     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2459       PetscCall(DMPlexCoordinatesLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2460     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2461 #else
2462     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2463 #endif
2464   }
2465   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesLoad, viewer, 0, 0, 0));
2466   PetscFunctionReturn(PETSC_SUCCESS);
2467 }
2468 
2469 /*@
2470   DMPlexLabelsLoad - Loads labels into a `DMPLEX`
2471 
2472   Collective
2473 
2474   Input Parameters:
2475 + dm                   - The `DM` into which the labels are loaded
2476 . viewer               - The `PetscViewer` for the saved labels
2477 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad()` when loading `dm` from viewer
2478 
2479   Level: advanced
2480 
2481   Note:
2482   The `PetscSF` argument must not be `NULL` if the `DM` is distributed, otherwise an error occurs.
2483 
2484 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2485           `PetscSF`, `PetscViewer`
2486 @*/
2487 PetscErrorCode DMPlexLabelsLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2488 {
2489   PetscBool ishdf5;
2490 
2491   PetscFunctionBegin;
2492   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2493   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2494   if (globalToLocalPointSF) PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2495   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2496   PetscCall(PetscLogEventBegin(DMPLEX_LabelsLoad, viewer, 0, 0, 0));
2497   if (ishdf5) {
2498 #if defined(PETSC_HAVE_HDF5)
2499     PetscViewerFormat format;
2500 
2501     PetscCall(PetscViewerGetFormat(viewer, &format));
2502     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2503       PetscCall(DMPlexLabelsLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2504     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2505 #else
2506     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2507 #endif
2508   }
2509   PetscCall(PetscLogEventEnd(DMPLEX_LabelsLoad, viewer, 0, 0, 0));
2510   PetscFunctionReturn(PETSC_SUCCESS);
2511 }
2512 
2513 /*@
2514   DMPlexSectionLoad - Loads section into a `DMPLEX`
2515 
2516   Collective
2517 
2518   Input Parameters:
2519 + dm                   - The `DM` that represents the topology
2520 . viewer               - The `PetscViewer` that represents the on-disk section (sectionA)
2521 . sectiondm            - The `DM` into which the on-disk section (sectionA) is migrated, can be `NULL`
2522 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad(`) when loading dm from viewer
2523 
2524   Output Parameters:
2525 + 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)
2526 - 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)
2527 
2528   Level: advanced
2529 
2530   Notes:
2531   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.
2532 
2533   In general `dm` and `sectiondm` are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object (or in case `sectiondm` is `NULL`) if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2534 
2535   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.
2536 
2537   Example using 2 processes:
2538 .vb
2539   NX (number of points on dm): 4
2540   sectionA                   : the on-disk section
2541   vecA                       : a vector associated with sectionA
2542   sectionB                   : sectiondm's local section constructed in this function
2543   vecB (local)               : a vector associated with sectiondm's local section
2544   vecB (global)              : a vector associated with sectiondm's global section
2545 
2546                                      rank 0    rank 1
2547   vecA (global)                  : [.0 .4 .1 | .2 .3]        <- to be loaded in DMPlexGlobalVectorLoad() or DMPlexLocalVectorLoad()
2548   sectionA->atlasOff             :       0 2 | 1             <- loaded in PetscSectionLoad()
2549   sectionA->atlasDof             :       1 3 | 1             <- loaded in PetscSectionLoad()
2550   sectionA's global point numbers:       0 2 | 3             <- loaded in DMPlexSectionLoad()
2551   [0, NX)                        :       0 1 | 2 3           <- conceptual partition used in globalToLocalPointSF
2552   sectionB's global point numbers:     0 1 3 | 3 2           <- associated with [0, NX) by globalToLocalPointSF
2553   sectionB->atlasDof             :     1 0 1 | 1 3
2554   sectionB->atlasOff (no perm)   :     0 1 1 | 0 1
2555   vecB (local)                   :   [.0 .4] | [.4 .1 .2 .3] <- to be constructed by calling DMPlexLocalVectorLoad() with localDofSF
2556   vecB (global)                  :    [.0 .4 | .1 .2 .3]     <- to be constructed by calling DMPlexGlobalVectorLoad() with globalDofSF
2557 .ve
2558   where "|" represents a partition of loaded data, and global point 3 is assumed to be owned by rank 0.
2559 
2560 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`, `PetscSectionLoad()`, `DMPlexSectionView()`, `PetscSF`, `PetscViewer`
2561 @*/
2562 PetscErrorCode DMPlexSectionLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF globalToLocalPointSF, PetscSF *globalDofSF, PetscSF *localDofSF)
2563 {
2564   PetscBool ishdf5;
2565 
2566   PetscFunctionBegin;
2567   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2568   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2569   if (!sectiondm) sectiondm = dm;
2570   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2571   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 4);
2572   if (globalDofSF) PetscAssertPointer(globalDofSF, 5);
2573   if (localDofSF) PetscAssertPointer(localDofSF, 6);
2574   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2575   PetscCall(PetscLogEventBegin(DMPLEX_SectionLoad, viewer, 0, 0, 0));
2576   if (ishdf5) {
2577 #if defined(PETSC_HAVE_HDF5)
2578     PetscCall(DMPlexSectionLoad_HDF5_Internal(dm, viewer, sectiondm, globalToLocalPointSF, globalDofSF, localDofSF));
2579 #else
2580     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2581 #endif
2582   }
2583   PetscCall(PetscLogEventEnd(DMPLEX_SectionLoad, viewer, 0, 0, 0));
2584   PetscFunctionReturn(PETSC_SUCCESS);
2585 }
2586 
2587 /*@
2588   DMPlexGlobalVectorLoad - Loads on-disk vector data into a global vector
2589 
2590   Collective
2591 
2592   Input Parameters:
2593 + dm        - The `DM` that represents the topology
2594 . viewer    - The `PetscViewer` that represents the on-disk vector data
2595 . sectiondm - The `DM` that contains the global section on which vec is defined, can be `NULL`
2596 . sf        - The `PetscSF` that migrates the on-disk vector data into vec
2597 - vec       - The global vector to set values of
2598 
2599   Level: advanced
2600 
2601   Notes:
2602   In general dm and sectiondm are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object (or in case `sectiondm` is `NULL`) if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2603 
2604   Calling sequence:
2605 .vb
2606        DMCreate(PETSC_COMM_WORLD, &dm);
2607        DMSetType(dm, DMPLEX);
2608        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2609        DMPlexTopologyLoad(dm, viewer, &sfX);
2610        DMClone(dm, &sectiondm);
2611        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2612        DMPlexSectionLoad(dm, viewer, sectiondm, sfX, &gsf, NULL);
2613        DMGetGlobalVector(sectiondm, &vec);
2614        PetscObjectSetName((PetscObject)vec, "vec_name");
2615        DMPlexGlobalVectorLoad(dm, viewer, sectiondm, gsf, vec);
2616        DMRestoreGlobalVector(sectiondm, &vec);
2617        PetscSFDestroy(&gsf);
2618        PetscSFDestroy(&sfX);
2619        DMDestroy(&sectiondm);
2620        DMDestroy(&dm);
2621 .ve
2622 
2623 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexLocalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`,
2624           `PetscSF`, `PetscViewer`
2625 @*/
2626 PetscErrorCode DMPlexGlobalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2627 {
2628   PetscBool ishdf5;
2629 
2630   PetscFunctionBegin;
2631   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2632   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2633   if (!sectiondm) sectiondm = dm;
2634   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2635   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2636   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2637   /* Check consistency */
2638   {
2639     PetscSection section;
2640     PetscBool    includesConstraints;
2641     PetscInt     m, m1;
2642 
2643     PetscCall(VecGetLocalSize(vec, &m1));
2644     PetscCall(DMGetGlobalSection(sectiondm, &section));
2645     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2646     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2647     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2648     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2649   }
2650   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2651   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorLoad, viewer, 0, 0, 0));
2652   if (ishdf5) {
2653 #if defined(PETSC_HAVE_HDF5)
2654     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2655 #else
2656     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2657 #endif
2658   }
2659   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorLoad, viewer, 0, 0, 0));
2660   PetscFunctionReturn(PETSC_SUCCESS);
2661 }
2662 
2663 /*@
2664   DMPlexLocalVectorLoad - Loads on-disk vector data into a local vector
2665 
2666   Collective
2667 
2668   Input Parameters:
2669 + dm        - The `DM` that represents the topology
2670 . viewer    - The `PetscViewer` that represents the on-disk vector data
2671 . sectiondm - The `DM` that contains the local section on which vec is defined, can be `NULL`
2672 . sf        - The `PetscSF` that migrates the on-disk vector data into vec
2673 - vec       - The local vector to set values of
2674 
2675   Level: advanced
2676 
2677   Notes:
2678   In general `dm` and `sectiondm` are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object (or in case `sectiondm` is `NULL`) if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2679 
2680   Calling sequence:
2681 .vb
2682        DMCreate(PETSC_COMM_WORLD, &dm);
2683        DMSetType(dm, DMPLEX);
2684        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2685        DMPlexTopologyLoad(dm, viewer, &sfX);
2686        DMClone(dm, &sectiondm);
2687        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2688        DMPlexSectionLoad(dm, viewer, sectiondm, sfX, NULL, &lsf);
2689        DMGetLocalVector(sectiondm, &vec);
2690        PetscObjectSetName((PetscObject)vec, "vec_name");
2691        DMPlexLocalVectorLoad(dm, viewer, sectiondm, lsf, vec);
2692        DMRestoreLocalVector(sectiondm, &vec);
2693        PetscSFDestroy(&lsf);
2694        PetscSFDestroy(&sfX);
2695        DMDestroy(&sectiondm);
2696        DMDestroy(&dm);
2697 .ve
2698 
2699 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`,
2700           `PetscSF`, `PetscViewer`
2701 @*/
2702 PetscErrorCode DMPlexLocalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2703 {
2704   PetscBool ishdf5;
2705 
2706   PetscFunctionBegin;
2707   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2708   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2709   if (!sectiondm) sectiondm = dm;
2710   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2711   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2712   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2713   /* Check consistency */
2714   {
2715     PetscSection section;
2716     PetscBool    includesConstraints;
2717     PetscInt     m, m1;
2718 
2719     PetscCall(VecGetLocalSize(vec, &m1));
2720     PetscCall(DMGetLocalSection(sectiondm, &section));
2721     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2722     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2723     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2724     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2725   }
2726   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2727   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorLoad, viewer, 0, 0, 0));
2728   if (ishdf5) {
2729 #if defined(PETSC_HAVE_HDF5)
2730     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2731 #else
2732     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2733 #endif
2734   }
2735   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorLoad, viewer, 0, 0, 0));
2736   PetscFunctionReturn(PETSC_SUCCESS);
2737 }
2738 
2739 PetscErrorCode DMDestroy_Plex(DM dm)
2740 {
2741   DM_Plex *mesh = (DM_Plex *)dm->data;
2742 
2743   PetscFunctionBegin;
2744   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMSetUpGLVisViewer_C", NULL));
2745   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertBoundaryValues_C", NULL));
2746   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMCreateNeumannOverlap_C", NULL));
2747   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMInterpolateSolution_C", NULL));
2748   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertTimeDerivativeBoundaryValues_C", NULL));
2749   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", NULL));
2750   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeGetDefault_C", NULL));
2751   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeSetDefault_C", NULL));
2752   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "MatComputeNeumannOverlap_C", NULL));
2753   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderGetDefault_C", NULL));
2754   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderSetDefault_C", NULL));
2755   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionGetDefault_C", NULL));
2756   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionSetDefault_C", NULL));
2757   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionGetType_C", NULL));
2758   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionSetType_C", NULL));
2759   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", NULL));
2760   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexSetOverlap_C", NULL));
2761   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetUseCeed_C", NULL));
2762   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexSetUseCeed_C", NULL));
2763   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMGetIsoperiodicPointSF_C", NULL));
2764   if (--mesh->refct > 0) PetscFunctionReturn(PETSC_SUCCESS);
2765   PetscCall(PetscSectionDestroy(&mesh->coneSection));
2766   PetscCall(PetscFree(mesh->cones));
2767   PetscCall(PetscFree(mesh->coneOrientations));
2768   PetscCall(PetscSectionDestroy(&mesh->supportSection));
2769   PetscCall(PetscSectionDestroy(&mesh->subdomainSection));
2770   PetscCall(PetscFree(mesh->supports));
2771   PetscCall(PetscFree(mesh->cellTypes));
2772   PetscCall(DMPlexTransformDestroy(&mesh->tr));
2773   PetscCall(PetscFree(mesh->tetgenOpts));
2774   PetscCall(PetscFree(mesh->triangleOpts));
2775   PetscCall(PetscFree(mesh->transformType));
2776   PetscCall(PetscFree(mesh->distributionName));
2777   PetscCall(PetscPartitionerDestroy(&mesh->partitioner));
2778   PetscCall(DMLabelDestroy(&mesh->subpointMap));
2779   PetscCall(ISDestroy(&mesh->subpointIS));
2780   PetscCall(ISDestroy(&mesh->globalVertexNumbers));
2781   PetscCall(ISDestroy(&mesh->globalCellNumbers));
2782   if (mesh->periodic.face_sfs) {
2783     for (PetscInt i = 0; i < mesh->periodic.num_face_sfs; i++) PetscCall(PetscSFDestroy(&mesh->periodic.face_sfs[i]));
2784     PetscCall(PetscFree(mesh->periodic.face_sfs));
2785   }
2786   PetscCall(PetscSFDestroy(&mesh->periodic.composed_sf));
2787   if (mesh->periodic.periodic_points) {
2788     for (PetscInt i = 0; i < mesh->periodic.num_face_sfs; i++) PetscCall(ISDestroy(&mesh->periodic.periodic_points[i]));
2789     PetscCall(PetscFree(mesh->periodic.periodic_points));
2790   }
2791   if (mesh->periodic.transform) PetscCall(PetscFree(mesh->periodic.transform));
2792   PetscCall(PetscSectionDestroy(&mesh->anchorSection));
2793   PetscCall(ISDestroy(&mesh->anchorIS));
2794   PetscCall(PetscSectionDestroy(&mesh->parentSection));
2795   PetscCall(PetscFree(mesh->parents));
2796   PetscCall(PetscFree(mesh->childIDs));
2797   PetscCall(PetscSectionDestroy(&mesh->childSection));
2798   PetscCall(PetscFree(mesh->children));
2799   PetscCall(DMDestroy(&mesh->referenceTree));
2800   PetscCall(PetscGridHashDestroy(&mesh->lbox));
2801   PetscCall(PetscFree(mesh->neighbors));
2802   if (mesh->metricCtx) PetscCall(PetscFree(mesh->metricCtx));
2803   if (mesh->nonempty_comm != MPI_COMM_NULL && mesh->nonempty_comm != MPI_COMM_SELF) PetscCallMPI(MPI_Comm_free(&mesh->nonempty_comm));
2804   /* This was originally freed in DMDestroy(), but that prevents reference counting of backend objects */
2805   PetscCall(PetscFree(mesh));
2806   PetscFunctionReturn(PETSC_SUCCESS);
2807 }
2808 
2809 PetscErrorCode DMCreateMatrix_Plex(DM dm, Mat *J)
2810 {
2811   PetscSection           sectionGlobal, sectionLocal;
2812   PetscInt               bs = -1, mbs;
2813   PetscInt               localSize, localStart = 0;
2814   PetscBool              isShell, isBlock, isSeqBlock, isMPIBlock, isSymBlock, isSymSeqBlock, isSymMPIBlock, isMatIS;
2815   MatType                mtype;
2816   ISLocalToGlobalMapping ltog;
2817 
2818   PetscFunctionBegin;
2819   PetscCall(MatInitializePackage());
2820   mtype = dm->mattype;
2821   PetscCall(DMGetLocalSection(dm, &sectionLocal));
2822   PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
2823   /* PetscCall(PetscSectionGetStorageSize(sectionGlobal, &localSize)); */
2824   PetscCall(PetscSectionGetConstrainedStorageSize(sectionGlobal, &localSize));
2825   PetscCallMPI(MPI_Exscan(&localSize, &localStart, 1, MPIU_INT, MPI_SUM, PetscObjectComm((PetscObject)dm)));
2826   PetscCall(MatCreate(PetscObjectComm((PetscObject)dm), J));
2827   PetscCall(MatSetSizes(*J, localSize, localSize, PETSC_DETERMINE, PETSC_DETERMINE));
2828   PetscCall(MatSetType(*J, mtype));
2829   PetscCall(MatSetFromOptions(*J));
2830   PetscCall(MatGetBlockSize(*J, &mbs));
2831   if (mbs > 1) bs = mbs;
2832   PetscCall(PetscStrcmp(mtype, MATSHELL, &isShell));
2833   PetscCall(PetscStrcmp(mtype, MATBAIJ, &isBlock));
2834   PetscCall(PetscStrcmp(mtype, MATSEQBAIJ, &isSeqBlock));
2835   PetscCall(PetscStrcmp(mtype, MATMPIBAIJ, &isMPIBlock));
2836   PetscCall(PetscStrcmp(mtype, MATSBAIJ, &isSymBlock));
2837   PetscCall(PetscStrcmp(mtype, MATSEQSBAIJ, &isSymSeqBlock));
2838   PetscCall(PetscStrcmp(mtype, MATMPISBAIJ, &isSymMPIBlock));
2839   PetscCall(PetscStrcmp(mtype, MATIS, &isMatIS));
2840   if (!isShell) {
2841     // There are three states with pblocks, since block starts can have no dofs:
2842     // UNKNOWN) New Block:   An open block has been signalled by pblocks[p] == 1
2843     // TRUE)    Block Start: The first entry in a block has been added
2844     // FALSE)   Block Add:   An additional block entry has been added, since pblocks[p] == 0
2845     PetscBT         blst;
2846     PetscBool3      bstate     = PETSC_BOOL3_UNKNOWN;
2847     PetscBool       fillMatrix = (PetscBool)(!dm->prealloc_only && !isMatIS);
2848     const PetscInt *perm       = NULL;
2849     PetscInt       *dnz, *onz, *dnzu, *onzu, bsLocal[2], bsMinMax[2], *pblocks;
2850     PetscInt        pStart, pEnd, dof, cdof, num_fields;
2851 
2852     PetscCall(DMGetLocalToGlobalMapping(dm, &ltog));
2853     PetscCall(PetscSectionGetBlockStarts(sectionLocal, &blst));
2854     if (sectionLocal->perm) PetscCall(ISGetIndices(sectionLocal->perm, &perm));
2855 
2856     PetscCall(PetscCalloc1(localSize, &pblocks));
2857     PetscCall(PetscSectionGetChart(sectionGlobal, &pStart, &pEnd));
2858     PetscCall(PetscSectionGetNumFields(sectionGlobal, &num_fields));
2859     // We need to process in the permuted order to get block sizes right
2860     for (PetscInt point = pStart; point < pEnd; ++point) {
2861       const PetscInt p = perm ? perm[point] : point;
2862 
2863       switch (dm->blocking_type) {
2864       case DM_BLOCKING_TOPOLOGICAL_POINT: { // One block per topological point
2865         PetscInt bdof, offset;
2866 
2867         PetscCall(PetscSectionGetDof(sectionGlobal, p, &dof));
2868         PetscCall(PetscSectionGetOffset(sectionGlobal, p, &offset));
2869         PetscCall(PetscSectionGetConstraintDof(sectionGlobal, p, &cdof));
2870         if (blst && PetscBTLookup(blst, p)) bstate = PETSC_BOOL3_UNKNOWN;
2871         if (dof > 0) {
2872           // State change
2873           if (bstate == PETSC_BOOL3_UNKNOWN) bstate = PETSC_BOOL3_TRUE;
2874           else if (bstate == PETSC_BOOL3_TRUE && blst && !PetscBTLookup(blst, p)) bstate = PETSC_BOOL3_FALSE;
2875 
2876           for (PetscInt i = 0; i < dof - cdof; ++i) pblocks[offset - localStart + i] = dof - cdof;
2877           // Signal block concatenation
2878           if (bstate == PETSC_BOOL3_FALSE && dof - cdof) pblocks[offset - localStart] = -(dof - cdof);
2879         }
2880         dof  = dof < 0 ? -(dof + 1) : dof;
2881         bdof = cdof && (dof - cdof) ? 1 : dof;
2882         if (dof) {
2883           if (bs < 0) {
2884             bs = bdof;
2885           } else if (bs != bdof) {
2886             bs = 1;
2887           }
2888         }
2889       } break;
2890       case DM_BLOCKING_FIELD_NODE: {
2891         for (PetscInt field = 0; field < num_fields; field++) {
2892           PetscInt num_comp, bdof, offset;
2893           PetscCall(PetscSectionGetFieldComponents(sectionGlobal, field, &num_comp));
2894           PetscCall(PetscSectionGetFieldDof(sectionGlobal, p, field, &dof));
2895           if (dof < 0) continue;
2896           PetscCall(PetscSectionGetFieldOffset(sectionGlobal, p, field, &offset));
2897           PetscCall(PetscSectionGetFieldConstraintDof(sectionGlobal, p, field, &cdof));
2898           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);
2899           PetscInt num_nodes = dof / num_comp;
2900           for (PetscInt i = 0; i < dof - cdof; i++) pblocks[offset - localStart + i] = (dof - cdof) / num_nodes;
2901           // Handle possibly constant block size (unlikely)
2902           bdof = cdof && (dof - cdof) ? 1 : dof;
2903           if (dof) {
2904             if (bs < 0) {
2905               bs = bdof;
2906             } else if (bs != bdof) {
2907               bs = 1;
2908             }
2909           }
2910         }
2911       } break;
2912       }
2913     }
2914     if (sectionLocal->perm) PetscCall(ISRestoreIndices(sectionLocal->perm, &perm));
2915     /* Must have same blocksize on all procs (some might have no points) */
2916     bsLocal[0] = bs < 0 ? PETSC_INT_MAX : bs;
2917     bsLocal[1] = bs;
2918     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
2919     if (bsMinMax[0] != bsMinMax[1]) bs = 1;
2920     else bs = bsMinMax[0];
2921     bs = PetscMax(1, bs);
2922     PetscCall(MatSetLocalToGlobalMapping(*J, ltog, ltog));
2923     if (dm->prealloc_skip) { // User will likely use MatSetPreallocationCOO(), but still set structural parameters
2924       PetscCall(MatSetBlockSize(*J, bs));
2925       PetscCall(MatSetUp(*J));
2926     } else {
2927       PetscCall(PetscCalloc4(localSize / bs, &dnz, localSize / bs, &onz, localSize / bs, &dnzu, localSize / bs, &onzu));
2928       PetscCall(DMPlexPreallocateOperator(dm, bs, dnz, onz, dnzu, onzu, *J, fillMatrix));
2929       PetscCall(PetscFree4(dnz, onz, dnzu, onzu));
2930     }
2931     if (pblocks) { // Consolidate blocks
2932       PetscInt nblocks = 0;
2933       pblocks[0]       = PetscAbs(pblocks[0]);
2934       for (PetscInt i = 0; i < localSize; i += PetscMax(1, pblocks[i])) {
2935         if (pblocks[i] == 0) continue;
2936         // Negative block size indicates the blocks should be concatenated
2937         if (pblocks[i] < 0) {
2938           pblocks[i] = -pblocks[i];
2939           pblocks[nblocks - 1] += pblocks[i];
2940         } else {
2941           pblocks[nblocks++] = pblocks[i]; // nblocks always <= i
2942         }
2943         for (PetscInt j = 1; j < pblocks[i]; j++)
2944           PetscCheck(pblocks[i + j] == pblocks[i], PETSC_COMM_SELF, PETSC_ERR_PLIB, "Block of size %" PetscInt_FMT " at %" PetscInt_FMT " mismatches entry %" PetscInt_FMT " at %" PetscInt_FMT, pblocks[i], i, pblocks[i + j], i + j);
2945       }
2946       PetscCall(MatSetVariableBlockSizes(*J, nblocks, pblocks));
2947     }
2948     PetscCall(PetscFree(pblocks));
2949   }
2950   PetscCall(MatSetDM(*J, dm));
2951   PetscFunctionReturn(PETSC_SUCCESS);
2952 }
2953 
2954 /*@
2955   DMPlexGetSubdomainSection - Returns the section associated with the subdomain
2956 
2957   Not Collective
2958 
2959   Input Parameter:
2960 . dm - The `DMPLEX`
2961 
2962   Output Parameter:
2963 . subsection - The subdomain section
2964 
2965   Level: developer
2966 
2967 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `PetscSection`
2968 @*/
2969 PetscErrorCode DMPlexGetSubdomainSection(DM dm, PetscSection *subsection)
2970 {
2971   DM_Plex *mesh = (DM_Plex *)dm->data;
2972 
2973   PetscFunctionBegin;
2974   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2975   if (!mesh->subdomainSection) {
2976     PetscSection section;
2977     PetscSF      sf;
2978 
2979     PetscCall(PetscSFCreate(PETSC_COMM_SELF, &sf));
2980     PetscCall(DMGetLocalSection(dm, &section));
2981     PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_TRUE, PETSC_FALSE, PETSC_TRUE, &mesh->subdomainSection));
2982     PetscCall(PetscSFDestroy(&sf));
2983   }
2984   *subsection = mesh->subdomainSection;
2985   PetscFunctionReturn(PETSC_SUCCESS);
2986 }
2987 
2988 /*@
2989   DMPlexGetChart - Return the interval for all mesh points [`pStart`, `pEnd`)
2990 
2991   Not Collective
2992 
2993   Input Parameter:
2994 . dm - The `DMPLEX`
2995 
2996   Output Parameters:
2997 + pStart - The first mesh point
2998 - pEnd   - The upper bound for mesh points
2999 
3000   Level: beginner
3001 
3002 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetChart()`
3003 @*/
3004 PetscErrorCode DMPlexGetChart(DM dm, PetscInt *pStart, PetscInt *pEnd)
3005 {
3006   DM_Plex *mesh = (DM_Plex *)dm->data;
3007 
3008   PetscFunctionBegin;
3009   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3010   if (mesh->tr) PetscCall(DMPlexTransformGetChart(mesh->tr, pStart, pEnd));
3011   else PetscCall(PetscSectionGetChart(mesh->coneSection, pStart, pEnd));
3012   PetscFunctionReturn(PETSC_SUCCESS);
3013 }
3014 
3015 /*@
3016   DMPlexSetChart - Set the interval for all mesh points [`pStart`, `pEnd`)
3017 
3018   Not Collective
3019 
3020   Input Parameters:
3021 + dm     - The `DMPLEX`
3022 . pStart - The first mesh point
3023 - pEnd   - The upper bound for mesh points
3024 
3025   Level: beginner
3026 
3027 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetChart()`
3028 @*/
3029 PetscErrorCode DMPlexSetChart(DM dm, PetscInt pStart, PetscInt pEnd)
3030 {
3031   DM_Plex *mesh = (DM_Plex *)dm->data;
3032 
3033   PetscFunctionBegin;
3034   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3035   PetscCall(PetscSectionSetChart(mesh->coneSection, pStart, pEnd));
3036   PetscCall(PetscSectionSetChart(mesh->supportSection, pStart, pEnd));
3037   PetscCall(PetscFree(mesh->cellTypes));
3038   PetscFunctionReturn(PETSC_SUCCESS);
3039 }
3040 
3041 /*@
3042   DMPlexGetConeSize - Return the number of in-edges for this point in the DAG
3043 
3044   Not Collective
3045 
3046   Input Parameters:
3047 + dm - The `DMPLEX`
3048 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3049 
3050   Output Parameter:
3051 . size - The cone size for point `p`
3052 
3053   Level: beginner
3054 
3055 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
3056 @*/
3057 PetscErrorCode DMPlexGetConeSize(DM dm, PetscInt p, PetscInt *size)
3058 {
3059   DM_Plex *mesh = (DM_Plex *)dm->data;
3060 
3061   PetscFunctionBegin;
3062   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3063   PetscAssertPointer(size, 3);
3064   if (mesh->tr) PetscCall(DMPlexTransformGetConeSize(mesh->tr, p, size));
3065   else PetscCall(PetscSectionGetDof(mesh->coneSection, p, size));
3066   PetscFunctionReturn(PETSC_SUCCESS);
3067 }
3068 
3069 /*@
3070   DMPlexSetConeSize - Set the number of in-edges for this point in the DAG
3071 
3072   Not Collective
3073 
3074   Input Parameters:
3075 + dm   - The `DMPLEX`
3076 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3077 - size - The cone size for point `p`
3078 
3079   Level: beginner
3080 
3081   Note:
3082   This should be called after `DMPlexSetChart()`.
3083 
3084 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetCone()`, `DMPlexCreate()`, `DMPlexGetConeSize()`, `DMPlexSetChart()`
3085 @*/
3086 PetscErrorCode DMPlexSetConeSize(DM dm, PetscInt p, PetscInt size)
3087 {
3088   DM_Plex *mesh = (DM_Plex *)dm->data;
3089 
3090   PetscFunctionBegin;
3091   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3092   PetscCheck(!mesh->tr, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Cannot call DMPlexSetConeSize() on a mesh with a transform defined.");
3093   PetscCall(PetscSectionSetDof(mesh->coneSection, p, size));
3094   PetscFunctionReturn(PETSC_SUCCESS);
3095 }
3096 
3097 /*@C
3098   DMPlexGetCone - Return the points on the in-edges for this point in the DAG
3099 
3100   Not Collective
3101 
3102   Input Parameters:
3103 + dm - The `DMPLEX`
3104 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3105 
3106   Output Parameter:
3107 . cone - An array of points which are on the in-edges for point `p`, the length of `cone` is the result of `DMPlexGetConeSize()`
3108 
3109   Level: beginner
3110 
3111   Fortran Notes:
3112   `cone` must be declared with
3113 .vb
3114   PetscInt, pointer :: cone(:)
3115 .ve
3116 
3117   You must also call `DMPlexRestoreCone()` after you finish using the array.
3118   `DMPlexRestoreCone()` is not needed/available in C.
3119 
3120 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSize()`, `DMPlexSetCone()`, `DMPlexGetConeTuple()`, `DMPlexSetChart()`, `DMPlexRestoreCone()`
3121 @*/
3122 PetscErrorCode DMPlexGetCone(DM dm, PetscInt p, const PetscInt *cone[])
3123 {
3124   DM_Plex *mesh = (DM_Plex *)dm->data;
3125   PetscInt off;
3126 
3127   PetscFunctionBegin;
3128   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3129   PetscAssertPointer(cone, 3);
3130   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3131   *cone = PetscSafePointerPlusOffset(mesh->cones, off);
3132   PetscFunctionReturn(PETSC_SUCCESS);
3133 }
3134 
3135 /*@
3136   DMPlexGetConeTuple - Return the points on the in-edges of several points in the DAG
3137 
3138   Not Collective
3139 
3140   Input Parameters:
3141 + dm - The `DMPLEX`
3142 - p  - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3143 
3144   Output Parameters:
3145 + pConesSection - `PetscSection` describing the layout of `pCones`
3146 - pCones        - An `IS` containing the points which are on the in-edges for the point set `p`
3147 
3148   Level: intermediate
3149 
3150 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeRecursive()`, `DMPlexSetChart()`, `PetscSection`, `IS`
3151 @*/
3152 PetscErrorCode DMPlexGetConeTuple(DM dm, IS p, PetscSection *pConesSection, IS *pCones)
3153 {
3154   PetscSection cs, newcs;
3155   PetscInt    *cones;
3156   PetscInt    *newarr = NULL;
3157   PetscInt     n;
3158 
3159   PetscFunctionBegin;
3160   PetscCall(DMPlexGetCones(dm, &cones));
3161   PetscCall(DMPlexGetConeSection(dm, &cs));
3162   PetscCall(PetscSectionExtractDofsFromArray(cs, MPIU_INT, cones, p, &newcs, pCones ? ((void **)&newarr) : NULL));
3163   if (pConesSection) *pConesSection = newcs;
3164   if (pCones) {
3165     PetscCall(PetscSectionGetStorageSize(newcs, &n));
3166     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)p), n, newarr, PETSC_OWN_POINTER, pCones));
3167   }
3168   PetscFunctionReturn(PETSC_SUCCESS);
3169 }
3170 
3171 /*@
3172   DMPlexGetConeRecursiveVertices - Expand each given point into its cone points and do that recursively until we end up just with vertices.
3173 
3174   Not Collective
3175 
3176   Input Parameters:
3177 + dm     - The `DMPLEX`
3178 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3179 
3180   Output Parameter:
3181 . expandedPoints - An `IS` containing the of vertices recursively expanded from input points
3182 
3183   Level: advanced
3184 
3185   Notes:
3186   Like `DMPlexGetConeRecursive()` but returns only the 0-depth `IS` (i.e. vertices only) and no sections.
3187 
3188   There is no corresponding Restore function, just call `ISDestroy()` on the returned `IS` to deallocate.
3189 
3190 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexRestoreConeRecursive()`,
3191           `DMPlexGetDepth()`, `IS`
3192 @*/
3193 PetscErrorCode DMPlexGetConeRecursiveVertices(DM dm, IS points, IS *expandedPoints)
3194 {
3195   IS      *expandedPointsAll;
3196   PetscInt depth;
3197 
3198   PetscFunctionBegin;
3199   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3200   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
3201   PetscAssertPointer(expandedPoints, 3);
3202   PetscCall(DMPlexGetConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
3203   *expandedPoints = expandedPointsAll[0];
3204   PetscCall(PetscObjectReference((PetscObject)expandedPointsAll[0]));
3205   PetscCall(DMPlexRestoreConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
3206   PetscFunctionReturn(PETSC_SUCCESS);
3207 }
3208 
3209 /*@
3210   DMPlexGetConeRecursive - Expand each given point into its cone points and do that recursively until we end up just with vertices
3211   (DAG points of depth 0, i.e., without cones).
3212 
3213   Not Collective
3214 
3215   Input Parameters:
3216 + dm     - The `DMPLEX`
3217 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3218 
3219   Output Parameters:
3220 + depth          - (optional) Size of the output arrays, equal to `DMPLEX` depth, returned by `DMPlexGetDepth()`
3221 . expandedPoints - (optional) An array of index sets with recursively expanded cones
3222 - sections       - (optional) An array of sections which describe mappings from points to their cone points
3223 
3224   Level: advanced
3225 
3226   Notes:
3227   Like `DMPlexGetConeTuple()` but recursive.
3228 
3229   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.
3230   For example, for d=0 it contains only vertices, for d=1 it can contain vertices and edges, etc.
3231 
3232   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\:
3233   (1) DAG points in `expandedPoints`[d+1] with `depth` d+1 to their cone points in `expandedPoints`[d];
3234   (2) DAG points in `expandedPoints`[d+1] with `depth` in [0,d] to the same points in `expandedPoints`[d].
3235 
3236 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexRestoreConeRecursive()`, `DMPlexGetConeRecursiveVertices()`,
3237           `DMPlexGetDepth()`, `PetscSection`, `IS`
3238 @*/
3239 PetscErrorCode DMPlexGetConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
3240 {
3241   const PetscInt *arr0 = NULL, *cone = NULL;
3242   PetscInt       *arr = NULL, *newarr = NULL;
3243   PetscInt        d, depth_, i, n, newn, cn, co, start, end;
3244   IS             *expandedPoints_;
3245   PetscSection   *sections_;
3246 
3247   PetscFunctionBegin;
3248   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3249   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
3250   if (depth) PetscAssertPointer(depth, 3);
3251   if (expandedPoints) PetscAssertPointer(expandedPoints, 4);
3252   if (sections) PetscAssertPointer(sections, 5);
3253   PetscCall(ISGetLocalSize(points, &n));
3254   PetscCall(ISGetIndices(points, &arr0));
3255   PetscCall(DMPlexGetDepth(dm, &depth_));
3256   PetscCall(PetscCalloc1(depth_, &expandedPoints_));
3257   PetscCall(PetscCalloc1(depth_, &sections_));
3258   arr = (PetscInt *)arr0; /* this is ok because first generation of arr is not modified */
3259   for (d = depth_ - 1; d >= 0; d--) {
3260     PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &sections_[d]));
3261     PetscCall(PetscSectionSetChart(sections_[d], 0, n));
3262     for (i = 0; i < n; i++) {
3263       PetscCall(DMPlexGetDepthStratum(dm, d + 1, &start, &end));
3264       if (arr[i] >= start && arr[i] < end) {
3265         PetscCall(DMPlexGetConeSize(dm, arr[i], &cn));
3266         PetscCall(PetscSectionSetDof(sections_[d], i, cn));
3267       } else {
3268         PetscCall(PetscSectionSetDof(sections_[d], i, 1));
3269       }
3270     }
3271     PetscCall(PetscSectionSetUp(sections_[d]));
3272     PetscCall(PetscSectionGetStorageSize(sections_[d], &newn));
3273     PetscCall(PetscMalloc1(newn, &newarr));
3274     for (i = 0; i < n; i++) {
3275       PetscCall(PetscSectionGetDof(sections_[d], i, &cn));
3276       PetscCall(PetscSectionGetOffset(sections_[d], i, &co));
3277       if (cn > 1) {
3278         PetscCall(DMPlexGetCone(dm, arr[i], &cone));
3279         PetscCall(PetscMemcpy(&newarr[co], cone, cn * sizeof(PetscInt)));
3280       } else {
3281         newarr[co] = arr[i];
3282       }
3283     }
3284     PetscCall(ISCreateGeneral(PETSC_COMM_SELF, newn, newarr, PETSC_OWN_POINTER, &expandedPoints_[d]));
3285     arr = newarr;
3286     n   = newn;
3287   }
3288   PetscCall(ISRestoreIndices(points, &arr0));
3289   *depth = depth_;
3290   if (expandedPoints) *expandedPoints = expandedPoints_;
3291   else {
3292     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&expandedPoints_[d]));
3293     PetscCall(PetscFree(expandedPoints_));
3294   }
3295   if (sections) *sections = sections_;
3296   else {
3297     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&sections_[d]));
3298     PetscCall(PetscFree(sections_));
3299   }
3300   PetscFunctionReturn(PETSC_SUCCESS);
3301 }
3302 
3303 /*@
3304   DMPlexRestoreConeRecursive - Deallocates arrays created by `DMPlexGetConeRecursive()`
3305 
3306   Not Collective
3307 
3308   Input Parameters:
3309 + dm     - The `DMPLEX`
3310 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3311 
3312   Output Parameters:
3313 + depth          - (optional) Size of the output arrays, equal to `DMPLEX` depth, returned by `DMPlexGetDepth()`
3314 . expandedPoints - (optional) An array of recursively expanded cones
3315 - sections       - (optional) An array of sections which describe mappings from points to their cone points
3316 
3317   Level: advanced
3318 
3319   Note:
3320   See `DMPlexGetConeRecursive()`
3321 
3322 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexGetConeRecursiveVertices()`,
3323           `DMPlexGetDepth()`, `IS`, `PetscSection`
3324 @*/
3325 PetscErrorCode DMPlexRestoreConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
3326 {
3327   PetscInt d, depth_;
3328 
3329   PetscFunctionBegin;
3330   PetscCall(DMPlexGetDepth(dm, &depth_));
3331   PetscCheck(!depth || *depth == depth_, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "depth changed since last call to DMPlexGetConeRecursive");
3332   if (depth) *depth = 0;
3333   if (expandedPoints) {
3334     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&((*expandedPoints)[d])));
3335     PetscCall(PetscFree(*expandedPoints));
3336   }
3337   if (sections) {
3338     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&((*sections)[d])));
3339     PetscCall(PetscFree(*sections));
3340   }
3341   PetscFunctionReturn(PETSC_SUCCESS);
3342 }
3343 
3344 /*@
3345   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
3346 
3347   Not Collective
3348 
3349   Input Parameters:
3350 + dm   - The `DMPLEX`
3351 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3352 - cone - An array of points which are on the in-edges for point `p`, its length must have been previously provided with `DMPlexSetConeSize()`
3353 
3354   Level: beginner
3355 
3356   Note:
3357   This should be called after all calls to `DMPlexSetConeSize()` and `DMSetUp()`.
3358 
3359 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`, `DMPlexSetSupport()`, `DMPlexSetSupportSize()`
3360 @*/
3361 PetscErrorCode DMPlexSetCone(DM dm, PetscInt p, const PetscInt cone[])
3362 {
3363   DM_Plex *mesh = (DM_Plex *)dm->data;
3364   PetscInt dof, off, c;
3365 
3366   PetscFunctionBegin;
3367   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3368   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3369   if (dof) PetscAssertPointer(cone, 3);
3370   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3371   if (PetscDefined(USE_DEBUG)) {
3372     PetscInt pStart, pEnd;
3373     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3374     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);
3375     for (c = 0; c < dof; ++c) {
3376       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);
3377       mesh->cones[off + c] = cone[c];
3378     }
3379   } else {
3380     for (c = 0; c < dof; ++c) mesh->cones[off + c] = cone[c];
3381   }
3382   PetscFunctionReturn(PETSC_SUCCESS);
3383 }
3384 
3385 /*@C
3386   DMPlexGetConeOrientation - Return the orientations on the in-edges for this point in the DAG
3387 
3388   Not Collective
3389 
3390   Input Parameters:
3391 + dm - The `DMPLEX`
3392 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3393 
3394   Output Parameter:
3395 . coneOrientation - An array of orientations which are on the in-edges for point `p`. An orientation is an
3396                     integer giving the prescription for cone traversal. Its length is given by the result of `DMPlexSetConeSize()`
3397 
3398   Level: beginner
3399 
3400   Note:
3401   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3402   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3403   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3404   with the identity.
3405 
3406   Fortran Notes:
3407   You must call `DMPlexRestoreConeOrientation()` after you finish using the returned array.
3408   `DMPlexRestoreConeOrientation()` is not needed/available in C.
3409 
3410 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetConeSize()`, `DMPolytopeTypeComposeOrientation()`, `DMPolytopeTypeComposeOrientationInv()`,
3411           `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetCone()`, `DMPlexSetChart()`
3412 @*/
3413 PetscErrorCode DMPlexGetConeOrientation(DM dm, PetscInt p, const PetscInt *coneOrientation[])
3414 {
3415   DM_Plex *mesh = (DM_Plex *)dm->data;
3416   PetscInt off;
3417 
3418   PetscFunctionBegin;
3419   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3420   if (PetscDefined(USE_DEBUG)) {
3421     PetscInt dof;
3422     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3423     if (dof) PetscAssertPointer(coneOrientation, 3);
3424   }
3425   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3426 
3427   *coneOrientation = &mesh->coneOrientations[off];
3428   PetscFunctionReturn(PETSC_SUCCESS);
3429 }
3430 
3431 /*@
3432   DMPlexSetConeOrientation - Set the 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 - coneOrientation - An array of orientations. Its length is given by the result of `DMPlexSetConeSize()`
3440 
3441   Level: beginner
3442 
3443   Notes:
3444   This should be called after all calls to `DMPlexSetConeSize()` and `DMSetUp()`.
3445 
3446   The meaning of coneOrientation is detailed in `DMPlexGetConeOrientation()`.
3447 
3448 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetConeOrientation()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3449 @*/
3450 PetscErrorCode DMPlexSetConeOrientation(DM dm, PetscInt p, const PetscInt coneOrientation[])
3451 {
3452   DM_Plex *mesh = (DM_Plex *)dm->data;
3453   PetscInt pStart, pEnd;
3454   PetscInt dof, off, c;
3455 
3456   PetscFunctionBegin;
3457   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3458   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3459   if (dof) PetscAssertPointer(coneOrientation, 3);
3460   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3461   if (PetscDefined(USE_DEBUG)) {
3462     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3463     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);
3464     for (c = 0; c < dof; ++c) {
3465       PetscInt cdof, o = coneOrientation[c];
3466 
3467       PetscCall(PetscSectionGetDof(mesh->coneSection, mesh->cones[off + c], &cdof));
3468       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);
3469       mesh->coneOrientations[off + c] = o;
3470     }
3471   } else {
3472     for (c = 0; c < dof; ++c) mesh->coneOrientations[off + c] = coneOrientation[c];
3473   }
3474   PetscFunctionReturn(PETSC_SUCCESS);
3475 }
3476 
3477 /*@
3478   DMPlexInsertCone - Insert a point into the in-edges for the point p in the DAG
3479 
3480   Not Collective
3481 
3482   Input Parameters:
3483 + dm        - The `DMPLEX`
3484 . p         - The point, which must lie in the chart set with `DMPlexSetChart()`
3485 . conePos   - The local index in the cone where the point should be put
3486 - conePoint - The mesh point to insert
3487 
3488   Level: beginner
3489 
3490 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3491 @*/
3492 PetscErrorCode DMPlexInsertCone(DM dm, PetscInt p, PetscInt conePos, PetscInt conePoint)
3493 {
3494   DM_Plex *mesh = (DM_Plex *)dm->data;
3495   PetscInt pStart, pEnd;
3496   PetscInt dof, off;
3497 
3498   PetscFunctionBegin;
3499   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3500   if (PetscDefined(USE_DEBUG)) {
3501     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3502     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);
3503     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);
3504     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3505     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);
3506   }
3507   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3508   mesh->cones[off + conePos] = conePoint;
3509   PetscFunctionReturn(PETSC_SUCCESS);
3510 }
3511 
3512 /*@
3513   DMPlexInsertConeOrientation - Insert a point orientation for the in-edge for the point p in the DAG
3514 
3515   Not Collective
3516 
3517   Input Parameters:
3518 + dm              - The `DMPLEX`
3519 . p               - The point, which must lie in the chart set with `DMPlexSetChart()`
3520 . conePos         - The local index in the cone where the point should be put
3521 - coneOrientation - The point orientation to insert
3522 
3523   Level: beginner
3524 
3525   Note:
3526   The meaning of coneOrientation values is detailed in `DMPlexGetConeOrientation()`.
3527 
3528 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3529 @*/
3530 PetscErrorCode DMPlexInsertConeOrientation(DM dm, PetscInt p, PetscInt conePos, PetscInt coneOrientation)
3531 {
3532   DM_Plex *mesh = (DM_Plex *)dm->data;
3533   PetscInt pStart, pEnd;
3534   PetscInt dof, off;
3535 
3536   PetscFunctionBegin;
3537   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3538   if (PetscDefined(USE_DEBUG)) {
3539     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3540     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);
3541     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3542     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);
3543   }
3544   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3545   mesh->coneOrientations[off + conePos] = coneOrientation;
3546   PetscFunctionReturn(PETSC_SUCCESS);
3547 }
3548 
3549 /*@C
3550   DMPlexGetOrientedCone - Return the points and orientations on the in-edges for this point in the DAG
3551 
3552   Not collective
3553 
3554   Input Parameters:
3555 + dm - The DMPlex
3556 - p  - The point, which must lie in the chart set with DMPlexSetChart()
3557 
3558   Output Parameters:
3559 + cone - An array of points which are on the in-edges for point `p`
3560 - ornt - An array of orientations which are on the in-edges for point `p`. An orientation is an
3561          integer giving the prescription for cone traversal.
3562 
3563   Level: beginner
3564 
3565   Notes:
3566   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3567   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3568   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3569   with the identity.
3570 
3571   You must also call `DMPlexRestoreOrientedCone()` after you finish using the returned array.
3572 
3573   Fortran Notes:
3574   `cone` and `ornt` must be declared with
3575 .vb
3576   PetscInt, pointer :: cone(:)
3577   PetscInt, pointer :: ornt(:)
3578 .ve
3579 
3580 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreOrientedCone()`, `DMPlexGetConeSize()`, `DMPlexGetCone()`, `DMPlexGetChart()`
3581 @*/
3582 PetscErrorCode DMPlexGetOrientedCone(DM dm, PetscInt p, const PetscInt *cone[], const PetscInt *ornt[])
3583 {
3584   DM_Plex *mesh = (DM_Plex *)dm->data;
3585 
3586   PetscFunctionBegin;
3587   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3588   if (mesh->tr) {
3589     PetscCall(DMPlexTransformGetCone(mesh->tr, p, cone, ornt));
3590   } else {
3591     PetscInt off;
3592     if (PetscDefined(USE_DEBUG)) {
3593       PetscInt dof;
3594       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3595       if (dof) {
3596         if (cone) PetscAssertPointer(cone, 3);
3597         if (ornt) PetscAssertPointer(ornt, 4);
3598       }
3599     }
3600     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3601     if (cone) *cone = PetscSafePointerPlusOffset(mesh->cones, off);
3602     if (ornt) *ornt = PetscSafePointerPlusOffset(mesh->coneOrientations, off);
3603   }
3604   PetscFunctionReturn(PETSC_SUCCESS);
3605 }
3606 
3607 /*@C
3608   DMPlexRestoreOrientedCone - Restore the points and orientations on the in-edges for this point in the DAG obtained with `DMPlexGetOrientedCone()`
3609 
3610   Not Collective
3611 
3612   Input Parameters:
3613 + dm   - The DMPlex
3614 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3615 . cone - An array of points which are on the in-edges for point p
3616 - ornt - An array of orientations which are on the in-edges for point `p`. An orientation is an
3617          integer giving the prescription for cone traversal.
3618 
3619   Level: beginner
3620 
3621 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetOrientedCone()`, `DMPlexGetConeSize()`, `DMPlexGetCone()`, `DMPlexGetChart()`
3622 @*/
3623 PetscErrorCode DMPlexRestoreOrientedCone(DM dm, PetscInt p, const PetscInt *cone[], const PetscInt *ornt[])
3624 {
3625   DM_Plex *mesh = (DM_Plex *)dm->data;
3626 
3627   PetscFunctionBegin;
3628   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3629   if (mesh->tr) PetscCall(DMPlexTransformRestoreCone(mesh->tr, p, cone, ornt));
3630   PetscFunctionReturn(PETSC_SUCCESS);
3631 }
3632 
3633 /*@
3634   DMPlexGetSupportSize - Return the number of out-edges for this point in the DAG
3635 
3636   Not Collective
3637 
3638   Input Parameters:
3639 + dm - The `DMPLEX`
3640 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3641 
3642   Output Parameter:
3643 . size - The support size for point `p`
3644 
3645   Level: beginner
3646 
3647 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`, `DMPlexGetConeSize()`
3648 @*/
3649 PetscErrorCode DMPlexGetSupportSize(DM dm, PetscInt p, PetscInt *size)
3650 {
3651   DM_Plex *mesh = (DM_Plex *)dm->data;
3652 
3653   PetscFunctionBegin;
3654   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3655   PetscAssertPointer(size, 3);
3656   PetscCall(PetscSectionGetDof(mesh->supportSection, p, size));
3657   PetscFunctionReturn(PETSC_SUCCESS);
3658 }
3659 
3660 /*@
3661   DMPlexSetSupportSize - Set the number of out-edges for this point in the DAG
3662 
3663   Not Collective
3664 
3665   Input Parameters:
3666 + dm   - The `DMPLEX`
3667 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3668 - size - The support size for point `p`
3669 
3670   Level: beginner
3671 
3672   Note:
3673   This should be called after `DMPlexSetChart()`.
3674 
3675 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetSupportSize()`, `DMPlexSetChart()`
3676 @*/
3677 PetscErrorCode DMPlexSetSupportSize(DM dm, PetscInt p, PetscInt size)
3678 {
3679   DM_Plex *mesh = (DM_Plex *)dm->data;
3680 
3681   PetscFunctionBegin;
3682   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3683   PetscCall(PetscSectionSetDof(mesh->supportSection, p, size));
3684   PetscFunctionReturn(PETSC_SUCCESS);
3685 }
3686 
3687 /*@C
3688   DMPlexGetSupport - Return the points on the out-edges for this point in the DAG
3689 
3690   Not Collective
3691 
3692   Input Parameters:
3693 + dm - The `DMPLEX`
3694 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3695 
3696   Output Parameter:
3697 . support - An array of points which are on the out-edges for point `p`, its length is that obtained from `DMPlexGetSupportSize()`
3698 
3699   Level: beginner
3700 
3701   Fortran Notes:
3702   `support` must be declared with
3703 .vb
3704   PetscInt, pointer :: support(:)
3705 .ve
3706 
3707   You must also call `DMPlexRestoreSupport()` after you finish using the returned array.
3708   `DMPlexRestoreSupport()` is not needed/available in C.
3709 
3710 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSupportSize()`, `DMPlexSetSupport()`, `DMPlexGetCone()`, `DMPlexSetChart()`
3711 @*/
3712 PetscErrorCode DMPlexGetSupport(DM dm, PetscInt p, const PetscInt *support[])
3713 {
3714   DM_Plex *mesh = (DM_Plex *)dm->data;
3715   PetscInt off;
3716 
3717   PetscFunctionBegin;
3718   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3719   PetscAssertPointer(support, 3);
3720   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3721   *support = PetscSafePointerPlusOffset(mesh->supports, off);
3722   PetscFunctionReturn(PETSC_SUCCESS);
3723 }
3724 
3725 /*@
3726   DMPlexSetSupport - Set the points on the out-edges for this point in the DAG, that is the list of points that this point covers
3727 
3728   Not Collective
3729 
3730   Input Parameters:
3731 + dm      - The `DMPLEX`
3732 . p       - The point, which must lie in the chart set with `DMPlexSetChart()`
3733 - support - An array of points which are on the out-edges for point `p`, its length is that obtained from `DMPlexGetSupportSize()`
3734 
3735   Level: beginner
3736 
3737   Note:
3738   This should be called after all calls to `DMPlexSetSupportSize()` and `DMSetUp()`.
3739 
3740 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetCone()`, `DMPlexSetConeSize()`, `DMPlexCreate()`, `DMPlexGetSupport()`, `DMPlexSetChart()`, `DMPlexSetSupportSize()`, `DMSetUp()`
3741 @*/
3742 PetscErrorCode DMPlexSetSupport(DM dm, PetscInt p, const PetscInt support[])
3743 {
3744   DM_Plex *mesh = (DM_Plex *)dm->data;
3745   PetscInt pStart, pEnd;
3746   PetscInt dof, off, c;
3747 
3748   PetscFunctionBegin;
3749   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3750   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3751   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3752   if (dof) PetscAssertPointer(support, 3);
3753   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3754   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);
3755   for (c = 0; c < dof; ++c) {
3756     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);
3757     mesh->supports[off + c] = support[c];
3758   }
3759   PetscFunctionReturn(PETSC_SUCCESS);
3760 }
3761 
3762 /*@
3763   DMPlexInsertSupport - Insert a point into the out-edges for the point p in the DAG
3764 
3765   Not Collective
3766 
3767   Input Parameters:
3768 + dm           - The `DMPLEX`
3769 . p            - The point, which must lie in the chart set with `DMPlexSetChart()`
3770 . supportPos   - The local index in the cone where the point should be put
3771 - supportPoint - The mesh point to insert
3772 
3773   Level: beginner
3774 
3775 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3776 @*/
3777 PetscErrorCode DMPlexInsertSupport(DM dm, PetscInt p, PetscInt supportPos, PetscInt supportPoint)
3778 {
3779   DM_Plex *mesh = (DM_Plex *)dm->data;
3780   PetscInt pStart, pEnd;
3781   PetscInt dof, off;
3782 
3783   PetscFunctionBegin;
3784   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3785   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3786   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3787   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3788   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);
3789   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);
3790   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);
3791   mesh->supports[off + supportPos] = supportPoint;
3792   PetscFunctionReturn(PETSC_SUCCESS);
3793 }
3794 
3795 /* Converts an orientation o in the current numbering to the previous scheme used in Plex */
3796 PetscInt DMPolytopeConvertNewOrientation_Internal(DMPolytopeType ct, PetscInt o)
3797 {
3798   switch (ct) {
3799   case DM_POLYTOPE_SEGMENT:
3800     if (o == -1) return -2;
3801     break;
3802   case DM_POLYTOPE_TRIANGLE:
3803     if (o == -3) return -1;
3804     if (o == -2) return -3;
3805     if (o == -1) return -2;
3806     break;
3807   case DM_POLYTOPE_QUADRILATERAL:
3808     if (o == -4) return -2;
3809     if (o == -3) return -1;
3810     if (o == -2) return -4;
3811     if (o == -1) return -3;
3812     break;
3813   default:
3814     return o;
3815   }
3816   return o;
3817 }
3818 
3819 /* Converts an orientation o in the previous scheme used in Plex to the current numbering */
3820 PetscInt DMPolytopeConvertOldOrientation_Internal(DMPolytopeType ct, PetscInt o)
3821 {
3822   switch (ct) {
3823   case DM_POLYTOPE_SEGMENT:
3824     if ((o == -2) || (o == 1)) return -1;
3825     if (o == -1) return 0;
3826     break;
3827   case DM_POLYTOPE_TRIANGLE:
3828     if (o == -3) return -2;
3829     if (o == -2) return -1;
3830     if (o == -1) return -3;
3831     break;
3832   case DM_POLYTOPE_QUADRILATERAL:
3833     if (o == -4) return -2;
3834     if (o == -3) return -1;
3835     if (o == -2) return -4;
3836     if (o == -1) return -3;
3837     break;
3838   default:
3839     return o;
3840   }
3841   return o;
3842 }
3843 
3844 /* Takes in a mesh whose orientations are in the previous scheme and converts them all to the current numbering */
3845 PetscErrorCode DMPlexConvertOldOrientations_Internal(DM dm)
3846 {
3847   PetscInt pStart, pEnd, p;
3848 
3849   PetscFunctionBegin;
3850   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3851   for (p = pStart; p < pEnd; ++p) {
3852     const PetscInt *cone, *ornt;
3853     PetscInt        coneSize, c;
3854 
3855     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
3856     PetscCall(DMPlexGetCone(dm, p, &cone));
3857     PetscCall(DMPlexGetConeOrientation(dm, p, &ornt));
3858     for (c = 0; c < coneSize; ++c) {
3859       DMPolytopeType ct;
3860       const PetscInt o = ornt[c];
3861 
3862       PetscCall(DMPlexGetCellType(dm, cone[c], &ct));
3863       switch (ct) {
3864       case DM_POLYTOPE_SEGMENT:
3865         if ((o == -2) || (o == 1)) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3866         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, 0));
3867         break;
3868       case DM_POLYTOPE_TRIANGLE:
3869         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3870         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3871         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3872         break;
3873       case DM_POLYTOPE_QUADRILATERAL:
3874         if (o == -4) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3875         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3876         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -4));
3877         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3878         break;
3879       default:
3880         break;
3881       }
3882     }
3883   }
3884   PetscFunctionReturn(PETSC_SUCCESS);
3885 }
3886 
3887 static inline PetscErrorCode DMPlexGetTransitiveClosure_Hot_Private(DM dm, PetscInt p, PetscBool useCone, PetscInt *size, const PetscInt *arr[], const PetscInt *ornt[])
3888 {
3889   DM_Plex *mesh = (DM_Plex *)dm->data;
3890 
3891   PetscFunctionBeginHot;
3892   if (PetscDefined(USE_DEBUG) || mesh->tr) {
3893     if (useCone) {
3894       PetscCall(DMPlexGetConeSize(dm, p, size));
3895       PetscCall(DMPlexGetOrientedCone(dm, p, arr, ornt));
3896     } else {
3897       PetscCall(DMPlexGetSupportSize(dm, p, size));
3898       PetscCall(DMPlexGetSupport(dm, p, arr));
3899     }
3900   } else {
3901     if (useCone) {
3902       const PetscSection s   = mesh->coneSection;
3903       const PetscInt     ps  = p - s->pStart;
3904       const PetscInt     off = s->atlasOff[ps];
3905 
3906       *size = s->atlasDof[ps];
3907       *arr  = mesh->cones + off;
3908       *ornt = mesh->coneOrientations + off;
3909     } else {
3910       const PetscSection s   = mesh->supportSection;
3911       const PetscInt     ps  = p - s->pStart;
3912       const PetscInt     off = s->atlasOff[ps];
3913 
3914       *size = s->atlasDof[ps];
3915       *arr  = mesh->supports + off;
3916     }
3917   }
3918   PetscFunctionReturn(PETSC_SUCCESS);
3919 }
3920 
3921 static inline PetscErrorCode DMPlexRestoreTransitiveClosure_Hot_Private(DM dm, PetscInt p, PetscBool useCone, PetscInt *size, const PetscInt *arr[], const PetscInt *ornt[])
3922 {
3923   DM_Plex *mesh = (DM_Plex *)dm->data;
3924 
3925   PetscFunctionBeginHot;
3926   if (PetscDefined(USE_DEBUG) || mesh->tr) {
3927     if (useCone) PetscCall(DMPlexRestoreOrientedCone(dm, p, arr, ornt));
3928   }
3929   PetscFunctionReturn(PETSC_SUCCESS);
3930 }
3931 
3932 static PetscErrorCode DMPlexGetTransitiveClosure_Depth1_Private(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3933 {
3934   DMPolytopeType  ct = DM_POLYTOPE_UNKNOWN;
3935   PetscInt       *closure;
3936   const PetscInt *tmp = NULL, *tmpO = NULL;
3937   PetscInt        off = 0, tmpSize, t;
3938 
3939   PetscFunctionBeginHot;
3940   if (ornt) {
3941     PetscCall(DMPlexGetCellType(dm, p, &ct));
3942     if (ct == DM_POLYTOPE_FV_GHOST || ct == DM_POLYTOPE_INTERIOR_GHOST || ct == DM_POLYTOPE_UNKNOWN || ct == DM_POLYTOPE_UNKNOWN_CELL || ct == DM_POLYTOPE_UNKNOWN_FACE) ct = DM_POLYTOPE_UNKNOWN;
3943   }
3944   if (*points) {
3945     closure = *points;
3946   } else {
3947     PetscInt maxConeSize, maxSupportSize;
3948     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3949     PetscCall(DMGetWorkArray(dm, 2 * (PetscMax(maxConeSize, maxSupportSize) + 1), MPIU_INT, &closure));
3950   }
3951   PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, p, useCone, &tmpSize, &tmp, &tmpO));
3952   if (ct == DM_POLYTOPE_UNKNOWN) {
3953     closure[off++] = p;
3954     closure[off++] = 0;
3955     for (t = 0; t < tmpSize; ++t) {
3956       closure[off++] = tmp[t];
3957       closure[off++] = tmpO ? tmpO[t] : 0;
3958     }
3959   } else {
3960     const PetscInt *arr = DMPolytopeTypeGetArrangement(ct, ornt);
3961 
3962     /* We assume that cells with a valid type have faces with a valid type */
3963     closure[off++] = p;
3964     closure[off++] = ornt;
3965     for (t = 0; t < tmpSize; ++t) {
3966       DMPolytopeType ft;
3967 
3968       PetscCall(DMPlexGetCellType(dm, tmp[t], &ft));
3969       closure[off++] = tmp[arr[t]];
3970       closure[off++] = tmpO ? DMPolytopeTypeComposeOrientation(ft, ornt, tmpO[t]) : 0;
3971     }
3972   }
3973   PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, p, useCone, &tmpSize, &tmp, &tmpO));
3974   if (numPoints) *numPoints = tmpSize + 1;
3975   if (points) *points = closure;
3976   PetscFunctionReturn(PETSC_SUCCESS);
3977 }
3978 
3979 /* We need a special tensor version because we want to allow duplicate points in the endcaps for hybrid cells */
3980 static PetscErrorCode DMPlexTransitiveClosure_Tensor_Internal(DM dm, PetscInt point, DMPolytopeType ct, PetscInt o, PetscBool useCone, PetscInt *numPoints, PetscInt **points)
3981 {
3982   const PetscInt *arr = DMPolytopeTypeGetArrangement(ct, o);
3983   const PetscInt *cone, *ornt;
3984   PetscInt       *pts, *closure = NULL;
3985   DMPolytopeType  ft;
3986   PetscInt        maxConeSize, maxSupportSize, coneSeries, supportSeries, maxSize;
3987   PetscInt        dim, coneSize, c, d, clSize, cl;
3988 
3989   PetscFunctionBeginHot;
3990   PetscCall(DMGetDimension(dm, &dim));
3991   PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, point, PETSC_TRUE, &coneSize, &cone, &ornt));
3992   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3993   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, dim + 1) - 1) / (maxConeSize - 1)) : dim + 1;
3994   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, dim + 1) - 1) / (maxSupportSize - 1)) : dim + 1;
3995   maxSize       = PetscMax(coneSeries, supportSeries);
3996   if (*points) {
3997     pts = *points;
3998   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &pts));
3999   c        = 0;
4000   pts[c++] = point;
4001   pts[c++] = o;
4002   PetscCall(DMPlexGetCellType(dm, cone[arr[0 * 2 + 0]], &ft));
4003   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[0 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[0 * 2 + 1], ornt[0]), useCone, &clSize, &closure));
4004   for (cl = 0; cl < clSize * 2; cl += 2) {
4005     pts[c++] = closure[cl];
4006     pts[c++] = closure[cl + 1];
4007   }
4008   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[1 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[1 * 2 + 1], ornt[1]), useCone, &clSize, &closure));
4009   for (cl = 0; cl < clSize * 2; cl += 2) {
4010     pts[c++] = closure[cl];
4011     pts[c++] = closure[cl + 1];
4012   }
4013   PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[0], useCone, &clSize, &closure));
4014   for (d = 2; d < coneSize; ++d) {
4015     PetscCall(DMPlexGetCellType(dm, cone[arr[d * 2 + 0]], &ft));
4016     pts[c++] = cone[arr[d * 2 + 0]];
4017     pts[c++] = DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]);
4018   }
4019   PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, point, PETSC_TRUE, &coneSize, &cone, &ornt));
4020   if (dim >= 3) {
4021     for (d = 2; d < coneSize; ++d) {
4022       const PetscInt  fpoint = cone[arr[d * 2 + 0]];
4023       const PetscInt *fcone, *fornt;
4024       PetscInt        fconeSize, fc, i;
4025 
4026       PetscCall(DMPlexGetCellType(dm, fpoint, &ft));
4027       const PetscInt *farr = DMPolytopeTypeGetArrangement(ft, DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]));
4028       PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, fpoint, PETSC_TRUE, &fconeSize, &fcone, &fornt));
4029       for (fc = 0; fc < fconeSize; ++fc) {
4030         const PetscInt cp = fcone[farr[fc * 2 + 0]];
4031         const PetscInt co = farr[fc * 2 + 1];
4032 
4033         for (i = 0; i < c; i += 2)
4034           if (pts[i] == cp) break;
4035         if (i == c) {
4036           PetscCall(DMPlexGetCellType(dm, cp, &ft));
4037           pts[c++] = cp;
4038           pts[c++] = DMPolytopeTypeComposeOrientation(ft, co, fornt[farr[fc * 2 + 0]]);
4039         }
4040       }
4041       PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, fpoint, PETSC_TRUE, &fconeSize, &fcone, &fornt));
4042     }
4043   }
4044   *numPoints = c / 2;
4045   *points    = pts;
4046   PetscFunctionReturn(PETSC_SUCCESS);
4047 }
4048 
4049 PetscErrorCode DMPlexGetTransitiveClosure_Internal(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
4050 {
4051   DMPolytopeType ct;
4052   PetscInt      *closure, *fifo;
4053   PetscInt       closureSize = 0, fifoStart = 0, fifoSize = 0;
4054   PetscInt       maxConeSize, maxSupportSize, coneSeries, supportSeries;
4055   PetscInt       depth, maxSize;
4056 
4057   PetscFunctionBeginHot;
4058   PetscCall(DMPlexGetDepth(dm, &depth));
4059   if (depth == 1) {
4060     PetscCall(DMPlexGetTransitiveClosure_Depth1_Private(dm, p, ornt, useCone, numPoints, points));
4061     PetscFunctionReturn(PETSC_SUCCESS);
4062   }
4063   PetscCall(DMPlexGetCellType(dm, p, &ct));
4064   if (ct == DM_POLYTOPE_FV_GHOST || ct == DM_POLYTOPE_INTERIOR_GHOST || ct == DM_POLYTOPE_UNKNOWN || ct == DM_POLYTOPE_UNKNOWN_CELL || ct == DM_POLYTOPE_UNKNOWN_FACE) ct = DM_POLYTOPE_UNKNOWN;
4065   if (DMPolytopeTypeIsHybrid(ct) && ct != DM_POLYTOPE_POINT_PRISM_TENSOR) {
4066     PetscCall(DMPlexTransitiveClosure_Tensor_Internal(dm, p, ct, ornt, useCone, numPoints, points));
4067     PetscFunctionReturn(PETSC_SUCCESS);
4068   }
4069   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
4070   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, depth + 1) - 1) / (maxConeSize - 1)) : depth + 1;
4071   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, depth + 1) - 1) / (maxSupportSize - 1)) : depth + 1;
4072   maxSize       = PetscMax(coneSeries, supportSeries);
4073   PetscCall(DMGetWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
4074   if (*points) {
4075     closure = *points;
4076   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &closure));
4077   closure[closureSize++] = p;
4078   closure[closureSize++] = ornt;
4079   fifo[fifoSize++]       = p;
4080   fifo[fifoSize++]       = ornt;
4081   fifo[fifoSize++]       = ct;
4082   /* Should kick out early when depth is reached, rather than checking all vertices for empty cones */
4083   while (fifoSize - fifoStart) {
4084     const PetscInt       q    = fifo[fifoStart++];
4085     const PetscInt       o    = fifo[fifoStart++];
4086     const DMPolytopeType qt   = (DMPolytopeType)fifo[fifoStart++];
4087     const PetscInt      *qarr = DMPolytopeTypeGetArrangement(qt, o);
4088     const PetscInt      *tmp, *tmpO = NULL;
4089     PetscInt             tmpSize, t;
4090 
4091     if (PetscDefined(USE_DEBUG)) {
4092       PetscInt nO = DMPolytopeTypeGetNumArrangements(qt) / 2;
4093       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);
4094     }
4095     PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, q, useCone, &tmpSize, &tmp, &tmpO));
4096     for (t = 0; t < tmpSize; ++t) {
4097       const PetscInt ip = useCone && qarr ? qarr[t * 2] : t;
4098       const PetscInt io = useCone && qarr ? qarr[t * 2 + 1] : 0;
4099       const PetscInt cp = tmp[ip];
4100       PetscCall(DMPlexGetCellType(dm, cp, &ct));
4101       const PetscInt co = tmpO ? DMPolytopeTypeComposeOrientation(ct, io, tmpO[ip]) : 0;
4102       PetscInt       c;
4103 
4104       /* Check for duplicate */
4105       for (c = 0; c < closureSize; c += 2) {
4106         if (closure[c] == cp) break;
4107       }
4108       if (c == closureSize) {
4109         closure[closureSize++] = cp;
4110         closure[closureSize++] = co;
4111         fifo[fifoSize++]       = cp;
4112         fifo[fifoSize++]       = co;
4113         fifo[fifoSize++]       = ct;
4114       }
4115     }
4116     PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, q, useCone, &tmpSize, &tmp, &tmpO));
4117   }
4118   PetscCall(DMRestoreWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
4119   if (numPoints) *numPoints = closureSize / 2;
4120   if (points) *points = closure;
4121   PetscFunctionReturn(PETSC_SUCCESS);
4122 }
4123 
4124 /*@C
4125   DMPlexGetTransitiveClosure - Return the points on the transitive closure of the in-edges or out-edges for this point in the DAG
4126 
4127   Not Collective
4128 
4129   Input Parameters:
4130 + dm      - The `DMPLEX`
4131 . p       - The mesh point
4132 - useCone - `PETSC_TRUE` for the closure, otherwise return the star
4133 
4134   Input/Output Parameter:
4135 . points - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...];
4136            if *points is `NULL` on input, internal storage will be returned, use `DMPlexRestoreTransitiveClosure()`,
4137            otherwise the provided array is used to hold the values
4138 
4139   Output Parameter:
4140 . numPoints - The number of points in the closure, so `points` is of size 2*`numPoints`
4141 
4142   Level: beginner
4143 
4144   Note:
4145   If using internal storage (points is `NULL` on input), each call overwrites the last output.
4146 
4147   Fortran Notes:
4148   `points` must be declared with
4149 .vb
4150   PetscInt, pointer :: points(:)
4151 .ve
4152   and is always allocated by the function.
4153 
4154   The `numPoints` argument is not present in the Fortran binding.
4155 
4156 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
4157 @*/
4158 PetscErrorCode DMPlexGetTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
4159 {
4160   PetscFunctionBeginHot;
4161   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4162   if (numPoints) PetscAssertPointer(numPoints, 4);
4163   if (points) PetscAssertPointer(points, 5);
4164   if (PetscDefined(USE_DEBUG)) {
4165     PetscInt pStart, pEnd;
4166     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4167     PetscCheck(p >= pStart && p < pEnd, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Point %" PetscInt_FMT " is not in [%" PetscInt_FMT ", %" PetscInt_FMT ")", p, pStart, pEnd);
4168   }
4169   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, p, 0, useCone, numPoints, points));
4170   PetscFunctionReturn(PETSC_SUCCESS);
4171 }
4172 
4173 /*@C
4174   DMPlexRestoreTransitiveClosure - Restore the array of points on the transitive closure of the in-edges or out-edges for this point in the DAG
4175 
4176   Not Collective
4177 
4178   Input Parameters:
4179 + dm        - The `DMPLEX`
4180 . p         - The mesh point
4181 . useCone   - `PETSC_TRUE` for the closure, otherwise return the star
4182 . numPoints - The number of points in the closure, so points[] is of size 2*`numPoints`
4183 - points    - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...]
4184 
4185   Level: beginner
4186 
4187   Note:
4188   If not using internal storage (points is not `NULL` on input), this call is unnecessary
4189 
4190 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
4191 @*/
4192 PetscErrorCode DMPlexRestoreTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
4193 {
4194   PetscFunctionBeginHot;
4195   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4196   if (numPoints) *numPoints = 0;
4197   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, points));
4198   PetscFunctionReturn(PETSC_SUCCESS);
4199 }
4200 
4201 /*@
4202   DMPlexGetMaxSizes - Return the maximum number of in-edges (cone) and out-edges (support) for any point in the DAG
4203 
4204   Not Collective
4205 
4206   Input Parameter:
4207 . dm - The `DMPLEX`
4208 
4209   Output Parameters:
4210 + maxConeSize    - The maximum number of in-edges
4211 - maxSupportSize - The maximum number of out-edges
4212 
4213   Level: beginner
4214 
4215 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
4216 @*/
4217 PetscErrorCode DMPlexGetMaxSizes(DM dm, PetscInt *maxConeSize, PetscInt *maxSupportSize)
4218 {
4219   DM_Plex *mesh = (DM_Plex *)dm->data;
4220 
4221   PetscFunctionBegin;
4222   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4223   if (maxConeSize) PetscCall(PetscSectionGetMaxDof(mesh->coneSection, maxConeSize));
4224   if (maxSupportSize) PetscCall(PetscSectionGetMaxDof(mesh->supportSection, maxSupportSize));
4225   PetscFunctionReturn(PETSC_SUCCESS);
4226 }
4227 
4228 PetscErrorCode DMSetUp_Plex(DM dm)
4229 {
4230   DM_Plex *mesh = (DM_Plex *)dm->data;
4231   PetscInt size, maxSupportSize;
4232 
4233   PetscFunctionBegin;
4234   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4235   PetscCall(PetscSectionSetUp(mesh->coneSection));
4236   PetscCall(PetscSectionGetStorageSize(mesh->coneSection, &size));
4237   PetscCall(PetscMalloc1(size, &mesh->cones));
4238   PetscCall(PetscCalloc1(size, &mesh->coneOrientations));
4239   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
4240   if (maxSupportSize) {
4241     PetscCall(PetscSectionSetUp(mesh->supportSection));
4242     PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &size));
4243     PetscCall(PetscMalloc1(size, &mesh->supports));
4244   }
4245   PetscFunctionReturn(PETSC_SUCCESS);
4246 }
4247 
4248 PetscErrorCode DMCreateSubDM_Plex(DM dm, PetscInt numFields, const PetscInt fields[], IS *is, DM *subdm)
4249 {
4250   PetscFunctionBegin;
4251   if (subdm) PetscCall(DMClone(dm, subdm));
4252   PetscCall(DMCreateSectionSubDM(dm, numFields, fields, NULL, NULL, is, subdm));
4253   if (subdm) (*subdm)->useNatural = dm->useNatural;
4254   if (dm->useNatural && dm->sfMigration) {
4255     PetscSF sfNatural;
4256 
4257     (*subdm)->sfMigration = dm->sfMigration;
4258     PetscCall(PetscObjectReference((PetscObject)dm->sfMigration));
4259     PetscCall(DMPlexCreateGlobalToNaturalSF(*subdm, NULL, (*subdm)->sfMigration, &sfNatural));
4260     (*subdm)->sfNatural = sfNatural;
4261   }
4262   PetscFunctionReturn(PETSC_SUCCESS);
4263 }
4264 
4265 PetscErrorCode DMCreateSuperDM_Plex(DM dms[], PetscInt len, IS **is, DM *superdm)
4266 {
4267   PetscInt i = 0;
4268 
4269   PetscFunctionBegin;
4270   PetscCall(DMClone(dms[0], superdm));
4271   PetscCall(DMCreateSectionSuperDM(dms, len, is, superdm));
4272   (*superdm)->useNatural = PETSC_FALSE;
4273   for (i = 0; i < len; i++) {
4274     if (dms[i]->useNatural && dms[i]->sfMigration) {
4275       PetscSF sfNatural;
4276 
4277       (*superdm)->sfMigration = dms[i]->sfMigration;
4278       PetscCall(PetscObjectReference((PetscObject)dms[i]->sfMigration));
4279       (*superdm)->useNatural = PETSC_TRUE;
4280       PetscCall(DMPlexCreateGlobalToNaturalSF(*superdm, NULL, (*superdm)->sfMigration, &sfNatural));
4281       (*superdm)->sfNatural = sfNatural;
4282       break;
4283     }
4284   }
4285   PetscFunctionReturn(PETSC_SUCCESS);
4286 }
4287 
4288 /*@
4289   DMPlexSymmetrize - Create support (out-edge) information from cone (in-edge) information
4290 
4291   Not Collective
4292 
4293   Input Parameter:
4294 . dm - The `DMPLEX`
4295 
4296   Level: beginner
4297 
4298   Note:
4299   This should be called after all calls to `DMPlexSetCone()`
4300 
4301 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMPlexSetCone()`
4302 @*/
4303 PetscErrorCode DMPlexSymmetrize(DM dm)
4304 {
4305   DM_Plex  *mesh = (DM_Plex *)dm->data;
4306   PetscInt *offsets;
4307   PetscInt  supportSize;
4308   PetscInt  pStart, pEnd, p;
4309 
4310   PetscFunctionBegin;
4311   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4312   PetscCheck(!mesh->supports, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONGSTATE, "Supports were already setup in this DMPlex");
4313   PetscCall(PetscLogEventBegin(DMPLEX_Symmetrize, dm, 0, 0, 0));
4314   /* Calculate support sizes */
4315   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4316   for (p = pStart; p < pEnd; ++p) {
4317     PetscInt dof, off, c;
4318 
4319     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
4320     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
4321     for (c = off; c < off + dof; ++c) PetscCall(PetscSectionAddDof(mesh->supportSection, mesh->cones[c], 1));
4322   }
4323   PetscCall(PetscSectionSetUp(mesh->supportSection));
4324   /* Calculate supports */
4325   PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &supportSize));
4326   PetscCall(PetscMalloc1(supportSize, &mesh->supports));
4327   PetscCall(PetscCalloc1(pEnd - pStart, &offsets));
4328   for (p = pStart; p < pEnd; ++p) {
4329     PetscInt dof, off, c;
4330 
4331     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
4332     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
4333     for (c = off; c < off + dof; ++c) {
4334       const PetscInt q = mesh->cones[c];
4335       PetscInt       offS;
4336 
4337       PetscCall(PetscSectionGetOffset(mesh->supportSection, q, &offS));
4338 
4339       mesh->supports[offS + offsets[q]] = p;
4340       ++offsets[q];
4341     }
4342   }
4343   PetscCall(PetscFree(offsets));
4344   PetscCall(PetscLogEventEnd(DMPLEX_Symmetrize, dm, 0, 0, 0));
4345   PetscFunctionReturn(PETSC_SUCCESS);
4346 }
4347 
4348 static PetscErrorCode DMPlexCreateDepthStratum(DM dm, DMLabel label, PetscInt depth, PetscInt pStart, PetscInt pEnd)
4349 {
4350   IS stratumIS;
4351 
4352   PetscFunctionBegin;
4353   if (pStart >= pEnd) PetscFunctionReturn(PETSC_SUCCESS);
4354   if (PetscDefined(USE_DEBUG)) {
4355     PetscInt  qStart, qEnd, numLevels, level;
4356     PetscBool overlap = PETSC_FALSE;
4357     PetscCall(DMLabelGetNumValues(label, &numLevels));
4358     for (level = 0; level < numLevels; level++) {
4359       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4360       if ((pStart >= qStart && pStart < qEnd) || (pEnd > qStart && pEnd <= qEnd)) {
4361         overlap = PETSC_TRUE;
4362         break;
4363       }
4364     }
4365     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);
4366   }
4367   PetscCall(ISCreateStride(PETSC_COMM_SELF, pEnd - pStart, pStart, 1, &stratumIS));
4368   PetscCall(DMLabelSetStratumIS(label, depth, stratumIS));
4369   PetscCall(ISDestroy(&stratumIS));
4370   PetscFunctionReturn(PETSC_SUCCESS);
4371 }
4372 
4373 static PetscErrorCode DMPlexStratify_CellType_Private(DM dm, DMLabel label)
4374 {
4375   PetscInt *pMin, *pMax;
4376   PetscInt  pStart, pEnd;
4377   PetscInt  dmin = PETSC_INT_MAX, dmax = PETSC_INT_MIN;
4378 
4379   PetscFunctionBegin;
4380   {
4381     DMLabel label2;
4382 
4383     PetscCall(DMPlexGetCellTypeLabel(dm, &label2));
4384     PetscCall(PetscObjectViewFromOptions((PetscObject)label2, NULL, "-ct_view"));
4385   }
4386   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4387   for (PetscInt p = pStart; p < pEnd; ++p) {
4388     DMPolytopeType ct;
4389 
4390     PetscCall(DMPlexGetCellType(dm, p, &ct));
4391     dmin = PetscMin(DMPolytopeTypeGetDim(ct), dmin);
4392     dmax = PetscMax(DMPolytopeTypeGetDim(ct), dmax);
4393   }
4394   PetscCall(PetscMalloc2(dmax + 1, &pMin, dmax + 1, &pMax));
4395   for (PetscInt d = dmin; d <= dmax; ++d) {
4396     pMin[d] = PETSC_INT_MAX;
4397     pMax[d] = PETSC_INT_MIN;
4398   }
4399   for (PetscInt p = pStart; p < pEnd; ++p) {
4400     DMPolytopeType ct;
4401     PetscInt       d;
4402 
4403     PetscCall(DMPlexGetCellType(dm, p, &ct));
4404     d       = DMPolytopeTypeGetDim(ct);
4405     pMin[d] = PetscMin(p, pMin[d]);
4406     pMax[d] = PetscMax(p, pMax[d]);
4407   }
4408   for (PetscInt d = dmin; d <= dmax; ++d) {
4409     if (pMin[d] > pMax[d]) continue;
4410     PetscCall(DMPlexCreateDepthStratum(dm, label, d, pMin[d], pMax[d] + 1));
4411   }
4412   PetscCall(PetscFree2(pMin, pMax));
4413   PetscFunctionReturn(PETSC_SUCCESS);
4414 }
4415 
4416 static PetscErrorCode DMPlexStratify_Topological_Private(DM dm, DMLabel label)
4417 {
4418   PetscInt pStart, pEnd;
4419   PetscInt numRoots = 0, numLeaves = 0;
4420 
4421   PetscFunctionBegin;
4422   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4423   {
4424     /* Initialize roots and count leaves */
4425     PetscInt sMin = PETSC_INT_MAX;
4426     PetscInt sMax = PETSC_INT_MIN;
4427     PetscInt coneSize, supportSize;
4428 
4429     for (PetscInt p = pStart; p < pEnd; ++p) {
4430       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4431       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
4432       if (!coneSize && supportSize) {
4433         sMin = PetscMin(p, sMin);
4434         sMax = PetscMax(p, sMax);
4435         ++numRoots;
4436       } else if (!supportSize && coneSize) {
4437         ++numLeaves;
4438       } else if (!supportSize && !coneSize) {
4439         /* Isolated points */
4440         sMin = PetscMin(p, sMin);
4441         sMax = PetscMax(p, sMax);
4442       }
4443     }
4444     PetscCall(DMPlexCreateDepthStratum(dm, label, 0, sMin, sMax + 1));
4445   }
4446 
4447   if (numRoots + numLeaves == (pEnd - pStart)) {
4448     PetscInt sMin = PETSC_INT_MAX;
4449     PetscInt sMax = PETSC_INT_MIN;
4450     PetscInt coneSize, supportSize;
4451 
4452     for (PetscInt p = pStart; p < pEnd; ++p) {
4453       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4454       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
4455       if (!supportSize && coneSize) {
4456         sMin = PetscMin(p, sMin);
4457         sMax = PetscMax(p, sMax);
4458       }
4459     }
4460     PetscCall(DMPlexCreateDepthStratum(dm, label, 1, sMin, sMax + 1));
4461   } else {
4462     PetscInt level = 0;
4463     PetscInt qStart, qEnd;
4464 
4465     PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4466     while (qEnd > qStart) {
4467       PetscInt sMin = PETSC_INT_MAX;
4468       PetscInt sMax = PETSC_INT_MIN;
4469 
4470       for (PetscInt q = qStart; q < qEnd; ++q) {
4471         const PetscInt *support;
4472         PetscInt        supportSize;
4473 
4474         PetscCall(DMPlexGetSupportSize(dm, q, &supportSize));
4475         PetscCall(DMPlexGetSupport(dm, q, &support));
4476         for (PetscInt s = 0; s < supportSize; ++s) {
4477           sMin = PetscMin(support[s], sMin);
4478           sMax = PetscMax(support[s], sMax);
4479         }
4480       }
4481       PetscCall(DMLabelGetNumValues(label, &level));
4482       PetscCall(DMPlexCreateDepthStratum(dm, label, level, sMin, sMax + 1));
4483       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4484     }
4485   }
4486   PetscFunctionReturn(PETSC_SUCCESS);
4487 }
4488 
4489 /*@
4490   DMPlexStratify - Computes the strata for all points in the `DMPLEX`
4491 
4492   Collective
4493 
4494   Input Parameter:
4495 . dm - The `DMPLEX`
4496 
4497   Level: beginner
4498 
4499   Notes:
4500   The strata group all points of the same grade, and this function calculates the strata. This
4501   grade can be seen as the height (or depth) of the point in the DAG.
4502 
4503   The DAG for most topologies is a graded poset (https://en.wikipedia.org/wiki/Graded_poset), and
4504   can be illustrated by a Hasse Diagram (https://en.wikipedia.org/wiki/Hasse_diagram).
4505   Concretely, `DMPlexStratify()` creates a new label named "depth" containing the depth in the DAG of each point. For cell-vertex
4506   meshes, vertices are depth 0 and cells are depth 1. For fully interpolated meshes, depth 0 for vertices, 1 for edges, and so on
4507   until cells have depth equal to the dimension of the mesh. The depth label can be accessed through `DMPlexGetDepthLabel()` or `DMPlexGetDepthStratum()`, or
4508   manually via `DMGetLabel()`.  The height is defined implicitly by height = maxDimension - depth, and can be accessed
4509   via `DMPlexGetHeightStratum()`.  For example, cells have height 0 and faces have height 1.
4510 
4511   The depth of a point is calculated by executing a breadth-first search (BFS) on the DAG. This could produce surprising results
4512   if run on a partially interpolated mesh, meaning one that had some edges and faces, but not others. For example, suppose that
4513   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
4514   to interpolate only that one (e0), so that
4515 .vb
4516   cone(c0) = {e0, v2}
4517   cone(e0) = {v0, v1}
4518 .ve
4519   If `DMPlexStratify()` is run on this mesh, it will give depths
4520 .vb
4521    depth 0 = {v0, v1, v2}
4522    depth 1 = {e0, c0}
4523 .ve
4524   where the triangle has been given depth 1, instead of 2, because it is reachable from vertex v2.
4525 
4526   `DMPlexStratify()` should be called after all calls to `DMPlexSymmetrize()`
4527 
4528 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexComputeCellTypes()`
4529 @*/
4530 PetscErrorCode DMPlexStratify(DM dm)
4531 {
4532   DM_Plex  *mesh = (DM_Plex *)dm->data;
4533   DMLabel   label;
4534   PetscBool flg = PETSC_FALSE;
4535 
4536   PetscFunctionBegin;
4537   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4538   PetscCall(PetscLogEventBegin(DMPLEX_Stratify, dm, 0, 0, 0));
4539 
4540   // Create depth label
4541   PetscCall(DMRemoveLabel(dm, "depth", NULL));
4542   PetscCall(DMCreateLabel(dm, "depth"));
4543   PetscCall(DMPlexGetDepthLabel(dm, &label));
4544 
4545   PetscCall(PetscOptionsGetBool(NULL, dm->hdr.prefix, "-dm_plex_stratify_celltype", &flg, NULL));
4546   if (flg) PetscCall(DMPlexStratify_CellType_Private(dm, label));
4547   else PetscCall(DMPlexStratify_Topological_Private(dm, label));
4548 
4549   { /* just in case there is an empty process */
4550     PetscInt numValues, maxValues = 0, v;
4551 
4552     PetscCall(DMLabelGetNumValues(label, &numValues));
4553     PetscCallMPI(MPIU_Allreduce(&numValues, &maxValues, 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
4554     for (v = numValues; v < maxValues; v++) PetscCall(DMLabelAddStratum(label, v));
4555   }
4556   PetscCall(PetscObjectStateGet((PetscObject)label, &mesh->depthState));
4557   PetscCall(PetscLogEventEnd(DMPLEX_Stratify, dm, 0, 0, 0));
4558   PetscFunctionReturn(PETSC_SUCCESS);
4559 }
4560 
4561 PetscErrorCode DMPlexComputeCellType_Internal(DM dm, PetscInt p, PetscInt pdepth, DMPolytopeType *pt)
4562 {
4563   DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4564   PetscInt       dim, depth, pheight, coneSize;
4565 
4566   PetscFunctionBeginHot;
4567   PetscCall(DMGetDimension(dm, &dim));
4568   PetscCall(DMPlexGetDepth(dm, &depth));
4569   PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4570   pheight = depth - pdepth;
4571   if (depth <= 1) {
4572     switch (pdepth) {
4573     case 0:
4574       ct = DM_POLYTOPE_POINT;
4575       break;
4576     case 1:
4577       switch (coneSize) {
4578       case 2:
4579         ct = DM_POLYTOPE_SEGMENT;
4580         break;
4581       case 3:
4582         ct = DM_POLYTOPE_TRIANGLE;
4583         break;
4584       case 4:
4585         switch (dim) {
4586         case 2:
4587           ct = DM_POLYTOPE_QUADRILATERAL;
4588           break;
4589         case 3:
4590           ct = DM_POLYTOPE_TETRAHEDRON;
4591           break;
4592         default:
4593           break;
4594         }
4595         break;
4596       case 5:
4597         ct = DM_POLYTOPE_PYRAMID;
4598         break;
4599       case 6:
4600         ct = DM_POLYTOPE_TRI_PRISM_TENSOR;
4601         break;
4602       case 8:
4603         ct = DM_POLYTOPE_HEXAHEDRON;
4604         break;
4605       default:
4606         break;
4607       }
4608     }
4609   } else {
4610     if (pdepth == 0) {
4611       ct = DM_POLYTOPE_POINT;
4612     } else if (pheight == 0) {
4613       switch (dim) {
4614       case 1:
4615         switch (coneSize) {
4616         case 2:
4617           ct = DM_POLYTOPE_SEGMENT;
4618           break;
4619         default:
4620           break;
4621         }
4622         break;
4623       case 2:
4624         switch (coneSize) {
4625         case 3:
4626           ct = DM_POLYTOPE_TRIANGLE;
4627           break;
4628         case 4:
4629           ct = DM_POLYTOPE_QUADRILATERAL;
4630           break;
4631         default:
4632           break;
4633         }
4634         break;
4635       case 3:
4636         switch (coneSize) {
4637         case 4:
4638           ct = DM_POLYTOPE_TETRAHEDRON;
4639           break;
4640         case 5: {
4641           const PetscInt *cone;
4642           PetscInt        faceConeSize;
4643 
4644           PetscCall(DMPlexGetCone(dm, p, &cone));
4645           PetscCall(DMPlexGetConeSize(dm, cone[0], &faceConeSize));
4646           switch (faceConeSize) {
4647           case 3:
4648             ct = DM_POLYTOPE_TRI_PRISM_TENSOR;
4649             break;
4650           case 4:
4651             ct = DM_POLYTOPE_PYRAMID;
4652             break;
4653           }
4654         } break;
4655         case 6:
4656           ct = DM_POLYTOPE_HEXAHEDRON;
4657           break;
4658         default:
4659           break;
4660         }
4661         break;
4662       default:
4663         break;
4664       }
4665     } else if (pheight > 0) {
4666       switch (coneSize) {
4667       case 2:
4668         ct = DM_POLYTOPE_SEGMENT;
4669         break;
4670       case 3:
4671         ct = DM_POLYTOPE_TRIANGLE;
4672         break;
4673       case 4:
4674         ct = DM_POLYTOPE_QUADRILATERAL;
4675         break;
4676       default:
4677         break;
4678       }
4679     }
4680   }
4681   *pt = ct;
4682   PetscFunctionReturn(PETSC_SUCCESS);
4683 }
4684 
4685 /*@
4686   DMPlexComputeCellTypes - Infer the polytope type of every cell using its dimension and cone size.
4687 
4688   Collective
4689 
4690   Input Parameter:
4691 . dm - The `DMPLEX`
4692 
4693   Level: developer
4694 
4695   Note:
4696   This function is normally called automatically when a cell type is requested. It creates an
4697   internal `DMLabel` named "celltype" which can be directly accessed using `DMGetLabel()`. A user may disable
4698   automatic creation by creating the label manually, using `DMCreateLabel`(dm, "celltype").
4699 
4700   `DMPlexComputeCellTypes()` should be called after all calls to `DMPlexSymmetrize()` and `DMPlexStratify()`
4701 
4702 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexStratify()`, `DMGetLabel()`, `DMCreateLabel()`
4703 @*/
4704 PetscErrorCode DMPlexComputeCellTypes(DM dm)
4705 {
4706   DM_Plex *mesh;
4707   DMLabel  ctLabel;
4708   PetscInt pStart, pEnd, p;
4709 
4710   PetscFunctionBegin;
4711   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4712   mesh = (DM_Plex *)dm->data;
4713   PetscCall(DMCreateLabel(dm, "celltype"));
4714   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
4715   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4716   PetscCall(PetscFree(mesh->cellTypes));
4717   PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
4718   for (p = pStart; p < pEnd; ++p) {
4719     DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4720     PetscInt       pdepth;
4721 
4722     PetscCall(DMPlexGetPointDepth(dm, p, &pdepth));
4723     PetscCall(DMPlexComputeCellType_Internal(dm, p, pdepth, &ct));
4724     PetscCheck(ct != DM_POLYTOPE_UNKNOWN && ct != DM_POLYTOPE_UNKNOWN_CELL && ct != DM_POLYTOPE_UNKNOWN_FACE, PETSC_COMM_SELF, PETSC_ERR_SUP, "Point %" PetscInt_FMT " has invalid celltype (%s)", p, DMPolytopeTypes[ct]);
4725     PetscCall(DMLabelSetValue(ctLabel, p, ct));
4726     mesh->cellTypes[p - pStart].value_as_uint8 = (uint8_t)ct;
4727   }
4728   PetscCall(PetscObjectStateGet((PetscObject)ctLabel, &mesh->celltypeState));
4729   PetscCall(PetscObjectViewFromOptions((PetscObject)ctLabel, NULL, "-dm_plex_celltypes_view"));
4730   PetscFunctionReturn(PETSC_SUCCESS);
4731 }
4732 
4733 /*@C
4734   DMPlexGetJoin - Get an array for the join of the set of points
4735 
4736   Not Collective
4737 
4738   Input Parameters:
4739 + dm        - The `DMPLEX` object
4740 . numPoints - The number of input points for the join
4741 - points    - The input points
4742 
4743   Output Parameters:
4744 + numCoveredPoints - The number of points in the join
4745 - coveredPoints    - The points in the join
4746 
4747   Level: intermediate
4748 
4749   Note:
4750   Currently, this is restricted to a single level join
4751 
4752   Fortran Notes:
4753   `converedPoints` must be declared with
4754 .vb
4755   PetscInt, pointer :: coveredPints(:)
4756 .ve
4757 
4758   The `numCoveredPoints` argument is not present in the Fortran binding.
4759 
4760 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4761 @*/
4762 PetscErrorCode DMPlexGetJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt *coveredPoints[])
4763 {
4764   DM_Plex  *mesh = (DM_Plex *)dm->data;
4765   PetscInt *join[2];
4766   PetscInt  joinSize, i = 0;
4767   PetscInt  dof, off, p, c, m;
4768   PetscInt  maxSupportSize;
4769 
4770   PetscFunctionBegin;
4771   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4772   PetscAssertPointer(points, 3);
4773   PetscAssertPointer(numCoveredPoints, 4);
4774   PetscAssertPointer(coveredPoints, 5);
4775   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
4776   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[0]));
4777   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[1]));
4778   /* Copy in support of first point */
4779   PetscCall(PetscSectionGetDof(mesh->supportSection, points[0], &dof));
4780   PetscCall(PetscSectionGetOffset(mesh->supportSection, points[0], &off));
4781   for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = mesh->supports[off + joinSize];
4782   /* Check each successive support */
4783   for (p = 1; p < numPoints; ++p) {
4784     PetscInt newJoinSize = 0;
4785 
4786     PetscCall(PetscSectionGetDof(mesh->supportSection, points[p], &dof));
4787     PetscCall(PetscSectionGetOffset(mesh->supportSection, points[p], &off));
4788     for (c = 0; c < dof; ++c) {
4789       const PetscInt point = mesh->supports[off + c];
4790 
4791       for (m = 0; m < joinSize; ++m) {
4792         if (point == join[i][m]) {
4793           join[1 - i][newJoinSize++] = point;
4794           break;
4795         }
4796       }
4797     }
4798     joinSize = newJoinSize;
4799     i        = 1 - i;
4800   }
4801   *numCoveredPoints = joinSize;
4802   *coveredPoints    = join[i];
4803   PetscCall(DMRestoreWorkArray(dm, maxSupportSize, MPIU_INT, &join[1 - i]));
4804   PetscFunctionReturn(PETSC_SUCCESS);
4805 }
4806 
4807 /*@C
4808   DMPlexRestoreJoin - Restore an array for the join of the set of points obtained with `DMPlexGetJoin()`
4809 
4810   Not Collective
4811 
4812   Input Parameters:
4813 + dm        - The `DMPLEX` object
4814 . numPoints - The number of input points for the join
4815 - points    - The input points
4816 
4817   Output Parameters:
4818 + numCoveredPoints - The number of points in the join
4819 - coveredPoints    - The points in the join
4820 
4821   Level: intermediate
4822 
4823   Fortran Notes:
4824   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4825 
4826 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetJoin()`, `DMPlexGetFullJoin()`, `DMPlexGetMeet()`
4827 @*/
4828 PetscErrorCode DMPlexRestoreJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt *coveredPoints[])
4829 {
4830   PetscFunctionBegin;
4831   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4832   if (points) PetscAssertPointer(points, 3);
4833   if (numCoveredPoints) PetscAssertPointer(numCoveredPoints, 4);
4834   PetscAssertPointer(coveredPoints, 5);
4835   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
4836   if (numCoveredPoints) *numCoveredPoints = 0;
4837   PetscFunctionReturn(PETSC_SUCCESS);
4838 }
4839 
4840 /*@C
4841   DMPlexGetFullJoin - Get an array for the join of the set of points
4842 
4843   Not Collective
4844 
4845   Input Parameters:
4846 + dm        - The `DMPLEX` object
4847 . numPoints - The number of input points for the join
4848 - points    - The input points, its length is `numPoints`
4849 
4850   Output Parameters:
4851 + numCoveredPoints - The number of points in the join
4852 - coveredPoints    - The points in the join, its length is `numCoveredPoints`
4853 
4854   Level: intermediate
4855 
4856   Fortran Notes:
4857   `points` and `converedPoints` must be declared with
4858 .vb
4859   PetscInt, pointer :: points(:)
4860   PetscInt, pointer :: coveredPints(:)
4861 .ve
4862 
4863   The `numCoveredPoints` argument is not present in the Fortran binding.
4864 
4865 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetJoin()`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4866 @*/
4867 PetscErrorCode DMPlexGetFullJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt *coveredPoints[])
4868 {
4869   PetscInt *offsets, **closures;
4870   PetscInt *join[2];
4871   PetscInt  depth = 0, maxSize, joinSize = 0, i = 0;
4872   PetscInt  p, d, c, m, ms;
4873 
4874   PetscFunctionBegin;
4875   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4876   PetscAssertPointer(points, 3);
4877   PetscAssertPointer(numCoveredPoints, 4);
4878   PetscAssertPointer(coveredPoints, 5);
4879 
4880   PetscCall(DMPlexGetDepth(dm, &depth));
4881   PetscCall(PetscCalloc1(numPoints, &closures));
4882   PetscCall(DMGetWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
4883   PetscCall(DMPlexGetMaxSizes(dm, NULL, &ms));
4884   maxSize = (ms > 1) ? ((PetscPowInt(ms, depth + 1) - 1) / (ms - 1)) : depth + 1;
4885   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[0]));
4886   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[1]));
4887 
4888   for (p = 0; p < numPoints; ++p) {
4889     PetscInt closureSize;
4890 
4891     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_FALSE, &closureSize, &closures[p]));
4892 
4893     offsets[p * (depth + 2) + 0] = 0;
4894     for (d = 0; d < depth + 1; ++d) {
4895       PetscInt pStart, pEnd, i;
4896 
4897       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
4898       for (i = offsets[p * (depth + 2) + d]; i < closureSize; ++i) {
4899         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
4900           offsets[p * (depth + 2) + d + 1] = i;
4901           break;
4902         }
4903       }
4904       if (i == closureSize) offsets[p * (depth + 2) + d + 1] = i;
4905     }
4906     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);
4907   }
4908   for (d = 0; d < depth + 1; ++d) {
4909     PetscInt dof;
4910 
4911     /* Copy in support of first point */
4912     dof = offsets[d + 1] - offsets[d];
4913     for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = closures[0][(offsets[d] + joinSize) * 2];
4914     /* Check each successive cone */
4915     for (p = 1; p < numPoints && joinSize; ++p) {
4916       PetscInt newJoinSize = 0;
4917 
4918       dof = offsets[p * (depth + 2) + d + 1] - offsets[p * (depth + 2) + d];
4919       for (c = 0; c < dof; ++c) {
4920         const PetscInt point = closures[p][(offsets[p * (depth + 2) + d] + c) * 2];
4921 
4922         for (m = 0; m < joinSize; ++m) {
4923           if (point == join[i][m]) {
4924             join[1 - i][newJoinSize++] = point;
4925             break;
4926           }
4927         }
4928       }
4929       joinSize = newJoinSize;
4930       i        = 1 - i;
4931     }
4932     if (joinSize) break;
4933   }
4934   *numCoveredPoints = joinSize;
4935   *coveredPoints    = join[i];
4936   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_FALSE, NULL, &closures[p]));
4937   PetscCall(PetscFree(closures));
4938   PetscCall(DMRestoreWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
4939   PetscCall(DMRestoreWorkArray(dm, ms, MPIU_INT, &join[1 - i]));
4940   PetscFunctionReturn(PETSC_SUCCESS);
4941 }
4942 
4943 /*@C
4944   DMPlexGetMeet - Get an array for the meet of the set of points
4945 
4946   Not Collective
4947 
4948   Input Parameters:
4949 + dm        - The `DMPLEX` object
4950 . numPoints - The number of input points for the meet
4951 - points    - The input points, of length `numPoints`
4952 
4953   Output Parameters:
4954 + numCoveringPoints - The number of points in the meet
4955 - coveringPoints    - The points in the meet, of length `numCoveringPoints`
4956 
4957   Level: intermediate
4958 
4959   Note:
4960   Currently, this is restricted to a single level meet
4961 
4962   Fortran Notes:
4963   `coveringPoints` must be declared with
4964 .vb
4965   PetscInt, pointer :: coveringPoints(:)
4966 .ve
4967 
4968   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4969 
4970 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
4971 @*/
4972 PetscErrorCode DMPlexGetMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveringPoints, const PetscInt *coveringPoints[])
4973 {
4974   DM_Plex  *mesh = (DM_Plex *)dm->data;
4975   PetscInt *meet[2];
4976   PetscInt  meetSize, i = 0;
4977   PetscInt  dof, off, p, c, m;
4978   PetscInt  maxConeSize;
4979 
4980   PetscFunctionBegin;
4981   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4982   PetscAssertPointer(points, 3);
4983   PetscAssertPointer(numCoveringPoints, 4);
4984   PetscAssertPointer(coveringPoints, 5);
4985   PetscCall(PetscSectionGetMaxDof(mesh->coneSection, &maxConeSize));
4986   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[0]));
4987   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[1]));
4988   /* Copy in cone of first point */
4989   PetscCall(PetscSectionGetDof(mesh->coneSection, points[0], &dof));
4990   PetscCall(PetscSectionGetOffset(mesh->coneSection, points[0], &off));
4991   for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = mesh->cones[off + meetSize];
4992   /* Check each successive cone */
4993   for (p = 1; p < numPoints; ++p) {
4994     PetscInt newMeetSize = 0;
4995 
4996     PetscCall(PetscSectionGetDof(mesh->coneSection, points[p], &dof));
4997     PetscCall(PetscSectionGetOffset(mesh->coneSection, points[p], &off));
4998     for (c = 0; c < dof; ++c) {
4999       const PetscInt point = mesh->cones[off + c];
5000 
5001       for (m = 0; m < meetSize; ++m) {
5002         if (point == meet[i][m]) {
5003           meet[1 - i][newMeetSize++] = point;
5004           break;
5005         }
5006       }
5007     }
5008     meetSize = newMeetSize;
5009     i        = 1 - i;
5010   }
5011   *numCoveringPoints = meetSize;
5012   *coveringPoints    = meet[i];
5013   PetscCall(DMRestoreWorkArray(dm, maxConeSize, MPIU_INT, &meet[1 - i]));
5014   PetscFunctionReturn(PETSC_SUCCESS);
5015 }
5016 
5017 /*@C
5018   DMPlexRestoreMeet - Restore an array for the meet of the set of points obtained with `DMPlexGetMeet()`
5019 
5020   Not Collective
5021 
5022   Input Parameters:
5023 + dm        - The `DMPLEX` object
5024 . numPoints - The number of input points for the meet
5025 - points    - The input points
5026 
5027   Output Parameters:
5028 + numCoveredPoints - The number of points in the meet
5029 - coveredPoints    - The points in the meet
5030 
5031   Level: intermediate
5032 
5033   Fortran Notes:
5034   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
5035 
5036 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetMeet()`, `DMPlexGetFullMeet()`, `DMPlexGetJoin()`
5037 @*/
5038 PetscErrorCode DMPlexRestoreMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt *coveredPoints[])
5039 {
5040   PetscFunctionBegin;
5041   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5042   if (points) PetscAssertPointer(points, 3);
5043   if (numCoveredPoints) PetscAssertPointer(numCoveredPoints, 4);
5044   PetscAssertPointer(coveredPoints, 5);
5045   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
5046   if (numCoveredPoints) *numCoveredPoints = 0;
5047   PetscFunctionReturn(PETSC_SUCCESS);
5048 }
5049 
5050 /*@C
5051   DMPlexGetFullMeet - Get an array for the meet of the set of points
5052 
5053   Not Collective
5054 
5055   Input Parameters:
5056 + dm        - The `DMPLEX` object
5057 . numPoints - The number of input points for the meet
5058 - points    - The input points, of length  `numPoints`
5059 
5060   Output Parameters:
5061 + numCoveredPoints - The number of points in the meet
5062 - coveredPoints    - The points in the meet, of length  `numCoveredPoints`
5063 
5064   Level: intermediate
5065 
5066   Fortran Notes:
5067   `points` and `coveredPoints` must be declared with
5068 .vb
5069   PetscInt, pointer :: points(:)
5070   PetscInt, pointer :: coveredPoints(:)
5071 .ve
5072 
5073   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
5074 
5075 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetMeet()`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
5076 @*/
5077 PetscErrorCode DMPlexGetFullMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt *coveredPoints[])
5078 {
5079   PetscInt *offsets, **closures;
5080   PetscInt *meet[2];
5081   PetscInt  height = 0, maxSize, meetSize = 0, i = 0;
5082   PetscInt  p, h, c, m, mc;
5083 
5084   PetscFunctionBegin;
5085   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5086   PetscAssertPointer(points, 3);
5087   PetscAssertPointer(numCoveredPoints, 4);
5088   PetscAssertPointer(coveredPoints, 5);
5089 
5090   PetscCall(DMPlexGetDepth(dm, &height));
5091   PetscCall(PetscMalloc1(numPoints, &closures));
5092   PetscCall(DMGetWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
5093   PetscCall(DMPlexGetMaxSizes(dm, &mc, NULL));
5094   maxSize = (mc > 1) ? ((PetscPowInt(mc, height + 1) - 1) / (mc - 1)) : height + 1;
5095   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[0]));
5096   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[1]));
5097 
5098   for (p = 0; p < numPoints; ++p) {
5099     PetscInt closureSize;
5100 
5101     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_TRUE, &closureSize, &closures[p]));
5102 
5103     offsets[p * (height + 2) + 0] = 0;
5104     for (h = 0; h < height + 1; ++h) {
5105       PetscInt pStart, pEnd, i;
5106 
5107       PetscCall(DMPlexGetHeightStratum(dm, h, &pStart, &pEnd));
5108       for (i = offsets[p * (height + 2) + h]; i < closureSize; ++i) {
5109         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
5110           offsets[p * (height + 2) + h + 1] = i;
5111           break;
5112         }
5113       }
5114       if (i == closureSize) offsets[p * (height + 2) + h + 1] = i;
5115     }
5116     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);
5117   }
5118   for (h = 0; h < height + 1; ++h) {
5119     PetscInt dof;
5120 
5121     /* Copy in cone of first point */
5122     dof = offsets[h + 1] - offsets[h];
5123     for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = closures[0][(offsets[h] + meetSize) * 2];
5124     /* Check each successive cone */
5125     for (p = 1; p < numPoints && meetSize; ++p) {
5126       PetscInt newMeetSize = 0;
5127 
5128       dof = offsets[p * (height + 2) + h + 1] - offsets[p * (height + 2) + h];
5129       for (c = 0; c < dof; ++c) {
5130         const PetscInt point = closures[p][(offsets[p * (height + 2) + h] + c) * 2];
5131 
5132         for (m = 0; m < meetSize; ++m) {
5133           if (point == meet[i][m]) {
5134             meet[1 - i][newMeetSize++] = point;
5135             break;
5136           }
5137         }
5138       }
5139       meetSize = newMeetSize;
5140       i        = 1 - i;
5141     }
5142     if (meetSize) break;
5143   }
5144   *numCoveredPoints = meetSize;
5145   *coveredPoints    = meet[i];
5146   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_TRUE, NULL, &closures[p]));
5147   PetscCall(PetscFree(closures));
5148   PetscCall(DMRestoreWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
5149   PetscCall(DMRestoreWorkArray(dm, mc, MPIU_INT, &meet[1 - i]));
5150   PetscFunctionReturn(PETSC_SUCCESS);
5151 }
5152 
5153 /*@
5154   DMPlexEqual - Determine if two `DM` have the same topology
5155 
5156   Not Collective
5157 
5158   Input Parameters:
5159 + dmA - A `DMPLEX` object
5160 - dmB - A `DMPLEX` object
5161 
5162   Output Parameter:
5163 . equal - `PETSC_TRUE` if the topologies are identical
5164 
5165   Level: intermediate
5166 
5167   Note:
5168   We are not solving graph isomorphism, so we do not permute.
5169 
5170 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCone()`
5171 @*/
5172 PetscErrorCode DMPlexEqual(DM dmA, DM dmB, PetscBool *equal)
5173 {
5174   PetscInt depth, depthB, pStart, pEnd, pStartB, pEndB, p;
5175 
5176   PetscFunctionBegin;
5177   PetscValidHeaderSpecific(dmA, DM_CLASSID, 1);
5178   PetscValidHeaderSpecific(dmB, DM_CLASSID, 2);
5179   PetscAssertPointer(equal, 3);
5180 
5181   *equal = PETSC_FALSE;
5182   PetscCall(DMPlexGetDepth(dmA, &depth));
5183   PetscCall(DMPlexGetDepth(dmB, &depthB));
5184   if (depth != depthB) PetscFunctionReturn(PETSC_SUCCESS);
5185   PetscCall(DMPlexGetChart(dmA, &pStart, &pEnd));
5186   PetscCall(DMPlexGetChart(dmB, &pStartB, &pEndB));
5187   if ((pStart != pStartB) || (pEnd != pEndB)) PetscFunctionReturn(PETSC_SUCCESS);
5188   for (p = pStart; p < pEnd; ++p) {
5189     const PetscInt *cone, *coneB, *ornt, *orntB, *support, *supportB;
5190     PetscInt        coneSize, coneSizeB, c, supportSize, supportSizeB, s;
5191 
5192     PetscCall(DMPlexGetConeSize(dmA, p, &coneSize));
5193     PetscCall(DMPlexGetCone(dmA, p, &cone));
5194     PetscCall(DMPlexGetConeOrientation(dmA, p, &ornt));
5195     PetscCall(DMPlexGetConeSize(dmB, p, &coneSizeB));
5196     PetscCall(DMPlexGetCone(dmB, p, &coneB));
5197     PetscCall(DMPlexGetConeOrientation(dmB, p, &orntB));
5198     if (coneSize != coneSizeB) PetscFunctionReturn(PETSC_SUCCESS);
5199     for (c = 0; c < coneSize; ++c) {
5200       if (cone[c] != coneB[c]) PetscFunctionReturn(PETSC_SUCCESS);
5201       if (ornt[c] != orntB[c]) PetscFunctionReturn(PETSC_SUCCESS);
5202     }
5203     PetscCall(DMPlexGetSupportSize(dmA, p, &supportSize));
5204     PetscCall(DMPlexGetSupport(dmA, p, &support));
5205     PetscCall(DMPlexGetSupportSize(dmB, p, &supportSizeB));
5206     PetscCall(DMPlexGetSupport(dmB, p, &supportB));
5207     if (supportSize != supportSizeB) PetscFunctionReturn(PETSC_SUCCESS);
5208     for (s = 0; s < supportSize; ++s) {
5209       if (support[s] != supportB[s]) PetscFunctionReturn(PETSC_SUCCESS);
5210     }
5211   }
5212   *equal = PETSC_TRUE;
5213   PetscFunctionReturn(PETSC_SUCCESS);
5214 }
5215 
5216 /*@
5217   DMPlexGetNumFaceVertices - Returns the number of vertices on a face
5218 
5219   Not Collective
5220 
5221   Input Parameters:
5222 + dm         - The `DMPLEX`
5223 . cellDim    - The cell dimension
5224 - numCorners - The number of vertices on a cell
5225 
5226   Output Parameter:
5227 . numFaceVertices - The number of vertices on a face
5228 
5229   Level: developer
5230 
5231   Note:
5232   Of course this can only work for a restricted set of symmetric shapes
5233 
5234 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCone()`
5235 @*/
5236 PetscErrorCode DMPlexGetNumFaceVertices(DM dm, PetscInt cellDim, PetscInt numCorners, PetscInt *numFaceVertices)
5237 {
5238   MPI_Comm comm;
5239 
5240   PetscFunctionBegin;
5241   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
5242   PetscAssertPointer(numFaceVertices, 4);
5243   switch (cellDim) {
5244   case 0:
5245     *numFaceVertices = 0;
5246     break;
5247   case 1:
5248     *numFaceVertices = 1;
5249     break;
5250   case 2:
5251     switch (numCorners) {
5252     case 3:                 /* triangle */
5253       *numFaceVertices = 2; /* Edge has 2 vertices */
5254       break;
5255     case 4:                 /* quadrilateral */
5256       *numFaceVertices = 2; /* Edge has 2 vertices */
5257       break;
5258     case 6:                 /* quadratic triangle, tri and quad cohesive Lagrange cells */
5259       *numFaceVertices = 3; /* Edge has 3 vertices */
5260       break;
5261     case 9:                 /* quadratic quadrilateral, quadratic quad cohesive Lagrange cells */
5262       *numFaceVertices = 3; /* Edge has 3 vertices */
5263       break;
5264     default:
5265       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
5266     }
5267     break;
5268   case 3:
5269     switch (numCorners) {
5270     case 4:                 /* tetradehdron */
5271       *numFaceVertices = 3; /* Face has 3 vertices */
5272       break;
5273     case 6:                 /* tet cohesive cells */
5274       *numFaceVertices = 4; /* Face has 4 vertices */
5275       break;
5276     case 8:                 /* hexahedron */
5277       *numFaceVertices = 4; /* Face has 4 vertices */
5278       break;
5279     case 9:                 /* tet cohesive Lagrange cells */
5280       *numFaceVertices = 6; /* Face has 6 vertices */
5281       break;
5282     case 10:                /* quadratic tetrahedron */
5283       *numFaceVertices = 6; /* Face has 6 vertices */
5284       break;
5285     case 12:                /* hex cohesive Lagrange cells */
5286       *numFaceVertices = 6; /* Face has 6 vertices */
5287       break;
5288     case 18:                /* quadratic tet cohesive Lagrange cells */
5289       *numFaceVertices = 6; /* Face has 6 vertices */
5290       break;
5291     case 27:                /* quadratic hexahedron, quadratic hex cohesive Lagrange cells */
5292       *numFaceVertices = 9; /* Face has 9 vertices */
5293       break;
5294     default:
5295       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
5296     }
5297     break;
5298   default:
5299     SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid cell dimension %" PetscInt_FMT, cellDim);
5300   }
5301   PetscFunctionReturn(PETSC_SUCCESS);
5302 }
5303 
5304 /*@
5305   DMPlexGetDepthLabel - Get the `DMLabel` recording the depth of each point
5306 
5307   Not Collective
5308 
5309   Input Parameter:
5310 . dm - The `DMPLEX` object
5311 
5312   Output Parameter:
5313 . depthLabel - The `DMLabel` recording point depth
5314 
5315   Level: developer
5316 
5317 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepth()`, `DMPlexGetHeightStratum()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`,
5318 @*/
5319 PetscErrorCode DMPlexGetDepthLabel(DM dm, DMLabel *depthLabel)
5320 {
5321   PetscFunctionBegin;
5322   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5323   PetscAssertPointer(depthLabel, 2);
5324   *depthLabel = dm->depthLabel;
5325   PetscFunctionReturn(PETSC_SUCCESS);
5326 }
5327 
5328 /*@
5329   DMPlexGetDepth - Get the depth of the DAG representing this mesh
5330 
5331   Not Collective
5332 
5333   Input Parameter:
5334 . dm - The `DMPLEX` object
5335 
5336   Output Parameter:
5337 . depth - The number of strata (breadth first levels) in the DAG
5338 
5339   Level: developer
5340 
5341   Notes:
5342   This returns maximum of point depths over all points, i.e. maximum value of the label returned by `DMPlexGetDepthLabel()`.
5343 
5344   The point depth is described more in detail in `DMPlexGetDepthStratum()`.
5345 
5346   An empty mesh gives -1.
5347 
5348 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepthLabel()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`
5349 @*/
5350 PetscErrorCode DMPlexGetDepth(DM dm, PetscInt *depth)
5351 {
5352   DM_Plex *mesh = (DM_Plex *)dm->data;
5353   DMLabel  label;
5354   PetscInt d = -1;
5355 
5356   PetscFunctionBegin;
5357   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5358   PetscAssertPointer(depth, 2);
5359   if (mesh->tr) {
5360     PetscCall(DMPlexTransformGetDepth(mesh->tr, depth));
5361   } else {
5362     PetscCall(DMPlexGetDepthLabel(dm, &label));
5363     // Allow missing depths
5364     if (label) PetscCall(DMLabelGetValueBounds(label, NULL, &d));
5365     *depth = d;
5366   }
5367   PetscFunctionReturn(PETSC_SUCCESS);
5368 }
5369 
5370 /*@
5371   DMPlexGetDepthStratum - Get the bounds [`start`, `end`) for all points at a certain depth.
5372 
5373   Not Collective
5374 
5375   Input Parameters:
5376 + dm    - The `DMPLEX` object
5377 - depth - The requested depth
5378 
5379   Output Parameters:
5380 + start - The first point at this `depth`
5381 - end   - One beyond the last point at this `depth`
5382 
5383   Level: developer
5384 
5385   Notes:
5386   Depth indexing is related to topological dimension.  Depth stratum 0 contains the lowest topological dimension points,
5387   often "vertices".  If the mesh is "interpolated" (see `DMPlexInterpolate()`), then depth stratum 1 contains the next
5388   higher dimension, e.g., "edges".
5389 
5390 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetHeightStratum()`, `DMPlexGetCellTypeStratum()`, `DMPlexGetDepth()`, `DMPlexGetDepthLabel()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`, `DMPlexInterpolate()`
5391 @*/
5392 PetscErrorCode DMPlexGetDepthStratum(DM dm, PetscInt depth, PetscInt *start, PetscInt *end)
5393 {
5394   DM_Plex *mesh = (DM_Plex *)dm->data;
5395   DMLabel  label;
5396   PetscInt pStart, pEnd;
5397 
5398   PetscFunctionBegin;
5399   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5400   if (start) {
5401     PetscAssertPointer(start, 3);
5402     *start = 0;
5403   }
5404   if (end) {
5405     PetscAssertPointer(end, 4);
5406     *end = 0;
5407   }
5408   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
5409   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
5410   if (depth < 0) {
5411     if (start) *start = pStart;
5412     if (end) *end = pEnd;
5413     PetscFunctionReturn(PETSC_SUCCESS);
5414   }
5415   if (mesh->tr) {
5416     PetscCall(DMPlexTransformGetDepthStratum(mesh->tr, depth, start, end));
5417   } else {
5418     PetscCall(DMPlexGetDepthLabel(dm, &label));
5419     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named depth was found");
5420     PetscCall(DMLabelGetStratumBounds(label, depth, start, end));
5421   }
5422   PetscFunctionReturn(PETSC_SUCCESS);
5423 }
5424 
5425 /*@
5426   DMPlexGetHeightStratum - Get the bounds [`start`, `end`) for all points at a certain height.
5427 
5428   Not Collective
5429 
5430   Input Parameters:
5431 + dm     - The `DMPLEX` object
5432 - height - The requested height
5433 
5434   Output Parameters:
5435 + start - The first point at this `height`
5436 - end   - One beyond the last point at this `height`
5437 
5438   Level: developer
5439 
5440   Notes:
5441   Height indexing is related to topological codimension.  Height stratum 0 contains the highest topological dimension
5442   points, often called "cells" or "elements".  If the mesh is "interpolated" (see `DMPlexInterpolate()`), then height
5443   stratum 1 contains the boundary of these "cells", often called "faces" or "facets".
5444 
5445 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepthStratum()`, `DMPlexGetCellTypeStratum()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
5446 @*/
5447 PetscErrorCode DMPlexGetHeightStratum(DM dm, PetscInt height, PetscInt *start, PetscInt *end)
5448 {
5449   DMLabel  label;
5450   PetscInt depth, pStart, pEnd;
5451 
5452   PetscFunctionBegin;
5453   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5454   if (start) {
5455     PetscAssertPointer(start, 3);
5456     *start = 0;
5457   }
5458   if (end) {
5459     PetscAssertPointer(end, 4);
5460     *end = 0;
5461   }
5462   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
5463   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
5464   if (height < 0) {
5465     if (start) *start = pStart;
5466     if (end) *end = pEnd;
5467     PetscFunctionReturn(PETSC_SUCCESS);
5468   }
5469   PetscCall(DMPlexGetDepthLabel(dm, &label));
5470   if (label) PetscCall(DMLabelGetNumValues(label, &depth));
5471   else PetscCall(DMGetDimension(dm, &depth));
5472   PetscCheck(depth >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Depth not yet computed");
5473   PetscCall(DMPlexGetDepthStratum(dm, depth - 1 - height, start, end));
5474   PetscFunctionReturn(PETSC_SUCCESS);
5475 }
5476 
5477 /*@
5478   DMPlexGetPointDepth - Get the `depth` of a given point
5479 
5480   Not Collective
5481 
5482   Input Parameters:
5483 + dm    - The `DMPLEX` object
5484 - point - The point
5485 
5486   Output Parameter:
5487 . depth - The depth of the `point`
5488 
5489   Level: intermediate
5490 
5491 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
5492 @*/
5493 PetscErrorCode DMPlexGetPointDepth(DM dm, PetscInt point, PetscInt *depth)
5494 {
5495   PetscFunctionBegin;
5496   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5497   PetscAssertPointer(depth, 3);
5498   PetscCall(DMLabelGetValue(dm->depthLabel, point, depth));
5499   PetscFunctionReturn(PETSC_SUCCESS);
5500 }
5501 
5502 /*@
5503   DMPlexGetPointHeight - Get the `height` of a given point
5504 
5505   Not Collective
5506 
5507   Input Parameters:
5508 + dm    - The `DMPLEX` object
5509 - point - The point
5510 
5511   Output Parameter:
5512 . height - The height of the `point`
5513 
5514   Level: intermediate
5515 
5516 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointDepth()`
5517 @*/
5518 PetscErrorCode DMPlexGetPointHeight(DM dm, PetscInt point, PetscInt *height)
5519 {
5520   PetscInt n, pDepth;
5521 
5522   PetscFunctionBegin;
5523   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5524   PetscAssertPointer(height, 3);
5525   PetscCall(DMLabelGetNumValues(dm->depthLabel, &n));
5526   PetscCall(DMLabelGetValue(dm->depthLabel, point, &pDepth));
5527   *height = n - 1 - pDepth; /* DAG depth is n-1 */
5528   PetscFunctionReturn(PETSC_SUCCESS);
5529 }
5530 
5531 /*@
5532   DMPlexGetCellTypeLabel - Get the `DMLabel` recording the polytope type of each cell
5533 
5534   Not Collective
5535 
5536   Input Parameter:
5537 . dm - The `DMPLEX` object
5538 
5539   Output Parameter:
5540 . celltypeLabel - The `DMLabel` recording cell polytope type
5541 
5542   Level: developer
5543 
5544   Note:
5545   This function will trigger automatica computation of cell types. This can be disabled by calling
5546   `DMCreateLabel`(dm, "celltype") beforehand.
5547 
5548 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMCreateLabel()`
5549 @*/
5550 PetscErrorCode DMPlexGetCellTypeLabel(DM dm, DMLabel *celltypeLabel)
5551 {
5552   PetscFunctionBegin;
5553   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5554   PetscAssertPointer(celltypeLabel, 2);
5555   if (!dm->celltypeLabel) PetscCall(DMPlexComputeCellTypes(dm));
5556   *celltypeLabel = dm->celltypeLabel;
5557   PetscFunctionReturn(PETSC_SUCCESS);
5558 }
5559 
5560 /*@
5561   DMPlexGetCellType - Get the polytope type of a given cell
5562 
5563   Not Collective
5564 
5565   Input Parameters:
5566 + dm   - The `DMPLEX` object
5567 - cell - The cell
5568 
5569   Output Parameter:
5570 . celltype - The polytope type of the cell
5571 
5572   Level: intermediate
5573 
5574 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPolytopeType`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`
5575 @*/
5576 PetscErrorCode DMPlexGetCellType(DM dm, PetscInt cell, DMPolytopeType *celltype)
5577 {
5578   DM_Plex *mesh = (DM_Plex *)dm->data;
5579   DMLabel  label;
5580   PetscInt ct;
5581 
5582   PetscFunctionBegin;
5583   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5584   PetscAssertPointer(celltype, 3);
5585   if (mesh->tr) {
5586     PetscCall(DMPlexTransformGetCellType(mesh->tr, cell, celltype));
5587   } else {
5588     PetscInt pStart, pEnd;
5589 
5590     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, NULL));
5591     if (!mesh->cellTypes) { /* XXX remove? optimize? */
5592       PetscCall(PetscSectionGetChart(mesh->coneSection, NULL, &pEnd));
5593       PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
5594       PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5595       for (PetscInt p = pStart; p < pEnd; p++) {
5596         PetscCall(DMLabelGetValue(label, p, &ct));
5597         mesh->cellTypes[p - pStart].value_as_uint8 = (uint8_t)ct;
5598       }
5599     }
5600     *celltype = (DMPolytopeType)mesh->cellTypes[cell - pStart].value_as_uint8;
5601     if (PetscDefined(USE_DEBUG)) {
5602       PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5603       PetscCall(DMLabelGetValue(label, cell, &ct));
5604       PetscCheck(ct >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Cell %" PetscInt_FMT " has not been assigned a cell type", cell);
5605       PetscCheck(ct == (PetscInt)*celltype, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid cellType for %" PetscInt_FMT ": %d != %" PetscInt_FMT, cell, (int)*celltype, ct);
5606     }
5607   }
5608   PetscFunctionReturn(PETSC_SUCCESS);
5609 }
5610 
5611 /*@
5612   DMPlexSetCellType - Set the polytope type of a given cell
5613 
5614   Not Collective
5615 
5616   Input Parameters:
5617 + dm       - The `DMPLEX` object
5618 . cell     - The cell
5619 - celltype - The polytope type of the cell
5620 
5621   Level: advanced
5622 
5623   Note:
5624   By default, cell types will be automatically computed using `DMPlexComputeCellTypes()` before this function
5625   is executed. This function will override the computed type. However, if automatic classification will not succeed
5626   and a user wants to manually specify all types, the classification must be disabled by calling
5627   DMCreateLabel(dm, "celltype") before getting or setting any cell types.
5628 
5629 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexComputeCellTypes()`, `DMCreateLabel()`
5630 @*/
5631 PetscErrorCode DMPlexSetCellType(DM dm, PetscInt cell, DMPolytopeType celltype)
5632 {
5633   DM_Plex *mesh = (DM_Plex *)dm->data;
5634   DMLabel  label;
5635   PetscInt pStart, pEnd;
5636 
5637   PetscFunctionBegin;
5638   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5639   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
5640   PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5641   PetscCall(DMLabelSetValue(label, cell, celltype));
5642   if (!mesh->cellTypes) PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
5643   mesh->cellTypes[cell - pStart].value_as_uint8 = (uint8_t)celltype;
5644   PetscFunctionReturn(PETSC_SUCCESS);
5645 }
5646 
5647 PetscErrorCode DMCreateCoordinateDM_Plex(DM dm, DM *cdm)
5648 {
5649   PetscSection section;
5650   PetscInt     maxHeight;
5651   const char  *prefix;
5652 
5653   PetscFunctionBegin;
5654   PetscCall(DMClone(dm, cdm));
5655   PetscCall(PetscObjectGetOptionsPrefix((PetscObject)dm, &prefix));
5656   PetscCall(PetscObjectSetOptionsPrefix((PetscObject)*cdm, prefix));
5657   PetscCall(PetscObjectAppendOptionsPrefix((PetscObject)*cdm, "cdm_"));
5658   PetscCall(DMPlexGetMaxProjectionHeight(dm, &maxHeight));
5659   PetscCall(DMPlexSetMaxProjectionHeight(*cdm, maxHeight));
5660   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
5661   PetscCall(DMSetLocalSection(*cdm, section));
5662   PetscCall(PetscSectionDestroy(&section));
5663 
5664   PetscCall(DMSetNumFields(*cdm, 1));
5665   PetscCall(DMCreateDS(*cdm));
5666   (*cdm)->cloneOpts = PETSC_TRUE;
5667   if (dm->setfromoptionscalled) PetscCall(DMSetFromOptions(*cdm));
5668   PetscFunctionReturn(PETSC_SUCCESS);
5669 }
5670 
5671 PetscErrorCode DMCreateCoordinateField_Plex(DM dm, DMField *field)
5672 {
5673   Vec coordsLocal, cellCoordsLocal;
5674   DM  coordsDM, cellCoordsDM;
5675 
5676   PetscFunctionBegin;
5677   *field = NULL;
5678   PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
5679   PetscCall(DMGetCoordinateDM(dm, &coordsDM));
5680   PetscCall(DMGetCellCoordinatesLocal(dm, &cellCoordsLocal));
5681   PetscCall(DMGetCellCoordinateDM(dm, &cellCoordsDM));
5682   if (coordsLocal && coordsDM) {
5683     if (cellCoordsLocal && cellCoordsDM) PetscCall(DMFieldCreateDSWithDG(coordsDM, cellCoordsDM, 0, coordsLocal, cellCoordsLocal, field));
5684     else PetscCall(DMFieldCreateDS(coordsDM, 0, coordsLocal, field));
5685   }
5686   PetscFunctionReturn(PETSC_SUCCESS);
5687 }
5688 
5689 /*@
5690   DMPlexGetConeSection - Return a section which describes the layout of cone data
5691 
5692   Not Collective
5693 
5694   Input Parameter:
5695 . dm - The `DMPLEX` object
5696 
5697   Output Parameter:
5698 . section - The `PetscSection` object
5699 
5700   Level: developer
5701 
5702 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSupportSection()`, `DMPlexGetCones()`, `DMPlexGetConeOrientations()`, `PetscSection`
5703 @*/
5704 PetscErrorCode DMPlexGetConeSection(DM dm, PetscSection *section)
5705 {
5706   DM_Plex *mesh = (DM_Plex *)dm->data;
5707 
5708   PetscFunctionBegin;
5709   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5710   if (section) *section = mesh->coneSection;
5711   PetscFunctionReturn(PETSC_SUCCESS);
5712 }
5713 
5714 /*@
5715   DMPlexGetSupportSection - Return a section which describes the layout of support data
5716 
5717   Not Collective
5718 
5719   Input Parameter:
5720 . dm - The `DMPLEX` object
5721 
5722   Output Parameter:
5723 . section - The `PetscSection` object
5724 
5725   Level: developer
5726 
5727 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `PetscSection`
5728 @*/
5729 PetscErrorCode DMPlexGetSupportSection(DM dm, PetscSection *section)
5730 {
5731   DM_Plex *mesh = (DM_Plex *)dm->data;
5732 
5733   PetscFunctionBegin;
5734   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5735   if (section) *section = mesh->supportSection;
5736   PetscFunctionReturn(PETSC_SUCCESS);
5737 }
5738 
5739 /*@C
5740   DMPlexGetCones - Return cone data
5741 
5742   Not Collective
5743 
5744   Input Parameter:
5745 . dm - The `DMPLEX` object
5746 
5747   Output Parameter:
5748 . cones - The cone for each point
5749 
5750   Level: developer
5751 
5752 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`
5753 @*/
5754 PetscErrorCode DMPlexGetCones(DM dm, PetscInt *cones[])
5755 {
5756   DM_Plex *mesh = (DM_Plex *)dm->data;
5757 
5758   PetscFunctionBegin;
5759   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5760   if (cones) *cones = mesh->cones;
5761   PetscFunctionReturn(PETSC_SUCCESS);
5762 }
5763 
5764 /*@C
5765   DMPlexGetConeOrientations - Return cone orientation data
5766 
5767   Not Collective
5768 
5769   Input Parameter:
5770 . dm - The `DMPLEX` object
5771 
5772   Output Parameter:
5773 . coneOrientations - The array of cone orientations for all points
5774 
5775   Level: developer
5776 
5777   Notes:
5778   The `PetscSection` returned by `DMPlexGetConeSection()` partitions coneOrientations into cone orientations of particular points
5779   as returned by `DMPlexGetConeOrientation()`.
5780 
5781   The meaning of coneOrientations values is detailed in `DMPlexGetConeOrientation()`.
5782 
5783 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `DMPlexGetConeOrientation()`, `PetscSection`
5784 @*/
5785 PetscErrorCode DMPlexGetConeOrientations(DM dm, PetscInt *coneOrientations[])
5786 {
5787   DM_Plex *mesh = (DM_Plex *)dm->data;
5788 
5789   PetscFunctionBegin;
5790   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5791   if (coneOrientations) *coneOrientations = mesh->coneOrientations;
5792   PetscFunctionReturn(PETSC_SUCCESS);
5793 }
5794 
5795 /* FEM Support */
5796 
5797 PetscErrorCode DMPlexGetAllCells_Internal(DM plex, IS *cellIS)
5798 {
5799   PetscInt depth;
5800 
5801   PetscFunctionBegin;
5802   PetscCall(DMPlexGetDepth(plex, &depth));
5803   PetscCall(DMGetStratumIS(plex, "dim", depth, cellIS));
5804   if (!*cellIS) PetscCall(DMGetStratumIS(plex, "depth", depth, cellIS));
5805   PetscFunctionReturn(PETSC_SUCCESS);
5806 }
5807 
5808 PetscErrorCode DMPlexGetAllFaces_Internal(DM plex, IS *faceIS)
5809 {
5810   PetscInt depth;
5811 
5812   PetscFunctionBegin;
5813   PetscCall(DMPlexGetDepth(plex, &depth));
5814   PetscCall(DMGetStratumIS(plex, "dim", depth - 1, faceIS));
5815   if (!*faceIS) PetscCall(DMGetStratumIS(plex, "depth", depth - 1, faceIS));
5816   PetscFunctionReturn(PETSC_SUCCESS);
5817 }
5818 
5819 /*
5820  Returns number of components and tensor degree for the field.  For interpolated meshes, line should be a point
5821  representing a line in the section.
5822 */
5823 static PetscErrorCode PetscSectionFieldGetTensorDegree_Private(DM dm, PetscSection section, PetscInt field, PetscInt line, PetscInt *Nc, PetscInt *k, PetscBool *continuous, PetscBool *tensor)
5824 {
5825   PetscObject  obj;
5826   PetscClassId id;
5827   PetscFE      fe = NULL;
5828 
5829   PetscFunctionBeginHot;
5830   PetscCall(PetscSectionGetFieldComponents(section, field, Nc));
5831   PetscCall(DMGetField(dm, field, NULL, &obj));
5832   PetscCall(PetscObjectGetClassId(obj, &id));
5833   if (id == PETSCFE_CLASSID) fe = (PetscFE)obj;
5834 
5835   if (!fe) {
5836     /* Assume the full interpolated mesh is in the chart; lines in particular */
5837     /* An order k SEM disc has k-1 dofs on an edge */
5838     PetscCall(PetscSectionGetFieldDof(section, line, field, k));
5839     *k = *k / *Nc + 1;
5840   } else {
5841     PetscInt       dual_space_size, dim;
5842     PetscDualSpace dsp;
5843 
5844     PetscCall(DMGetDimension(dm, &dim));
5845     PetscCall(PetscFEGetDualSpace(fe, &dsp));
5846     PetscCall(PetscDualSpaceGetDimension(dsp, &dual_space_size));
5847     *k = (PetscInt)PetscCeilReal(PetscPowReal(dual_space_size / *Nc, 1.0 / dim)) - 1;
5848     PetscCall(PetscDualSpaceLagrangeGetContinuity(dsp, continuous));
5849     PetscCall(PetscDualSpaceLagrangeGetTensor(dsp, tensor));
5850   }
5851   PetscFunctionReturn(PETSC_SUCCESS);
5852 }
5853 
5854 static PetscErrorCode GetFieldSize_Private(PetscInt dim, PetscInt k, PetscBool tensor, PetscInt *dof)
5855 {
5856   PetscFunctionBeginHot;
5857   if (tensor) {
5858     *dof = PetscPowInt(k + 1, dim);
5859   } else {
5860     switch (dim) {
5861     case 1:
5862       *dof = k + 1;
5863       break;
5864     case 2:
5865       *dof = ((k + 1) * (k + 2)) / 2;
5866       break;
5867     case 3:
5868       *dof = ((k + 1) * (k + 2) * (k + 3)) / 6;
5869       break;
5870     default:
5871       *dof = 0;
5872     }
5873   }
5874   PetscFunctionReturn(PETSC_SUCCESS);
5875 }
5876 
5877 /*@
5878   DMPlexSetClosurePermutationTensor - Create a permutation from the default (BFS) point ordering in the closure, to a
5879   lexicographic ordering over the tensor product cell (i.e., line, quad, hex, etc.), and set this permutation in the
5880   section provided (or the section of the `DM`).
5881 
5882   Input Parameters:
5883 + dm      - The `DM`
5884 . point   - Either a cell (highest dim point) or an edge (dim 1 point), or `PETSC_DETERMINE`
5885 - section - The `PetscSection` to reorder, or `NULL` for the default section
5886 
5887   Example:
5888   A typical interpolated single-quad mesh might order points as
5889 .vb
5890   [c0, v1, v2, v3, v4, e5, e6, e7, e8]
5891 
5892   v4 -- e6 -- v3
5893   |           |
5894   e7    c0    e8
5895   |           |
5896   v1 -- e5 -- v2
5897 .ve
5898 
5899   (There is no significance to the ordering described here.)  The default section for a Q3 quad might typically assign
5900   dofs in the order of points, e.g.,
5901 .vb
5902     c0 -> [0,1,2,3]
5903     v1 -> [4]
5904     ...
5905     e5 -> [8, 9]
5906 .ve
5907 
5908   which corresponds to the dofs
5909 .vb
5910     6   10  11  7
5911     13  2   3   15
5912     12  0   1   14
5913     4   8   9   5
5914 .ve
5915 
5916   The closure in BFS ordering works through height strata (cells, edges, vertices) to produce the ordering
5917 .vb
5918   0 1 2 3 8 9 14 15 11 10 13 12 4 5 7 6
5919 .ve
5920 
5921   After calling DMPlexSetClosurePermutationTensor(), the closure will be ordered lexicographically,
5922 .vb
5923    4 8 9 5 12 0 1 14 13 2 3 15 6 10 11 7
5924 .ve
5925 
5926   Level: developer
5927 
5928   Notes:
5929   The point is used to determine the number of dofs/field on an edge. For SEM, this is related to the polynomial
5930   degree of the basis.
5931 
5932   This is required to run with libCEED.
5933 
5934 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMGetLocalSection()`, `PetscSectionSetClosurePermutation()`, `DMSetGlobalSection()`
5935 @*/
5936 PetscErrorCode DMPlexSetClosurePermutationTensor(DM dm, PetscInt point, PetscSection section)
5937 {
5938   DMLabel   label;
5939   PetscInt  dim, depth = -1, eStart = -1, Nf;
5940   PetscBool continuous = PETSC_TRUE, tensor = PETSC_TRUE;
5941 
5942   PetscFunctionBegin;
5943   PetscCall(DMGetDimension(dm, &dim));
5944   if (dim < 1) PetscFunctionReturn(PETSC_SUCCESS);
5945   if (point < 0) {
5946     PetscInt sStart, sEnd;
5947 
5948     PetscCall(DMPlexGetDepthStratum(dm, 1, &sStart, &sEnd));
5949     point = sEnd - sStart ? sStart : point;
5950   }
5951   PetscCall(DMPlexGetDepthLabel(dm, &label));
5952   if (point >= 0) PetscCall(DMLabelGetValue(label, point, &depth));
5953   if (!section) PetscCall(DMGetLocalSection(dm, &section));
5954   if (depth == 1) {
5955     eStart = point;
5956   } else if (depth == dim) {
5957     const PetscInt *cone;
5958 
5959     PetscCall(DMPlexGetCone(dm, point, &cone));
5960     if (dim == 2) eStart = cone[0];
5961     else if (dim == 3) {
5962       const PetscInt *cone2;
5963       PetscCall(DMPlexGetCone(dm, cone[0], &cone2));
5964       eStart = cone2[0];
5965     } 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);
5966   } 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);
5967 
5968   PetscCall(PetscSectionGetNumFields(section, &Nf));
5969   for (PetscInt d = 1; d <= dim; d++) {
5970     PetscInt  k, f, Nc, c, i, j, size = 0, offset = 0, foffset = 0;
5971     PetscInt *perm;
5972 
5973     for (f = 0; f < Nf; ++f) {
5974       PetscInt dof;
5975 
5976       PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
5977       PetscCheck(dim == 1 || tensor || !continuous, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Continuous field %" PetscInt_FMT " must have a tensor product discretization", f);
5978       if (!continuous && d < dim) continue;
5979       PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
5980       size += dof * Nc;
5981     }
5982     PetscCall(PetscMalloc1(size, &perm));
5983     for (f = 0; f < Nf; ++f) {
5984       switch (d) {
5985       case 1:
5986         PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
5987         if (!continuous && d < dim) continue;
5988         /*
5989          Original ordering is [ edge of length k-1; vtx0; vtx1 ]
5990          We want              [ vtx0; edge of length k-1; vtx1 ]
5991          */
5992         if (continuous) {
5993           for (c = 0; c < Nc; c++, offset++) perm[offset] = (k - 1) * Nc + c + foffset;
5994           for (i = 0; i < k - 1; i++)
5995             for (c = 0; c < Nc; c++, offset++) perm[offset] = i * Nc + c + foffset;
5996           for (c = 0; c < Nc; c++, offset++) perm[offset] = k * Nc + c + foffset;
5997           foffset = offset;
5998         } else {
5999           PetscInt dof;
6000 
6001           PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
6002           for (i = 0; i < dof * Nc; ++i, ++offset) perm[offset] = i + foffset;
6003           foffset = offset;
6004         }
6005         break;
6006       case 2:
6007         /* The original quad closure is oriented clockwise, {f, e_b, e_r, e_t, e_l, v_lb, v_rb, v_tr, v_tl} */
6008         PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
6009         if (!continuous && d < dim) continue;
6010         /* The SEM order is
6011 
6012          v_lb, {e_b}, v_rb,
6013          e^{(k-1)-i}_l, {f^{i*(k-1)}}, e^i_r,
6014          v_lt, reverse {e_t}, v_rt
6015          */
6016         if (continuous) {
6017           const PetscInt of   = 0;
6018           const PetscInt oeb  = of + PetscSqr(k - 1);
6019           const PetscInt oer  = oeb + (k - 1);
6020           const PetscInt oet  = oer + (k - 1);
6021           const PetscInt oel  = oet + (k - 1);
6022           const PetscInt ovlb = oel + (k - 1);
6023           const PetscInt ovrb = ovlb + 1;
6024           const PetscInt ovrt = ovrb + 1;
6025           const PetscInt ovlt = ovrt + 1;
6026           PetscInt       o;
6027 
6028           /* bottom */
6029           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlb * Nc + c + foffset;
6030           for (o = oeb; o < oer; ++o)
6031             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6032           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrb * Nc + c + foffset;
6033           /* middle */
6034           for (i = 0; i < k - 1; ++i) {
6035             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oel + (k - 2) - i) * Nc + c + foffset;
6036             for (o = of + (k - 1) * i; o < of + (k - 1) * (i + 1); ++o)
6037               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6038             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oer + i) * Nc + c + foffset;
6039           }
6040           /* top */
6041           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlt * Nc + c + foffset;
6042           for (o = oel - 1; o >= oet; --o)
6043             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6044           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrt * Nc + c + foffset;
6045           foffset = offset;
6046         } else {
6047           PetscInt dof;
6048 
6049           PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
6050           for (i = 0; i < dof * Nc; ++i, ++offset) perm[offset] = i + foffset;
6051           foffset = offset;
6052         }
6053         break;
6054       case 3:
6055         /* The original hex closure is
6056 
6057          {c,
6058          f_b, f_t, f_f, f_b, f_r, f_l,
6059          e_bl, e_bb, e_br, e_bf,  e_tf, e_tr, e_tb, e_tl,  e_rf, e_lf, e_lb, e_rb,
6060          v_blf, v_blb, v_brb, v_brf, v_tlf, v_trf, v_trb, v_tlb}
6061          */
6062         PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
6063         if (!continuous && d < dim) continue;
6064         /* The SEM order is
6065          Bottom Slice
6066          v_blf, {e^{(k-1)-n}_bf}, v_brf,
6067          e^{i}_bl, f^{n*(k-1)+(k-1)-i}_b, e^{(k-1)-i}_br,
6068          v_blb, {e_bb}, v_brb,
6069 
6070          Middle Slice (j)
6071          {e^{(k-1)-j}_lf}, {f^{j*(k-1)+n}_f}, e^j_rf,
6072          f^{i*(k-1)+j}_l, {c^{(j*(k-1) + i)*(k-1)+n}_t}, f^{j*(k-1)+i}_r,
6073          e^j_lb, {f^{j*(k-1)+(k-1)-n}_b}, e^{(k-1)-j}_rb,
6074 
6075          Top Slice
6076          v_tlf, {e_tf}, v_trf,
6077          e^{(k-1)-i}_tl, {f^{i*(k-1)}_t}, e^{i}_tr,
6078          v_tlb, {e^{(k-1)-n}_tb}, v_trb,
6079          */
6080         if (continuous) {
6081           const PetscInt oc    = 0;
6082           const PetscInt ofb   = oc + PetscSqr(k - 1) * (k - 1);
6083           const PetscInt oft   = ofb + PetscSqr(k - 1);
6084           const PetscInt off   = oft + PetscSqr(k - 1);
6085           const PetscInt ofk   = off + PetscSqr(k - 1);
6086           const PetscInt ofr   = ofk + PetscSqr(k - 1);
6087           const PetscInt ofl   = ofr + PetscSqr(k - 1);
6088           const PetscInt oebl  = ofl + PetscSqr(k - 1);
6089           const PetscInt oebb  = oebl + (k - 1);
6090           const PetscInt oebr  = oebb + (k - 1);
6091           const PetscInt oebf  = oebr + (k - 1);
6092           const PetscInt oetf  = oebf + (k - 1);
6093           const PetscInt oetr  = oetf + (k - 1);
6094           const PetscInt oetb  = oetr + (k - 1);
6095           const PetscInt oetl  = oetb + (k - 1);
6096           const PetscInt oerf  = oetl + (k - 1);
6097           const PetscInt oelf  = oerf + (k - 1);
6098           const PetscInt oelb  = oelf + (k - 1);
6099           const PetscInt oerb  = oelb + (k - 1);
6100           const PetscInt ovblf = oerb + (k - 1);
6101           const PetscInt ovblb = ovblf + 1;
6102           const PetscInt ovbrb = ovblb + 1;
6103           const PetscInt ovbrf = ovbrb + 1;
6104           const PetscInt ovtlf = ovbrf + 1;
6105           const PetscInt ovtrf = ovtlf + 1;
6106           const PetscInt ovtrb = ovtrf + 1;
6107           const PetscInt ovtlb = ovtrb + 1;
6108           PetscInt       o, n;
6109 
6110           /* Bottom Slice */
6111           /*   bottom */
6112           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblf * Nc + c + foffset;
6113           for (o = oetf - 1; o >= oebf; --o)
6114             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6115           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrf * Nc + c + foffset;
6116           /*   middle */
6117           for (i = 0; i < k - 1; ++i) {
6118             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebl + i) * Nc + c + foffset;
6119             for (n = 0; n < k - 1; ++n) {
6120               o = ofb + n * (k - 1) + i;
6121               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6122             }
6123             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebr + (k - 2) - i) * Nc + c + foffset;
6124           }
6125           /*   top */
6126           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblb * Nc + c + foffset;
6127           for (o = oebb; o < oebr; ++o)
6128             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6129           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrb * Nc + c + foffset;
6130 
6131           /* Middle Slice */
6132           for (j = 0; j < k - 1; ++j) {
6133             /*   bottom */
6134             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelf + (k - 2) - j) * Nc + c + foffset;
6135             for (o = off + j * (k - 1); o < off + (j + 1) * (k - 1); ++o)
6136               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6137             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerf + j) * Nc + c + foffset;
6138             /*   middle */
6139             for (i = 0; i < k - 1; ++i) {
6140               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofl + i * (k - 1) + j) * Nc + c + foffset;
6141               for (n = 0; n < k - 1; ++n)
6142                 for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oc + (j * (k - 1) + i) * (k - 1) + n) * Nc + c + foffset;
6143               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofr + j * (k - 1) + i) * Nc + c + foffset;
6144             }
6145             /*   top */
6146             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelb + j) * Nc + c + foffset;
6147             for (o = ofk + j * (k - 1) + (k - 2); o >= ofk + j * (k - 1); --o)
6148               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6149             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerb + (k - 2) - j) * Nc + c + foffset;
6150           }
6151 
6152           /* Top Slice */
6153           /*   bottom */
6154           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlf * Nc + c + foffset;
6155           for (o = oetf; o < oetr; ++o)
6156             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6157           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrf * Nc + c + foffset;
6158           /*   middle */
6159           for (i = 0; i < k - 1; ++i) {
6160             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetl + (k - 2) - i) * Nc + c + foffset;
6161             for (n = 0; n < k - 1; ++n)
6162               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oft + i * (k - 1) + n) * Nc + c + foffset;
6163             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetr + i) * Nc + c + foffset;
6164           }
6165           /*   top */
6166           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlb * Nc + c + foffset;
6167           for (o = oetl - 1; o >= oetb; --o)
6168             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6169           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrb * Nc + c + foffset;
6170 
6171           foffset = offset;
6172         } else {
6173           PetscInt dof;
6174 
6175           PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
6176           for (i = 0; i < dof * Nc; ++i, ++offset) perm[offset] = i + foffset;
6177           foffset = offset;
6178         }
6179         break;
6180       default:
6181         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "No spectral ordering for dimension %" PetscInt_FMT, d);
6182       }
6183     }
6184     PetscCheck(offset == size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Number of permutation entries %" PetscInt_FMT " != %" PetscInt_FMT, offset, size);
6185     /* Check permutation */
6186     {
6187       PetscInt *check;
6188 
6189       PetscCall(PetscMalloc1(size, &check));
6190       for (i = 0; i < size; ++i) {
6191         check[i] = -1;
6192         PetscCheck(perm[i] >= 0 && perm[i] < size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid permutation index p[%" PetscInt_FMT "] = %" PetscInt_FMT, i, perm[i]);
6193       }
6194       for (i = 0; i < size; ++i) check[perm[i]] = i;
6195       for (i = 0; i < size; ++i) PetscCheck(check[i] >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Missing permutation index %" PetscInt_FMT, i);
6196       PetscCall(PetscFree(check));
6197     }
6198     PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size, PETSC_OWN_POINTER, perm));
6199     if (d == dim) { // Add permutation for localized (in case this is a coordinate DM)
6200       PetscInt *loc_perm;
6201       PetscCall(PetscMalloc1(size * 2, &loc_perm));
6202       for (PetscInt i = 0; i < size; i++) {
6203         loc_perm[i]        = perm[i];
6204         loc_perm[size + i] = size + perm[i];
6205       }
6206       PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size * 2, PETSC_OWN_POINTER, loc_perm));
6207     }
6208   }
6209   PetscFunctionReturn(PETSC_SUCCESS);
6210 }
6211 
6212 PetscErrorCode DMPlexGetPointDualSpaceFEM(DM dm, PetscInt point, PetscInt field, PetscDualSpace *dspace)
6213 {
6214   PetscDS  prob;
6215   PetscInt depth, Nf, h;
6216   DMLabel  label;
6217 
6218   PetscFunctionBeginHot;
6219   PetscCall(DMGetDS(dm, &prob));
6220   Nf      = prob->Nf;
6221   label   = dm->depthLabel;
6222   *dspace = NULL;
6223   if (field < Nf) {
6224     PetscObject disc = prob->disc[field];
6225 
6226     if (disc->classid == PETSCFE_CLASSID) {
6227       PetscDualSpace dsp;
6228 
6229       PetscCall(PetscFEGetDualSpace((PetscFE)disc, &dsp));
6230       PetscCall(DMLabelGetNumValues(label, &depth));
6231       PetscCall(DMLabelGetValue(label, point, &h));
6232       h = depth - 1 - h;
6233       if (h) {
6234         PetscCall(PetscDualSpaceGetHeightSubspace(dsp, h, dspace));
6235       } else {
6236         *dspace = dsp;
6237       }
6238     }
6239   }
6240   PetscFunctionReturn(PETSC_SUCCESS);
6241 }
6242 
6243 static inline PetscErrorCode DMPlexVecGetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6244 {
6245   PetscScalar       *array;
6246   const PetscScalar *vArray;
6247   const PetscInt    *cone, *coneO;
6248   PetscInt           pStart, pEnd, p, numPoints, size = 0, offset = 0;
6249 
6250   PetscFunctionBeginHot;
6251   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6252   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
6253   PetscCall(DMPlexGetCone(dm, point, &cone));
6254   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
6255   if (!values || !*values) {
6256     if ((point >= pStart) && (point < pEnd)) {
6257       PetscInt dof;
6258 
6259       PetscCall(PetscSectionGetDof(section, point, &dof));
6260       size += dof;
6261     }
6262     for (p = 0; p < numPoints; ++p) {
6263       const PetscInt cp = cone[p];
6264       PetscInt       dof;
6265 
6266       if ((cp < pStart) || (cp >= pEnd)) continue;
6267       PetscCall(PetscSectionGetDof(section, cp, &dof));
6268       size += dof;
6269     }
6270     if (!values) {
6271       if (csize) *csize = size;
6272       PetscFunctionReturn(PETSC_SUCCESS);
6273     }
6274     PetscCall(DMGetWorkArray(dm, size, MPIU_SCALAR, &array));
6275   } else {
6276     array = *values;
6277   }
6278   size = 0;
6279   PetscCall(VecGetArrayRead(v, &vArray));
6280   if ((point >= pStart) && (point < pEnd)) {
6281     PetscInt           dof, off, d;
6282     const PetscScalar *varr;
6283 
6284     PetscCall(PetscSectionGetDof(section, point, &dof));
6285     PetscCall(PetscSectionGetOffset(section, point, &off));
6286     varr = PetscSafePointerPlusOffset(vArray, off);
6287     for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
6288     size += dof;
6289   }
6290   for (p = 0; p < numPoints; ++p) {
6291     const PetscInt     cp = cone[p];
6292     PetscInt           o  = coneO[p];
6293     PetscInt           dof, off, d;
6294     const PetscScalar *varr;
6295 
6296     if ((cp < pStart) || (cp >= pEnd)) continue;
6297     PetscCall(PetscSectionGetDof(section, cp, &dof));
6298     PetscCall(PetscSectionGetOffset(section, cp, &off));
6299     varr = PetscSafePointerPlusOffset(vArray, off);
6300     if (o >= 0) {
6301       for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
6302     } else {
6303       for (d = dof - 1; d >= 0; --d, ++offset) array[offset] = varr[d];
6304     }
6305     size += dof;
6306   }
6307   PetscCall(VecRestoreArrayRead(v, &vArray));
6308   if (!*values) {
6309     if (csize) *csize = size;
6310     *values = array;
6311   } else {
6312     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
6313     *csize = size;
6314   }
6315   PetscFunctionReturn(PETSC_SUCCESS);
6316 }
6317 
6318 /* Compress out points not in the section */
6319 static inline PetscErrorCode CompressPoints_Private(PetscSection section, PetscInt *numPoints, PetscInt points[])
6320 {
6321   const PetscInt np = *numPoints;
6322   PetscInt       pStart, pEnd, p, q;
6323 
6324   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6325   for (p = 0, q = 0; p < np; ++p) {
6326     const PetscInt r = points[p * 2];
6327     if ((r >= pStart) && (r < pEnd)) {
6328       points[q * 2]     = r;
6329       points[q * 2 + 1] = points[p * 2 + 1];
6330       ++q;
6331     }
6332   }
6333   *numPoints = q;
6334   return PETSC_SUCCESS;
6335 }
6336 
6337 /* Compressed closure does not apply closure permutation */
6338 PetscErrorCode DMPlexGetCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt ornt, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
6339 {
6340   const PetscInt *cla = NULL;
6341   PetscInt        np, *pts = NULL;
6342 
6343   PetscFunctionBeginHot;
6344   PetscCall(PetscSectionGetClosureIndex(section, (PetscObject)dm, clSec, clPoints));
6345   if (!ornt && *clPoints) {
6346     PetscInt dof, off;
6347 
6348     PetscCall(PetscSectionGetDof(*clSec, point, &dof));
6349     PetscCall(PetscSectionGetOffset(*clSec, point, &off));
6350     PetscCall(ISGetIndices(*clPoints, &cla));
6351     np  = dof / 2;
6352     pts = PetscSafePointerPlusOffset((PetscInt *)cla, off);
6353   } else {
6354     PetscCall(DMPlexGetTransitiveClosure_Internal(dm, point, ornt, PETSC_TRUE, &np, &pts));
6355     PetscCall(CompressPoints_Private(section, &np, pts));
6356   }
6357   *numPoints = np;
6358   *points    = pts;
6359   *clp       = cla;
6360   PetscFunctionReturn(PETSC_SUCCESS);
6361 }
6362 
6363 PetscErrorCode DMPlexRestoreCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
6364 {
6365   PetscFunctionBeginHot;
6366   if (!*clPoints) {
6367     PetscCall(DMPlexRestoreTransitiveClosure(dm, point, PETSC_TRUE, numPoints, points));
6368   } else {
6369     PetscCall(ISRestoreIndices(*clPoints, clp));
6370   }
6371   *numPoints = 0;
6372   *points    = NULL;
6373   *clSec     = NULL;
6374   *clPoints  = NULL;
6375   *clp       = NULL;
6376   PetscFunctionReturn(PETSC_SUCCESS);
6377 }
6378 
6379 static inline PetscErrorCode DMPlexVecGetClosure_Static(DM dm, PetscSection section, PetscInt numPoints, const PetscInt points[], const PetscInt clperm[], const PetscScalar vArray[], PetscInt *size, PetscScalar array[])
6380 {
6381   PetscInt            offset = 0, p;
6382   const PetscInt    **perms  = NULL;
6383   const PetscScalar **flips  = NULL;
6384 
6385   PetscFunctionBeginHot;
6386   *size = 0;
6387   PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
6388   for (p = 0; p < numPoints; p++) {
6389     const PetscInt     point = points[2 * p];
6390     const PetscInt    *perm  = perms ? perms[p] : NULL;
6391     const PetscScalar *flip  = flips ? flips[p] : NULL;
6392     PetscInt           dof, off, d;
6393     const PetscScalar *varr;
6394 
6395     PetscCall(PetscSectionGetDof(section, point, &dof));
6396     PetscCall(PetscSectionGetOffset(section, point, &off));
6397     varr = PetscSafePointerPlusOffset(vArray, off);
6398     if (clperm) {
6399       if (perm) {
6400         for (d = 0; d < dof; d++) array[clperm[offset + perm[d]]] = varr[d];
6401       } else {
6402         for (d = 0; d < dof; d++) array[clperm[offset + d]] = varr[d];
6403       }
6404       if (flip) {
6405         for (d = 0; d < dof; d++) array[clperm[offset + d]] *= flip[d];
6406       }
6407     } else {
6408       if (perm) {
6409         for (d = 0; d < dof; d++) array[offset + perm[d]] = varr[d];
6410       } else {
6411         for (d = 0; d < dof; d++) array[offset + d] = varr[d];
6412       }
6413       if (flip) {
6414         for (d = 0; d < dof; d++) array[offset + d] *= flip[d];
6415       }
6416     }
6417     offset += dof;
6418   }
6419   PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
6420   *size = offset;
6421   PetscFunctionReturn(PETSC_SUCCESS);
6422 }
6423 
6424 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[])
6425 {
6426   PetscInt offset = 0, f;
6427 
6428   PetscFunctionBeginHot;
6429   *size = 0;
6430   for (f = 0; f < numFields; ++f) {
6431     PetscInt            p;
6432     const PetscInt    **perms = NULL;
6433     const PetscScalar **flips = NULL;
6434 
6435     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6436     for (p = 0; p < numPoints; p++) {
6437       const PetscInt     point = points[2 * p];
6438       PetscInt           fdof, foff, b;
6439       const PetscScalar *varr;
6440       const PetscInt    *perm = perms ? perms[p] : NULL;
6441       const PetscScalar *flip = flips ? flips[p] : NULL;
6442 
6443       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6444       PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6445       varr = &vArray[foff];
6446       if (clperm) {
6447         if (perm) {
6448           for (b = 0; b < fdof; b++) array[clperm[offset + perm[b]]] = varr[b];
6449         } else {
6450           for (b = 0; b < fdof; b++) array[clperm[offset + b]] = varr[b];
6451         }
6452         if (flip) {
6453           for (b = 0; b < fdof; b++) array[clperm[offset + b]] *= flip[b];
6454         }
6455       } else {
6456         if (perm) {
6457           for (b = 0; b < fdof; b++) array[offset + perm[b]] = varr[b];
6458         } else {
6459           for (b = 0; b < fdof; b++) array[offset + b] = varr[b];
6460         }
6461         if (flip) {
6462           for (b = 0; b < fdof; b++) array[offset + b] *= flip[b];
6463         }
6464       }
6465       offset += fdof;
6466     }
6467     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6468   }
6469   *size = offset;
6470   PetscFunctionReturn(PETSC_SUCCESS);
6471 }
6472 
6473 PetscErrorCode DMPlexVecGetOrientedClosure_Internal(DM dm, PetscSection section, PetscBool useClPerm, Vec v, PetscInt point, PetscInt ornt, PetscInt *csize, PetscScalar *values[])
6474 {
6475   PetscSection    clSection;
6476   IS              clPoints;
6477   PetscInt       *points = NULL;
6478   const PetscInt *clp, *perm = NULL;
6479   PetscInt        depth, numFields, numPoints, asize;
6480 
6481   PetscFunctionBeginHot;
6482   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6483   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6484   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6485   PetscValidHeaderSpecific(v, VEC_CLASSID, 4);
6486   PetscCall(DMPlexGetDepth(dm, &depth));
6487   PetscCall(PetscSectionGetNumFields(section, &numFields));
6488   if (depth == 1 && numFields < 2) {
6489     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6490     PetscFunctionReturn(PETSC_SUCCESS);
6491   }
6492   /* Get points */
6493   PetscCall(DMPlexGetCompressedClosure(dm, section, point, ornt, &numPoints, &points, &clSection, &clPoints, &clp));
6494   /* Get sizes */
6495   asize = 0;
6496   for (PetscInt p = 0; p < numPoints * 2; p += 2) {
6497     PetscInt dof;
6498     PetscCall(PetscSectionGetDof(section, points[p], &dof));
6499     asize += dof;
6500   }
6501   if (values) {
6502     const PetscScalar *vArray;
6503     PetscInt           size;
6504 
6505     if (*values) {
6506       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);
6507     } else PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, values));
6508     if (useClPerm) PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, asize, &perm));
6509     PetscCall(VecGetArrayRead(v, &vArray));
6510     /* Get values */
6511     if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, numPoints, points, numFields, perm, vArray, &size, *values));
6512     else PetscCall(DMPlexVecGetClosure_Static(dm, section, numPoints, points, perm, vArray, &size, *values));
6513     PetscCheck(asize == size, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Section size %" PetscInt_FMT " does not match Vec closure size %" PetscInt_FMT, asize, size);
6514     /* Cleanup array */
6515     PetscCall(VecRestoreArrayRead(v, &vArray));
6516   }
6517   if (csize) *csize = asize;
6518   /* Cleanup points */
6519   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6520   PetscFunctionReturn(PETSC_SUCCESS);
6521 }
6522 
6523 /*@C
6524   DMPlexVecGetClosure - Get an array of the values on the closure of 'point'
6525 
6526   Not collective
6527 
6528   Input Parameters:
6529 + dm      - The `DM`
6530 . section - The section describing the layout in `v`, or `NULL` to use the default section
6531 . v       - The local vector
6532 - point   - The point in the `DM`
6533 
6534   Input/Output Parameters:
6535 + csize  - The size of the input values array, or `NULL`; on output the number of values in the closure
6536 - values - An array to use for the values, or *values = `NULL` to have it allocated automatically;
6537            if the user provided `NULL`, it is a borrowed array and should not be freed, use  `DMPlexVecRestoreClosure()` to return it
6538 
6539   Level: intermediate
6540 
6541   Notes:
6542   `DMPlexVecGetClosure()`/`DMPlexVecRestoreClosure()` only allocates the values array if it set to `NULL` in the
6543   calling function. This is because `DMPlexVecGetClosure()` is typically called in the inner loop of a `Vec` or `Mat`
6544   assembly function, and a user may already have allocated storage for this operation.
6545 
6546   A typical use could be
6547 .vb
6548    values = NULL;
6549    PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
6550    for (cl = 0; cl < clSize; ++cl) {
6551      <Compute on closure>
6552    }
6553    PetscCall(DMPlexVecRestoreClosure(dm, NULL, v, p, &clSize, &values));
6554 .ve
6555   or
6556 .vb
6557    PetscMalloc1(clMaxSize, &values);
6558    for (p = pStart; p < pEnd; ++p) {
6559      clSize = clMaxSize;
6560      PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
6561      for (cl = 0; cl < clSize; ++cl) {
6562        <Compute on closure>
6563      }
6564    }
6565    PetscFree(values);
6566 .ve
6567 
6568   Fortran Notes:
6569   The `csize` argument is not present in the Fortran binding.
6570 
6571   `values` must be declared with
6572 .vb
6573   PetscScalar,dimension(:),pointer   :: values
6574 .ve
6575   and it will be allocated internally by PETSc to hold the values returned
6576 
6577 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecRestoreClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6578 @*/
6579 PetscErrorCode DMPlexVecGetClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6580 {
6581   PetscFunctionBeginHot;
6582   PetscCall(DMPlexVecGetOrientedClosure_Internal(dm, section, PETSC_TRUE, v, point, 0, csize, values));
6583   PetscFunctionReturn(PETSC_SUCCESS);
6584 }
6585 
6586 PetscErrorCode DMPlexVecGetClosureAtDepth_Internal(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt depth, PetscInt *csize, PetscScalar *values[])
6587 {
6588   DMLabel            depthLabel;
6589   PetscSection       clSection;
6590   IS                 clPoints;
6591   PetscScalar       *array;
6592   const PetscScalar *vArray;
6593   PetscInt          *points = NULL;
6594   const PetscInt    *clp, *perm = NULL;
6595   PetscInt           mdepth, numFields, numPoints, Np = 0, p, clsize, size;
6596 
6597   PetscFunctionBeginHot;
6598   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6599   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6600   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6601   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6602   PetscCall(DMPlexGetDepth(dm, &mdepth));
6603   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
6604   PetscCall(PetscSectionGetNumFields(section, &numFields));
6605   if (mdepth == 1 && numFields < 2) {
6606     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6607     PetscFunctionReturn(PETSC_SUCCESS);
6608   }
6609   /* Get points */
6610   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
6611   for (clsize = 0, p = 0; p < Np; p++) {
6612     PetscInt dof;
6613     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
6614     clsize += dof;
6615   }
6616   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &perm));
6617   /* Filter points */
6618   for (p = 0; p < numPoints * 2; p += 2) {
6619     PetscInt dep;
6620 
6621     PetscCall(DMLabelGetValue(depthLabel, points[p], &dep));
6622     if (dep != depth) continue;
6623     points[Np * 2 + 0] = points[p];
6624     points[Np * 2 + 1] = points[p + 1];
6625     ++Np;
6626   }
6627   /* Get array */
6628   if (!values || !*values) {
6629     PetscInt asize = 0, dof;
6630 
6631     for (p = 0; p < Np * 2; p += 2) {
6632       PetscCall(PetscSectionGetDof(section, points[p], &dof));
6633       asize += dof;
6634     }
6635     if (!values) {
6636       PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6637       if (csize) *csize = asize;
6638       PetscFunctionReturn(PETSC_SUCCESS);
6639     }
6640     PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, &array));
6641   } else {
6642     array = *values;
6643   }
6644   PetscCall(VecGetArrayRead(v, &vArray));
6645   /* Get values */
6646   if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, Np, points, numFields, perm, vArray, &size, array));
6647   else PetscCall(DMPlexVecGetClosure_Static(dm, section, Np, points, perm, vArray, &size, array));
6648   /* Cleanup points */
6649   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6650   /* Cleanup array */
6651   PetscCall(VecRestoreArrayRead(v, &vArray));
6652   if (!*values) {
6653     if (csize) *csize = size;
6654     *values = array;
6655   } else {
6656     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
6657     *csize = size;
6658   }
6659   PetscFunctionReturn(PETSC_SUCCESS);
6660 }
6661 
6662 /*@C
6663   DMPlexVecRestoreClosure - Restore the array of the values on the closure of 'point' obtained with `DMPlexVecGetClosure()`
6664 
6665   Not collective
6666 
6667   Input Parameters:
6668 + dm      - The `DM`
6669 . section - The section describing the layout in `v`, or `NULL` to use the default section
6670 . v       - The local vector
6671 . point   - The point in the `DM`
6672 . csize   - The number of values in the closure, or `NULL`
6673 - values  - The array of values
6674 
6675   Level: intermediate
6676 
6677   Note:
6678   The array values are discarded and not copied back into `v`. In order to copy values back to `v`, use `DMPlexVecSetClosure()`
6679 
6680   Fortran Note:
6681   The `csize` argument is not present in the Fortran binding since it is internal to the array.
6682 
6683 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6684 @*/
6685 PetscErrorCode DMPlexVecRestoreClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6686 {
6687   PetscInt size = 0;
6688 
6689   PetscFunctionBegin;
6690   /* Should work without recalculating size */
6691   PetscCall(DMRestoreWorkArray(dm, size, MPIU_SCALAR, (void *)values));
6692   *values = NULL;
6693   PetscFunctionReturn(PETSC_SUCCESS);
6694 }
6695 
6696 static inline void add(PetscScalar *x, PetscScalar y)
6697 {
6698   *x += y;
6699 }
6700 static inline void insert(PetscScalar *x, PetscScalar y)
6701 {
6702   *x = y;
6703 }
6704 
6705 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[])
6706 {
6707   PetscInt        cdof;  /* The number of constraints on this point */
6708   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6709   PetscScalar    *a;
6710   PetscInt        off, cind = 0, k;
6711 
6712   PetscFunctionBegin;
6713   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6714   PetscCall(PetscSectionGetOffset(section, point, &off));
6715   a = &array[off];
6716   if (!cdof || setBC) {
6717     if (clperm) {
6718       if (perm) {
6719         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6720       } else {
6721         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6722       }
6723     } else {
6724       if (perm) {
6725         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6726       } else {
6727         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6728       }
6729     }
6730   } else {
6731     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6732     if (clperm) {
6733       if (perm) {
6734         for (k = 0; k < dof; ++k) {
6735           if ((cind < cdof) && (k == cdofs[cind])) {
6736             ++cind;
6737             continue;
6738           }
6739           fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6740         }
6741       } else {
6742         for (k = 0; k < dof; ++k) {
6743           if ((cind < cdof) && (k == cdofs[cind])) {
6744             ++cind;
6745             continue;
6746           }
6747           fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6748         }
6749       }
6750     } else {
6751       if (perm) {
6752         for (k = 0; k < dof; ++k) {
6753           if ((cind < cdof) && (k == cdofs[cind])) {
6754             ++cind;
6755             continue;
6756           }
6757           fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6758         }
6759       } else {
6760         for (k = 0; k < dof; ++k) {
6761           if ((cind < cdof) && (k == cdofs[cind])) {
6762             ++cind;
6763             continue;
6764           }
6765           fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6766         }
6767       }
6768     }
6769   }
6770   PetscFunctionReturn(PETSC_SUCCESS);
6771 }
6772 
6773 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[])
6774 {
6775   PetscInt        cdof;  /* The number of constraints on this point */
6776   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6777   PetscScalar    *a;
6778   PetscInt        off, cind = 0, k;
6779 
6780   PetscFunctionBegin;
6781   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6782   PetscCall(PetscSectionGetOffset(section, point, &off));
6783   a = &array[off];
6784   if (cdof) {
6785     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6786     if (clperm) {
6787       if (perm) {
6788         for (k = 0; k < dof; ++k) {
6789           if ((cind < cdof) && (k == cdofs[cind])) {
6790             fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6791             cind++;
6792           }
6793         }
6794       } else {
6795         for (k = 0; k < dof; ++k) {
6796           if ((cind < cdof) && (k == cdofs[cind])) {
6797             fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6798             cind++;
6799           }
6800         }
6801       }
6802     } else {
6803       if (perm) {
6804         for (k = 0; k < dof; ++k) {
6805           if ((cind < cdof) && (k == cdofs[cind])) {
6806             fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6807             cind++;
6808           }
6809         }
6810       } else {
6811         for (k = 0; k < dof; ++k) {
6812           if ((cind < cdof) && (k == cdofs[cind])) {
6813             fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6814             cind++;
6815           }
6816         }
6817       }
6818     }
6819   }
6820   PetscFunctionReturn(PETSC_SUCCESS);
6821 }
6822 
6823 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[])
6824 {
6825   PetscScalar    *a;
6826   PetscInt        fdof, foff, fcdof, foffset = *offset;
6827   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6828   PetscInt        cind = 0, b;
6829 
6830   PetscFunctionBegin;
6831   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6832   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6833   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6834   a = &array[foff];
6835   if (!fcdof || setBC) {
6836     if (clperm) {
6837       if (perm) {
6838         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6839       } else {
6840         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6841       }
6842     } else {
6843       if (perm) {
6844         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6845       } else {
6846         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6847       }
6848     }
6849   } else {
6850     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6851     if (clperm) {
6852       if (perm) {
6853         for (b = 0; b < fdof; b++) {
6854           if ((cind < fcdof) && (b == fcdofs[cind])) {
6855             ++cind;
6856             continue;
6857           }
6858           fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6859         }
6860       } else {
6861         for (b = 0; b < fdof; b++) {
6862           if ((cind < fcdof) && (b == fcdofs[cind])) {
6863             ++cind;
6864             continue;
6865           }
6866           fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6867         }
6868       }
6869     } else {
6870       if (perm) {
6871         for (b = 0; b < fdof; b++) {
6872           if ((cind < fcdof) && (b == fcdofs[cind])) {
6873             ++cind;
6874             continue;
6875           }
6876           fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6877         }
6878       } else {
6879         for (b = 0; b < fdof; b++) {
6880           if ((cind < fcdof) && (b == fcdofs[cind])) {
6881             ++cind;
6882             continue;
6883           }
6884           fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6885         }
6886       }
6887     }
6888   }
6889   *offset += fdof;
6890   PetscFunctionReturn(PETSC_SUCCESS);
6891 }
6892 
6893 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[])
6894 {
6895   PetscScalar    *a;
6896   PetscInt        fdof, foff, fcdof, foffset = *offset;
6897   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6898   PetscInt        Nc, cind = 0, ncind = 0, b;
6899   PetscBool       ncSet, fcSet;
6900 
6901   PetscFunctionBegin;
6902   PetscCall(PetscSectionGetFieldComponents(section, f, &Nc));
6903   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6904   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6905   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6906   a = &array[foff];
6907   if (fcdof) {
6908     /* We just override fcdof and fcdofs with Ncc and comps */
6909     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6910     if (clperm) {
6911       if (perm) {
6912         if (comps) {
6913           for (b = 0; b < fdof; b++) {
6914             ncSet = fcSet = PETSC_FALSE;
6915             if (b % Nc == comps[ncind]) {
6916               ncind = (ncind + 1) % Ncc;
6917               ncSet = PETSC_TRUE;
6918             }
6919             if ((cind < fcdof) && (b == fcdofs[cind])) {
6920               ++cind;
6921               fcSet = PETSC_TRUE;
6922             }
6923             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6924           }
6925         } else {
6926           for (b = 0; b < fdof; b++) {
6927             if ((cind < fcdof) && (b == fcdofs[cind])) {
6928               fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6929               ++cind;
6930             }
6931           }
6932         }
6933       } else {
6934         if (comps) {
6935           for (b = 0; b < fdof; b++) {
6936             ncSet = fcSet = PETSC_FALSE;
6937             if (b % Nc == comps[ncind]) {
6938               ncind = (ncind + 1) % Ncc;
6939               ncSet = PETSC_TRUE;
6940             }
6941             if ((cind < fcdof) && (b == fcdofs[cind])) {
6942               ++cind;
6943               fcSet = PETSC_TRUE;
6944             }
6945             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6946           }
6947         } else {
6948           for (b = 0; b < fdof; b++) {
6949             if ((cind < fcdof) && (b == fcdofs[cind])) {
6950               fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6951               ++cind;
6952             }
6953           }
6954         }
6955       }
6956     } else {
6957       if (perm) {
6958         if (comps) {
6959           for (b = 0; b < fdof; b++) {
6960             ncSet = fcSet = PETSC_FALSE;
6961             if (b % Nc == comps[ncind]) {
6962               ncind = (ncind + 1) % Ncc;
6963               ncSet = PETSC_TRUE;
6964             }
6965             if ((cind < fcdof) && (b == fcdofs[cind])) {
6966               ++cind;
6967               fcSet = PETSC_TRUE;
6968             }
6969             if (ncSet && fcSet) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6970           }
6971         } else {
6972           for (b = 0; b < fdof; b++) {
6973             if ((cind < fcdof) && (b == fcdofs[cind])) {
6974               fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6975               ++cind;
6976             }
6977           }
6978         }
6979       } else {
6980         if (comps) {
6981           for (b = 0; b < fdof; b++) {
6982             ncSet = fcSet = PETSC_FALSE;
6983             if (b % Nc == comps[ncind]) {
6984               ncind = (ncind + 1) % Ncc;
6985               ncSet = PETSC_TRUE;
6986             }
6987             if ((cind < fcdof) && (b == fcdofs[cind])) {
6988               ++cind;
6989               fcSet = PETSC_TRUE;
6990             }
6991             if (ncSet && fcSet) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6992           }
6993         } else {
6994           for (b = 0; b < fdof; b++) {
6995             if ((cind < fcdof) && (b == fcdofs[cind])) {
6996               fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6997               ++cind;
6998             }
6999           }
7000         }
7001       }
7002     }
7003   }
7004   *offset += fdof;
7005   PetscFunctionReturn(PETSC_SUCCESS);
7006 }
7007 
7008 static inline PetscErrorCode DMPlexVecSetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
7009 {
7010   PetscScalar    *array;
7011   const PetscInt *cone, *coneO;
7012   PetscInt        pStart, pEnd, p, numPoints, off, dof;
7013 
7014   PetscFunctionBeginHot;
7015   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
7016   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
7017   PetscCall(DMPlexGetCone(dm, point, &cone));
7018   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
7019   PetscCall(VecGetArray(v, &array));
7020   for (p = 0, off = 0; p <= numPoints; ++p, off += dof) {
7021     const PetscInt cp = !p ? point : cone[p - 1];
7022     const PetscInt o  = !p ? 0 : coneO[p - 1];
7023 
7024     if ((cp < pStart) || (cp >= pEnd)) {
7025       dof = 0;
7026       continue;
7027     }
7028     PetscCall(PetscSectionGetDof(section, cp, &dof));
7029     /* ADD_VALUES */
7030     {
7031       const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
7032       PetscScalar    *a;
7033       PetscInt        cdof, coff, cind = 0, k;
7034 
7035       PetscCall(PetscSectionGetConstraintDof(section, cp, &cdof));
7036       PetscCall(PetscSectionGetOffset(section, cp, &coff));
7037       a = &array[coff];
7038       if (!cdof) {
7039         if (o >= 0) {
7040           for (k = 0; k < dof; ++k) a[k] += values[off + k];
7041         } else {
7042           for (k = 0; k < dof; ++k) a[k] += values[off + dof - k - 1];
7043         }
7044       } else {
7045         PetscCall(PetscSectionGetConstraintIndices(section, cp, &cdofs));
7046         if (o >= 0) {
7047           for (k = 0; k < dof; ++k) {
7048             if ((cind < cdof) && (k == cdofs[cind])) {
7049               ++cind;
7050               continue;
7051             }
7052             a[k] += values[off + k];
7053           }
7054         } else {
7055           for (k = 0; k < dof; ++k) {
7056             if ((cind < cdof) && (k == cdofs[cind])) {
7057               ++cind;
7058               continue;
7059             }
7060             a[k] += values[off + dof - k - 1];
7061           }
7062         }
7063       }
7064     }
7065   }
7066   PetscCall(VecRestoreArray(v, &array));
7067   PetscFunctionReturn(PETSC_SUCCESS);
7068 }
7069 
7070 /*@C
7071   DMPlexVecSetClosure - Set an array of the values on the closure of `point`
7072 
7073   Not collective
7074 
7075   Input Parameters:
7076 + dm      - The `DM`
7077 . section - The section describing the layout in `v`, or `NULL` to use the default section
7078 . v       - The local vector
7079 . point   - The point in the `DM`
7080 . values  - The array of values
7081 - mode    - The insert mode. One of `INSERT_ALL_VALUES`, `ADD_ALL_VALUES`, `INSERT_VALUES`, `ADD_VALUES`, `INSERT_BC_VALUES`, and `ADD_BC_VALUES`,
7082             where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions.
7083 
7084   Level: intermediate
7085 
7086   Note:
7087   Usually the input arrays were obtained with `DMPlexVecGetClosure()`
7088 
7089   Fortran Note:
7090   `values` must be declared with
7091 .vb
7092   PetscScalar,dimension(:),pointer   :: values
7093 .ve
7094 
7095 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`
7096 @*/
7097 PetscErrorCode DMPlexVecSetClosure(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
7098 {
7099   PetscSection    clSection;
7100   IS              clPoints;
7101   PetscScalar    *array;
7102   PetscInt       *points = NULL;
7103   const PetscInt *clp, *clperm = NULL;
7104   PetscInt        depth, numFields, numPoints, p, clsize;
7105 
7106   PetscFunctionBeginHot;
7107   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7108   if (!section) PetscCall(DMGetLocalSection(dm, &section));
7109   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7110   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
7111   PetscCall(DMPlexGetDepth(dm, &depth));
7112   PetscCall(PetscSectionGetNumFields(section, &numFields));
7113   if (depth == 1 && numFields < 2 && mode == ADD_VALUES) {
7114     PetscCall(DMPlexVecSetClosure_Depth1_Static(dm, section, v, point, values, mode));
7115     PetscFunctionReturn(PETSC_SUCCESS);
7116   }
7117   /* Get points */
7118   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
7119   for (clsize = 0, p = 0; p < numPoints; p++) {
7120     PetscInt dof;
7121     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
7122     clsize += dof;
7123   }
7124   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
7125   /* Get array */
7126   PetscCall(VecGetArray(v, &array));
7127   /* Get values */
7128   if (numFields > 0) {
7129     PetscInt offset = 0, f;
7130     for (f = 0; f < numFields; ++f) {
7131       const PetscInt    **perms = NULL;
7132       const PetscScalar **flips = NULL;
7133 
7134       PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7135       switch (mode) {
7136       case INSERT_VALUES:
7137         for (p = 0; p < numPoints; p++) {
7138           const PetscInt     point = points[2 * p];
7139           const PetscInt    *perm  = perms ? perms[p] : NULL;
7140           const PetscScalar *flip  = flips ? flips[p] : NULL;
7141           PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, clperm, values, &offset, array));
7142         }
7143         break;
7144       case INSERT_ALL_VALUES:
7145         for (p = 0; p < numPoints; p++) {
7146           const PetscInt     point = points[2 * p];
7147           const PetscInt    *perm  = perms ? perms[p] : NULL;
7148           const PetscScalar *flip  = flips ? flips[p] : NULL;
7149           PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, clperm, values, &offset, array));
7150         }
7151         break;
7152       case INSERT_BC_VALUES:
7153         for (p = 0; p < numPoints; p++) {
7154           const PetscInt     point = points[2 * p];
7155           const PetscInt    *perm  = perms ? perms[p] : NULL;
7156           const PetscScalar *flip  = flips ? flips[p] : NULL;
7157           PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, insert, clperm, values, &offset, array));
7158         }
7159         break;
7160       case ADD_VALUES:
7161         for (p = 0; p < numPoints; p++) {
7162           const PetscInt     point = points[2 * p];
7163           const PetscInt    *perm  = perms ? perms[p] : NULL;
7164           const PetscScalar *flip  = flips ? flips[p] : NULL;
7165           PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, clperm, values, &offset, array));
7166         }
7167         break;
7168       case ADD_ALL_VALUES:
7169         for (p = 0; p < numPoints; p++) {
7170           const PetscInt     point = points[2 * p];
7171           const PetscInt    *perm  = perms ? perms[p] : NULL;
7172           const PetscScalar *flip  = flips ? flips[p] : NULL;
7173           PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, clperm, values, &offset, array));
7174         }
7175         break;
7176       case ADD_BC_VALUES:
7177         for (p = 0; p < numPoints; p++) {
7178           const PetscInt     point = points[2 * p];
7179           const PetscInt    *perm  = perms ? perms[p] : NULL;
7180           const PetscScalar *flip  = flips ? flips[p] : NULL;
7181           PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, add, clperm, values, &offset, array));
7182         }
7183         break;
7184       default:
7185         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
7186       }
7187       PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7188     }
7189   } else {
7190     PetscInt            dof, off;
7191     const PetscInt    **perms = NULL;
7192     const PetscScalar **flips = NULL;
7193 
7194     PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
7195     switch (mode) {
7196     case INSERT_VALUES:
7197       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7198         const PetscInt     point = points[2 * p];
7199         const PetscInt    *perm  = perms ? perms[p] : NULL;
7200         const PetscScalar *flip  = flips ? flips[p] : NULL;
7201         PetscCall(PetscSectionGetDof(section, point, &dof));
7202         PetscCall(updatePoint_private(section, point, dof, insert, PETSC_FALSE, perm, flip, clperm, values, off, array));
7203       }
7204       break;
7205     case INSERT_ALL_VALUES:
7206       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7207         const PetscInt     point = points[2 * p];
7208         const PetscInt    *perm  = perms ? perms[p] : NULL;
7209         const PetscScalar *flip  = flips ? flips[p] : NULL;
7210         PetscCall(PetscSectionGetDof(section, point, &dof));
7211         PetscCall(updatePoint_private(section, point, dof, insert, PETSC_TRUE, perm, flip, clperm, values, off, array));
7212       }
7213       break;
7214     case INSERT_BC_VALUES:
7215       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7216         const PetscInt     point = points[2 * p];
7217         const PetscInt    *perm  = perms ? perms[p] : NULL;
7218         const PetscScalar *flip  = flips ? flips[p] : NULL;
7219         PetscCall(PetscSectionGetDof(section, point, &dof));
7220         PetscCall(updatePointBC_private(section, point, dof, insert, perm, flip, clperm, values, off, array));
7221       }
7222       break;
7223     case ADD_VALUES:
7224       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7225         const PetscInt     point = points[2 * p];
7226         const PetscInt    *perm  = perms ? perms[p] : NULL;
7227         const PetscScalar *flip  = flips ? flips[p] : NULL;
7228         PetscCall(PetscSectionGetDof(section, point, &dof));
7229         PetscCall(updatePoint_private(section, point, dof, add, PETSC_FALSE, perm, flip, clperm, values, off, array));
7230       }
7231       break;
7232     case ADD_ALL_VALUES:
7233       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7234         const PetscInt     point = points[2 * p];
7235         const PetscInt    *perm  = perms ? perms[p] : NULL;
7236         const PetscScalar *flip  = flips ? flips[p] : NULL;
7237         PetscCall(PetscSectionGetDof(section, point, &dof));
7238         PetscCall(updatePoint_private(section, point, dof, add, PETSC_TRUE, perm, flip, clperm, values, off, array));
7239       }
7240       break;
7241     case ADD_BC_VALUES:
7242       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7243         const PetscInt     point = points[2 * p];
7244         const PetscInt    *perm  = perms ? perms[p] : NULL;
7245         const PetscScalar *flip  = flips ? flips[p] : NULL;
7246         PetscCall(PetscSectionGetDof(section, point, &dof));
7247         PetscCall(updatePointBC_private(section, point, dof, add, perm, flip, clperm, values, off, array));
7248       }
7249       break;
7250     default:
7251       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
7252     }
7253     PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
7254   }
7255   /* Cleanup points */
7256   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
7257   /* Cleanup array */
7258   PetscCall(VecRestoreArray(v, &array));
7259   PetscFunctionReturn(PETSC_SUCCESS);
7260 }
7261 
7262 /* Check whether the given point is in the label. If not, update the offset to skip this point */
7263 static inline PetscErrorCode CheckPoint_Private(DMLabel label, PetscInt labelId, PetscSection section, PetscInt point, PetscInt f, PetscInt *offset, PetscBool *contains)
7264 {
7265   PetscFunctionBegin;
7266   *contains = PETSC_TRUE;
7267   if (label) {
7268     PetscInt fdof;
7269 
7270     PetscCall(DMLabelStratumHasPoint(label, labelId, point, contains));
7271     if (!*contains) {
7272       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7273       *offset += fdof;
7274       PetscFunctionReturn(PETSC_SUCCESS);
7275     }
7276   }
7277   PetscFunctionReturn(PETSC_SUCCESS);
7278 }
7279 
7280 /* Unlike DMPlexVecSetClosure(), this uses plex-native closure permutation, not a user-specified permutation such as DMPlexSetClosurePermutationTensor(). */
7281 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)
7282 {
7283   PetscSection    clSection;
7284   IS              clPoints;
7285   PetscScalar    *array;
7286   PetscInt       *points = NULL;
7287   const PetscInt *clp;
7288   PetscInt        numFields, numPoints, p;
7289   PetscInt        offset = 0, f;
7290 
7291   PetscFunctionBeginHot;
7292   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7293   if (!section) PetscCall(DMGetLocalSection(dm, &section));
7294   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7295   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
7296   PetscCall(PetscSectionGetNumFields(section, &numFields));
7297   /* Get points */
7298   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
7299   /* Get array */
7300   PetscCall(VecGetArray(v, &array));
7301   /* Get values */
7302   for (f = 0; f < numFields; ++f) {
7303     const PetscInt    **perms = NULL;
7304     const PetscScalar **flips = NULL;
7305     PetscBool           contains;
7306 
7307     if (!fieldActive[f]) {
7308       for (p = 0; p < numPoints * 2; p += 2) {
7309         PetscInt fdof;
7310         PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
7311         offset += fdof;
7312       }
7313       continue;
7314     }
7315     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7316     switch (mode) {
7317     case INSERT_VALUES:
7318       for (p = 0; p < numPoints; p++) {
7319         const PetscInt     point = points[2 * p];
7320         const PetscInt    *perm  = perms ? perms[p] : NULL;
7321         const PetscScalar *flip  = flips ? flips[p] : NULL;
7322         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7323         if (!contains) continue;
7324         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, NULL, values, &offset, array));
7325       }
7326       break;
7327     case INSERT_ALL_VALUES:
7328       for (p = 0; p < numPoints; p++) {
7329         const PetscInt     point = points[2 * p];
7330         const PetscInt    *perm  = perms ? perms[p] : NULL;
7331         const PetscScalar *flip  = flips ? flips[p] : NULL;
7332         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7333         if (!contains) continue;
7334         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, NULL, values, &offset, array));
7335       }
7336       break;
7337     case INSERT_BC_VALUES:
7338       for (p = 0; p < numPoints; p++) {
7339         const PetscInt     point = points[2 * p];
7340         const PetscInt    *perm  = perms ? perms[p] : NULL;
7341         const PetscScalar *flip  = flips ? flips[p] : NULL;
7342         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7343         if (!contains) continue;
7344         PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, Ncc, comps, insert, NULL, values, &offset, array));
7345       }
7346       break;
7347     case ADD_VALUES:
7348       for (p = 0; p < numPoints; p++) {
7349         const PetscInt     point = points[2 * p];
7350         const PetscInt    *perm  = perms ? perms[p] : NULL;
7351         const PetscScalar *flip  = flips ? flips[p] : NULL;
7352         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7353         if (!contains) continue;
7354         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, NULL, values, &offset, array));
7355       }
7356       break;
7357     case ADD_ALL_VALUES:
7358       for (p = 0; p < numPoints; p++) {
7359         const PetscInt     point = points[2 * p];
7360         const PetscInt    *perm  = perms ? perms[p] : NULL;
7361         const PetscScalar *flip  = flips ? flips[p] : NULL;
7362         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7363         if (!contains) continue;
7364         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, NULL, values, &offset, array));
7365       }
7366       break;
7367     default:
7368       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
7369     }
7370     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7371   }
7372   /* Cleanup points */
7373   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
7374   /* Cleanup array */
7375   PetscCall(VecRestoreArray(v, &array));
7376   PetscFunctionReturn(PETSC_SUCCESS);
7377 }
7378 
7379 static PetscErrorCode DMPlexPrintMatSetValues(PetscViewer viewer, Mat A, PetscInt point, PetscInt numRIndices, const PetscInt rindices[], PetscInt numCIndices, const PetscInt cindices[], const PetscScalar values[])
7380 {
7381   PetscMPIInt rank;
7382   PetscInt    i, j;
7383 
7384   PetscFunctionBegin;
7385   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7386   PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat for point %" PetscInt_FMT "\n", rank, point));
7387   for (i = 0; i < numRIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat row indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, rindices[i]));
7388   for (i = 0; i < numCIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat col indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, cindices[i]));
7389   numCIndices = numCIndices ? numCIndices : numRIndices;
7390   if (!values) PetscFunctionReturn(PETSC_SUCCESS);
7391   for (i = 0; i < numRIndices; i++) {
7392     PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]", rank));
7393     for (j = 0; j < numCIndices; j++) {
7394 #if defined(PETSC_USE_COMPLEX)
7395       PetscCall(PetscViewerASCIIPrintf(viewer, " (%g,%g)", (double)PetscRealPart(values[i * numCIndices + j]), (double)PetscImaginaryPart(values[i * numCIndices + j])));
7396 #else
7397       PetscCall(PetscViewerASCIIPrintf(viewer, " %g", (double)values[i * numCIndices + j]));
7398 #endif
7399     }
7400     PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
7401   }
7402   PetscFunctionReturn(PETSC_SUCCESS);
7403 }
7404 
7405 /*
7406   DMPlexGetIndicesPoint_Internal - Add the indices for dofs on a point to an index array
7407 
7408   Input Parameters:
7409 + section - The section for this data layout
7410 . islocal - Is the section (and thus indices being requested) local or global?
7411 . point   - The point contributing dofs with these indices
7412 . off     - The global offset of this point
7413 . loff    - The local offset of each field
7414 . setBC   - The flag determining whether to include indices of boundary values
7415 . perm    - A permutation of the dofs on this point, or NULL
7416 - indperm - A permutation of the entire indices array, or NULL
7417 
7418   Output Parameter:
7419 . indices - Indices for dofs on this point
7420 
7421   Level: developer
7422 
7423   Note: The indices could be local or global, depending on the value of 'off'.
7424 */
7425 PetscErrorCode DMPlexGetIndicesPoint_Internal(PetscSection section, PetscBool islocal, PetscInt point, PetscInt off, PetscInt *loff, PetscBool setBC, const PetscInt perm[], const PetscInt indperm[], PetscInt indices[])
7426 {
7427   PetscInt        dof;   /* The number of unknowns on this point */
7428   PetscInt        cdof;  /* The number of constraints on this point */
7429   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
7430   PetscInt        cind = 0, k;
7431 
7432   PetscFunctionBegin;
7433   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
7434   PetscCall(PetscSectionGetDof(section, point, &dof));
7435   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
7436   if (!cdof || setBC) {
7437     for (k = 0; k < dof; ++k) {
7438       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
7439       const PetscInt ind    = indperm ? indperm[preind] : preind;
7440 
7441       indices[ind] = off + k;
7442     }
7443   } else {
7444     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
7445     for (k = 0; k < dof; ++k) {
7446       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
7447       const PetscInt ind    = indperm ? indperm[preind] : preind;
7448 
7449       if ((cind < cdof) && (k == cdofs[cind])) {
7450         /* Insert check for returning constrained indices */
7451         indices[ind] = -(off + k + 1);
7452         ++cind;
7453       } else {
7454         indices[ind] = off + k - (islocal ? 0 : cind);
7455       }
7456     }
7457   }
7458   *loff += dof;
7459   PetscFunctionReturn(PETSC_SUCCESS);
7460 }
7461 
7462 /*
7463  DMPlexGetIndicesPointFields_Internal - gets section indices for a point in its canonical ordering.
7464 
7465  Input Parameters:
7466 + section - a section (global or local)
7467 - islocal - `PETSC_TRUE` if requesting local indices (i.e., section is local); `PETSC_FALSE` for global
7468 . point - point within section
7469 . off - The offset of this point in the (local or global) indexed space - should match islocal and (usually) the section
7470 . foffs - array of length numFields containing the offset in canonical point ordering (the location in indices) of each field
7471 . setBC - identify constrained (boundary condition) points via involution.
7472 . perms - perms[f][permsoff][:] is a permutation of dofs within each field
7473 . permsoff - offset
7474 - indperm - index permutation
7475 
7476  Output Parameter:
7477 . foffs - each entry is incremented by the number of (unconstrained if setBC=FALSE) dofs in that field
7478 . indices - array to hold indices (as defined by section) of each dof associated with point
7479 
7480  Notes:
7481  If section is local and setBC=true, there is no distinction between constrained and unconstrained dofs.
7482  If section is local and setBC=false, the indices for constrained points are the involution -(i+1) of their position
7483  in the local vector.
7484 
7485  If section is global and setBC=false, the indices for constrained points are negative (and their value is not
7486  significant).  It is invalid to call with a global section and setBC=true.
7487 
7488  Developer Note:
7489  The section is only used for field layout, so islocal is technically a statement about the offset (off).  At some point
7490  in the future, global sections may have fields set, in which case we could pass the global section and obtain the
7491  offset could be obtained from the section instead of passing it explicitly as we do now.
7492 
7493  Example:
7494  Suppose a point contains one field with three components, and for which the unconstrained indices are {10, 11, 12}.
7495  When the middle component is constrained, we get the array {10, -12, 12} for (islocal=TRUE, setBC=FALSE).
7496  Note that -12 is the involution of 11, so the user can involute negative indices to recover local indices.
7497  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.
7498 
7499  Level: developer
7500 */
7501 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[])
7502 {
7503   PetscInt numFields, foff, f;
7504 
7505   PetscFunctionBegin;
7506   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
7507   PetscCall(PetscSectionGetNumFields(section, &numFields));
7508   for (f = 0, foff = 0; f < numFields; ++f) {
7509     PetscInt        fdof, cfdof;
7510     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7511     PetscInt        cind = 0, b;
7512     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7513 
7514     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7515     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7516     if (!cfdof || setBC) {
7517       for (b = 0; b < fdof; ++b) {
7518         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7519         const PetscInt ind    = indperm ? indperm[preind] : preind;
7520 
7521         indices[ind] = off + foff + b;
7522       }
7523     } else {
7524       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7525       for (b = 0; b < fdof; ++b) {
7526         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7527         const PetscInt ind    = indperm ? indperm[preind] : preind;
7528 
7529         if ((cind < cfdof) && (b == fcdofs[cind])) {
7530           indices[ind] = -(off + foff + b + 1);
7531           ++cind;
7532         } else {
7533           indices[ind] = off + foff + b - (islocal ? 0 : cind);
7534         }
7535       }
7536     }
7537     foff += (setBC || islocal ? fdof : (fdof - cfdof));
7538     foffs[f] += fdof;
7539   }
7540   PetscFunctionReturn(PETSC_SUCCESS);
7541 }
7542 
7543 /*
7544   This version believes the globalSection offsets for each field, rather than just the point offset
7545 
7546  . foffs - The offset into 'indices' for each field, since it is segregated by field
7547 
7548  Notes:
7549  The semantics of this function relate to that of setBC=FALSE in DMPlexGetIndicesPointFields_Internal.
7550  Since this function uses global indices, setBC=TRUE would be invalid, so no such argument exists.
7551 */
7552 static PetscErrorCode DMPlexGetIndicesPointFieldsSplit_Internal(PetscSection section, PetscSection globalSection, PetscInt point, PetscInt foffs[], const PetscInt ***perms, PetscInt permsoff, const PetscInt indperm[], PetscInt indices[])
7553 {
7554   PetscInt numFields, foff, f;
7555 
7556   PetscFunctionBegin;
7557   PetscCall(PetscSectionGetNumFields(section, &numFields));
7558   for (f = 0; f < numFields; ++f) {
7559     PetscInt        fdof, cfdof;
7560     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7561     PetscInt        cind = 0, b;
7562     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7563 
7564     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7565     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7566     PetscCall(PetscSectionGetFieldOffset(globalSection, point, f, &foff));
7567     if (!cfdof) {
7568       for (b = 0; b < fdof; ++b) {
7569         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7570         const PetscInt ind    = indperm ? indperm[preind] : preind;
7571 
7572         indices[ind] = foff + b;
7573       }
7574     } else {
7575       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7576       for (b = 0; b < fdof; ++b) {
7577         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7578         const PetscInt ind    = indperm ? indperm[preind] : preind;
7579 
7580         if ((cind < cfdof) && (b == fcdofs[cind])) {
7581           indices[ind] = -(foff + b + 1);
7582           ++cind;
7583         } else {
7584           indices[ind] = foff + b - cind;
7585         }
7586       }
7587     }
7588     foffs[f] += fdof;
7589   }
7590   PetscFunctionReturn(PETSC_SUCCESS);
7591 }
7592 
7593 static PetscErrorCode DMPlexAnchorsGetSubMatIndices(PetscInt nPoints, const PetscInt pnts[], PetscSection section, PetscSection cSec, PetscInt tmpIndices[], PetscInt fieldOffsets[], PetscInt indices[], const PetscInt ***perms)
7594 {
7595   PetscInt numFields, sStart, sEnd, cStart, cEnd;
7596 
7597   PetscFunctionBegin;
7598   PetscCall(PetscSectionGetNumFields(section, &numFields));
7599   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
7600   PetscCall(PetscSectionGetChart(cSec, &cStart, &cEnd));
7601   for (PetscInt p = 0; p < nPoints; p++) {
7602     PetscInt     b       = pnts[2 * p];
7603     PetscInt     bSecDof = 0, bOff;
7604     PetscInt     cSecDof = 0;
7605     PetscSection indices_section;
7606 
7607     if (b >= sStart && b < sEnd) PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7608     if (!bSecDof) continue;
7609     if (b >= cStart && b < cEnd) PetscCall(PetscSectionGetDof(cSec, b, &cSecDof));
7610     indices_section = cSecDof > 0 ? cSec : section;
7611     if (numFields) {
7612       PetscInt fStart[32], fEnd[32];
7613 
7614       fStart[0] = 0;
7615       fEnd[0]   = 0;
7616       for (PetscInt f = 0; f < numFields; f++) {
7617         PetscInt fDof = 0;
7618 
7619         PetscCall(PetscSectionGetFieldDof(indices_section, b, f, &fDof));
7620         fStart[f + 1] = fStart[f] + fDof;
7621         fEnd[f + 1]   = fStart[f + 1];
7622       }
7623       PetscCall(PetscSectionGetOffset(indices_section, b, &bOff));
7624       // only apply permutations on one side
7625       PetscCall(DMPlexGetIndicesPointFields_Internal(indices_section, PETSC_TRUE, b, bOff, fEnd, PETSC_TRUE, perms, perms ? p : -1, NULL, tmpIndices));
7626       for (PetscInt f = 0; f < numFields; f++) {
7627         for (PetscInt i = fStart[f]; i < fEnd[f]; i++) { indices[fieldOffsets[f]++] = (cSecDof > 0) ? tmpIndices[i] : -(tmpIndices[i] + 1); }
7628       }
7629     } else {
7630       PetscInt bEnd = 0;
7631 
7632       PetscCall(PetscSectionGetOffset(indices_section, b, &bOff));
7633       PetscCall(DMPlexGetIndicesPoint_Internal(indices_section, PETSC_TRUE, b, bOff, &bEnd, PETSC_TRUE, (perms && perms[0]) ? perms[0][p] : NULL, NULL, tmpIndices));
7634 
7635       for (PetscInt i = 0; i < bEnd; i++) indices[fieldOffsets[0]++] = (cSecDof > 0) ? tmpIndices[i] : -(tmpIndices[i] + 1);
7636     }
7637   }
7638   PetscFunctionReturn(PETSC_SUCCESS);
7639 }
7640 
7641 PETSC_INTERN PetscErrorCode DMPlexAnchorsGetSubMatModification(DM dm, PetscSection section, PetscInt numPoints, PetscInt numIndices, const PetscInt points[], const PetscInt ***perms, PetscInt *outNumPoints, PetscInt *outNumIndices, PetscInt *outPoints[], PetscInt offsets[], PetscScalar *outMat[])
7642 {
7643   Mat             cMat;
7644   PetscSection    aSec, cSec;
7645   IS              aIS;
7646   PetscInt        aStart = -1, aEnd = -1;
7647   PetscInt        sStart = -1, sEnd = -1;
7648   PetscInt        cStart = -1, cEnd = -1;
7649   const PetscInt *anchors;
7650   PetscInt        numFields, p;
7651   PetscInt        newNumPoints = 0, newNumIndices = 0;
7652   PetscInt       *newPoints, *indices, *newIndices, *tmpIndices, *tmpNewIndices;
7653   PetscInt        oldOffsets[32];
7654   PetscInt        newOffsets[32];
7655   PetscInt        oldOffsetsCopy[32];
7656   PetscInt        newOffsetsCopy[32];
7657   PetscScalar    *modMat         = NULL;
7658   PetscBool       anyConstrained = PETSC_FALSE;
7659 
7660   PetscFunctionBegin;
7661   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7662   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7663   PetscCall(PetscSectionGetNumFields(section, &numFields));
7664 
7665   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
7666   /* if there are point-to-point constraints */
7667   if (aSec) {
7668     PetscCall(PetscArrayzero(newOffsets, 32));
7669     PetscCall(PetscArrayzero(oldOffsets, 32));
7670     PetscCall(ISGetIndices(aIS, &anchors));
7671     PetscCall(PetscSectionGetChart(aSec, &aStart, &aEnd));
7672     PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
7673     /* figure out how many points are going to be in the new element matrix
7674      * (we allow double counting, because it's all just going to be summed
7675      * into the global matrix anyway) */
7676     for (p = 0; p < 2 * numPoints; p += 2) {
7677       PetscInt b    = points[p];
7678       PetscInt bDof = 0, bSecDof = 0;
7679 
7680       if (b >= sStart && b < sEnd) PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7681       if (!bSecDof) continue;
7682 
7683       for (PetscInt f = 0; f < numFields; f++) {
7684         PetscInt fDof = 0;
7685 
7686         PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7687         oldOffsets[f + 1] += fDof;
7688       }
7689       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7690       if (bDof) {
7691         /* this point is constrained */
7692         /* it is going to be replaced by its anchors */
7693         PetscInt bOff, q;
7694 
7695         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7696         for (q = 0; q < bDof; q++) {
7697           PetscInt a    = anchors[bOff + q];
7698           PetscInt aDof = 0;
7699 
7700           if (a >= sStart && a < sEnd) PetscCall(PetscSectionGetDof(section, a, &aDof));
7701           if (aDof) {
7702             anyConstrained = PETSC_TRUE;
7703             newNumPoints += 1;
7704           }
7705           newNumIndices += aDof;
7706           for (PetscInt f = 0; f < numFields; ++f) {
7707             PetscInt fDof = 0;
7708 
7709             if (a >= sStart && a < sEnd) PetscCall(PetscSectionGetFieldDof(section, a, f, &fDof));
7710             newOffsets[f + 1] += fDof;
7711           }
7712         }
7713       } else {
7714         /* this point is not constrained */
7715         newNumPoints++;
7716         newNumIndices += bSecDof;
7717         for (PetscInt f = 0; f < numFields; ++f) {
7718           PetscInt fDof;
7719 
7720           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7721           newOffsets[f + 1] += fDof;
7722         }
7723       }
7724     }
7725   }
7726   if (!anyConstrained) {
7727     if (outNumPoints) *outNumPoints = 0;
7728     if (outNumIndices) *outNumIndices = 0;
7729     if (outPoints) *outPoints = NULL;
7730     if (outMat) *outMat = NULL;
7731     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7732     PetscFunctionReturn(PETSC_SUCCESS);
7733   }
7734 
7735   if (outNumPoints) *outNumPoints = newNumPoints;
7736   if (outNumIndices) *outNumIndices = newNumIndices;
7737 
7738   for (PetscInt f = 0; f < numFields; ++f) newOffsets[f + 1] += newOffsets[f];
7739   for (PetscInt f = 0; f < numFields; ++f) oldOffsets[f + 1] += oldOffsets[f];
7740 
7741   if (!outPoints && !outMat) {
7742     if (offsets) {
7743       for (PetscInt f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7744     }
7745     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7746     PetscFunctionReturn(PETSC_SUCCESS);
7747   }
7748 
7749   PetscCheck(!numFields || newOffsets[numFields] == newNumIndices, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, newOffsets[numFields], newNumIndices);
7750   PetscCheck(!numFields || oldOffsets[numFields] == numIndices, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, oldOffsets[numFields], numIndices);
7751 
7752   PetscCall(DMGetDefaultConstraints(dm, &cSec, &cMat, NULL));
7753   PetscCall(PetscSectionGetChart(cSec, &cStart, &cEnd));
7754 
7755   /* output arrays */
7756   PetscCall(DMGetWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7757   PetscCall(PetscArrayzero(newPoints, 2 * newNumPoints));
7758 
7759   // get the new Points
7760   for (PetscInt p = 0, newP = 0; p < numPoints; p++) {
7761     PetscInt b    = points[2 * p];
7762     PetscInt bDof = 0, bSecDof = 0, bOff;
7763 
7764     if (b >= sStart && b < sEnd) PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7765     if (!bSecDof) continue;
7766     if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7767     if (bDof) {
7768       PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7769       for (PetscInt q = 0; q < bDof; q++) {
7770         PetscInt a = anchors[bOff + q], aDof = 0;
7771 
7772         if (a >= sStart && a < sEnd) PetscCall(PetscSectionGetDof(section, a, &aDof));
7773         if (aDof) {
7774           newPoints[2 * newP]     = a;
7775           newPoints[2 * newP + 1] = 0; // orientations are accounted for in constructing the matrix, newly added points are in default orientation
7776           newP++;
7777         }
7778       }
7779     } else {
7780       newPoints[2 * newP]     = b;
7781       newPoints[2 * newP + 1] = points[2 * p + 1];
7782       newP++;
7783     }
7784   }
7785 
7786   if (outMat) {
7787     PetscScalar *tmpMat;
7788     PetscCall(PetscArraycpy(oldOffsetsCopy, oldOffsets, 32));
7789     PetscCall(PetscArraycpy(newOffsetsCopy, newOffsets, 32));
7790 
7791     PetscCall(DMGetWorkArray(dm, numIndices, MPIU_INT, &indices));
7792     PetscCall(DMGetWorkArray(dm, numIndices, MPIU_INT, &tmpIndices));
7793     PetscCall(DMGetWorkArray(dm, newNumIndices, MPIU_INT, &newIndices));
7794     PetscCall(DMGetWorkArray(dm, newNumIndices, MPIU_INT, &tmpNewIndices));
7795 
7796     for (PetscInt i = 0; i < numIndices; i++) indices[i] = -1;
7797     for (PetscInt i = 0; i < newNumIndices; i++) newIndices[i] = -1;
7798 
7799     PetscCall(DMPlexAnchorsGetSubMatIndices(numPoints, points, section, cSec, tmpIndices, oldOffsetsCopy, indices, perms));
7800     PetscCall(DMPlexAnchorsGetSubMatIndices(newNumPoints, newPoints, section, section, tmpNewIndices, newOffsetsCopy, newIndices, NULL));
7801 
7802     PetscCall(DMGetWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &modMat));
7803     PetscCall(DMGetWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &tmpMat));
7804     PetscCall(PetscArrayzero(modMat, newNumIndices * numIndices));
7805     // for each field, insert the anchor modification into modMat
7806     for (PetscInt f = 0; f < PetscMax(1, numFields); f++) {
7807       PetscInt fStart    = oldOffsets[f];
7808       PetscInt fNewStart = newOffsets[f];
7809       for (PetscInt p = 0, newP = 0, o = fStart, oNew = fNewStart; p < numPoints; p++) {
7810         PetscInt b    = points[2 * p];
7811         PetscInt bDof = 0, bSecDof = 0, bOff;
7812 
7813         if (b >= sStart && b < sEnd) {
7814           if (numFields) {
7815             PetscCall(PetscSectionGetFieldDof(section, b, f, &bSecDof));
7816           } else {
7817             PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7818           }
7819         }
7820         if (!bSecDof) continue;
7821         if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7822         if (bDof) {
7823           PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7824           for (PetscInt q = 0; q < bDof; q++, newP++) {
7825             PetscInt a = anchors[bOff + q], aDof = 0;
7826 
7827             if (a >= sStart && a < sEnd) {
7828               if (numFields) {
7829                 PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
7830               } else {
7831                 PetscCall(PetscSectionGetDof(section, a, &aDof));
7832               }
7833             }
7834             if (aDof) {
7835               PetscCall(MatGetValues(cMat, bSecDof, &indices[o], aDof, &newIndices[oNew], tmpMat));
7836               for (PetscInt d = 0; d < bSecDof; d++) {
7837                 for (PetscInt e = 0; e < aDof; e++) modMat[(o + d) * newNumIndices + oNew + e] = tmpMat[d * aDof + e];
7838               }
7839             }
7840             oNew += aDof;
7841           }
7842         } else {
7843           // Insert the identity matrix in this block
7844           for (PetscInt d = 0; d < bSecDof; d++) modMat[(o + d) * newNumIndices + oNew + d] = 1;
7845           oNew += bSecDof;
7846           newP++;
7847         }
7848         o += bSecDof;
7849       }
7850     }
7851 
7852     *outMat = modMat;
7853 
7854     PetscCall(DMRestoreWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &tmpMat));
7855     PetscCall(DMRestoreWorkArray(dm, newNumIndices, MPIU_INT, &tmpNewIndices));
7856     PetscCall(DMRestoreWorkArray(dm, newNumIndices, MPIU_INT, &newIndices));
7857     PetscCall(DMRestoreWorkArray(dm, numIndices, MPIU_INT, &tmpIndices));
7858     PetscCall(DMRestoreWorkArray(dm, numIndices, MPIU_INT, &indices));
7859   }
7860   PetscCall(ISRestoreIndices(aIS, &anchors));
7861 
7862   /* output */
7863   if (outPoints) {
7864     *outPoints = newPoints;
7865   } else {
7866     PetscCall(DMRestoreWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7867   }
7868   for (PetscInt f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7869   PetscFunctionReturn(PETSC_SUCCESS);
7870 }
7871 
7872 PETSC_INTERN PetscErrorCode DMPlexAnchorsModifyMat_Internal(DM dm, PetscSection section, PetscInt numPoints, PetscInt numIndices, const PetscInt points[], const PetscInt ***perms, PetscInt numRows, PetscInt numCols, const PetscScalar values[], PetscInt *outNumPoints, PetscInt *outNumIndices, PetscInt *outPoints[], PetscScalar *outValues[], PetscInt offsets[], PetscBool multiplyRight, PetscBool multiplyLeft)
7873 {
7874   PetscScalar *modMat        = NULL;
7875   PetscInt     newNumIndices = -1;
7876 
7877   PetscFunctionBegin;
7878   /* If M is the matrix represented by values, get the matrix C such that we will add M * C (or, if multiplyLeft, C^T * M * C) into the global matrix.
7879      modMat is that matrix C */
7880   PetscCall(DMPlexAnchorsGetSubMatModification(dm, section, numPoints, numIndices, points, perms, outNumPoints, &newNumIndices, outPoints, offsets, outValues ? &modMat : NULL));
7881   if (outNumIndices) *outNumIndices = newNumIndices;
7882   if (modMat) {
7883     const PetscScalar *newValues = values;
7884 
7885     if (multiplyRight) {
7886       PetscScalar *newNewValues = NULL;
7887       PetscBLASInt M, N, K;
7888       PetscScalar  a = 1.0, b = 0.0;
7889 
7890       PetscCheck(numCols == numIndices, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "values matrix has the wrong number of columns: %" PetscInt_FMT ", expected %" PetscInt_FMT, numCols, numIndices);
7891 
7892       PetscCall(PetscBLASIntCast(newNumIndices, &M));
7893       PetscCall(PetscBLASIntCast(numRows, &N));
7894       PetscCall(PetscBLASIntCast(numIndices, &K));
7895       PetscCall(DMGetWorkArray(dm, numRows * newNumIndices, MPIU_SCALAR, &newNewValues));
7896       // row-major to column-major conversion, right multiplication becomes left multiplication
7897       PetscCallBLAS("BLASgemm", BLASgemm_("N", "N", &M, &N, &K, &a, modMat, &M, newValues, &K, &b, newNewValues, &M));
7898       numCols   = newNumIndices;
7899       newValues = newNewValues;
7900     }
7901 
7902     if (multiplyLeft) {
7903       PetscScalar *newNewValues = NULL;
7904       PetscBLASInt M, N, K;
7905       PetscScalar  a = 1.0, b = 0.0;
7906 
7907       PetscCheck(numRows == numIndices, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "values matrix has the wrong number of rows: %" PetscInt_FMT ", expected %" PetscInt_FMT, numRows, numIndices);
7908 
7909       PetscCall(PetscBLASIntCast(numCols, &M));
7910       PetscCall(PetscBLASIntCast(newNumIndices, &N));
7911       PetscCall(PetscBLASIntCast(numIndices, &K));
7912       PetscCall(DMGetWorkArray(dm, newNumIndices * numCols, MPIU_SCALAR, &newNewValues));
7913       // row-major to column-major conversion, left multiplication becomes right multiplication
7914       PetscCallBLAS("BLASgemm", BLASgemm_("N", "T", &M, &N, &K, &a, newValues, &M, modMat, &N, &b, newNewValues, &M));
7915       if (newValues != values) PetscCall(DMRestoreWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &newValues));
7916       newValues = newNewValues;
7917     }
7918     *outValues = (PetscScalar *)newValues;
7919     PetscCall(DMRestoreWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &modMat));
7920   }
7921   PetscFunctionReturn(PETSC_SUCCESS);
7922 }
7923 
7924 PETSC_INTERN PetscErrorCode DMPlexAnchorsModifyMat(DM dm, PetscSection section, PetscInt numPoints, PetscInt numIndices, const PetscInt points[], const PetscInt ***perms, const PetscScalar values[], PetscInt *outNumPoints, PetscInt *outNumIndices, PetscInt *outPoints[], PetscScalar *outValues[], PetscInt offsets[], PetscBool multiplyLeft)
7925 {
7926   PetscFunctionBegin;
7927   PetscCall(DMPlexAnchorsModifyMat_Internal(dm, section, numPoints, numIndices, points, perms, numIndices, numIndices, values, outNumPoints, outNumIndices, outPoints, outValues, offsets, PETSC_TRUE, multiplyLeft));
7928   PetscFunctionReturn(PETSC_SUCCESS);
7929 }
7930 
7931 static PetscErrorCode DMPlexGetClosureIndicesSize_Internal(DM dm, PetscSection section, PetscInt point, PetscInt *closureSize)
7932 {
7933   /* Closure ordering */
7934   PetscSection    clSection;
7935   IS              clPoints;
7936   const PetscInt *clp;
7937   PetscInt       *points;
7938   PetscInt        Ncl, Ni = 0;
7939 
7940   PetscFunctionBeginHot;
7941   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &Ncl, &points, &clSection, &clPoints, &clp));
7942   for (PetscInt p = 0; p < Ncl * 2; p += 2) {
7943     PetscInt dof;
7944 
7945     PetscCall(PetscSectionGetDof(section, points[p], &dof));
7946     Ni += dof;
7947   }
7948   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7949   *closureSize = Ni;
7950   PetscFunctionReturn(PETSC_SUCCESS);
7951 }
7952 
7953 static PetscErrorCode DMPlexGetClosureIndices_Internal(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numRows, PetscInt *numCols, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[], PetscBool multiplyRight, PetscBool multiplyLeft)
7954 {
7955   /* Closure ordering */
7956   PetscSection    clSection;
7957   IS              clPoints;
7958   const PetscInt *clp;
7959   PetscInt       *points;
7960   const PetscInt *clperm = NULL;
7961   /* Dof permutation and sign flips */
7962   const PetscInt    **perms[32] = {NULL};
7963   const PetscScalar **flips[32] = {NULL};
7964   PetscScalar        *valCopy   = NULL;
7965   /* Hanging node constraints */
7966   PetscInt    *pointsC = NULL;
7967   PetscScalar *valuesC = NULL;
7968   PetscInt     NclC, NiC;
7969 
7970   PetscInt *idx;
7971   PetscInt  Nf, Ncl, Ni = 0, offsets[32], p, f;
7972   PetscBool isLocal = (section == idxSection) ? PETSC_TRUE : PETSC_FALSE;
7973   PetscInt  idxStart, idxEnd;
7974   PetscInt  nRows, nCols;
7975 
7976   PetscFunctionBeginHot;
7977   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7978   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7979   PetscValidHeaderSpecific(idxSection, PETSC_SECTION_CLASSID, 3);
7980   PetscAssertPointer(numRows, 6);
7981   PetscAssertPointer(numCols, 7);
7982   if (indices) PetscAssertPointer(indices, 8);
7983   if (outOffsets) PetscAssertPointer(outOffsets, 9);
7984   if (values) PetscAssertPointer(values, 10);
7985   PetscCall(PetscSectionGetNumFields(section, &Nf));
7986   PetscCheck(Nf <= 31, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", Nf);
7987   PetscCall(PetscArrayzero(offsets, 32));
7988   /* 1) Get points in closure */
7989   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &Ncl, &points, &clSection, &clPoints, &clp));
7990   if (useClPerm) {
7991     PetscInt depth, clsize;
7992     PetscCall(DMPlexGetPointDepth(dm, point, &depth));
7993     for (clsize = 0, p = 0; p < Ncl; p++) {
7994       PetscInt dof;
7995       PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
7996       clsize += dof;
7997     }
7998     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
7999   }
8000   /* 2) Get number of indices on these points and field offsets from section */
8001   for (p = 0; p < Ncl * 2; p += 2) {
8002     PetscInt dof, fdof;
8003 
8004     PetscCall(PetscSectionGetDof(section, points[p], &dof));
8005     for (f = 0; f < Nf; ++f) {
8006       PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
8007       offsets[f + 1] += fdof;
8008     }
8009     Ni += dof;
8010   }
8011   if (*numRows == -1) *numRows = Ni;
8012   if (*numCols == -1) *numCols = Ni;
8013   nRows = *numRows;
8014   nCols = *numCols;
8015   for (f = 1; f < Nf; ++f) offsets[f + 1] += offsets[f];
8016   PetscCheck(!Nf || offsets[Nf] == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, offsets[Nf], Ni);
8017   /* 3) Get symmetries and sign flips. Apply sign flips to values if passed in (only works for square values matrix) */
8018   if (multiplyRight) PetscCheck(nCols == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Expected %" PetscInt_FMT " columns, got %" PetscInt_FMT, Ni, nCols);
8019   if (multiplyLeft) PetscCheck(nRows == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Expected %" PetscInt_FMT " rows, got %" PetscInt_FMT, Ni, nRows);
8020   for (f = 0; f < PetscMax(1, Nf); ++f) {
8021     if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
8022     else PetscCall(PetscSectionGetPointSyms(section, Ncl, points, &perms[f], &flips[f]));
8023     /* may need to apply sign changes to the element matrix */
8024     if (values && flips[f]) {
8025       PetscInt foffset = offsets[f];
8026 
8027       for (p = 0; p < Ncl; ++p) {
8028         PetscInt           pnt  = points[2 * p], fdof;
8029         const PetscScalar *flip = flips[f] ? flips[f][p] : NULL;
8030 
8031         if (!Nf) PetscCall(PetscSectionGetDof(section, pnt, &fdof));
8032         else PetscCall(PetscSectionGetFieldDof(section, pnt, f, &fdof));
8033         if (flip) {
8034           PetscInt i, j, k;
8035 
8036           if (!valCopy) {
8037             PetscCall(DMGetWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
8038             for (j = 0; j < Ni * Ni; ++j) valCopy[j] = (*values)[j];
8039             *values = valCopy;
8040           }
8041           for (i = 0; i < fdof; ++i) {
8042             PetscScalar fval = flip[i];
8043 
8044             if (multiplyRight) {
8045               for (k = 0; k < nRows; ++k) { valCopy[Ni * k + (foffset + i)] *= fval; }
8046             }
8047             if (multiplyLeft) {
8048               for (k = 0; k < nCols; ++k) { valCopy[nCols * (foffset + i) + k] *= fval; }
8049             }
8050           }
8051         }
8052         foffset += fdof;
8053       }
8054     }
8055   }
8056   /* 4) Apply hanging node constraints. Get new symmetries and replace all storage with constrained storage */
8057   PetscCall(DMPlexAnchorsModifyMat_Internal(dm, section, Ncl, Ni, points, perms, nRows, nCols, values ? *values : NULL, &NclC, &NiC, &pointsC, values ? &valuesC : NULL, offsets, multiplyRight, multiplyLeft));
8058   if (NclC) {
8059     if (multiplyRight) { *numCols = nCols = NiC; }
8060     if (multiplyLeft) { *numRows = nRows = NiC; }
8061     if (valCopy) PetscCall(DMRestoreWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
8062     for (f = 0; f < PetscMax(1, Nf); ++f) {
8063       if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
8064       else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
8065     }
8066     for (f = 0; f < PetscMax(1, Nf); ++f) {
8067       if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, NclC, pointsC, &perms[f], &flips[f]));
8068       else PetscCall(PetscSectionGetPointSyms(section, NclC, pointsC, &perms[f], &flips[f]));
8069     }
8070     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
8071     Ncl    = NclC;
8072     Ni     = NiC;
8073     points = pointsC;
8074     if (values) *values = valuesC;
8075   }
8076   /* 5) Calculate indices */
8077   PetscCall(DMGetWorkArray(dm, Ni, MPIU_INT, &idx));
8078   PetscCall(PetscSectionGetChart(idxSection, &idxStart, &idxEnd));
8079   if (Nf) {
8080     PetscInt  idxOff;
8081     PetscBool useFieldOffsets;
8082 
8083     if (outOffsets) {
8084       for (f = 0; f <= Nf; f++) outOffsets[f] = offsets[f];
8085     }
8086     PetscCall(PetscSectionGetUseFieldOffsets(idxSection, &useFieldOffsets));
8087     if (useFieldOffsets) {
8088       for (p = 0; p < Ncl; ++p) {
8089         const PetscInt pnt = points[p * 2];
8090 
8091         PetscCall(DMPlexGetIndicesPointFieldsSplit_Internal(section, idxSection, pnt, offsets, perms, p, clperm, idx));
8092       }
8093     } else {
8094       for (p = 0; p < Ncl; ++p) {
8095         const PetscInt pnt = points[p * 2];
8096 
8097         if (pnt < idxStart || pnt >= idxEnd) continue;
8098         PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
8099         /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
8100          * not (at the time of this writing) have fields set. They probably should, in which case we would pass the
8101          * global section. */
8102         PetscCall(DMPlexGetIndicesPointFields_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, offsets, PETSC_FALSE, perms, p, clperm, idx));
8103       }
8104     }
8105   } else {
8106     PetscInt off = 0, idxOff;
8107 
8108     for (p = 0; p < Ncl; ++p) {
8109       const PetscInt  pnt  = points[p * 2];
8110       const PetscInt *perm = perms[0] ? perms[0][p] : NULL;
8111 
8112       if (pnt < idxStart || pnt >= idxEnd) continue;
8113       PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
8114       /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
8115        * not (at the time of this writing) have fields set. They probably should, in which case we would pass the global section. */
8116       PetscCall(DMPlexGetIndicesPoint_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, &off, PETSC_FALSE, perm, clperm, idx));
8117     }
8118   }
8119   /* 6) Cleanup */
8120   for (f = 0; f < PetscMax(1, Nf); ++f) {
8121     if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
8122     else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
8123   }
8124   if (NclC) {
8125     PetscCall(DMRestoreWorkArray(dm, NclC * 2, MPIU_INT, &pointsC));
8126   } else {
8127     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
8128   }
8129 
8130   if (indices) *indices = idx;
8131   PetscFunctionReturn(PETSC_SUCCESS);
8132 }
8133 
8134 /*@C
8135   DMPlexGetClosureIndices - Gets the global dof indices associated with the closure of the given point within the provided sections.
8136 
8137   Not collective
8138 
8139   Input Parameters:
8140 + dm         - The `DM`
8141 . section    - The `PetscSection` describing the points (a local section)
8142 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
8143 . point      - The point defining the closure
8144 - useClPerm  - Use the closure point permutation if available
8145 
8146   Output Parameters:
8147 + numIndices - The number of dof indices in the closure of point with the input sections
8148 . indices    - The dof indices
8149 . outOffsets - Array to write the field offsets into, or `NULL`
8150 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or `NULL`
8151 
8152   Level: advanced
8153 
8154   Notes:
8155   Call `DMPlexRestoreClosureIndices()` to free allocated memory
8156 
8157   If `idxSection` is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
8158   of those indices is not significant.  If `idxSection` is local, the constrained dofs will yield the involution -(idx+1)
8159   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
8160   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when `idxSection` == section, otherwise global
8161   indices (with the above semantics) are implied.
8162 
8163 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`,
8164           `PetscSection`, `DMGetGlobalSection()`
8165 @*/
8166 PetscErrorCode DMPlexGetClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
8167 {
8168   PetscInt numRows = -1, numCols = -1;
8169 
8170   PetscFunctionBeginHot;
8171   PetscCall(DMPlexGetClosureIndices_Internal(dm, section, idxSection, point, useClPerm, &numRows, &numCols, indices, outOffsets, values, PETSC_TRUE, PETSC_TRUE));
8172   PetscCheck(numRows == numCols, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Symmetric matrix transformation produces rectangular dimensions (%" PetscInt_FMT ", %" PetscInt_FMT ")", numRows, numCols);
8173   *numIndices = numRows;
8174   PetscFunctionReturn(PETSC_SUCCESS);
8175 }
8176 
8177 /*@C
8178   DMPlexRestoreClosureIndices - Restores the global dof indices associated with the closure of the given point within the provided sections.
8179 
8180   Not collective
8181 
8182   Input Parameters:
8183 + dm         - The `DM`
8184 . section    - The `PetscSection` describing the points (a local section)
8185 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
8186 . point      - The point defining the closure
8187 - useClPerm  - Use the closure point permutation if available
8188 
8189   Output Parameters:
8190 + numIndices - The number of dof indices in the closure of point with the input sections
8191 . indices    - The dof indices
8192 . outOffsets - Array to write the field offsets into, or `NULL`
8193 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or `NULL`
8194 
8195   Level: advanced
8196 
8197   Notes:
8198   If values were modified, the user is responsible for calling `DMRestoreWorkArray`(dm, 0, `MPIU_SCALAR`, &values).
8199 
8200   If idxSection is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
8201   of those indices is not significant.  If idxSection is local, the constrained dofs will yield the involution -(idx+1)
8202   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
8203   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when idxSection == section, otherwise global
8204   indices (with the above semantics) are implied.
8205 
8206 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`, `DMGetGlobalSection()`
8207 @*/
8208 PetscErrorCode DMPlexRestoreClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
8209 {
8210   PetscFunctionBegin;
8211   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8212   PetscAssertPointer(indices, 7);
8213   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, indices));
8214   PetscFunctionReturn(PETSC_SUCCESS);
8215 }
8216 
8217 PetscErrorCode DMPlexMatSetClosure_Internal(DM dm, PetscSection section, PetscSection globalSection, PetscBool useClPerm, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8218 {
8219   DM_Plex           *mesh = (DM_Plex *)dm->data;
8220   PetscInt          *indices;
8221   PetscInt           numIndices;
8222   const PetscScalar *valuesOrig = values;
8223   PetscErrorCode     ierr;
8224 
8225   PetscFunctionBegin;
8226   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8227   if (!section) PetscCall(DMGetLocalSection(dm, &section));
8228   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
8229   if (!globalSection) PetscCall(DMGetGlobalSection(dm, &globalSection));
8230   PetscValidHeaderSpecific(globalSection, PETSC_SECTION_CLASSID, 3);
8231   PetscValidHeaderSpecific(A, MAT_CLASSID, 5);
8232 
8233   PetscCall(DMPlexGetClosureIndices(dm, section, globalSection, point, useClPerm, &numIndices, &indices, NULL, (PetscScalar **)&values));
8234 
8235   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndices, indices, 0, NULL, values));
8236   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8237   ierr = MatSetValues(A, numIndices, indices, numIndices, indices, values, mode);
8238   if (ierr) {
8239     PetscMPIInt rank;
8240 
8241     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8242     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8243     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndices, indices, 0, NULL, values));
8244     PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
8245     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
8246     SETERRQ(PetscObjectComm((PetscObject)dm), ierr, "Not possible to set matrix values");
8247   }
8248   if (mesh->printFEM > 1) {
8249     PetscInt i;
8250     PetscCall(PetscPrintf(PETSC_COMM_SELF, "  Indices:"));
8251     for (i = 0; i < numIndices; ++i) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, indices[i]));
8252     PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8253   }
8254 
8255   PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
8256   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
8257   PetscFunctionReturn(PETSC_SUCCESS);
8258 }
8259 
8260 /*@C
8261   DMPlexMatSetClosure - Set an array of the values on the closure of 'point'
8262 
8263   Not collective
8264 
8265   Input Parameters:
8266 + dm            - The `DM`
8267 . section       - The section describing the layout in `v`, or `NULL` to use the default section
8268 . globalSection - The section describing the layout in `v`, or `NULL` to use the default global section
8269 . A             - The matrix
8270 . point         - The point in the `DM`
8271 . values        - The array of values
8272 - mode          - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
8273 
8274   Level: intermediate
8275 
8276 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosureGeneral()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
8277 @*/
8278 PetscErrorCode DMPlexMatSetClosure(DM dm, PetscSection section, PetscSection globalSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8279 {
8280   PetscFunctionBegin;
8281   PetscCall(DMPlexMatSetClosure_Internal(dm, section, globalSection, PETSC_TRUE, A, point, values, mode));
8282   PetscFunctionReturn(PETSC_SUCCESS);
8283 }
8284 
8285 /*@C
8286   DMPlexMatSetClosureGeneral - Set an array of the values on the closure of 'point' using a different row and column section
8287 
8288   Not collective
8289 
8290   Input Parameters:
8291 + dmRow            - The `DM` for the row fields
8292 . sectionRow       - The section describing the layout, or `NULL` to use the default section in `dmRow`
8293 . useRowPerm       - The flag to use the closure permutation of the `dmRow` if available
8294 . globalSectionRow - The section describing the layout, or `NULL` to use the default global section in `dmRow`
8295 . dmCol            - The `DM` for the column fields
8296 . sectionCol       - The section describing the layout, or `NULL` to use the default section in `dmCol`
8297 . useColPerm       - The flag to use the closure permutation of the `dmCol` if available
8298 . globalSectionCol - The section describing the layout, or `NULL` to use the default global section in `dmCol`
8299 . A                - The matrix
8300 . point            - The point in the `DM`
8301 . values           - The array of values
8302 - mode             - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
8303 
8304   Level: intermediate
8305 
8306 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosure()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
8307 @*/
8308 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)
8309 {
8310   DM_Plex           *mesh = (DM_Plex *)dmRow->data;
8311   PetscInt          *indicesRow, *indicesCol;
8312   PetscInt           numIndicesRow = -1, numIndicesCol = -1;
8313   const PetscScalar *valuesV0 = values, *valuesV1, *valuesV2;
8314 
8315   PetscErrorCode ierr;
8316 
8317   PetscFunctionBegin;
8318   PetscValidHeaderSpecific(dmRow, DM_CLASSID, 1);
8319   if (!sectionRow) PetscCall(DMGetLocalSection(dmRow, &sectionRow));
8320   PetscValidHeaderSpecific(sectionRow, PETSC_SECTION_CLASSID, 2);
8321   if (!globalSectionRow) PetscCall(DMGetGlobalSection(dmRow, &globalSectionRow));
8322   PetscValidHeaderSpecific(globalSectionRow, PETSC_SECTION_CLASSID, 3);
8323   PetscValidHeaderSpecific(dmCol, DM_CLASSID, 5);
8324   if (!sectionCol) PetscCall(DMGetLocalSection(dmCol, &sectionCol));
8325   PetscValidHeaderSpecific(sectionCol, PETSC_SECTION_CLASSID, 6);
8326   if (!globalSectionCol) PetscCall(DMGetGlobalSection(dmCol, &globalSectionCol));
8327   PetscValidHeaderSpecific(globalSectionCol, PETSC_SECTION_CLASSID, 7);
8328   PetscValidHeaderSpecific(A, MAT_CLASSID, 9);
8329 
8330   PetscCall(DMPlexGetClosureIndicesSize_Internal(dmRow, sectionRow, point, &numIndicesRow));
8331   PetscCall(DMPlexGetClosureIndicesSize_Internal(dmCol, sectionCol, point, &numIndicesCol));
8332   valuesV1 = valuesV0;
8333   PetscCall(DMPlexGetClosureIndices_Internal(dmRow, sectionRow, globalSectionRow, point, useRowPerm, &numIndicesRow, &numIndicesCol, &indicesRow, NULL, (PetscScalar **)&valuesV1, PETSC_FALSE, PETSC_TRUE));
8334   valuesV2 = valuesV1;
8335   PetscCall(DMPlexGetClosureIndices_Internal(dmCol, sectionCol, globalSectionCol, point, useColPerm, &numIndicesRow, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&valuesV2, PETSC_TRUE, PETSC_FALSE));
8336 
8337   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, valuesV2));
8338   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8339   ierr = MatSetValues(A, numIndicesRow, indicesRow, numIndicesCol, indicesCol, valuesV2, mode);
8340   if (ierr) {
8341     PetscMPIInt rank;
8342 
8343     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8344     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8345     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
8346     PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesRow, NULL, (PetscScalar **)&valuesV2));
8347     PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&valuesV1));
8348     if (valuesV2 != valuesV1) PetscCall(DMRestoreWorkArray(dmCol, 0, MPIU_SCALAR, &valuesV2));
8349     if (valuesV1 != valuesV0) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &valuesV1));
8350   }
8351 
8352   PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, useColPerm, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&valuesV2));
8353   PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, useRowPerm, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&valuesV1));
8354   if (valuesV2 != valuesV1) PetscCall(DMRestoreWorkArray(dmCol, 0, MPIU_SCALAR, &valuesV2));
8355   if (valuesV1 != valuesV0) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &valuesV1));
8356   PetscFunctionReturn(PETSC_SUCCESS);
8357 }
8358 
8359 PetscErrorCode DMPlexMatSetClosureRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8360 {
8361   DM_Plex        *mesh    = (DM_Plex *)dmf->data;
8362   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
8363   PetscInt       *cpoints = NULL;
8364   PetscInt       *findices, *cindices;
8365   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
8366   PetscInt        foffsets[32], coffsets[32];
8367   DMPolytopeType  ct;
8368   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
8369   PetscErrorCode  ierr;
8370 
8371   PetscFunctionBegin;
8372   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
8373   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
8374   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
8375   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
8376   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
8377   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
8378   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
8379   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
8380   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
8381   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
8382   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
8383   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
8384   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
8385   PetscCall(PetscArrayzero(foffsets, 32));
8386   PetscCall(PetscArrayzero(coffsets, 32));
8387   /* Column indices */
8388   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8389   maxFPoints = numCPoints;
8390   /* Compress out points not in the section */
8391   /*   TODO: Squeeze out points with 0 dof as well */
8392   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
8393   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
8394     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
8395       cpoints[q * 2]     = cpoints[p];
8396       cpoints[q * 2 + 1] = cpoints[p + 1];
8397       ++q;
8398     }
8399   }
8400   numCPoints = q;
8401   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
8402     PetscInt fdof;
8403 
8404     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
8405     if (!dof) continue;
8406     for (f = 0; f < numFields; ++f) {
8407       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
8408       coffsets[f + 1] += fdof;
8409     }
8410     numCIndices += dof;
8411   }
8412   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
8413   /* Row indices */
8414   PetscCall(DMPlexGetCellType(dmc, point, &ct));
8415   {
8416     DMPlexTransform tr;
8417     DMPolytopeType *rct;
8418     PetscInt       *rsize, *rcone, *rornt, Nt;
8419 
8420     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8421     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8422     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8423     numSubcells = rsize[Nt - 1];
8424     PetscCall(DMPlexTransformDestroy(&tr));
8425   }
8426   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8427   for (r = 0, q = 0; r < numSubcells; ++r) {
8428     /* TODO Map from coarse to fine cells */
8429     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8430     /* Compress out points not in the section */
8431     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8432     for (p = 0; p < numFPoints * 2; p += 2) {
8433       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8434         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8435         if (!dof) continue;
8436         for (s = 0; s < q; ++s)
8437           if (fpoints[p] == ftotpoints[s * 2]) break;
8438         if (s < q) continue;
8439         ftotpoints[q * 2]     = fpoints[p];
8440         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8441         ++q;
8442       }
8443     }
8444     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8445   }
8446   numFPoints = q;
8447   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8448     PetscInt fdof;
8449 
8450     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8451     if (!dof) continue;
8452     for (f = 0; f < numFields; ++f) {
8453       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8454       foffsets[f + 1] += fdof;
8455     }
8456     numFIndices += dof;
8457   }
8458   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8459 
8460   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8461   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8462   PetscCall(DMGetWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8463   PetscCall(DMGetWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8464   if (numFields) {
8465     const PetscInt **permsF[32] = {NULL};
8466     const PetscInt **permsC[32] = {NULL};
8467 
8468     for (f = 0; f < numFields; f++) {
8469       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8470       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8471     }
8472     for (p = 0; p < numFPoints; p++) {
8473       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8474       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8475     }
8476     for (p = 0; p < numCPoints; p++) {
8477       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8478       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8479     }
8480     for (f = 0; f < numFields; f++) {
8481       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8482       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8483     }
8484   } else {
8485     const PetscInt **permsF = NULL;
8486     const PetscInt **permsC = NULL;
8487 
8488     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8489     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8490     for (p = 0, off = 0; p < numFPoints; p++) {
8491       const PetscInt *perm = permsF ? permsF[p] : NULL;
8492 
8493       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8494       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8495     }
8496     for (p = 0, off = 0; p < numCPoints; p++) {
8497       const PetscInt *perm = permsC ? permsC[p] : NULL;
8498 
8499       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8500       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8501     }
8502     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8503     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8504   }
8505   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8506   /* TODO: flips */
8507   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8508   ierr = MatSetValues(A, numFIndices, findices, numCIndices, cindices, values, mode);
8509   if (ierr) {
8510     PetscMPIInt rank;
8511 
8512     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8513     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8514     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8515     PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8516     PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8517   }
8518   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8519   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8520   PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8521   PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8522   PetscFunctionReturn(PETSC_SUCCESS);
8523 }
8524 
8525 PetscErrorCode DMPlexMatGetClosureIndicesRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, PetscInt point, PetscInt cindices[], PetscInt findices[])
8526 {
8527   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
8528   PetscInt       *cpoints      = NULL;
8529   PetscInt        foffsets[32] = {0}, coffsets[32] = {0};
8530   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
8531   DMPolytopeType  ct;
8532   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
8533 
8534   PetscFunctionBegin;
8535   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
8536   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
8537   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
8538   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
8539   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
8540   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
8541   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
8542   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
8543   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
8544   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
8545   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
8546   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
8547   /* Column indices */
8548   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8549   maxFPoints = numCPoints;
8550   /* Compress out points not in the section */
8551   /*   TODO: Squeeze out points with 0 dof as well */
8552   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
8553   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
8554     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
8555       cpoints[q * 2]     = cpoints[p];
8556       cpoints[q * 2 + 1] = cpoints[p + 1];
8557       ++q;
8558     }
8559   }
8560   numCPoints = q;
8561   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
8562     PetscInt fdof;
8563 
8564     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
8565     if (!dof) continue;
8566     for (f = 0; f < numFields; ++f) {
8567       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
8568       coffsets[f + 1] += fdof;
8569     }
8570     numCIndices += dof;
8571   }
8572   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
8573   /* Row indices */
8574   PetscCall(DMPlexGetCellType(dmc, point, &ct));
8575   {
8576     DMPlexTransform tr;
8577     DMPolytopeType *rct;
8578     PetscInt       *rsize, *rcone, *rornt, Nt;
8579 
8580     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8581     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8582     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8583     numSubcells = rsize[Nt - 1];
8584     PetscCall(DMPlexTransformDestroy(&tr));
8585   }
8586   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8587   for (r = 0, q = 0; r < numSubcells; ++r) {
8588     /* TODO Map from coarse to fine cells */
8589     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8590     /* Compress out points not in the section */
8591     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8592     for (p = 0; p < numFPoints * 2; p += 2) {
8593       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8594         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8595         if (!dof) continue;
8596         for (s = 0; s < q; ++s)
8597           if (fpoints[p] == ftotpoints[s * 2]) break;
8598         if (s < q) continue;
8599         ftotpoints[q * 2]     = fpoints[p];
8600         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8601         ++q;
8602       }
8603     }
8604     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8605   }
8606   numFPoints = q;
8607   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8608     PetscInt fdof;
8609 
8610     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8611     if (!dof) continue;
8612     for (f = 0; f < numFields; ++f) {
8613       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8614       foffsets[f + 1] += fdof;
8615     }
8616     numFIndices += dof;
8617   }
8618   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8619 
8620   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8621   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8622   if (numFields) {
8623     const PetscInt **permsF[32] = {NULL};
8624     const PetscInt **permsC[32] = {NULL};
8625 
8626     for (f = 0; f < numFields; f++) {
8627       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8628       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8629     }
8630     for (p = 0; p < numFPoints; p++) {
8631       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8632       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8633     }
8634     for (p = 0; p < numCPoints; p++) {
8635       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8636       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8637     }
8638     for (f = 0; f < numFields; f++) {
8639       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8640       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8641     }
8642   } else {
8643     const PetscInt **permsF = NULL;
8644     const PetscInt **permsC = NULL;
8645 
8646     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8647     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8648     for (p = 0, off = 0; p < numFPoints; p++) {
8649       const PetscInt *perm = permsF ? permsF[p] : NULL;
8650 
8651       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8652       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8653     }
8654     for (p = 0, off = 0; p < numCPoints; p++) {
8655       const PetscInt *perm = permsC ? permsC[p] : NULL;
8656 
8657       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8658       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8659     }
8660     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8661     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8662   }
8663   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8664   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8665   PetscFunctionReturn(PETSC_SUCCESS);
8666 }
8667 
8668 /*@
8669   DMPlexGetVTKCellHeight - Returns the height in the DAG used to determine which points are cells (normally 0)
8670 
8671   Input Parameter:
8672 . dm - The `DMPLEX` object
8673 
8674   Output Parameter:
8675 . cellHeight - The height of a cell
8676 
8677   Level: developer
8678 
8679 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetVTKCellHeight()`
8680 @*/
8681 PetscErrorCode DMPlexGetVTKCellHeight(DM dm, PetscInt *cellHeight)
8682 {
8683   DM_Plex *mesh = (DM_Plex *)dm->data;
8684 
8685   PetscFunctionBegin;
8686   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8687   PetscAssertPointer(cellHeight, 2);
8688   *cellHeight = mesh->vtkCellHeight;
8689   PetscFunctionReturn(PETSC_SUCCESS);
8690 }
8691 
8692 /*@
8693   DMPlexSetVTKCellHeight - Sets the height in the DAG used to determine which points are cells (normally 0)
8694 
8695   Input Parameters:
8696 + dm         - The `DMPLEX` object
8697 - cellHeight - The height of a cell
8698 
8699   Level: developer
8700 
8701 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetVTKCellHeight()`
8702 @*/
8703 PetscErrorCode DMPlexSetVTKCellHeight(DM dm, PetscInt cellHeight)
8704 {
8705   DM_Plex *mesh = (DM_Plex *)dm->data;
8706 
8707   PetscFunctionBegin;
8708   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8709   mesh->vtkCellHeight = cellHeight;
8710   PetscFunctionReturn(PETSC_SUCCESS);
8711 }
8712 
8713 /*@
8714   DMPlexGetCellTypeStratum - Get the range of cells of a given celltype
8715 
8716   Input Parameters:
8717 + dm - The `DMPLEX` object
8718 - ct - The `DMPolytopeType` of the cell
8719 
8720   Output Parameters:
8721 + start - The first cell of this type, or `NULL`
8722 - end   - The upper bound on this celltype, or `NULL`
8723 
8724   Level: advanced
8725 
8726 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexConstructGhostCells()`, `DMPlexGetDepthStratum()`, `DMPlexGetHeightStratum()`
8727 @*/
8728 PetscErrorCode DMPlexGetCellTypeStratum(DM dm, DMPolytopeType ct, PetscInt *start, PetscInt *end)
8729 {
8730   DM_Plex *mesh = (DM_Plex *)dm->data;
8731   DMLabel  label;
8732   PetscInt pStart, pEnd;
8733 
8734   PetscFunctionBegin;
8735   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8736   if (start) {
8737     PetscAssertPointer(start, 3);
8738     *start = 0;
8739   }
8740   if (end) {
8741     PetscAssertPointer(end, 4);
8742     *end = 0;
8743   }
8744   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8745   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
8746   if (mesh->tr) {
8747     PetscCall(DMPlexTransformGetCellTypeStratum(mesh->tr, ct, start, end));
8748   } else {
8749     PetscCall(DMPlexGetCellTypeLabel(dm, &label));
8750     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named celltype was found");
8751     PetscCall(DMLabelGetStratumBounds(label, ct, start, end));
8752   }
8753   PetscFunctionReturn(PETSC_SUCCESS);
8754 }
8755 
8756 PetscErrorCode DMPlexCreateNumbering_Plex(DM dm, PetscInt pStart, PetscInt pEnd, PetscInt shift, PetscInt *globalSize, PetscSF sf, IS *numbering)
8757 {
8758   PetscSection section, globalSection;
8759   PetscInt    *numbers, p;
8760 
8761   PetscFunctionBegin;
8762   if (PetscDefined(USE_DEBUG)) PetscCall(DMPlexCheckPointSF(dm, sf, PETSC_TRUE));
8763   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
8764   PetscCall(PetscSectionSetChart(section, pStart, pEnd));
8765   for (p = pStart; p < pEnd; ++p) PetscCall(PetscSectionSetDof(section, p, 1));
8766   PetscCall(PetscSectionSetUp(section));
8767   PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &globalSection));
8768   PetscCall(PetscMalloc1(pEnd - pStart, &numbers));
8769   for (p = pStart; p < pEnd; ++p) {
8770     PetscCall(PetscSectionGetOffset(globalSection, p, &numbers[p - pStart]));
8771     if (numbers[p - pStart] < 0) numbers[p - pStart] -= shift;
8772     else numbers[p - pStart] += shift;
8773   }
8774   PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), pEnd - pStart, numbers, PETSC_OWN_POINTER, numbering));
8775   if (globalSize) {
8776     PetscLayout layout;
8777     PetscCall(PetscSectionGetPointLayout(PetscObjectComm((PetscObject)dm), globalSection, &layout));
8778     PetscCall(PetscLayoutGetSize(layout, globalSize));
8779     PetscCall(PetscLayoutDestroy(&layout));
8780   }
8781   PetscCall(PetscSectionDestroy(&section));
8782   PetscCall(PetscSectionDestroy(&globalSection));
8783   PetscFunctionReturn(PETSC_SUCCESS);
8784 }
8785 
8786 /*@
8787   DMPlexCreateCellNumbering - Get a global cell numbering for all cells on this process
8788 
8789   Input Parameters:
8790 + dm         - The `DMPLEX` object
8791 - includeAll - Whether to include all cells, or just the simplex and box cells
8792 
8793   Output Parameter:
8794 . globalCellNumbers - Global cell numbers for all cells on this process
8795 
8796   Level: developer
8797 
8798 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`, `DMPlexGetVertexNumbering()`
8799 @*/
8800 PetscErrorCode DMPlexCreateCellNumbering(DM dm, PetscBool includeAll, IS *globalCellNumbers)
8801 {
8802   PetscInt cellHeight, cStart, cEnd;
8803 
8804   PetscFunctionBegin;
8805   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8806   if (includeAll) PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8807   else PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd));
8808   PetscCall(DMPlexCreateNumbering_Plex(dm, cStart, cEnd, 0, NULL, dm->sf, globalCellNumbers));
8809   PetscFunctionReturn(PETSC_SUCCESS);
8810 }
8811 
8812 /*@
8813   DMPlexGetCellNumbering - Get a global cell numbering for all cells on this process
8814 
8815   Input Parameter:
8816 . dm - The `DMPLEX` object
8817 
8818   Output Parameter:
8819 . globalCellNumbers - Global cell numbers for all cells on this process
8820 
8821   Level: developer
8822 
8823 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreateCellNumbering()`, `DMPlexGetVertexNumbering()`
8824 @*/
8825 PetscErrorCode DMPlexGetCellNumbering(DM dm, IS *globalCellNumbers)
8826 {
8827   DM_Plex *mesh = (DM_Plex *)dm->data;
8828 
8829   PetscFunctionBegin;
8830   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8831   if (!mesh->globalCellNumbers) PetscCall(DMPlexCreateCellNumbering(dm, PETSC_FALSE, &mesh->globalCellNumbers));
8832   *globalCellNumbers = mesh->globalCellNumbers;
8833   PetscFunctionReturn(PETSC_SUCCESS);
8834 }
8835 
8836 PetscErrorCode DMPlexCreateVertexNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalVertexNumbers)
8837 {
8838   PetscInt vStart, vEnd;
8839 
8840   PetscFunctionBegin;
8841   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8842   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8843   PetscCall(DMPlexCreateNumbering_Plex(dm, vStart, vEnd, 0, NULL, dm->sf, globalVertexNumbers));
8844   PetscFunctionReturn(PETSC_SUCCESS);
8845 }
8846 
8847 /*@
8848   DMPlexGetVertexNumbering - Get a global vertex numbering for all vertices on this process
8849 
8850   Input Parameter:
8851 . dm - The `DMPLEX` object
8852 
8853   Output Parameter:
8854 . globalVertexNumbers - Global vertex numbers for all vertices on this process
8855 
8856   Level: developer
8857 
8858 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
8859 @*/
8860 PetscErrorCode DMPlexGetVertexNumbering(DM dm, IS *globalVertexNumbers)
8861 {
8862   DM_Plex *mesh = (DM_Plex *)dm->data;
8863 
8864   PetscFunctionBegin;
8865   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8866   if (!mesh->globalVertexNumbers) PetscCall(DMPlexCreateVertexNumbering_Internal(dm, PETSC_FALSE, &mesh->globalVertexNumbers));
8867   *globalVertexNumbers = mesh->globalVertexNumbers;
8868   PetscFunctionReturn(PETSC_SUCCESS);
8869 }
8870 
8871 /*@
8872   DMPlexCreatePointNumbering - Create a global numbering for all points.
8873 
8874   Collective
8875 
8876   Input Parameter:
8877 . dm - The `DMPLEX` object
8878 
8879   Output Parameter:
8880 . globalPointNumbers - Global numbers for all points on this process
8881 
8882   Level: developer
8883 
8884   Notes:
8885   The point numbering `IS` is parallel, with local portion indexed by local points (see `DMGetLocalSection()`). The global
8886   points are taken as stratified, with each MPI rank owning a contiguous subset of each stratum. In the IS, owned points
8887   will have their non-negative value while points owned by different ranks will be involuted -(idx+1). As an example,
8888   consider a parallel mesh in which the first two elements and first two vertices are owned by rank 0.
8889 
8890   The partitioned mesh is
8891   ```
8892   (2)--0--(3)--1--(4)    (1)--0--(2)
8893   ```
8894   and its global numbering is
8895   ```
8896   (3)--0--(4)--1--(5)--2--(6)
8897   ```
8898   Then the global numbering is provided as
8899   ```
8900   [0] Number of indices in set 5
8901   [0] 0 0
8902   [0] 1 1
8903   [0] 2 3
8904   [0] 3 4
8905   [0] 4 -6
8906   [1] Number of indices in set 3
8907   [1] 0 2
8908   [1] 1 5
8909   [1] 2 6
8910   ```
8911 
8912 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
8913 @*/
8914 PetscErrorCode DMPlexCreatePointNumbering(DM dm, IS *globalPointNumbers)
8915 {
8916   IS        nums[4];
8917   PetscInt  depths[4], gdepths[4], starts[4];
8918   PetscInt  depth, d, shift = 0;
8919   PetscBool empty = PETSC_FALSE;
8920 
8921   PetscFunctionBegin;
8922   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8923   PetscCall(DMPlexGetDepth(dm, &depth));
8924   // For unstratified meshes use dim instead of depth
8925   if (depth < 0) PetscCall(DMGetDimension(dm, &depth));
8926   // If any stratum is empty, we must mark all empty
8927   for (d = 0; d <= depth; ++d) {
8928     PetscInt end;
8929 
8930     depths[d] = depth - d;
8931     PetscCall(DMPlexGetDepthStratum(dm, depths[d], &starts[d], &end));
8932     if (!(starts[d] - end)) empty = PETSC_TRUE;
8933   }
8934   if (empty)
8935     for (d = 0; d <= depth; ++d) {
8936       depths[d] = -1;
8937       starts[d] = -1;
8938     }
8939   else PetscCall(PetscSortIntWithArray(depth + 1, starts, depths));
8940   PetscCallMPI(MPIU_Allreduce(depths, gdepths, (PetscMPIInt)(depth + 1), MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
8941   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]);
8942   // Note here that 'shift' is collective, so that the numbering is stratified by depth
8943   for (d = 0; d <= depth; ++d) {
8944     PetscInt pStart, pEnd, gsize;
8945 
8946     PetscCall(DMPlexGetDepthStratum(dm, gdepths[d], &pStart, &pEnd));
8947     PetscCall(DMPlexCreateNumbering_Plex(dm, pStart, pEnd, shift, &gsize, dm->sf, &nums[d]));
8948     shift += gsize;
8949   }
8950   PetscCall(ISConcatenate(PETSC_COMM_SELF, depth + 1, nums, globalPointNumbers));
8951   for (d = 0; d <= depth; ++d) PetscCall(ISDestroy(&nums[d]));
8952   PetscFunctionReturn(PETSC_SUCCESS);
8953 }
8954 
8955 /*@
8956   DMPlexCreateEdgeNumbering - Create a global numbering for edges.
8957 
8958   Collective
8959 
8960   Input Parameter:
8961 . dm - The `DMPLEX` object
8962 
8963   Output Parameter:
8964 . globalEdgeNumbers - Global numbers for all edges on this process
8965 
8966   Level: developer
8967 
8968   Notes:
8969   The point numbering `IS` is parallel, with local portion indexed by local points (see `DMGetLocalSection()`). In the IS, owned edges will have their non-negative value while edges owned by different ranks will be involuted -(idx+1).
8970 
8971 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`, `DMPlexGetVertexNumbering()`, `DMPlexCreatePointNumbering()`
8972 @*/
8973 PetscErrorCode DMPlexCreateEdgeNumbering(DM dm, IS *globalEdgeNumbers)
8974 {
8975   PetscSF  sf;
8976   PetscInt eStart, eEnd;
8977 
8978   PetscFunctionBegin;
8979   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8980   PetscCall(DMGetPointSF(dm, &sf));
8981   PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
8982   PetscCall(DMPlexCreateNumbering_Plex(dm, eStart, eEnd, 0, NULL, sf, globalEdgeNumbers));
8983   PetscFunctionReturn(PETSC_SUCCESS);
8984 }
8985 
8986 /*@
8987   DMPlexCreateRankField - Create a cell field whose value is the rank of the owner
8988 
8989   Input Parameter:
8990 . dm - The `DMPLEX` object
8991 
8992   Output Parameter:
8993 . ranks - The rank field
8994 
8995   Options Database Key:
8996 . -dm_partition_view - Adds the rank field into the `DM` output from `-dm_view` using the same viewer
8997 
8998   Level: intermediate
8999 
9000 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`
9001 @*/
9002 PetscErrorCode DMPlexCreateRankField(DM dm, Vec *ranks)
9003 {
9004   DM             rdm;
9005   PetscFE        fe;
9006   PetscScalar   *r;
9007   PetscMPIInt    rank;
9008   DMPolytopeType ct;
9009   PetscInt       dim, cStart, cEnd, c;
9010   PetscBool      simplex;
9011 
9012   PetscFunctionBeginUser;
9013   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9014   PetscAssertPointer(ranks, 2);
9015   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
9016   PetscCall(DMClone(dm, &rdm));
9017   PetscCall(DMGetDimension(rdm, &dim));
9018   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
9019   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
9020   simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct) + 1 ? PETSC_TRUE : PETSC_FALSE;
9021   PetscCall(PetscFECreateDefault(PETSC_COMM_SELF, dim, 1, simplex, "PETSc___rank_", -1, &fe));
9022   PetscCall(PetscObjectSetName((PetscObject)fe, "rank"));
9023   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
9024   PetscCall(PetscFEDestroy(&fe));
9025   PetscCall(DMCreateDS(rdm));
9026   PetscCall(DMCreateGlobalVector(rdm, ranks));
9027   PetscCall(PetscObjectSetName((PetscObject)*ranks, "partition"));
9028   PetscCall(VecGetArray(*ranks, &r));
9029   for (c = cStart; c < cEnd; ++c) {
9030     PetscScalar *lr;
9031 
9032     PetscCall(DMPlexPointGlobalRef(rdm, c, r, &lr));
9033     if (lr) *lr = rank;
9034   }
9035   PetscCall(VecRestoreArray(*ranks, &r));
9036   PetscCall(DMDestroy(&rdm));
9037   PetscFunctionReturn(PETSC_SUCCESS);
9038 }
9039 
9040 /*@
9041   DMPlexCreateLabelField - Create a field whose value is the label value for that point
9042 
9043   Input Parameters:
9044 + dm    - The `DMPLEX`
9045 - label - The `DMLabel`
9046 
9047   Output Parameter:
9048 . val - The label value field
9049 
9050   Options Database Key:
9051 . -dm_label_view - Adds the label value field into the `DM` output from `-dm_view` using the same viewer
9052 
9053   Level: intermediate
9054 
9055 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`
9056 @*/
9057 PetscErrorCode DMPlexCreateLabelField(DM dm, DMLabel label, Vec *val)
9058 {
9059   DM             rdm, plex;
9060   Vec            lval;
9061   PetscSection   section;
9062   PetscFE        fe;
9063   PetscScalar   *v;
9064   PetscInt       dim, pStart, pEnd, p, cStart;
9065   DMPolytopeType ct;
9066   char           name[PETSC_MAX_PATH_LEN];
9067   const char    *lname, *prefix;
9068 
9069   PetscFunctionBeginUser;
9070   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9071   PetscAssertPointer(label, 2);
9072   PetscAssertPointer(val, 3);
9073   PetscCall(DMClone(dm, &rdm));
9074   PetscCall(DMConvert(rdm, DMPLEX, &plex));
9075   PetscCall(DMPlexGetHeightStratum(plex, 0, &cStart, NULL));
9076   PetscCall(DMPlexGetCellType(plex, cStart, &ct));
9077   PetscCall(DMDestroy(&plex));
9078   PetscCall(DMGetDimension(rdm, &dim));
9079   PetscCall(DMGetOptionsPrefix(dm, &prefix));
9080   PetscCall(PetscObjectGetName((PetscObject)label, &lname));
9081   PetscCall(PetscSNPrintf(name, sizeof(name), "%s%s_", prefix ? prefix : "", lname));
9082   PetscCall(PetscFECreateByCell(PETSC_COMM_SELF, dim, 1, ct, name, -1, &fe));
9083   PetscCall(PetscObjectSetName((PetscObject)fe, ""));
9084   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
9085   PetscCall(PetscFEDestroy(&fe));
9086   PetscCall(DMCreateDS(rdm));
9087   PetscCall(DMCreateGlobalVector(rdm, val));
9088   PetscCall(DMCreateLocalVector(rdm, &lval));
9089   PetscCall(PetscObjectSetName((PetscObject)*val, lname));
9090   PetscCall(DMGetLocalSection(rdm, &section));
9091   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
9092   PetscCall(VecGetArray(lval, &v));
9093   for (p = pStart; p < pEnd; ++p) {
9094     PetscInt cval, dof, off;
9095 
9096     PetscCall(PetscSectionGetDof(section, p, &dof));
9097     if (!dof) continue;
9098     PetscCall(DMLabelGetValue(label, p, &cval));
9099     PetscCall(PetscSectionGetOffset(section, p, &off));
9100     for (PetscInt d = 0; d < dof; d++) v[off + d] = cval;
9101   }
9102   PetscCall(VecRestoreArray(lval, &v));
9103   PetscCall(DMLocalToGlobal(rdm, lval, INSERT_VALUES, *val));
9104   PetscCall(VecDestroy(&lval));
9105   PetscCall(DMDestroy(&rdm));
9106   PetscFunctionReturn(PETSC_SUCCESS);
9107 }
9108 
9109 /*@
9110   DMPlexCheckSymmetry - Check that the adjacency information in the mesh is symmetric.
9111 
9112   Input Parameter:
9113 . dm - The `DMPLEX` object
9114 
9115   Level: developer
9116 
9117   Notes:
9118   This is a useful diagnostic when creating meshes programmatically.
9119 
9120   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9121 
9122 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9123 @*/
9124 PetscErrorCode DMPlexCheckSymmetry(DM dm)
9125 {
9126   PetscSection    coneSection, supportSection;
9127   const PetscInt *cone, *support;
9128   PetscInt        coneSize, c, supportSize, s;
9129   PetscInt        pStart, pEnd, p, pp, csize, ssize;
9130   PetscBool       storagecheck = PETSC_TRUE;
9131 
9132   PetscFunctionBegin;
9133   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9134   PetscCall(DMViewFromOptions(dm, NULL, "-sym_dm_view"));
9135   PetscCall(DMPlexGetConeSection(dm, &coneSection));
9136   PetscCall(DMPlexGetSupportSection(dm, &supportSection));
9137   /* Check that point p is found in the support of its cone points, and vice versa */
9138   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
9139   for (p = pStart; p < pEnd; ++p) {
9140     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
9141     PetscCall(DMPlexGetCone(dm, p, &cone));
9142     for (c = 0; c < coneSize; ++c) {
9143       PetscBool dup = PETSC_FALSE;
9144       PetscInt  d;
9145       for (d = c - 1; d >= 0; --d) {
9146         if (cone[c] == cone[d]) {
9147           dup = PETSC_TRUE;
9148           break;
9149         }
9150       }
9151       PetscCall(DMPlexGetSupportSize(dm, cone[c], &supportSize));
9152       PetscCall(DMPlexGetSupport(dm, cone[c], &support));
9153       for (s = 0; s < supportSize; ++s) {
9154         if (support[s] == p) break;
9155       }
9156       if ((s >= supportSize) || (dup && (support[s + 1] != p))) {
9157         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", p));
9158         for (s = 0; s < coneSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[s]));
9159         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9160         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", cone[c]));
9161         for (s = 0; s < supportSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[s]));
9162         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9163         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]);
9164         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in support of cone point %" PetscInt_FMT, p, cone[c]);
9165       }
9166     }
9167     PetscCall(DMPlexGetTreeParent(dm, p, &pp, NULL));
9168     if (p != pp) {
9169       storagecheck = PETSC_FALSE;
9170       continue;
9171     }
9172     PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
9173     PetscCall(DMPlexGetSupport(dm, p, &support));
9174     for (s = 0; s < supportSize; ++s) {
9175       PetscCall(DMPlexGetConeSize(dm, support[s], &coneSize));
9176       PetscCall(DMPlexGetCone(dm, support[s], &cone));
9177       for (c = 0; c < coneSize; ++c) {
9178         PetscCall(DMPlexGetTreeParent(dm, cone[c], &pp, NULL));
9179         if (cone[c] != pp) {
9180           c = 0;
9181           break;
9182         }
9183         if (cone[c] == p) break;
9184       }
9185       if (c >= coneSize) {
9186         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", p));
9187         for (c = 0; c < supportSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[c]));
9188         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9189         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", support[s]));
9190         for (c = 0; c < coneSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[c]));
9191         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9192         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in cone of support point %" PetscInt_FMT, p, support[s]);
9193       }
9194     }
9195   }
9196   if (storagecheck) {
9197     PetscCall(PetscSectionGetStorageSize(coneSection, &csize));
9198     PetscCall(PetscSectionGetStorageSize(supportSection, &ssize));
9199     PetscCheck(csize == ssize, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Total cone size %" PetscInt_FMT " != Total support size %" PetscInt_FMT, csize, ssize);
9200   }
9201   PetscFunctionReturn(PETSC_SUCCESS);
9202 }
9203 
9204 /*
9205   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.
9206 */
9207 static PetscErrorCode DMPlexCellUnsplitVertices_Private(DM dm, PetscInt c, DMPolytopeType ct, PetscInt *unsplit)
9208 {
9209   DMPolytopeType  cct;
9210   PetscInt        ptpoints[4];
9211   const PetscInt *cone, *ccone, *ptcone;
9212   PetscInt        coneSize, cp, cconeSize, ccp, npt = 0, pt;
9213 
9214   PetscFunctionBegin;
9215   *unsplit = 0;
9216   switch (ct) {
9217   case DM_POLYTOPE_POINT_PRISM_TENSOR:
9218     ptpoints[npt++] = c;
9219     break;
9220   case DM_POLYTOPE_SEG_PRISM_TENSOR:
9221     PetscCall(DMPlexGetCone(dm, c, &cone));
9222     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9223     for (cp = 0; cp < coneSize; ++cp) {
9224       PetscCall(DMPlexGetCellType(dm, cone[cp], &cct));
9225       if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) ptpoints[npt++] = cone[cp];
9226     }
9227     break;
9228   case DM_POLYTOPE_TRI_PRISM_TENSOR:
9229   case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9230     PetscCall(DMPlexGetCone(dm, c, &cone));
9231     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9232     for (cp = 0; cp < coneSize; ++cp) {
9233       PetscCall(DMPlexGetCone(dm, cone[cp], &ccone));
9234       PetscCall(DMPlexGetConeSize(dm, cone[cp], &cconeSize));
9235       for (ccp = 0; ccp < cconeSize; ++ccp) {
9236         PetscCall(DMPlexGetCellType(dm, ccone[ccp], &cct));
9237         if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) {
9238           PetscInt p;
9239           for (p = 0; p < npt; ++p)
9240             if (ptpoints[p] == ccone[ccp]) break;
9241           if (p == npt) ptpoints[npt++] = ccone[ccp];
9242         }
9243       }
9244     }
9245     break;
9246   default:
9247     break;
9248   }
9249   for (pt = 0; pt < npt; ++pt) {
9250     PetscCall(DMPlexGetCone(dm, ptpoints[pt], &ptcone));
9251     if (ptcone[0] == ptcone[1]) ++(*unsplit);
9252   }
9253   PetscFunctionReturn(PETSC_SUCCESS);
9254 }
9255 
9256 /*@
9257   DMPlexCheckSkeleton - Check that each cell has the correct number of vertices
9258 
9259   Input Parameters:
9260 + dm         - The `DMPLEX` object
9261 - cellHeight - Normally 0
9262 
9263   Level: developer
9264 
9265   Notes:
9266   This is a useful diagnostic when creating meshes programmatically.
9267   Currently applicable only to homogeneous simplex or tensor meshes.
9268 
9269   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9270 
9271 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9272 @*/
9273 PetscErrorCode DMPlexCheckSkeleton(DM dm, PetscInt cellHeight)
9274 {
9275   DMPlexInterpolatedFlag interp;
9276   DMPolytopeType         ct;
9277   PetscInt               vStart, vEnd, cStart, cEnd, c;
9278 
9279   PetscFunctionBegin;
9280   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9281   PetscCall(DMPlexIsInterpolated(dm, &interp));
9282   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9283   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
9284   for (c = cStart; c < cEnd; ++c) {
9285     PetscInt *closure = NULL;
9286     PetscInt  coneSize, closureSize, cl, Nv = 0;
9287 
9288     PetscCall(DMPlexGetCellType(dm, c, &ct));
9289     if (ct == DM_POLYTOPE_UNKNOWN) continue;
9290     if (interp == DMPLEX_INTERPOLATED_FULL) {
9291       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9292       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));
9293     }
9294     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9295     for (cl = 0; cl < closureSize * 2; cl += 2) {
9296       const PetscInt p = closure[cl];
9297       if ((p >= vStart) && (p < vEnd)) ++Nv;
9298     }
9299     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9300     /* Special Case: Tensor faces with identified vertices */
9301     if (Nv < DMPolytopeTypeGetNumVertices(ct)) {
9302       PetscInt unsplit;
9303 
9304       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9305       if (Nv + unsplit == DMPolytopeTypeGetNumVertices(ct)) continue;
9306     }
9307     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));
9308   }
9309   PetscFunctionReturn(PETSC_SUCCESS);
9310 }
9311 
9312 /*@
9313   DMPlexCheckFaces - Check that the faces of each cell give a vertex order this is consistent with what we expect from the cell type
9314 
9315   Collective
9316 
9317   Input Parameters:
9318 + dm         - The `DMPLEX` object
9319 - cellHeight - Normally 0
9320 
9321   Level: developer
9322 
9323   Notes:
9324   This is a useful diagnostic when creating meshes programmatically.
9325   This routine is only relevant for meshes that are fully interpolated across all ranks.
9326   It will error out if a partially interpolated mesh is given on some rank.
9327   It will do nothing for locally uninterpolated mesh (as there is nothing to check).
9328 
9329   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9330 
9331 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMPlexGetVTKCellHeight()`, `DMSetFromOptions()`
9332 @*/
9333 PetscErrorCode DMPlexCheckFaces(DM dm, PetscInt cellHeight)
9334 {
9335   PetscInt               dim, depth, vStart, vEnd, cStart, cEnd, c, h;
9336   DMPlexInterpolatedFlag interpEnum;
9337 
9338   PetscFunctionBegin;
9339   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9340   PetscCall(DMPlexIsInterpolatedCollective(dm, &interpEnum));
9341   if (interpEnum == DMPLEX_INTERPOLATED_NONE) PetscFunctionReturn(PETSC_SUCCESS);
9342   if (interpEnum != DMPLEX_INTERPOLATED_FULL) {
9343     PetscCall(PetscPrintf(PetscObjectComm((PetscObject)dm), "DMPlexCheckFaces() warning: Mesh is only partially interpolated, this is currently not supported"));
9344     PetscFunctionReturn(PETSC_SUCCESS);
9345   }
9346 
9347   PetscCall(DMGetDimension(dm, &dim));
9348   PetscCall(DMPlexGetDepth(dm, &depth));
9349   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
9350   for (h = cellHeight; h < PetscMin(depth, dim); ++h) {
9351     PetscCall(DMPlexGetHeightStratum(dm, h, &cStart, &cEnd));
9352     for (c = cStart; c < cEnd; ++c) {
9353       const PetscInt       *cone, *ornt, *faceSizes, *faces;
9354       const DMPolytopeType *faceTypes;
9355       DMPolytopeType        ct;
9356       PetscInt              numFaces, coneSize, f;
9357       PetscInt             *closure = NULL, closureSize, cl, numCorners = 0, fOff = 0, unsplit;
9358 
9359       PetscCall(DMPlexGetCellType(dm, c, &ct));
9360       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9361       if (unsplit) continue;
9362       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9363       PetscCall(DMPlexGetCone(dm, c, &cone));
9364       PetscCall(DMPlexGetConeOrientation(dm, c, &ornt));
9365       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9366       for (cl = 0; cl < closureSize * 2; cl += 2) {
9367         const PetscInt p = closure[cl];
9368         if ((p >= vStart) && (p < vEnd)) closure[numCorners++] = p;
9369       }
9370       PetscCall(DMPlexGetRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
9371       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);
9372       for (f = 0; f < numFaces; ++f) {
9373         DMPolytopeType fct;
9374         PetscInt      *fclosure = NULL, fclosureSize, cl, fnumCorners = 0, v;
9375 
9376         PetscCall(DMPlexGetCellType(dm, cone[f], &fct));
9377         PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[f], ornt[f], PETSC_TRUE, &fclosureSize, &fclosure));
9378         for (cl = 0; cl < fclosureSize * 2; cl += 2) {
9379           const PetscInt p = fclosure[cl];
9380           if ((p >= vStart) && (p < vEnd)) fclosure[fnumCorners++] = p;
9381         }
9382         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]);
9383         for (v = 0; v < fnumCorners; ++v) {
9384           if (fclosure[v] != faces[fOff + v]) {
9385             PetscInt v1;
9386 
9387             PetscCall(PetscPrintf(PETSC_COMM_SELF, "face closure:"));
9388             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, fclosure[v1]));
9389             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\ncell face:"));
9390             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, faces[fOff + v1]));
9391             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9392             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]);
9393           }
9394         }
9395         PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[f], PETSC_TRUE, &fclosureSize, &fclosure));
9396         fOff += faceSizes[f];
9397       }
9398       PetscCall(DMPlexRestoreRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
9399       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9400     }
9401   }
9402   PetscFunctionReturn(PETSC_SUCCESS);
9403 }
9404 
9405 /*@
9406   DMPlexCheckGeometry - Check the geometry of mesh cells
9407 
9408   Input Parameter:
9409 . dm - The `DMPLEX` object
9410 
9411   Level: developer
9412 
9413   Notes:
9414   This is a useful diagnostic when creating meshes programmatically.
9415 
9416   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9417 
9418 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9419 @*/
9420 PetscErrorCode DMPlexCheckGeometry(DM dm)
9421 {
9422   Vec       coordinates;
9423   PetscReal detJ, J[9], refVol = 1.0;
9424   PetscReal vol;
9425   PetscInt  dim, depth, dE, d, cStart, cEnd, c;
9426 
9427   PetscFunctionBegin;
9428   PetscCall(DMGetDimension(dm, &dim));
9429   PetscCall(DMGetCoordinateDim(dm, &dE));
9430   if (dim != dE) PetscFunctionReturn(PETSC_SUCCESS);
9431   PetscCall(DMPlexGetDepth(dm, &depth));
9432   for (d = 0; d < dim; ++d) refVol *= 2.0;
9433   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
9434   /* Make sure local coordinates are created, because that step is collective */
9435   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
9436   if (!coordinates) PetscFunctionReturn(PETSC_SUCCESS);
9437   for (c = cStart; c < cEnd; ++c) {
9438     DMPolytopeType ct;
9439     PetscInt       unsplit;
9440     PetscBool      ignoreZeroVol = PETSC_FALSE;
9441 
9442     PetscCall(DMPlexGetCellType(dm, c, &ct));
9443     switch (ct) {
9444     case DM_POLYTOPE_SEG_PRISM_TENSOR:
9445     case DM_POLYTOPE_TRI_PRISM_TENSOR:
9446     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9447       ignoreZeroVol = PETSC_TRUE;
9448       break;
9449     default:
9450       break;
9451     }
9452     switch (ct) {
9453     case DM_POLYTOPE_TRI_PRISM:
9454     case DM_POLYTOPE_TRI_PRISM_TENSOR:
9455     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9456     case DM_POLYTOPE_PYRAMID:
9457       continue;
9458     default:
9459       break;
9460     }
9461     PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9462     if (unsplit) continue;
9463     PetscCall(DMPlexComputeCellGeometryFEM(dm, c, NULL, NULL, J, NULL, &detJ));
9464     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);
9465     PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FEM Volume %g\n", c, (double)(detJ * refVol)));
9466     /* This should work with periodicity since DG coordinates should be used */
9467     if (depth > 1) {
9468       PetscCall(DMPlexComputeCellGeometryFVM(dm, c, &vol, NULL, NULL));
9469       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);
9470       PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FVM Volume %g\n", c, (double)vol));
9471     }
9472   }
9473   PetscFunctionReturn(PETSC_SUCCESS);
9474 }
9475 
9476 /*@
9477   DMPlexCheckPointSF - Check that several necessary conditions are met for the point `PetscSF` of this plex.
9478 
9479   Collective
9480 
9481   Input Parameters:
9482 + dm              - The `DMPLEX` object
9483 . pointSF         - The `PetscSF`, or `NULL` for `PointSF` attached to `DM`
9484 - allowExtraRoots - Flag to allow extra points not present in the `DM`
9485 
9486   Level: developer
9487 
9488   Notes:
9489   This is mainly intended for debugging/testing purposes.
9490 
9491   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9492 
9493   Extra roots can come from periodic cuts, where additional points appear on the boundary
9494 
9495 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMGetPointSF()`, `DMSetFromOptions()`
9496 @*/
9497 PetscErrorCode DMPlexCheckPointSF(DM dm, PetscSF pointSF, PetscBool allowExtraRoots)
9498 {
9499   PetscInt           l, nleaves, nroots, overlap;
9500   const PetscInt    *locals;
9501   const PetscSFNode *remotes;
9502   PetscBool          distributed;
9503   MPI_Comm           comm;
9504   PetscMPIInt        rank;
9505 
9506   PetscFunctionBegin;
9507   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9508   if (pointSF) PetscValidHeaderSpecific(pointSF, PETSCSF_CLASSID, 2);
9509   else pointSF = dm->sf;
9510   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9511   PetscCheck(pointSF, comm, PETSC_ERR_ARG_WRONGSTATE, "DMPlex must have Point SF attached");
9512   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9513   {
9514     PetscMPIInt mpiFlag;
9515 
9516     PetscCallMPI(MPI_Comm_compare(comm, PetscObjectComm((PetscObject)pointSF), &mpiFlag));
9517     PetscCheck(mpiFlag == MPI_CONGRUENT || mpiFlag == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "DM and Point SF have different communicators (flag %d)", mpiFlag);
9518   }
9519   PetscCall(PetscSFGetGraph(pointSF, &nroots, &nleaves, &locals, &remotes));
9520   PetscCall(DMPlexIsDistributed(dm, &distributed));
9521   if (!distributed) {
9522     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);
9523     PetscFunctionReturn(PETSC_SUCCESS);
9524   }
9525   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);
9526   PetscCall(DMPlexGetOverlap(dm, &overlap));
9527 
9528   /* Check SF graph is compatible with DMPlex chart */
9529   {
9530     PetscInt pStart, pEnd, maxLeaf;
9531 
9532     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
9533     PetscCall(PetscSFGetLeafRange(pointSF, NULL, &maxLeaf));
9534     PetscCheck(allowExtraRoots || pEnd - pStart == nroots, PETSC_COMM_SELF, PETSC_ERR_PLIB, "pEnd - pStart = %" PetscInt_FMT " != nroots = %" PetscInt_FMT, pEnd - pStart, nroots);
9535     PetscCheck(maxLeaf < pEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "maxLeaf = %" PetscInt_FMT " >= pEnd = %" PetscInt_FMT, maxLeaf, pEnd);
9536   }
9537 
9538   /* Check Point SF has no local points referenced */
9539   for (l = 0; l < nleaves; l++) {
9540     PetscAssert(remotes[l].rank != (PetscInt)rank, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains local point %" PetscInt_FMT " <- (%d,%" PetscInt_FMT ")", locals ? locals[l] : l, (PetscMPIInt)remotes[l].rank, remotes[l].index);
9541   }
9542 
9543   /* Check there are no cells in interface */
9544   if (!overlap) {
9545     PetscInt cellHeight, cStart, cEnd;
9546 
9547     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9548     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9549     for (l = 0; l < nleaves; ++l) {
9550       const PetscInt point = locals ? locals[l] : l;
9551 
9552       PetscCheck(point < cStart || point >= cEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " which is a cell", point);
9553     }
9554   }
9555 
9556   /* If some point is in interface, then all its cone points must be also in interface (either as leaves or roots) */
9557   {
9558     const PetscInt *rootdegree;
9559 
9560     PetscCall(PetscSFComputeDegreeBegin(pointSF, &rootdegree));
9561     PetscCall(PetscSFComputeDegreeEnd(pointSF, &rootdegree));
9562     for (l = 0; l < nleaves; ++l) {
9563       const PetscInt  point = locals ? locals[l] : l;
9564       const PetscInt *cone;
9565       PetscInt        coneSize, c, idx;
9566 
9567       PetscCall(DMPlexGetConeSize(dm, point, &coneSize));
9568       PetscCall(DMPlexGetCone(dm, point, &cone));
9569       for (c = 0; c < coneSize; ++c) {
9570         if (!rootdegree[cone[c]]) {
9571           if (locals) {
9572             PetscCall(PetscFindInt(cone[c], nleaves, locals, &idx));
9573           } else {
9574             idx = (cone[c] < nleaves) ? cone[c] : -1;
9575           }
9576           PetscCheck(idx >= 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " but not %" PetscInt_FMT " from its cone", point, cone[c]);
9577         }
9578       }
9579     }
9580   }
9581   PetscFunctionReturn(PETSC_SUCCESS);
9582 }
9583 
9584 /*@
9585   DMPlexCheckOrphanVertices - Check that no vertices are disconnected from the mesh, unless the mesh only consists of disconnected vertices.
9586 
9587   Collective
9588 
9589   Input Parameter:
9590 . dm - The `DMPLEX` object
9591 
9592   Level: developer
9593 
9594   Notes:
9595   This is mainly intended for debugging/testing purposes.
9596 
9597   Other cell types which are disconnected would be caught by the symmetry and face checks.
9598 
9599   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9600 
9601 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCheck()`, `DMSetFromOptions()`
9602 @*/
9603 PetscErrorCode DMPlexCheckOrphanVertices(DM dm)
9604 {
9605   PetscInt pStart, pEnd, vStart, vEnd;
9606 
9607   PetscFunctionBegin;
9608   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
9609   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
9610   if (pStart == vStart && pEnd == vEnd) PetscFunctionReturn(PETSC_SUCCESS);
9611   for (PetscInt v = vStart; v < vEnd; ++v) {
9612     PetscInt suppSize;
9613 
9614     PetscCall(DMPlexGetSupportSize(dm, v, &suppSize));
9615     PetscCheck(suppSize, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Vertex %" PetscInt_FMT " is disconnected from the mesh", v);
9616   }
9617   PetscFunctionReturn(PETSC_SUCCESS);
9618 }
9619 
9620 /*@
9621   DMPlexCheck - Perform various checks of `DMPLEX` sanity
9622 
9623   Input Parameter:
9624 . dm - The `DMPLEX` object
9625 
9626   Level: developer
9627 
9628   Notes:
9629   This is a useful diagnostic when creating meshes programmatically.
9630 
9631   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9632 
9633   Currently does not include `DMPlexCheckCellShape()`.
9634 
9635 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9636 @*/
9637 PetscErrorCode DMPlexCheck(DM dm)
9638 {
9639   PetscInt cellHeight;
9640 
9641   PetscFunctionBegin;
9642   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9643   PetscCall(DMPlexCheckSymmetry(dm));
9644   PetscCall(DMPlexCheckSkeleton(dm, cellHeight));
9645   PetscCall(DMPlexCheckFaces(dm, cellHeight));
9646   PetscCall(DMPlexCheckGeometry(dm));
9647   PetscCall(DMPlexCheckPointSF(dm, NULL, PETSC_FALSE));
9648   PetscCall(DMPlexCheckInterfaceCones(dm));
9649   PetscCall(DMPlexCheckOrphanVertices(dm));
9650   PetscFunctionReturn(PETSC_SUCCESS);
9651 }
9652 
9653 typedef struct cell_stats {
9654   PetscReal min, max, sum, squaresum;
9655   PetscInt  count;
9656 } cell_stats_t;
9657 
9658 static void MPIAPI cell_stats_reduce(void *a, void *b, int *len, MPI_Datatype *datatype)
9659 {
9660   PetscInt i, N = *len;
9661 
9662   for (i = 0; i < N; i++) {
9663     cell_stats_t *A = (cell_stats_t *)a;
9664     cell_stats_t *B = (cell_stats_t *)b;
9665 
9666     B->min = PetscMin(A->min, B->min);
9667     B->max = PetscMax(A->max, B->max);
9668     B->sum += A->sum;
9669     B->squaresum += A->squaresum;
9670     B->count += A->count;
9671   }
9672 }
9673 
9674 /*@
9675   DMPlexCheckCellShape - Checks the Jacobian of the mapping from reference to real cells and computes some minimal statistics.
9676 
9677   Collective
9678 
9679   Input Parameters:
9680 + dm        - The `DMPLEX` object
9681 . output    - If true, statistics will be displayed on `stdout`
9682 - condLimit - Display all cells above this condition number, or `PETSC_DETERMINE` for no cell output
9683 
9684   Level: developer
9685 
9686   Notes:
9687   This is mainly intended for debugging/testing purposes.
9688 
9689   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9690 
9691 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexComputeOrthogonalQuality()`
9692 @*/
9693 PetscErrorCode DMPlexCheckCellShape(DM dm, PetscBool output, PetscReal condLimit)
9694 {
9695   DM           dmCoarse;
9696   cell_stats_t stats, globalStats;
9697   MPI_Comm     comm = PetscObjectComm((PetscObject)dm);
9698   PetscReal   *J, *invJ, min = 0, max = 0, mean = 0, stdev = 0;
9699   PetscReal    limit = condLimit > 0 ? condLimit : PETSC_MAX_REAL;
9700   PetscInt     cdim, cStart, cEnd, c, eStart, eEnd, count = 0;
9701   PetscMPIInt  rank, size;
9702 
9703   PetscFunctionBegin;
9704   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9705   stats.min = PETSC_MAX_REAL;
9706   stats.max = PETSC_MIN_REAL;
9707   stats.sum = stats.squaresum = 0.;
9708   stats.count                 = 0;
9709 
9710   PetscCallMPI(MPI_Comm_size(comm, &size));
9711   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9712   PetscCall(DMGetCoordinateDim(dm, &cdim));
9713   PetscCall(PetscMalloc2(PetscSqr(cdim), &J, PetscSqr(cdim), &invJ));
9714   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
9715   PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
9716   for (c = cStart; c < cEnd; c++) {
9717     PetscInt  i;
9718     PetscReal frobJ = 0., frobInvJ = 0., cond2, cond, detJ;
9719 
9720     PetscCall(DMPlexComputeCellGeometryAffineFEM(dm, c, NULL, J, invJ, &detJ));
9721     PetscCheck(detJ >= 0.0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Mesh cell %" PetscInt_FMT " is inverted", c);
9722     for (i = 0; i < PetscSqr(cdim); ++i) {
9723       frobJ += J[i] * J[i];
9724       frobInvJ += invJ[i] * invJ[i];
9725     }
9726     cond2 = frobJ * frobInvJ;
9727     cond  = PetscSqrtReal(cond2);
9728 
9729     stats.min = PetscMin(stats.min, cond);
9730     stats.max = PetscMax(stats.max, cond);
9731     stats.sum += cond;
9732     stats.squaresum += cond2;
9733     stats.count++;
9734     if (output && cond > limit) {
9735       PetscSection coordSection;
9736       Vec          coordsLocal;
9737       PetscScalar *coords = NULL;
9738       PetscInt     Nv, d, clSize, cl, *closure = NULL;
9739 
9740       PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
9741       PetscCall(DMGetCoordinateSection(dm, &coordSection));
9742       PetscCall(DMPlexVecGetClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9743       PetscCall(PetscSynchronizedPrintf(comm, "[%d] Cell %" PetscInt_FMT " cond %g\n", rank, c, (double)cond));
9744       for (i = 0; i < Nv / cdim; ++i) {
9745         PetscCall(PetscSynchronizedPrintf(comm, "  Vertex %" PetscInt_FMT ": (", i));
9746         for (d = 0; d < cdim; ++d) {
9747           if (d > 0) PetscCall(PetscSynchronizedPrintf(comm, ", "));
9748           PetscCall(PetscSynchronizedPrintf(comm, "%g", (double)PetscRealPart(coords[i * cdim + d])));
9749         }
9750         PetscCall(PetscSynchronizedPrintf(comm, ")\n"));
9751       }
9752       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9753       for (cl = 0; cl < clSize * 2; cl += 2) {
9754         const PetscInt edge = closure[cl];
9755 
9756         if ((edge >= eStart) && (edge < eEnd)) {
9757           PetscReal len;
9758 
9759           PetscCall(DMPlexComputeCellGeometryFVM(dm, edge, &len, NULL, NULL));
9760           PetscCall(PetscSynchronizedPrintf(comm, "  Edge %" PetscInt_FMT ": length %g\n", edge, (double)len));
9761         }
9762       }
9763       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9764       PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9765     }
9766   }
9767   if (output) PetscCall(PetscSynchronizedFlush(comm, NULL));
9768 
9769   if (size > 1) {
9770     PetscMPIInt  blockLengths[2] = {4, 1};
9771     MPI_Aint     blockOffsets[2] = {offsetof(cell_stats_t, min), offsetof(cell_stats_t, count)};
9772     MPI_Datatype blockTypes[2]   = {MPIU_REAL, MPIU_INT}, statType;
9773     MPI_Op       statReduce;
9774 
9775     PetscCallMPI(MPI_Type_create_struct(2, blockLengths, blockOffsets, blockTypes, &statType));
9776     PetscCallMPI(MPI_Type_commit(&statType));
9777     PetscCallMPI(MPI_Op_create(cell_stats_reduce, PETSC_TRUE, &statReduce));
9778     PetscCallMPI(MPI_Reduce(&stats, &globalStats, 1, statType, statReduce, 0, comm));
9779     PetscCallMPI(MPI_Op_free(&statReduce));
9780     PetscCallMPI(MPI_Type_free(&statType));
9781   } else {
9782     PetscCall(PetscArraycpy(&globalStats, &stats, 1));
9783   }
9784   if (rank == 0) {
9785     count = globalStats.count;
9786     min   = globalStats.min;
9787     max   = globalStats.max;
9788     mean  = globalStats.sum / globalStats.count;
9789     stdev = globalStats.count > 1 ? PetscSqrtReal(PetscMax((globalStats.squaresum - globalStats.count * mean * mean) / (globalStats.count - 1), 0)) : 0.0;
9790   }
9791 
9792   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));
9793   PetscCall(PetscFree2(J, invJ));
9794 
9795   PetscCall(DMGetCoarseDM(dm, &dmCoarse));
9796   if (dmCoarse) {
9797     PetscBool isplex;
9798 
9799     PetscCall(PetscObjectTypeCompare((PetscObject)dmCoarse, DMPLEX, &isplex));
9800     if (isplex) PetscCall(DMPlexCheckCellShape(dmCoarse, output, condLimit));
9801   }
9802   PetscFunctionReturn(PETSC_SUCCESS);
9803 }
9804 
9805 /*@
9806   DMPlexComputeOrthogonalQuality - Compute cell-wise orthogonal quality mesh statistic. Optionally tags all cells with
9807   orthogonal quality below given tolerance.
9808 
9809   Collective
9810 
9811   Input Parameters:
9812 + dm   - The `DMPLEX` object
9813 . fv   - Optional `PetscFV` object for pre-computed cell/face centroid information
9814 - atol - [0, 1] Absolute tolerance for tagging cells.
9815 
9816   Output Parameters:
9817 + OrthQual      - `Vec` containing orthogonal quality per cell
9818 - OrthQualLabel - `DMLabel` tagging cells below atol with `DM_ADAPT_REFINE`
9819 
9820   Options Database Keys:
9821 + -dm_plex_orthogonal_quality_label_view - view OrthQualLabel if label is requested. Currently only `PETSCVIEWERASCII` is supported.
9822 - -dm_plex_orthogonal_quality_vec_view   - view OrthQual vector.
9823 
9824   Level: intermediate
9825 
9826   Notes:
9827   Orthogonal quality is given by the following formula\:
9828 
9829   $ \min \left[ \frac{A_i \cdot f_i}{\|A_i\| \|f_i\|} , \frac{A_i \cdot c_i}{\|A_i\| \|c_i\|} \right]$
9830 
9831   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
9832   is the vector from the current cells centroid to the centroid of its i'th neighbor (which shares a face with the
9833   current cell). This computes the vector similarity between each cell face and its corresponding neighbor centroid by
9834   calculating the cosine of the angle between these vectors.
9835 
9836   Orthogonal quality ranges from 1 (best) to 0 (worst).
9837 
9838   This routine is mainly useful for FVM, however is not restricted to only FVM. The `PetscFV` object is optionally used to check for
9839   pre-computed FVM cell data, but if it is not passed in then this data will be computed.
9840 
9841   Cells are tagged if they have an orthogonal quality less than or equal to the absolute tolerance.
9842 
9843 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCheckCellShape()`, `DMCreateLabel()`, `PetscFV`, `DMLabel`, `Vec`
9844 @*/
9845 PetscErrorCode DMPlexComputeOrthogonalQuality(DM dm, PetscFV fv, PetscReal atol, Vec *OrthQual, DMLabel *OrthQualLabel)
9846 {
9847   PetscInt               nc, cellHeight, cStart, cEnd, cell, cellIter = 0;
9848   PetscInt              *idx;
9849   PetscScalar           *oqVals;
9850   const PetscScalar     *cellGeomArr, *faceGeomArr;
9851   PetscReal             *ci, *fi, *Ai;
9852   MPI_Comm               comm;
9853   Vec                    cellgeom, facegeom;
9854   DM                     dmFace, dmCell;
9855   IS                     glob;
9856   ISLocalToGlobalMapping ltog;
9857   PetscViewer            vwr;
9858 
9859   PetscFunctionBegin;
9860   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9861   if (fv) PetscValidHeaderSpecific(fv, PETSCFV_CLASSID, 2);
9862   PetscAssertPointer(OrthQual, 4);
9863   PetscCheck(atol >= 0.0 && atol <= 1.0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Absolute tolerance %g not in [0,1]", (double)atol);
9864   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9865   PetscCall(DMGetDimension(dm, &nc));
9866   PetscCheck(nc >= 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must have dimension >= 2 (current %" PetscInt_FMT ")", nc);
9867   {
9868     DMPlexInterpolatedFlag interpFlag;
9869 
9870     PetscCall(DMPlexIsInterpolated(dm, &interpFlag));
9871     if (interpFlag != DMPLEX_INTERPOLATED_FULL) {
9872       PetscMPIInt rank;
9873 
9874       PetscCallMPI(MPI_Comm_rank(comm, &rank));
9875       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must be fully interpolated, DM on rank %d is not fully interpolated", rank);
9876     }
9877   }
9878   if (OrthQualLabel) {
9879     PetscAssertPointer(OrthQualLabel, 5);
9880     PetscCall(DMCreateLabel(dm, "Orthogonal_Quality"));
9881     PetscCall(DMGetLabel(dm, "Orthogonal_Quality", OrthQualLabel));
9882   } else {
9883     *OrthQualLabel = NULL;
9884   }
9885   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9886   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9887   PetscCall(DMPlexCreateCellNumbering(dm, PETSC_TRUE, &glob));
9888   PetscCall(ISLocalToGlobalMappingCreateIS(glob, &ltog));
9889   PetscCall(ISLocalToGlobalMappingSetType(ltog, ISLOCALTOGLOBALMAPPINGHASH));
9890   PetscCall(VecCreate(comm, OrthQual));
9891   PetscCall(VecSetType(*OrthQual, VECSTANDARD));
9892   PetscCall(VecSetSizes(*OrthQual, cEnd - cStart, PETSC_DETERMINE));
9893   PetscCall(VecSetLocalToGlobalMapping(*OrthQual, ltog));
9894   PetscCall(VecSetUp(*OrthQual));
9895   PetscCall(ISDestroy(&glob));
9896   PetscCall(ISLocalToGlobalMappingDestroy(&ltog));
9897   PetscCall(DMPlexGetDataFVM(dm, fv, &cellgeom, &facegeom, NULL));
9898   PetscCall(VecGetArrayRead(cellgeom, &cellGeomArr));
9899   PetscCall(VecGetArrayRead(facegeom, &faceGeomArr));
9900   PetscCall(VecGetDM(cellgeom, &dmCell));
9901   PetscCall(VecGetDM(facegeom, &dmFace));
9902   PetscCall(PetscMalloc5(cEnd - cStart, &idx, cEnd - cStart, &oqVals, nc, &ci, nc, &fi, nc, &Ai));
9903   for (cell = cStart; cell < cEnd; cellIter++, cell++) {
9904     PetscInt         cellneigh, cellneighiter = 0, adjSize = PETSC_DETERMINE;
9905     PetscInt         cellarr[2], *adj = NULL;
9906     PetscScalar     *cArr, *fArr;
9907     PetscReal        minvalc = 1.0, minvalf = 1.0;
9908     PetscFVCellGeom *cg;
9909 
9910     idx[cellIter] = cell - cStart;
9911     cellarr[0]    = cell;
9912     /* Make indexing into cellGeom easier */
9913     PetscCall(DMPlexPointLocalRead(dmCell, cell, cellGeomArr, &cg));
9914     PetscCall(DMPlexGetAdjacency_Internal(dm, cell, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &adjSize, &adj));
9915     /* Technically 1 too big, but easier than fiddling with empty adjacency array */
9916     PetscCall(PetscCalloc2(adjSize, &cArr, adjSize, &fArr));
9917     for (cellneigh = 0; cellneigh < adjSize; cellneighiter++, cellneigh++) {
9918       PetscInt         i;
9919       const PetscInt   neigh  = adj[cellneigh];
9920       PetscReal        normci = 0, normfi = 0, normai = 0;
9921       PetscFVCellGeom *cgneigh;
9922       PetscFVFaceGeom *fg;
9923 
9924       /* Don't count ourselves in the neighbor list */
9925       if (neigh == cell) continue;
9926       PetscCall(DMPlexPointLocalRead(dmCell, neigh, cellGeomArr, &cgneigh));
9927       cellarr[1] = neigh;
9928       {
9929         PetscInt        numcovpts;
9930         const PetscInt *covpts;
9931 
9932         PetscCall(DMPlexGetMeet(dm, 2, cellarr, &numcovpts, &covpts));
9933         PetscCall(DMPlexPointLocalRead(dmFace, covpts[0], faceGeomArr, &fg));
9934         PetscCall(DMPlexRestoreMeet(dm, 2, cellarr, &numcovpts, &covpts));
9935       }
9936 
9937       /* Compute c_i, f_i and their norms */
9938       for (i = 0; i < nc; i++) {
9939         ci[i] = cgneigh->centroid[i] - cg->centroid[i];
9940         fi[i] = fg->centroid[i] - cg->centroid[i];
9941         Ai[i] = fg->normal[i];
9942         normci += PetscPowReal(ci[i], 2);
9943         normfi += PetscPowReal(fi[i], 2);
9944         normai += PetscPowReal(Ai[i], 2);
9945       }
9946       normci = PetscSqrtReal(normci);
9947       normfi = PetscSqrtReal(normfi);
9948       normai = PetscSqrtReal(normai);
9949 
9950       /* Normalize and compute for each face-cell-normal pair */
9951       for (i = 0; i < nc; i++) {
9952         ci[i] = ci[i] / normci;
9953         fi[i] = fi[i] / normfi;
9954         Ai[i] = Ai[i] / normai;
9955         /* PetscAbs because I don't know if normals are guaranteed to point out */
9956         cArr[cellneighiter] += PetscAbs(Ai[i] * ci[i]);
9957         fArr[cellneighiter] += PetscAbs(Ai[i] * fi[i]);
9958       }
9959       if (PetscRealPart(cArr[cellneighiter]) < minvalc) minvalc = PetscRealPart(cArr[cellneighiter]);
9960       if (PetscRealPart(fArr[cellneighiter]) < minvalf) minvalf = PetscRealPart(fArr[cellneighiter]);
9961     }
9962     PetscCall(PetscFree(adj));
9963     PetscCall(PetscFree2(cArr, fArr));
9964     /* Defer to cell if they're equal */
9965     oqVals[cellIter] = PetscMin(minvalf, minvalc);
9966     if (OrthQualLabel) {
9967       if (PetscRealPart(oqVals[cellIter]) <= atol) PetscCall(DMLabelSetValue(*OrthQualLabel, cell, DM_ADAPT_REFINE));
9968     }
9969   }
9970   PetscCall(VecSetValuesLocal(*OrthQual, cEnd - cStart, idx, oqVals, INSERT_VALUES));
9971   PetscCall(VecAssemblyBegin(*OrthQual));
9972   PetscCall(VecAssemblyEnd(*OrthQual));
9973   PetscCall(VecRestoreArrayRead(cellgeom, &cellGeomArr));
9974   PetscCall(VecRestoreArrayRead(facegeom, &faceGeomArr));
9975   PetscCall(PetscOptionsCreateViewer(comm, NULL, NULL, "-dm_plex_orthogonal_quality_label_view", &vwr, NULL, NULL));
9976   if (OrthQualLabel) {
9977     if (vwr) PetscCall(DMLabelView(*OrthQualLabel, vwr));
9978   }
9979   PetscCall(PetscFree5(idx, oqVals, ci, fi, Ai));
9980   PetscCall(PetscViewerDestroy(&vwr));
9981   PetscCall(VecViewFromOptions(*OrthQual, NULL, "-dm_plex_orthogonal_quality_vec_view"));
9982   PetscFunctionReturn(PETSC_SUCCESS);
9983 }
9984 
9985 /* this is here instead of DMGetOutputDM because output DM still has constraints in the local indices that affect
9986  * interpolator construction */
9987 static PetscErrorCode DMGetFullDM(DM dm, DM *odm)
9988 {
9989   PetscSection section, newSection, gsection;
9990   PetscSF      sf;
9991   PetscBool    hasConstraints, ghasConstraints;
9992 
9993   PetscFunctionBegin;
9994   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9995   PetscAssertPointer(odm, 2);
9996   PetscCall(DMGetLocalSection(dm, &section));
9997   PetscCall(PetscSectionHasConstraints(section, &hasConstraints));
9998   PetscCallMPI(MPIU_Allreduce(&hasConstraints, &ghasConstraints, 1, MPIU_BOOL, MPI_LOR, PetscObjectComm((PetscObject)dm)));
9999   if (!ghasConstraints) {
10000     PetscCall(PetscObjectReference((PetscObject)dm));
10001     *odm = dm;
10002     PetscFunctionReturn(PETSC_SUCCESS);
10003   }
10004   PetscCall(DMClone(dm, odm));
10005   PetscCall(DMCopyFields(dm, PETSC_DETERMINE, PETSC_DETERMINE, *odm));
10006   PetscCall(DMGetLocalSection(*odm, &newSection));
10007   PetscCall(DMGetPointSF(*odm, &sf));
10008   PetscCall(PetscSectionCreateGlobalSection(newSection, sf, PETSC_TRUE, PETSC_TRUE, PETSC_FALSE, &gsection));
10009   PetscCall(DMSetGlobalSection(*odm, gsection));
10010   PetscCall(PetscSectionDestroy(&gsection));
10011   PetscFunctionReturn(PETSC_SUCCESS);
10012 }
10013 
10014 static PetscErrorCode DMCreateAffineInterpolationCorrection_Plex(DM dmc, DM dmf, Vec *shift)
10015 {
10016   DM        dmco, dmfo;
10017   Mat       interpo;
10018   Vec       rscale;
10019   Vec       cglobalo, clocal;
10020   Vec       fglobal, fglobalo, flocal;
10021   PetscBool regular;
10022 
10023   PetscFunctionBegin;
10024   PetscCall(DMGetFullDM(dmc, &dmco));
10025   PetscCall(DMGetFullDM(dmf, &dmfo));
10026   PetscCall(DMSetCoarseDM(dmfo, dmco));
10027   PetscCall(DMPlexGetRegularRefinement(dmf, &regular));
10028   PetscCall(DMPlexSetRegularRefinement(dmfo, regular));
10029   PetscCall(DMCreateInterpolation(dmco, dmfo, &interpo, &rscale));
10030   PetscCall(DMCreateGlobalVector(dmco, &cglobalo));
10031   PetscCall(DMCreateLocalVector(dmc, &clocal));
10032   PetscCall(VecSet(cglobalo, 0.));
10033   PetscCall(VecSet(clocal, 0.));
10034   PetscCall(DMCreateGlobalVector(dmf, &fglobal));
10035   PetscCall(DMCreateGlobalVector(dmfo, &fglobalo));
10036   PetscCall(DMCreateLocalVector(dmf, &flocal));
10037   PetscCall(VecSet(fglobal, 0.));
10038   PetscCall(VecSet(fglobalo, 0.));
10039   PetscCall(VecSet(flocal, 0.));
10040   PetscCall(DMPlexInsertBoundaryValues(dmc, PETSC_TRUE, clocal, 0., NULL, NULL, NULL));
10041   PetscCall(DMLocalToGlobalBegin(dmco, clocal, INSERT_VALUES, cglobalo));
10042   PetscCall(DMLocalToGlobalEnd(dmco, clocal, INSERT_VALUES, cglobalo));
10043   PetscCall(MatMult(interpo, cglobalo, fglobalo));
10044   PetscCall(DMGlobalToLocalBegin(dmfo, fglobalo, INSERT_VALUES, flocal));
10045   PetscCall(DMGlobalToLocalEnd(dmfo, fglobalo, INSERT_VALUES, flocal));
10046   PetscCall(DMLocalToGlobalBegin(dmf, flocal, INSERT_VALUES, fglobal));
10047   PetscCall(DMLocalToGlobalEnd(dmf, flocal, INSERT_VALUES, fglobal));
10048   *shift = fglobal;
10049   PetscCall(VecDestroy(&flocal));
10050   PetscCall(VecDestroy(&fglobalo));
10051   PetscCall(VecDestroy(&clocal));
10052   PetscCall(VecDestroy(&cglobalo));
10053   PetscCall(VecDestroy(&rscale));
10054   PetscCall(MatDestroy(&interpo));
10055   PetscCall(DMDestroy(&dmfo));
10056   PetscCall(DMDestroy(&dmco));
10057   PetscFunctionReturn(PETSC_SUCCESS);
10058 }
10059 
10060 PETSC_INTERN PetscErrorCode DMInterpolateSolution_Plex(DM coarse, DM fine, Mat interp, Vec coarseSol, Vec fineSol)
10061 {
10062   PetscObject shifto;
10063   Vec         shift;
10064 
10065   PetscFunctionBegin;
10066   if (!interp) {
10067     Vec rscale;
10068 
10069     PetscCall(DMCreateInterpolation(coarse, fine, &interp, &rscale));
10070     PetscCall(VecDestroy(&rscale));
10071   } else {
10072     PetscCall(PetscObjectReference((PetscObject)interp));
10073   }
10074   PetscCall(PetscObjectQuery((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", &shifto));
10075   if (!shifto) {
10076     PetscCall(DMCreateAffineInterpolationCorrection_Plex(coarse, fine, &shift));
10077     PetscCall(PetscObjectCompose((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", (PetscObject)shift));
10078     shifto = (PetscObject)shift;
10079     PetscCall(VecDestroy(&shift));
10080   }
10081   shift = (Vec)shifto;
10082   PetscCall(MatInterpolate(interp, coarseSol, fineSol));
10083   PetscCall(VecAXPY(fineSol, 1.0, shift));
10084   PetscCall(MatDestroy(&interp));
10085   PetscFunctionReturn(PETSC_SUCCESS);
10086 }
10087 
10088 /* Pointwise interpolation
10089      Just code FEM for now
10090      u^f = I u^c
10091      sum_k u^f_k phi^f_k = I sum_j u^c_j phi^c_j
10092      u^f_i = sum_j psi^f_i I phi^c_j u^c_j
10093      I_{ij} = psi^f_i phi^c_j
10094 */
10095 PetscErrorCode DMCreateInterpolation_Plex(DM dmCoarse, DM dmFine, Mat *interpolation, Vec *scaling)
10096 {
10097   PetscSection gsc, gsf;
10098   PetscInt     m, n;
10099   void        *ctx;
10100   DM           cdm;
10101   PetscBool    regular, ismatis, isRefined = dmCoarse->data == dmFine->data ? PETSC_FALSE : PETSC_TRUE;
10102 
10103   PetscFunctionBegin;
10104   PetscCall(DMGetGlobalSection(dmFine, &gsf));
10105   PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
10106   PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
10107   PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
10108 
10109   PetscCall(PetscStrcmp(dmCoarse->mattype, MATIS, &ismatis));
10110   PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), interpolation));
10111   PetscCall(MatSetSizes(*interpolation, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
10112   PetscCall(MatSetType(*interpolation, ismatis ? MATAIJ : dmCoarse->mattype));
10113   PetscCall(DMGetApplicationContext(dmFine, &ctx));
10114 
10115   PetscCall(DMGetCoarseDM(dmFine, &cdm));
10116   PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
10117   if (!isRefined || (regular && cdm == dmCoarse)) PetscCall(DMPlexComputeInterpolatorNested(dmCoarse, dmFine, isRefined, *interpolation, ctx));
10118   else PetscCall(DMPlexComputeInterpolatorGeneral(dmCoarse, dmFine, *interpolation, ctx));
10119   PetscCall(MatViewFromOptions(*interpolation, NULL, "-interp_mat_view"));
10120   if (scaling) {
10121     /* Use naive scaling */
10122     PetscCall(DMCreateInterpolationScale(dmCoarse, dmFine, *interpolation, scaling));
10123   }
10124   PetscFunctionReturn(PETSC_SUCCESS);
10125 }
10126 
10127 PetscErrorCode DMCreateInjection_Plex(DM dmCoarse, DM dmFine, Mat *mat)
10128 {
10129   VecScatter ctx;
10130 
10131   PetscFunctionBegin;
10132   PetscCall(DMPlexComputeInjectorFEM(dmCoarse, dmFine, &ctx, NULL));
10133   PetscCall(MatCreateScatter(PetscObjectComm((PetscObject)ctx), ctx, mat));
10134   PetscCall(VecScatterDestroy(&ctx));
10135   PetscFunctionReturn(PETSC_SUCCESS);
10136 }
10137 
10138 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[])
10139 {
10140   const PetscInt f  = (PetscInt)PetscRealPart(constants[numConstants]);
10141   const PetscInt Nc = uOff[f + 1] - uOff[f];
10142   for (PetscInt c = 0; c < Nc; ++c) g0[c * Nc + c] = 1.0;
10143 }
10144 
10145 PetscErrorCode DMCreateMassMatrixLumped_Plex(DM dm, Vec *lmass, Vec *mass)
10146 {
10147   DM           dmc;
10148   PetscDS      ds;
10149   Vec          ones, locmass;
10150   IS           cellIS;
10151   PetscFormKey key;
10152   PetscInt     depth;
10153 
10154   PetscFunctionBegin;
10155   PetscCall(DMClone(dm, &dmc));
10156   PetscCall(DMCopyDisc(dm, dmc));
10157   PetscCall(DMGetDS(dmc, &ds));
10158   for (PetscInt f = 0; f < dmc->Nf; ++f) PetscCall(PetscDSSetJacobian(ds, f, f, g0_identity_private, NULL, NULL, NULL));
10159   if (mass) PetscCall(DMCreateGlobalVector(dm, mass));
10160   if (lmass) PetscCall(DMCreateLocalVector(dm, &locmass));
10161   else PetscCall(DMGetLocalVector(dm, &locmass));
10162   PetscCall(DMGetLocalVector(dm, &ones));
10163   PetscCall(DMPlexGetDepth(dm, &depth));
10164   PetscCall(DMGetStratumIS(dm, "depth", depth, &cellIS));
10165   PetscCall(VecSet(locmass, 0.0));
10166   PetscCall(VecSet(ones, 1.0));
10167   key.label = NULL;
10168   key.value = 0;
10169   key.field = 0;
10170   key.part  = 0;
10171   PetscCall(DMPlexComputeJacobian_Action_Internal(dmc, key, cellIS, 0.0, 0.0, ones, NULL, ones, locmass, NULL));
10172   PetscCall(ISDestroy(&cellIS));
10173   if (mass) {
10174     PetscCall(DMLocalToGlobalBegin(dm, locmass, ADD_VALUES, *mass));
10175     PetscCall(DMLocalToGlobalEnd(dm, locmass, ADD_VALUES, *mass));
10176   }
10177   PetscCall(DMRestoreLocalVector(dm, &ones));
10178   if (lmass) *lmass = locmass;
10179   else PetscCall(DMRestoreLocalVector(dm, &locmass));
10180   PetscCall(DMDestroy(&dmc));
10181   PetscFunctionReturn(PETSC_SUCCESS);
10182 }
10183 
10184 PetscErrorCode DMCreateMassMatrix_Plex(DM dmCoarse, DM dmFine, Mat *mass)
10185 {
10186   PetscSection gsc, gsf;
10187   PetscInt     m, n;
10188   void        *ctx;
10189   DM           cdm;
10190   PetscBool    regular;
10191 
10192   PetscFunctionBegin;
10193   if (dmFine == dmCoarse) {
10194     DM            dmc;
10195     PetscDS       ds;
10196     PetscWeakForm wf;
10197     Vec           u;
10198     IS            cellIS;
10199     PetscFormKey  key;
10200     PetscInt      depth;
10201 
10202     PetscCall(DMClone(dmFine, &dmc));
10203     PetscCall(DMCopyDisc(dmFine, dmc));
10204     PetscCall(DMGetDS(dmc, &ds));
10205     PetscCall(PetscDSGetWeakForm(ds, &wf));
10206     PetscCall(PetscWeakFormClear(wf));
10207     for (PetscInt f = 0; f < dmc->Nf; ++f) PetscCall(PetscDSSetJacobian(ds, f, f, g0_identity_private, NULL, NULL, NULL));
10208     PetscCall(DMCreateMatrix(dmc, mass));
10209     PetscCall(DMGetLocalVector(dmc, &u));
10210     PetscCall(DMPlexGetDepth(dmc, &depth));
10211     PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
10212     PetscCall(MatZeroEntries(*mass));
10213     key.label = NULL;
10214     key.value = 0;
10215     key.field = 0;
10216     key.part  = 0;
10217     PetscCall(DMPlexComputeJacobian_Internal(dmc, key, cellIS, 0.0, 0.0, u, NULL, *mass, *mass, NULL));
10218     PetscCall(ISDestroy(&cellIS));
10219     PetscCall(DMRestoreLocalVector(dmc, &u));
10220     PetscCall(DMDestroy(&dmc));
10221   } else {
10222     PetscCall(DMGetGlobalSection(dmFine, &gsf));
10223     PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
10224     PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
10225     PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
10226 
10227     PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), mass));
10228     PetscCall(MatSetSizes(*mass, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
10229     PetscCall(MatSetType(*mass, dmCoarse->mattype));
10230     PetscCall(DMGetApplicationContext(dmFine, &ctx));
10231 
10232     PetscCall(DMGetCoarseDM(dmFine, &cdm));
10233     PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
10234     if (regular && cdm == dmCoarse) PetscCall(DMPlexComputeMassMatrixNested(dmCoarse, dmFine, *mass, ctx));
10235     else PetscCall(DMPlexComputeMassMatrixGeneral(dmCoarse, dmFine, *mass, ctx));
10236   }
10237   PetscCall(MatViewFromOptions(*mass, NULL, "-mass_mat_view"));
10238   PetscFunctionReturn(PETSC_SUCCESS);
10239 }
10240 
10241 /*@
10242   DMPlexGetRegularRefinement - Get the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
10243 
10244   Input Parameter:
10245 . dm - The `DMPLEX` object
10246 
10247   Output Parameter:
10248 . regular - The flag
10249 
10250   Level: intermediate
10251 
10252 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetRegularRefinement()`
10253 @*/
10254 PetscErrorCode DMPlexGetRegularRefinement(DM dm, PetscBool *regular)
10255 {
10256   PetscFunctionBegin;
10257   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10258   PetscAssertPointer(regular, 2);
10259   *regular = ((DM_Plex *)dm->data)->regularRefinement;
10260   PetscFunctionReturn(PETSC_SUCCESS);
10261 }
10262 
10263 /*@
10264   DMPlexSetRegularRefinement - Set the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
10265 
10266   Input Parameters:
10267 + dm      - The `DMPLEX` object
10268 - regular - The flag
10269 
10270   Level: intermediate
10271 
10272 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetRegularRefinement()`
10273 @*/
10274 PetscErrorCode DMPlexSetRegularRefinement(DM dm, PetscBool regular)
10275 {
10276   PetscFunctionBegin;
10277   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10278   ((DM_Plex *)dm->data)->regularRefinement = regular;
10279   PetscFunctionReturn(PETSC_SUCCESS);
10280 }
10281 
10282 /*@
10283   DMPlexGetAnchors - Get the layout of the anchor (point-to-point) constraints.  Typically, the user will not have to
10284   call DMPlexGetAnchors() directly: if there are anchors, then `DMPlexGetAnchors()` is called during `DMGetDefaultConstraints()`.
10285 
10286   Not Collective
10287 
10288   Input Parameter:
10289 . dm - The `DMPLEX` object
10290 
10291   Output Parameters:
10292 + anchorSection - If not `NULL`, set to the section describing which points anchor the constrained points.
10293 - anchorIS      - If not `NULL`, set to the list of anchors indexed by `anchorSection`
10294 
10295   Level: intermediate
10296 
10297 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`, `IS`, `PetscSection`
10298 @*/
10299 PetscErrorCode DMPlexGetAnchors(DM dm, PetscSection *anchorSection, IS *anchorIS)
10300 {
10301   DM_Plex *plex = (DM_Plex *)dm->data;
10302 
10303   PetscFunctionBegin;
10304   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10305   if (!plex->anchorSection && !plex->anchorIS && plex->createanchors) PetscCall((*plex->createanchors)(dm));
10306   if (anchorSection) *anchorSection = plex->anchorSection;
10307   if (anchorIS) *anchorIS = plex->anchorIS;
10308   PetscFunctionReturn(PETSC_SUCCESS);
10309 }
10310 
10311 /*@
10312   DMPlexSetAnchors - Set the layout of the local anchor (point-to-point) constraints.
10313 
10314   Collective
10315 
10316   Input Parameters:
10317 + dm            - The `DMPLEX` object
10318 . anchorSection - The section that describes the mapping from constrained points to the anchor points listed in anchorIS.
10319                   Must have a local communicator (`PETSC_COMM_SELF` or derivative).
10320 - anchorIS      - The list of all anchor points.  Must have a local communicator (`PETSC_COMM_SELF` or derivative).
10321 
10322   Level: intermediate
10323 
10324   Notes:
10325   Unlike boundary conditions, when a point's degrees of freedom in a section are constrained to
10326   an outside value, the anchor constraints set a point's degrees of freedom to be a linear
10327   combination of other points' degrees of freedom.
10328 
10329   After specifying the layout of constraints with `DMPlexSetAnchors()`, one specifies the constraints by calling
10330   `DMGetDefaultConstraints()` and filling in the entries in the constraint matrix.
10331 
10332   The reference counts of `anchorSection` and `anchorIS` are incremented.
10333 
10334 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`
10335 @*/
10336 PetscErrorCode DMPlexSetAnchors(DM dm, PetscSection anchorSection, IS anchorIS)
10337 {
10338   DM_Plex    *plex = (DM_Plex *)dm->data;
10339   PetscMPIInt result;
10340 
10341   PetscFunctionBegin;
10342   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10343   if (anchorSection) {
10344     PetscValidHeaderSpecific(anchorSection, PETSC_SECTION_CLASSID, 2);
10345     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorSection), &result));
10346     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor section must have local communicator");
10347   }
10348   if (anchorIS) {
10349     PetscValidHeaderSpecific(anchorIS, IS_CLASSID, 3);
10350     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorIS), &result));
10351     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor IS must have local communicator");
10352   }
10353 
10354   PetscCall(PetscObjectReference((PetscObject)anchorSection));
10355   PetscCall(PetscSectionDestroy(&plex->anchorSection));
10356   plex->anchorSection = anchorSection;
10357 
10358   PetscCall(PetscObjectReference((PetscObject)anchorIS));
10359   PetscCall(ISDestroy(&plex->anchorIS));
10360   plex->anchorIS = anchorIS;
10361 
10362   if (PetscUnlikelyDebug(anchorIS && anchorSection)) {
10363     PetscInt        size, a, pStart, pEnd;
10364     const PetscInt *anchors;
10365 
10366     PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
10367     PetscCall(ISGetLocalSize(anchorIS, &size));
10368     PetscCall(ISGetIndices(anchorIS, &anchors));
10369     for (a = 0; a < size; a++) {
10370       PetscInt p;
10371 
10372       p = anchors[a];
10373       if (p >= pStart && p < pEnd) {
10374         PetscInt dof;
10375 
10376         PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
10377         if (dof) {
10378           PetscCall(ISRestoreIndices(anchorIS, &anchors));
10379           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Point %" PetscInt_FMT " cannot be constrained and an anchor", p);
10380         }
10381       }
10382     }
10383     PetscCall(ISRestoreIndices(anchorIS, &anchors));
10384   }
10385   /* reset the generic constraints */
10386   PetscCall(DMSetDefaultConstraints(dm, NULL, NULL, NULL));
10387   PetscFunctionReturn(PETSC_SUCCESS);
10388 }
10389 
10390 static PetscErrorCode DMPlexCreateConstraintSection_Anchors(DM dm, PetscSection section, PetscSection *cSec)
10391 {
10392   PetscSection anchorSection;
10393   PetscInt     pStart, pEnd, sStart, sEnd, p, dof, numFields, f;
10394 
10395   PetscFunctionBegin;
10396   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10397   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
10398   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, cSec));
10399   PetscCall(PetscSectionGetNumFields(section, &numFields));
10400   if (numFields) {
10401     PetscInt f;
10402     PetscCall(PetscSectionSetNumFields(*cSec, numFields));
10403 
10404     for (f = 0; f < numFields; f++) {
10405       PetscInt numComp;
10406 
10407       PetscCall(PetscSectionGetFieldComponents(section, f, &numComp));
10408       PetscCall(PetscSectionSetFieldComponents(*cSec, f, numComp));
10409     }
10410   }
10411   PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
10412   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
10413   pStart = PetscMax(pStart, sStart);
10414   pEnd   = PetscMin(pEnd, sEnd);
10415   pEnd   = PetscMax(pStart, pEnd);
10416   PetscCall(PetscSectionSetChart(*cSec, pStart, pEnd));
10417   for (p = pStart; p < pEnd; p++) {
10418     PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
10419     if (dof) {
10420       PetscCall(PetscSectionGetDof(section, p, &dof));
10421       PetscCall(PetscSectionSetDof(*cSec, p, dof));
10422       for (f = 0; f < numFields; f++) {
10423         PetscCall(PetscSectionGetFieldDof(section, p, f, &dof));
10424         PetscCall(PetscSectionSetFieldDof(*cSec, p, f, dof));
10425       }
10426     }
10427   }
10428   PetscCall(PetscSectionSetUp(*cSec));
10429   PetscCall(PetscObjectSetName((PetscObject)*cSec, "Constraint Section"));
10430   PetscFunctionReturn(PETSC_SUCCESS);
10431 }
10432 
10433 static PetscErrorCode DMPlexCreateConstraintMatrix_Anchors(DM dm, PetscSection section, PetscSection cSec, Mat *cMat)
10434 {
10435   PetscSection    aSec;
10436   PetscInt        pStart, pEnd, p, sStart, sEnd, dof, aDof, aOff, off, nnz, annz, m, n, q, a, offset, *i, *j;
10437   const PetscInt *anchors;
10438   PetscInt        numFields, f;
10439   IS              aIS;
10440   MatType         mtype;
10441   PetscBool       iscuda, iskokkos;
10442 
10443   PetscFunctionBegin;
10444   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10445   PetscCall(PetscSectionGetStorageSize(cSec, &m));
10446   PetscCall(PetscSectionGetStorageSize(section, &n));
10447   PetscCall(MatCreate(PETSC_COMM_SELF, cMat));
10448   PetscCall(MatSetSizes(*cMat, m, n, m, n));
10449   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJCUSPARSE, &iscuda));
10450   if (!iscuda) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJCUSPARSE, &iscuda));
10451   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJKOKKOS, &iskokkos));
10452   if (!iskokkos) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJKOKKOS, &iskokkos));
10453   if (iscuda) mtype = MATSEQAIJCUSPARSE;
10454   else if (iskokkos) mtype = MATSEQAIJKOKKOS;
10455   else mtype = MATSEQAIJ;
10456   PetscCall(MatSetType(*cMat, mtype));
10457   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
10458   PetscCall(ISGetIndices(aIS, &anchors));
10459   /* cSec will be a subset of aSec and section */
10460   PetscCall(PetscSectionGetChart(cSec, &pStart, &pEnd));
10461   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
10462   PetscCall(PetscMalloc1(m + 1, &i));
10463   i[0] = 0;
10464   PetscCall(PetscSectionGetNumFields(section, &numFields));
10465   for (p = pStart; p < pEnd; p++) {
10466     PetscInt rDof, rOff, r;
10467 
10468     PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10469     if (!rDof) continue;
10470     PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10471     if (numFields) {
10472       for (f = 0; f < numFields; f++) {
10473         annz = 0;
10474         for (r = 0; r < rDof; r++) {
10475           a = anchors[rOff + r];
10476           if (a < sStart || a >= sEnd) continue;
10477           PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
10478           annz += aDof;
10479         }
10480         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
10481         PetscCall(PetscSectionGetFieldOffset(cSec, p, f, &off));
10482         for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
10483       }
10484     } else {
10485       annz = 0;
10486       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10487       for (q = 0; q < dof; q++) {
10488         a = anchors[rOff + q];
10489         if (a < sStart || a >= sEnd) continue;
10490         PetscCall(PetscSectionGetDof(section, a, &aDof));
10491         annz += aDof;
10492       }
10493       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10494       PetscCall(PetscSectionGetOffset(cSec, p, &off));
10495       for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
10496     }
10497   }
10498   nnz = i[m];
10499   PetscCall(PetscMalloc1(nnz, &j));
10500   offset = 0;
10501   for (p = pStart; p < pEnd; p++) {
10502     if (numFields) {
10503       for (f = 0; f < numFields; f++) {
10504         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
10505         for (q = 0; q < dof; q++) {
10506           PetscInt rDof, rOff, r;
10507           PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10508           PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10509           for (r = 0; r < rDof; r++) {
10510             PetscInt s;
10511 
10512             a = anchors[rOff + r];
10513             if (a < sStart || a >= sEnd) continue;
10514             PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
10515             PetscCall(PetscSectionGetFieldOffset(section, a, f, &aOff));
10516             for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
10517           }
10518         }
10519       }
10520     } else {
10521       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10522       for (q = 0; q < dof; q++) {
10523         PetscInt rDof, rOff, r;
10524         PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10525         PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10526         for (r = 0; r < rDof; r++) {
10527           PetscInt s;
10528 
10529           a = anchors[rOff + r];
10530           if (a < sStart || a >= sEnd) continue;
10531           PetscCall(PetscSectionGetDof(section, a, &aDof));
10532           PetscCall(PetscSectionGetOffset(section, a, &aOff));
10533           for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
10534         }
10535       }
10536     }
10537   }
10538   PetscCall(MatSeqAIJSetPreallocationCSR(*cMat, i, j, NULL));
10539   PetscCall(PetscFree(i));
10540   PetscCall(PetscFree(j));
10541   PetscCall(ISRestoreIndices(aIS, &anchors));
10542   PetscFunctionReturn(PETSC_SUCCESS);
10543 }
10544 
10545 PetscErrorCode DMCreateDefaultConstraints_Plex(DM dm)
10546 {
10547   DM_Plex     *plex = (DM_Plex *)dm->data;
10548   PetscSection anchorSection, section, cSec;
10549   Mat          cMat;
10550 
10551   PetscFunctionBegin;
10552   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10553   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
10554   if (anchorSection) {
10555     PetscInt Nf;
10556 
10557     PetscCall(DMGetLocalSection(dm, &section));
10558     PetscCall(DMPlexCreateConstraintSection_Anchors(dm, section, &cSec));
10559     PetscCall(DMPlexCreateConstraintMatrix_Anchors(dm, section, cSec, &cMat));
10560     PetscCall(DMGetNumFields(dm, &Nf));
10561     if (Nf && plex->computeanchormatrix) PetscCall((*plex->computeanchormatrix)(dm, section, cSec, cMat));
10562     PetscCall(DMSetDefaultConstraints(dm, cSec, cMat, NULL));
10563     PetscCall(PetscSectionDestroy(&cSec));
10564     PetscCall(MatDestroy(&cMat));
10565   }
10566   PetscFunctionReturn(PETSC_SUCCESS);
10567 }
10568 
10569 PetscErrorCode DMCreateSubDomainDM_Plex(DM dm, DMLabel label, PetscInt value, IS *is, DM *subdm)
10570 {
10571   IS           subis;
10572   PetscSection section, subsection;
10573 
10574   PetscFunctionBegin;
10575   PetscCall(DMGetLocalSection(dm, &section));
10576   PetscCheck(section, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set default section for DM before splitting subdomain");
10577   PetscCheck(subdm, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set output subDM for splitting subdomain");
10578   /* Create subdomain */
10579   PetscCall(DMPlexFilter(dm, label, value, PETSC_FALSE, PETSC_FALSE, NULL, subdm));
10580   /* Create submodel */
10581   PetscCall(DMPlexGetSubpointIS(*subdm, &subis));
10582   PetscCall(PetscSectionCreateSubmeshSection(section, subis, &subsection));
10583   PetscCall(DMSetLocalSection(*subdm, subsection));
10584   PetscCall(PetscSectionDestroy(&subsection));
10585   PetscCall(DMCopyDisc(dm, *subdm));
10586   /* Create map from submodel to global model */
10587   if (is) {
10588     PetscSection    sectionGlobal, subsectionGlobal;
10589     IS              spIS;
10590     const PetscInt *spmap;
10591     PetscInt       *subIndices;
10592     PetscInt        subSize = 0, subOff = 0, pStart, pEnd, p;
10593     PetscInt        Nf, f, bs = -1, bsLocal[2], bsMinMax[2];
10594 
10595     PetscCall(DMPlexGetSubpointIS(*subdm, &spIS));
10596     PetscCall(ISGetIndices(spIS, &spmap));
10597     PetscCall(PetscSectionGetNumFields(section, &Nf));
10598     PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
10599     PetscCall(DMGetGlobalSection(*subdm, &subsectionGlobal));
10600     PetscCall(PetscSectionGetChart(subsection, &pStart, &pEnd));
10601     for (p = pStart; p < pEnd; ++p) {
10602       PetscInt gdof, pSubSize = 0;
10603 
10604       PetscCall(PetscSectionGetDof(sectionGlobal, p, &gdof));
10605       if (gdof > 0) {
10606         for (f = 0; f < Nf; ++f) {
10607           PetscInt fdof, fcdof;
10608 
10609           PetscCall(PetscSectionGetFieldDof(subsection, p, f, &fdof));
10610           PetscCall(PetscSectionGetFieldConstraintDof(subsection, p, f, &fcdof));
10611           pSubSize += fdof - fcdof;
10612         }
10613         subSize += pSubSize;
10614         if (pSubSize) {
10615           if (bs < 0) {
10616             bs = pSubSize;
10617           } else if (bs != pSubSize) {
10618             /* Layout does not admit a pointwise block size */
10619             bs = 1;
10620           }
10621         }
10622       }
10623     }
10624     /* Must have same blocksize on all procs (some might have no points) */
10625     bsLocal[0] = bs < 0 ? PETSC_INT_MAX : bs;
10626     bsLocal[1] = bs;
10627     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
10628     if (bsMinMax[0] != bsMinMax[1]) {
10629       bs = 1;
10630     } else {
10631       bs = bsMinMax[0];
10632     }
10633     PetscCall(PetscMalloc1(subSize, &subIndices));
10634     for (p = pStart; p < pEnd; ++p) {
10635       PetscInt gdof, goff;
10636 
10637       PetscCall(PetscSectionGetDof(subsectionGlobal, p, &gdof));
10638       if (gdof > 0) {
10639         const PetscInt point = spmap[p];
10640 
10641         PetscCall(PetscSectionGetOffset(sectionGlobal, point, &goff));
10642         for (f = 0; f < Nf; ++f) {
10643           PetscInt fdof, fcdof, fc, f2, poff = 0;
10644 
10645           /* Can get rid of this loop by storing field information in the global section */
10646           for (f2 = 0; f2 < f; ++f2) {
10647             PetscCall(PetscSectionGetFieldDof(section, p, f2, &fdof));
10648             PetscCall(PetscSectionGetFieldConstraintDof(section, p, f2, &fcdof));
10649             poff += fdof - fcdof;
10650           }
10651           PetscCall(PetscSectionGetFieldDof(section, p, f, &fdof));
10652           PetscCall(PetscSectionGetFieldConstraintDof(section, p, f, &fcdof));
10653           for (fc = 0; fc < fdof - fcdof; ++fc, ++subOff) subIndices[subOff] = goff + poff + fc;
10654         }
10655       }
10656     }
10657     PetscCall(ISRestoreIndices(spIS, &spmap));
10658     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), subSize, subIndices, PETSC_OWN_POINTER, is));
10659     if (bs > 1) {
10660       /* We need to check that the block size does not come from non-contiguous fields */
10661       PetscInt i, j, set = 1;
10662       for (i = 0; i < subSize; i += bs) {
10663         for (j = 0; j < bs; ++j) {
10664           if (subIndices[i + j] != subIndices[i] + j) {
10665             set = 0;
10666             break;
10667           }
10668         }
10669       }
10670       if (set) PetscCall(ISSetBlockSize(*is, bs));
10671     }
10672     /* Attach nullspace */
10673     for (f = 0; f < Nf; ++f) {
10674       (*subdm)->nullspaceConstructors[f] = dm->nullspaceConstructors[f];
10675       if ((*subdm)->nullspaceConstructors[f]) break;
10676     }
10677     if (f < Nf) {
10678       MatNullSpace nullSpace;
10679       PetscCall((*(*subdm)->nullspaceConstructors[f])(*subdm, f, f, &nullSpace));
10680 
10681       PetscCall(PetscObjectCompose((PetscObject)*is, "nullspace", (PetscObject)nullSpace));
10682       PetscCall(MatNullSpaceDestroy(&nullSpace));
10683     }
10684   }
10685   PetscFunctionReturn(PETSC_SUCCESS);
10686 }
10687 
10688 /*@
10689   DMPlexMonitorThroughput - Report the cell throughput of FE integration
10690 
10691   Input Parameters:
10692 + dm    - The `DM`
10693 - dummy - unused argument
10694 
10695   Options Database Key:
10696 . -dm_plex_monitor_throughput - Activate the monitor
10697 
10698   Level: developer
10699 
10700 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexCreate()`
10701 @*/
10702 PetscErrorCode DMPlexMonitorThroughput(DM dm, void *dummy)
10703 {
10704   PetscLogHandler default_handler;
10705 
10706   PetscFunctionBegin;
10707   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10708   PetscCall(PetscLogGetDefaultHandler(&default_handler));
10709   if (default_handler) {
10710     PetscLogEvent      event;
10711     PetscEventPerfInfo eventInfo;
10712     PetscReal          cellRate, flopRate;
10713     PetscInt           cStart, cEnd, Nf, N;
10714     const char        *name;
10715 
10716     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
10717     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
10718     PetscCall(DMGetNumFields(dm, &Nf));
10719     PetscCall(PetscLogEventGetId("DMPlexResidualFE", &event));
10720     PetscCall(PetscLogEventGetPerfInfo(PETSC_DEFAULT, event, &eventInfo));
10721     N        = (cEnd - cStart) * Nf * eventInfo.count;
10722     flopRate = eventInfo.flops / eventInfo.time;
10723     cellRate = N / eventInfo.time;
10724     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)));
10725   } else {
10726     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.");
10727   }
10728   PetscFunctionReturn(PETSC_SUCCESS);
10729 }
10730