xref: /petsc/src/dm/impls/plex/plex.c (revision b755009767ecdcc03c99ba60bae46fc15e0ca1cb)
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_MAX_INT, 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   PetscCall(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   PetscCall(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, color[4] = {-1, -1, -1, -1};
434 
435         PetscCall(DMPlexGetCellType(dm, c, &ct));
436         PetscCall(DMPlexPointLocalRead(fdm, c, array, &a));
437         if (a) {
438           color[0] = PetscDrawRealToColor(PetscRealPart(a[comp]), vbound[0], vbound[1]);
439           color[1] = color[2] = color[3] = color[0];
440         } else {
441           PetscScalar *vals = NULL;
442           PetscInt     numVals, va;
443 
444           PetscCall(DMPlexVecGetClosure(fdm, NULL, fv, c, &numVals, &vals));
445           if (!numVals) {
446             PetscCall(DMPlexVecRestoreClosure(fdm, NULL, fv, c, &numVals, &vals));
447             continue;
448           }
449           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);
450           switch (numVals / Nc) {
451           case 1: /* P1 Clamped Segment Prism */
452           case 2: /* P1 Segment Prism, P2 Clamped Segment Prism */
453             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]);
454             for (va = 0; va < numVals / Nc; ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp]), vbound[0], vbound[1]);
455             break;
456           case 3: /* P1 Triangle */
457           case 4: /* P1 Quadrangle */
458             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]);
459             for (va = 0; va < numVals / Nc; ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp]), vbound[0], vbound[1]);
460             break;
461           case 6: /* P2 Triangle */
462           case 8: /* P2 Quadrangle */
463             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]);
464             for (va = 0; va < numVals / (Nc * 2); ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp + numVals / (Nc * 2)]), vbound[0], vbound[1]);
465             break;
466           default:
467             SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of values for cell closure %" PetscInt_FMT " cannot be handled", numVals / Nc);
468           }
469           PetscCall(DMPlexVecRestoreClosure(fdm, NULL, fv, c, &numVals, &vals));
470         }
471         PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
472         switch (numCoords) {
473         case 6:
474         case 12: /* Localized triangle */
475           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]));
476           break;
477         case 8:
478         case 16: /* Localized quadrilateral */
479           if (ct == DM_POLYTOPE_SEG_PRISM_TENSOR) {
480             PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscMax(color[0], color[1])));
481           } else {
482             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]));
483             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]));
484           }
485           break;
486         default:
487           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells with %" PetscInt_FMT " coordinates", numCoords);
488         }
489         PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
490       }
491       PetscCall(VecRestoreArrayRead(fv, &array));
492       PetscCall(PetscDrawFlush(draw));
493       PetscCall(PetscDrawPause(draw));
494       PetscCall(PetscDrawSave(draw));
495     }
496     if (Nf > 1) {
497       PetscCall(VecRestoreSubVector(v, fis, &fv));
498       PetscCall(ISDestroy(&fis));
499       PetscCall(DMDestroy(&fdm));
500     }
501   }
502   PetscFunctionReturn(PETSC_SUCCESS);
503 }
504 
505 static PetscErrorCode VecView_Plex_Local_Draw(Vec v, PetscViewer viewer)
506 {
507   DM        dm;
508   PetscDraw draw;
509   PetscInt  dim;
510   PetscBool isnull;
511 
512   PetscFunctionBegin;
513   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
514   PetscCall(PetscDrawIsNull(draw, &isnull));
515   if (isnull) PetscFunctionReturn(PETSC_SUCCESS);
516 
517   PetscCall(VecGetDM(v, &dm));
518   PetscCall(DMGetCoordinateDim(dm, &dim));
519   switch (dim) {
520   case 1:
521     PetscCall(VecView_Plex_Local_Draw_1D(v, viewer));
522     break;
523   case 2:
524     PetscCall(VecView_Plex_Local_Draw_2D(v, viewer));
525     break;
526   default:
527     SETERRQ(PetscObjectComm((PetscObject)v), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT ". Try PETSCVIEWERGLVIS", dim);
528   }
529   PetscFunctionReturn(PETSC_SUCCESS);
530 }
531 
532 static PetscErrorCode VecView_Plex_Local_VTK(Vec v, PetscViewer viewer)
533 {
534   DM                      dm;
535   Vec                     locv;
536   const char             *name;
537   PetscSection            section;
538   PetscInt                pStart, pEnd;
539   PetscInt                numFields;
540   PetscViewerVTKFieldType ft;
541 
542   PetscFunctionBegin;
543   PetscCall(VecGetDM(v, &dm));
544   PetscCall(DMCreateLocalVector(dm, &locv)); /* VTK viewer requires exclusive ownership of the vector */
545   PetscCall(PetscObjectGetName((PetscObject)v, &name));
546   PetscCall(PetscObjectSetName((PetscObject)locv, name));
547   PetscCall(VecCopy(v, locv));
548   PetscCall(DMGetLocalSection(dm, &section));
549   PetscCall(PetscSectionGetNumFields(section, &numFields));
550   if (!numFields) {
551     PetscCall(DMPlexGetFieldType_Internal(dm, section, PETSC_DETERMINE, &pStart, &pEnd, &ft));
552     PetscCall(PetscViewerVTKAddField(viewer, (PetscObject)dm, DMPlexVTKWriteAll, PETSC_DEFAULT, ft, PETSC_TRUE, (PetscObject)locv));
553   } else {
554     PetscInt f;
555 
556     for (f = 0; f < numFields; f++) {
557       PetscCall(DMPlexGetFieldType_Internal(dm, section, f, &pStart, &pEnd, &ft));
558       if (ft == PETSC_VTK_INVALID) continue;
559       PetscCall(PetscObjectReference((PetscObject)locv));
560       PetscCall(PetscViewerVTKAddField(viewer, (PetscObject)dm, DMPlexVTKWriteAll, f, ft, PETSC_TRUE, (PetscObject)locv));
561     }
562     PetscCall(VecDestroy(&locv));
563   }
564   PetscFunctionReturn(PETSC_SUCCESS);
565 }
566 
567 PetscErrorCode VecView_Plex_Local(Vec v, PetscViewer viewer)
568 {
569   DM        dm;
570   PetscBool isvtk, ishdf5, isdraw, isglvis, iscgns;
571 
572   PetscFunctionBegin;
573   PetscCall(VecGetDM(v, &dm));
574   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
575   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
576   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
577   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
578   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
579   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
580   if (isvtk || ishdf5 || isdraw || isglvis || iscgns) {
581     PetscInt    i, numFields;
582     PetscObject fe;
583     PetscBool   fem  = PETSC_FALSE;
584     Vec         locv = v;
585     const char *name;
586     PetscInt    step;
587     PetscReal   time;
588 
589     PetscCall(DMGetNumFields(dm, &numFields));
590     for (i = 0; i < numFields; i++) {
591       PetscCall(DMGetField(dm, i, NULL, &fe));
592       if (fe->classid == PETSCFE_CLASSID) {
593         fem = PETSC_TRUE;
594         break;
595       }
596     }
597     if (fem) {
598       PetscObject isZero;
599 
600       PetscCall(DMGetLocalVector(dm, &locv));
601       PetscCall(PetscObjectGetName((PetscObject)v, &name));
602       PetscCall(PetscObjectSetName((PetscObject)locv, name));
603       PetscCall(PetscObjectQuery((PetscObject)v, "__Vec_bc_zero__", &isZero));
604       PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", isZero));
605       PetscCall(VecCopy(v, locv));
606       PetscCall(DMGetOutputSequenceNumber(dm, NULL, &time));
607       PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_TRUE, locv, time, NULL, NULL, NULL));
608     }
609     if (isvtk) {
610       PetscCall(VecView_Plex_Local_VTK(locv, viewer));
611     } else if (ishdf5) {
612 #if defined(PETSC_HAVE_HDF5)
613       PetscCall(VecView_Plex_Local_HDF5_Internal(locv, viewer));
614 #else
615       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
616 #endif
617     } else if (isdraw) {
618       PetscCall(VecView_Plex_Local_Draw(locv, viewer));
619     } else if (isglvis) {
620       PetscCall(DMGetOutputSequenceNumber(dm, &step, NULL));
621       PetscCall(PetscViewerGLVisSetSnapId(viewer, step));
622       PetscCall(VecView_GLVis(locv, viewer));
623     } else if (iscgns) {
624 #if defined(PETSC_HAVE_CGNS)
625       PetscCall(VecView_Plex_Local_CGNS(locv, viewer));
626 #else
627       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "CGNS not supported in this build.\nPlease reconfigure using --download-cgns");
628 #endif
629     }
630     if (fem) {
631       PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", NULL));
632       PetscCall(DMRestoreLocalVector(dm, &locv));
633     }
634   } else {
635     PetscBool isseq;
636 
637     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
638     if (isseq) PetscCall(VecView_Seq(v, viewer));
639     else PetscCall(VecView_MPI(v, viewer));
640   }
641   PetscFunctionReturn(PETSC_SUCCESS);
642 }
643 
644 PetscErrorCode VecView_Plex(Vec v, PetscViewer viewer)
645 {
646   DM        dm;
647   PetscBool isvtk, ishdf5, isdraw, isglvis, isexodusii, iscgns;
648 
649   PetscFunctionBegin;
650   PetscCall(VecGetDM(v, &dm));
651   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
652   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
653   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
654   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
655   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
656   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
657   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodusii));
658   if (isvtk || isdraw || isglvis || iscgns) {
659     Vec         locv;
660     PetscObject isZero;
661     const char *name;
662 
663     PetscCall(DMGetLocalVector(dm, &locv));
664     PetscCall(PetscObjectGetName((PetscObject)v, &name));
665     PetscCall(PetscObjectSetName((PetscObject)locv, name));
666     PetscCall(DMGlobalToLocalBegin(dm, v, INSERT_VALUES, locv));
667     PetscCall(DMGlobalToLocalEnd(dm, v, INSERT_VALUES, locv));
668     PetscCall(PetscObjectQuery((PetscObject)v, "__Vec_bc_zero__", &isZero));
669     PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", isZero));
670     PetscCall(VecView_Plex_Local(locv, viewer));
671     PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", NULL));
672     PetscCall(DMRestoreLocalVector(dm, &locv));
673   } else if (ishdf5) {
674 #if defined(PETSC_HAVE_HDF5)
675     PetscCall(VecView_Plex_HDF5_Internal(v, viewer));
676 #else
677     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
678 #endif
679   } else if (isexodusii) {
680 #if defined(PETSC_HAVE_EXODUSII)
681     PetscCall(VecView_PlexExodusII_Internal(v, viewer));
682 #else
683     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
684 #endif
685   } else {
686     PetscBool isseq;
687 
688     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
689     if (isseq) PetscCall(VecView_Seq(v, viewer));
690     else PetscCall(VecView_MPI(v, viewer));
691   }
692   PetscFunctionReturn(PETSC_SUCCESS);
693 }
694 
695 PetscErrorCode VecView_Plex_Native(Vec originalv, PetscViewer viewer)
696 {
697   DM                dm;
698   MPI_Comm          comm;
699   PetscViewerFormat format;
700   Vec               v;
701   PetscBool         isvtk, ishdf5;
702 
703   PetscFunctionBegin;
704   PetscCall(VecGetDM(originalv, &dm));
705   PetscCall(PetscObjectGetComm((PetscObject)originalv, &comm));
706   PetscCheck(dm, comm, PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
707   PetscCall(PetscViewerGetFormat(viewer, &format));
708   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
709   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
710   if (format == PETSC_VIEWER_NATIVE) {
711     /* Natural ordering is the common case for DMDA, NATIVE means plain vector, for PLEX is the opposite */
712     /* this need a better fix */
713     if (dm->useNatural) {
714       if (dm->sfNatural) {
715         const char *vecname;
716         PetscInt    n, nroots;
717 
718         PetscCall(VecGetLocalSize(originalv, &n));
719         PetscCall(PetscSFGetGraph(dm->sfNatural, &nroots, NULL, NULL, NULL));
720         if (n == nroots) {
721           PetscCall(DMPlexCreateNaturalVector(dm, &v));
722           PetscCall(DMPlexGlobalToNaturalBegin(dm, originalv, v));
723           PetscCall(DMPlexGlobalToNaturalEnd(dm, originalv, v));
724           PetscCall(PetscObjectGetName((PetscObject)originalv, &vecname));
725           PetscCall(PetscObjectSetName((PetscObject)v, vecname));
726         } else SETERRQ(comm, PETSC_ERR_ARG_WRONG, "DM global to natural SF only handles global vectors");
727       } else SETERRQ(comm, PETSC_ERR_ARG_WRONGSTATE, "DM global to natural SF was not created");
728     } else v = originalv;
729   } else v = originalv;
730 
731   if (ishdf5) {
732 #if defined(PETSC_HAVE_HDF5)
733     PetscCall(VecView_Plex_HDF5_Native_Internal(v, viewer));
734 #else
735     SETERRQ(comm, PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
736 #endif
737   } else if (isvtk) {
738     SETERRQ(comm, PETSC_ERR_SUP, "VTK format does not support viewing in natural order. Please switch to HDF5.");
739   } else {
740     PetscBool isseq;
741 
742     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
743     if (isseq) PetscCall(VecView_Seq(v, viewer));
744     else PetscCall(VecView_MPI(v, viewer));
745   }
746   if (v != originalv) PetscCall(VecDestroy(&v));
747   PetscFunctionReturn(PETSC_SUCCESS);
748 }
749 
750 PetscErrorCode VecLoad_Plex_Local(Vec v, PetscViewer viewer)
751 {
752   DM        dm;
753   PetscBool ishdf5;
754 
755   PetscFunctionBegin;
756   PetscCall(VecGetDM(v, &dm));
757   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
758   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
759   if (ishdf5) {
760     DM          dmBC;
761     Vec         gv;
762     const char *name;
763 
764     PetscCall(DMGetOutputDM(dm, &dmBC));
765     PetscCall(DMGetGlobalVector(dmBC, &gv));
766     PetscCall(PetscObjectGetName((PetscObject)v, &name));
767     PetscCall(PetscObjectSetName((PetscObject)gv, name));
768     PetscCall(VecLoad_Default(gv, viewer));
769     PetscCall(DMGlobalToLocalBegin(dmBC, gv, INSERT_VALUES, v));
770     PetscCall(DMGlobalToLocalEnd(dmBC, gv, INSERT_VALUES, v));
771     PetscCall(DMRestoreGlobalVector(dmBC, &gv));
772   } else PetscCall(VecLoad_Default(v, viewer));
773   PetscFunctionReturn(PETSC_SUCCESS);
774 }
775 
776 PetscErrorCode VecLoad_Plex(Vec v, PetscViewer viewer)
777 {
778   DM        dm;
779   PetscBool ishdf5, isexodusii;
780 
781   PetscFunctionBegin;
782   PetscCall(VecGetDM(v, &dm));
783   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
784   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
785   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodusii));
786   if (ishdf5) {
787 #if defined(PETSC_HAVE_HDF5)
788     PetscCall(VecLoad_Plex_HDF5_Internal(v, viewer));
789 #else
790     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
791 #endif
792   } else if (isexodusii) {
793 #if defined(PETSC_HAVE_EXODUSII)
794     PetscCall(VecLoad_PlexExodusII_Internal(v, viewer));
795 #else
796     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
797 #endif
798   } else PetscCall(VecLoad_Default(v, viewer));
799   PetscFunctionReturn(PETSC_SUCCESS);
800 }
801 
802 PetscErrorCode VecLoad_Plex_Native(Vec originalv, PetscViewer viewer)
803 {
804   DM                dm;
805   PetscViewerFormat format;
806   PetscBool         ishdf5;
807 
808   PetscFunctionBegin;
809   PetscCall(VecGetDM(originalv, &dm));
810   PetscCheck(dm, PetscObjectComm((PetscObject)originalv), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
811   PetscCall(PetscViewerGetFormat(viewer, &format));
812   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
813   if (format == PETSC_VIEWER_NATIVE) {
814     if (dm->useNatural) {
815       if (dm->sfNatural) {
816         if (ishdf5) {
817 #if defined(PETSC_HAVE_HDF5)
818           Vec         v;
819           const char *vecname;
820 
821           PetscCall(DMPlexCreateNaturalVector(dm, &v));
822           PetscCall(PetscObjectGetName((PetscObject)originalv, &vecname));
823           PetscCall(PetscObjectSetName((PetscObject)v, vecname));
824           PetscCall(VecLoad_Plex_HDF5_Native_Internal(v, viewer));
825           PetscCall(DMPlexNaturalToGlobalBegin(dm, v, originalv));
826           PetscCall(DMPlexNaturalToGlobalEnd(dm, v, originalv));
827           PetscCall(VecDestroy(&v));
828 #else
829           SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
830 #endif
831         } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Reading in natural order is not supported for anything but HDF5.");
832       }
833     } else PetscCall(VecLoad_Default(originalv, viewer));
834   }
835   PetscFunctionReturn(PETSC_SUCCESS);
836 }
837 
838 PETSC_UNUSED static PetscErrorCode DMPlexView_Ascii_Geometry(DM dm, PetscViewer viewer)
839 {
840   PetscSection       coordSection;
841   Vec                coordinates;
842   DMLabel            depthLabel, celltypeLabel;
843   const char        *name[4];
844   const PetscScalar *a;
845   PetscInt           dim, pStart, pEnd, cStart, cEnd, c;
846 
847   PetscFunctionBegin;
848   PetscCall(DMGetDimension(dm, &dim));
849   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
850   PetscCall(DMGetCoordinateSection(dm, &coordSection));
851   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
852   PetscCall(DMPlexGetCellTypeLabel(dm, &celltypeLabel));
853   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
854   PetscCall(PetscSectionGetChart(coordSection, &pStart, &pEnd));
855   PetscCall(VecGetArrayRead(coordinates, &a));
856   name[0]       = "vertex";
857   name[1]       = "edge";
858   name[dim - 1] = "face";
859   name[dim]     = "cell";
860   for (c = cStart; c < cEnd; ++c) {
861     PetscInt *closure = NULL;
862     PetscInt  closureSize, cl, ct;
863 
864     PetscCall(DMLabelGetValue(celltypeLabel, c, &ct));
865     PetscCall(PetscViewerASCIIPrintf(viewer, "Geometry for cell %" PetscInt_FMT " polytope type %s:\n", c, DMPolytopeTypes[ct]));
866     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
867     PetscCall(PetscViewerASCIIPushTab(viewer));
868     for (cl = 0; cl < closureSize * 2; cl += 2) {
869       PetscInt point = closure[cl], depth, dof, off, d, p;
870 
871       if ((point < pStart) || (point >= pEnd)) continue;
872       PetscCall(PetscSectionGetDof(coordSection, point, &dof));
873       if (!dof) continue;
874       PetscCall(DMLabelGetValue(depthLabel, point, &depth));
875       PetscCall(PetscSectionGetOffset(coordSection, point, &off));
876       PetscCall(PetscViewerASCIIPrintf(viewer, "%s %" PetscInt_FMT " coords:", name[depth], point));
877       for (p = 0; p < dof / dim; ++p) {
878         PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
879         for (d = 0; d < dim; ++d) {
880           if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
881           PetscCall(PetscViewerASCIIPrintf(viewer, "%g", (double)PetscRealPart(a[off + p * dim + d])));
882         }
883         PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
884       }
885       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
886     }
887     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
888     PetscCall(PetscViewerASCIIPopTab(viewer));
889   }
890   PetscCall(VecRestoreArrayRead(coordinates, &a));
891   PetscFunctionReturn(PETSC_SUCCESS);
892 }
893 
894 typedef enum {
895   CS_CARTESIAN,
896   CS_POLAR,
897   CS_CYLINDRICAL,
898   CS_SPHERICAL
899 } CoordSystem;
900 const char *CoordSystems[] = {"cartesian", "polar", "cylindrical", "spherical", "CoordSystem", "CS_", NULL};
901 
902 static PetscErrorCode DMPlexView_Ascii_Coordinates(PetscViewer viewer, CoordSystem cs, PetscInt dim, const PetscScalar x[])
903 {
904   PetscInt i;
905 
906   PetscFunctionBegin;
907   if (dim > 3) {
908     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double)PetscRealPart(x[i])));
909   } else {
910     PetscReal coords[3], trcoords[3] = {0., 0., 0.};
911 
912     for (i = 0; i < dim; ++i) coords[i] = PetscRealPart(x[i]);
913     switch (cs) {
914     case CS_CARTESIAN:
915       for (i = 0; i < dim; ++i) trcoords[i] = coords[i];
916       break;
917     case CS_POLAR:
918       PetscCheck(dim == 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Polar coordinates are for 2 dimension, not %" PetscInt_FMT, dim);
919       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
920       trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
921       break;
922     case CS_CYLINDRICAL:
923       PetscCheck(dim == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cylindrical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
924       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
925       trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
926       trcoords[2] = coords[2];
927       break;
928     case CS_SPHERICAL:
929       PetscCheck(dim == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Spherical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
930       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]) + PetscSqr(coords[2]));
931       trcoords[1] = PetscAtan2Real(PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1])), coords[2]);
932       trcoords[2] = PetscAtan2Real(coords[1], coords[0]);
933       break;
934     }
935     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double)trcoords[i]));
936   }
937   PetscFunctionReturn(PETSC_SUCCESS);
938 }
939 
940 static PetscErrorCode DMPlexView_Ascii(DM dm, PetscViewer viewer)
941 {
942   DM_Plex          *mesh = (DM_Plex *)dm->data;
943   DM                cdm, cdmCell;
944   PetscSection      coordSection, coordSectionCell;
945   Vec               coordinates, coordinatesCell;
946   PetscViewerFormat format;
947 
948   PetscFunctionBegin;
949   PetscCall(PetscViewerGetFormat(viewer, &format));
950   if (format == PETSC_VIEWER_ASCII_INFO_DETAIL) {
951     const char *name;
952     PetscInt    dim, cellHeight, maxConeSize, maxSupportSize;
953     PetscInt    pStart, pEnd, p, numLabels, l;
954     PetscMPIInt rank, size;
955 
956     PetscCall(DMGetCoordinateDM(dm, &cdm));
957     PetscCall(DMGetCoordinateSection(dm, &coordSection));
958     PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
959     PetscCall(DMGetCellCoordinateDM(dm, &cdmCell));
960     PetscCall(DMGetCellCoordinateSection(dm, &coordSectionCell));
961     PetscCall(DMGetCellCoordinatesLocal(dm, &coordinatesCell));
962     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
963     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
964     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
965     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
966     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
967     PetscCall(DMGetDimension(dm, &dim));
968     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
969     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
970     else PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
971     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
972     PetscCall(PetscViewerASCIIPrintf(viewer, "Supports:\n"));
973     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
974     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max support size: %" PetscInt_FMT "\n", rank, maxSupportSize));
975     for (p = pStart; p < pEnd; ++p) {
976       PetscInt dof, off, s;
977 
978       PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
979       PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
980       for (s = off; s < off + dof; ++s) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d]: %" PetscInt_FMT " ----> %" PetscInt_FMT "\n", rank, p, mesh->supports[s]));
981     }
982     PetscCall(PetscViewerFlush(viewer));
983     PetscCall(PetscViewerASCIIPrintf(viewer, "Cones:\n"));
984     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max cone size: %" PetscInt_FMT "\n", rank, maxConeSize));
985     for (p = pStart; p < pEnd; ++p) {
986       PetscInt dof, off, c;
987 
988       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
989       PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
990       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]));
991     }
992     PetscCall(PetscViewerFlush(viewer));
993     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
994     if (coordSection && coordinates) {
995       CoordSystem        cs = CS_CARTESIAN;
996       const PetscScalar *array, *arrayCell = NULL;
997       PetscInt           Nf, Nc, pvStart, pvEnd, pcStart = PETSC_MAX_INT, pcEnd = PETSC_MIN_INT, pStart, pEnd, p;
998       PetscMPIInt        rank;
999       const char        *name;
1000 
1001       PetscCall(PetscOptionsGetEnum(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_coord_system", CoordSystems, (PetscEnum *)&cs, NULL));
1002       PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)viewer), &rank));
1003       PetscCall(PetscSectionGetNumFields(coordSection, &Nf));
1004       PetscCheck(Nf == 1, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Coordinate section should have 1 field, not %" PetscInt_FMT, Nf);
1005       PetscCall(PetscSectionGetFieldComponents(coordSection, 0, &Nc));
1006       PetscCall(PetscSectionGetChart(coordSection, &pvStart, &pvEnd));
1007       if (coordSectionCell) PetscCall(PetscSectionGetChart(coordSectionCell, &pcStart, &pcEnd));
1008       pStart = PetscMin(pvStart, pcStart);
1009       pEnd   = PetscMax(pvEnd, pcEnd);
1010       PetscCall(PetscObjectGetName((PetscObject)coordinates, &name));
1011       PetscCall(PetscViewerASCIIPrintf(viewer, "%s with %" PetscInt_FMT " fields\n", name, Nf));
1012       PetscCall(PetscViewerASCIIPrintf(viewer, "  field 0 with %" PetscInt_FMT " components\n", Nc));
1013       if (cs != CS_CARTESIAN) PetscCall(PetscViewerASCIIPrintf(viewer, "  output coordinate system: %s\n", CoordSystems[cs]));
1014 
1015       PetscCall(VecGetArrayRead(coordinates, &array));
1016       if (coordinatesCell) PetscCall(VecGetArrayRead(coordinatesCell, &arrayCell));
1017       PetscCall(PetscViewerASCIIPushSynchronized(viewer));
1018       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "Process %d:\n", rank));
1019       for (p = pStart; p < pEnd; ++p) {
1020         PetscInt dof, off;
1021 
1022         if (p >= pvStart && p < pvEnd) {
1023           PetscCall(PetscSectionGetDof(coordSection, p, &dof));
1024           PetscCall(PetscSectionGetOffset(coordSection, p, &off));
1025           if (dof) {
1026             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dim %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
1027             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &array[off]));
1028             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
1029           }
1030         }
1031         if (cdmCell && p >= pcStart && p < pcEnd) {
1032           PetscCall(PetscSectionGetDof(coordSectionCell, p, &dof));
1033           PetscCall(PetscSectionGetOffset(coordSectionCell, p, &off));
1034           if (dof) {
1035             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dim %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
1036             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &arrayCell[off]));
1037             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
1038           }
1039         }
1040       }
1041       PetscCall(PetscViewerFlush(viewer));
1042       PetscCall(PetscViewerASCIIPopSynchronized(viewer));
1043       PetscCall(VecRestoreArrayRead(coordinates, &array));
1044       if (coordinatesCell) PetscCall(VecRestoreArrayRead(coordinatesCell, &arrayCell));
1045     }
1046     PetscCall(DMGetNumLabels(dm, &numLabels));
1047     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
1048     for (l = 0; l < numLabels; ++l) {
1049       DMLabel     label;
1050       PetscBool   isdepth;
1051       const char *name;
1052 
1053       PetscCall(DMGetLabelName(dm, l, &name));
1054       PetscCall(PetscStrcmp(name, "depth", &isdepth));
1055       if (isdepth) continue;
1056       PetscCall(DMGetLabel(dm, name, &label));
1057       PetscCall(DMLabelView(label, viewer));
1058     }
1059     if (size > 1) {
1060       PetscSF sf;
1061 
1062       PetscCall(DMGetPointSF(dm, &sf));
1063       PetscCall(PetscSFView(sf, viewer));
1064     }
1065     if (mesh->periodic.face_sfs)
1066       for (PetscInt i = 0; i < mesh->periodic.num_face_sfs; i++) PetscCall(PetscSFView(mesh->periodic.face_sfs[i], viewer));
1067     PetscCall(PetscViewerFlush(viewer));
1068   } else if (format == PETSC_VIEWER_ASCII_LATEX) {
1069     const char  *name, *color;
1070     const char  *defcolors[3]  = {"gray", "orange", "green"};
1071     const char  *deflcolors[4] = {"blue", "cyan", "red", "magenta"};
1072     char         lname[PETSC_MAX_PATH_LEN];
1073     PetscReal    scale      = 2.0;
1074     PetscReal    tikzscale  = 1.0;
1075     PetscBool    useNumbers = PETSC_TRUE, drawNumbers[4], drawColors[4], useLabels, useColors, plotEdges, drawHasse = PETSC_FALSE;
1076     double       tcoords[3];
1077     PetscScalar *coords;
1078     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;
1079     PetscMPIInt  rank, size;
1080     char       **names, **colors, **lcolors;
1081     PetscBool    flg, lflg;
1082     PetscBT      wp = NULL;
1083     PetscInt     pEnd, pStart;
1084 
1085     PetscCall(DMGetCoordinateDM(dm, &cdm));
1086     PetscCall(DMGetCoordinateSection(dm, &coordSection));
1087     PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1088     PetscCall(DMGetCellCoordinateDM(dm, &cdmCell));
1089     PetscCall(DMGetCellCoordinateSection(dm, &coordSectionCell));
1090     PetscCall(DMGetCellCoordinatesLocal(dm, &coordinatesCell));
1091     PetscCall(DMGetDimension(dm, &dim));
1092     PetscCall(DMPlexGetDepth(dm, &depth));
1093     PetscCall(DMGetNumLabels(dm, &numLabels));
1094     numLabels  = PetscMax(numLabels, 10);
1095     numColors  = 10;
1096     numLColors = 10;
1097     PetscCall(PetscCalloc3(numLabels, &names, numColors, &colors, numLColors, &lcolors));
1098     PetscCall(PetscOptionsGetReal(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_scale", &scale, NULL));
1099     PetscCall(PetscOptionsGetReal(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_tikzscale", &tikzscale, NULL));
1100     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_numbers", &useNumbers, NULL));
1101     for (d = 0; d < 4; ++d) drawNumbers[d] = useNumbers;
1102     for (d = 0; d < 4; ++d) drawColors[d] = PETSC_TRUE;
1103     n = 4;
1104     PetscCall(PetscOptionsGetBoolArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_numbers_depth", drawNumbers, &n, &flg));
1105     PetscCheck(!flg || n == dim + 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim + 1);
1106     n = 4;
1107     PetscCall(PetscOptionsGetBoolArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_colors_depth", drawColors, &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     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_labels", names, &numLabels, &useLabels));
1110     if (!useLabels) numLabels = 0;
1111     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_colors", colors, &numColors, &useColors));
1112     if (!useColors) {
1113       numColors = 3;
1114       for (c = 0; c < numColors; ++c) PetscCall(PetscStrallocpy(defcolors[c], &colors[c]));
1115     }
1116     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_lcolors", lcolors, &numLColors, &useColors));
1117     if (!useColors) {
1118       numLColors = 4;
1119       for (c = 0; c < numLColors; ++c) PetscCall(PetscStrallocpy(deflcolors[c], &lcolors[c]));
1120     }
1121     PetscCall(PetscOptionsGetString(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_label_filter", lname, sizeof(lname), &lflg));
1122     plotEdges = (PetscBool)(depth > 1 && drawNumbers[1] && dim < 3);
1123     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_edges", &plotEdges, &flg));
1124     PetscCheck(!flg || !plotEdges || depth >= dim, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Mesh must be interpolated");
1125     if (depth < dim) plotEdges = PETSC_FALSE;
1126     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_hasse", &drawHasse, NULL));
1127 
1128     /* filter points with labelvalue != labeldefaultvalue */
1129     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
1130     PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1131     PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
1132     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1133     PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd));
1134     if (lflg) {
1135       DMLabel lbl;
1136 
1137       PetscCall(DMGetLabel(dm, lname, &lbl));
1138       if (lbl) {
1139         PetscInt val, defval;
1140 
1141         PetscCall(DMLabelGetDefaultValue(lbl, &defval));
1142         PetscCall(PetscBTCreate(pEnd - pStart, &wp));
1143         for (c = pStart; c < pEnd; c++) {
1144           PetscInt *closure = NULL;
1145           PetscInt  closureSize;
1146 
1147           PetscCall(DMLabelGetValue(lbl, c, &val));
1148           if (val == defval) continue;
1149 
1150           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1151           for (p = 0; p < closureSize * 2; p += 2) PetscCall(PetscBTSet(wp, closure[p] - pStart));
1152           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1153         }
1154       }
1155     }
1156 
1157     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1158     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
1159     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1160     PetscCall(PetscViewerASCIIPrintf(viewer, "\
1161 \\documentclass[tikz]{standalone}\n\n\
1162 \\usepackage{pgflibraryshapes}\n\
1163 \\usetikzlibrary{backgrounds}\n\
1164 \\usetikzlibrary{arrows}\n\
1165 \\begin{document}\n"));
1166     if (size > 1) {
1167       PetscCall(PetscViewerASCIIPrintf(viewer, "%s for process ", name));
1168       for (p = 0; p < size; ++p) {
1169         if (p) PetscCall(PetscViewerASCIIPrintf(viewer, (p == size - 1) ? ", and " : ", "));
1170         PetscCall(PetscViewerASCIIPrintf(viewer, "{\\textcolor{%s}%" PetscInt_FMT "}", colors[p % numColors], p));
1171       }
1172       PetscCall(PetscViewerASCIIPrintf(viewer, ".\n\n\n"));
1173     }
1174     if (drawHasse) {
1175       PetscInt maxStratum = PetscMax(vEnd - vStart, PetscMax(eEnd - eStart, PetscMax(fEnd - fStart, cEnd - cStart)));
1176 
1177       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vStart}{%" PetscInt_FMT "}\n", vStart));
1178       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vEnd}{%" PetscInt_FMT "}\n", vEnd - 1));
1179       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numVertices}{%" PetscInt_FMT "}\n", vEnd - vStart));
1180       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vShift}{%.2f}\n", 3 + (maxStratum - (vEnd - vStart)) / 2.));
1181       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eStart}{%" PetscInt_FMT "}\n", eStart));
1182       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eEnd}{%" PetscInt_FMT "}\n", eEnd - 1));
1183       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eShift}{%.2f}\n", 3 + (maxStratum - (eEnd - eStart)) / 2.));
1184       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numEdges}{%" PetscInt_FMT "}\n", eEnd - eStart));
1185       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\fStart}{%" PetscInt_FMT "}\n", fStart));
1186       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\fEnd}{%" PetscInt_FMT "}\n", fEnd - 1));
1187       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\fShift}{%.2f}\n", 3 + (maxStratum - (fEnd - fStart)) / 2.));
1188       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numFaces}{%" PetscInt_FMT "}\n", fEnd - fStart));
1189       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cStart}{%" PetscInt_FMT "}\n", cStart));
1190       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cEnd}{%" PetscInt_FMT "}\n", cEnd - 1));
1191       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numCells}{%" PetscInt_FMT "}\n", cEnd - cStart));
1192       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cShift}{%.2f}\n", 3 + (maxStratum - (cEnd - cStart)) / 2.));
1193     }
1194     PetscCall(PetscViewerASCIIPrintf(viewer, "\\begin{tikzpicture}[scale = %g,font=\\fontsize{8}{8}\\selectfont]\n", (double)tikzscale));
1195 
1196     /* Plot vertices */
1197     PetscCall(VecGetArray(coordinates, &coords));
1198     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
1199     for (v = vStart; v < vEnd; ++v) {
1200       PetscInt  off, dof, d;
1201       PetscBool isLabeled = PETSC_FALSE;
1202 
1203       if (wp && !PetscBTLookup(wp, v - pStart)) continue;
1204       PetscCall(PetscSectionGetDof(coordSection, v, &dof));
1205       PetscCall(PetscSectionGetOffset(coordSection, v, &off));
1206       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1207       PetscCheck(dof <= 3, PETSC_COMM_SELF, PETSC_ERR_PLIB, "coordSection vertex %" PetscInt_FMT " has dof %" PetscInt_FMT " > 3", v, dof);
1208       for (d = 0; d < dof; ++d) {
1209         tcoords[d] = (double)(scale * PetscRealPart(coords[off + d]));
1210         tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1211       }
1212       /* Rotate coordinates since PGF makes z point out of the page instead of up */
1213       if (dim == 3) {
1214         PetscReal tmp = tcoords[1];
1215         tcoords[1]    = tcoords[2];
1216         tcoords[2]    = -tmp;
1217       }
1218       for (d = 0; d < dof; ++d) {
1219         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1220         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)tcoords[d]));
1221       }
1222       if (drawHasse) color = colors[0 % numColors];
1223       else color = colors[rank % numColors];
1224       for (l = 0; l < numLabels; ++l) {
1225         PetscInt val;
1226         PetscCall(DMGetLabelValue(dm, names[l], v, &val));
1227         if (val >= 0) {
1228           color     = lcolors[l % numLColors];
1229           isLabeled = PETSC_TRUE;
1230           break;
1231         }
1232       }
1233       if (drawNumbers[0]) {
1234         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", v, rank, color, v));
1235       } else if (drawColors[0]) {
1236         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", v, rank, !isLabeled ? 1 : 2, color));
1237       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", v, rank));
1238     }
1239     PetscCall(VecRestoreArray(coordinates, &coords));
1240     PetscCall(PetscViewerFlush(viewer));
1241     /* Plot edges */
1242     if (plotEdges) {
1243       PetscCall(VecGetArray(coordinates, &coords));
1244       PetscCall(PetscViewerASCIIPrintf(viewer, "\\path\n"));
1245       for (e = eStart; e < eEnd; ++e) {
1246         const PetscInt *cone;
1247         PetscInt        coneSize, offA, offB, dof, d;
1248 
1249         if (wp && !PetscBTLookup(wp, e - pStart)) continue;
1250         PetscCall(DMPlexGetConeSize(dm, e, &coneSize));
1251         PetscCheck(coneSize == 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Edge %" PetscInt_FMT " cone should have two vertices, not %" PetscInt_FMT, e, coneSize);
1252         PetscCall(DMPlexGetCone(dm, e, &cone));
1253         PetscCall(PetscSectionGetDof(coordSection, cone[0], &dof));
1254         PetscCall(PetscSectionGetOffset(coordSection, cone[0], &offA));
1255         PetscCall(PetscSectionGetOffset(coordSection, cone[1], &offB));
1256         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "("));
1257         for (d = 0; d < dof; ++d) {
1258           tcoords[d] = (double)(0.5 * scale * PetscRealPart(coords[offA + d] + coords[offB + d]));
1259           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1260         }
1261         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1262         if (dim == 3) {
1263           PetscReal tmp = tcoords[1];
1264           tcoords[1]    = tcoords[2];
1265           tcoords[2]    = -tmp;
1266         }
1267         for (d = 0; d < dof; ++d) {
1268           if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1269           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)tcoords[d]));
1270         }
1271         if (drawHasse) color = colors[1 % numColors];
1272         else color = colors[rank % numColors];
1273         for (l = 0; l < numLabels; ++l) {
1274           PetscInt val;
1275           PetscCall(DMGetLabelValue(dm, names[l], e, &val));
1276           if (val >= 0) {
1277             color = lcolors[l % numLColors];
1278             break;
1279           }
1280         }
1281         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "} --\n", e, rank, color, e));
1282       }
1283       PetscCall(VecRestoreArray(coordinates, &coords));
1284       PetscCall(PetscViewerFlush(viewer));
1285       PetscCall(PetscViewerASCIIPrintf(viewer, "(0,0);\n"));
1286     }
1287     /* Plot cells */
1288     if (dim == 3 || !drawNumbers[1]) {
1289       for (e = eStart; e < eEnd; ++e) {
1290         const PetscInt *cone;
1291 
1292         if (wp && !PetscBTLookup(wp, e - pStart)) continue;
1293         color = colors[rank % numColors];
1294         for (l = 0; l < numLabels; ++l) {
1295           PetscInt val;
1296           PetscCall(DMGetLabelValue(dm, names[l], e, &val));
1297           if (val >= 0) {
1298             color = lcolors[l % numLColors];
1299             break;
1300           }
1301         }
1302         PetscCall(DMPlexGetCone(dm, e, &cone));
1303         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", color, cone[0], rank, cone[1], rank));
1304       }
1305     } else {
1306       DMPolytopeType ct;
1307 
1308       /* Drawing a 2D polygon */
1309       for (c = cStart; c < cEnd; ++c) {
1310         if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1311         PetscCall(DMPlexGetCellType(dm, c, &ct));
1312         if (DMPolytopeTypeIsHybrid(ct)) {
1313           const PetscInt *cone;
1314           PetscInt        coneSize, e;
1315 
1316           PetscCall(DMPlexGetCone(dm, c, &cone));
1317           PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
1318           for (e = 0; e < coneSize; ++e) {
1319             const PetscInt *econe;
1320 
1321             PetscCall(DMPlexGetCone(dm, cone[e], &econe));
1322             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));
1323           }
1324         } else {
1325           PetscInt *closure = NULL;
1326           PetscInt  closureSize, Nv = 0, v;
1327 
1328           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1329           for (p = 0; p < closureSize * 2; p += 2) {
1330             const PetscInt point = closure[p];
1331 
1332             if ((point >= vStart) && (point < vEnd)) closure[Nv++] = point;
1333           }
1334           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] ", colors[rank % numColors]));
1335           for (v = 0; v <= Nv; ++v) {
1336             const PetscInt vertex = closure[v % Nv];
1337 
1338             if (v > 0) {
1339               if (plotEdges) {
1340                 const PetscInt *edge;
1341                 PetscInt        endpoints[2], ne;
1342 
1343                 endpoints[0] = closure[v - 1];
1344                 endpoints[1] = vertex;
1345                 PetscCall(DMPlexGetJoin(dm, 2, endpoints, &ne, &edge));
1346                 PetscCheck(ne == 1, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Could not find edge for vertices %" PetscInt_FMT ", %" PetscInt_FMT, endpoints[0], endpoints[1]);
1347                 PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- (%" PetscInt_FMT "_%d) -- ", edge[0], rank));
1348                 PetscCall(DMPlexRestoreJoin(dm, 2, endpoints, &ne, &edge));
1349               } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- "));
1350             }
1351             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "(%" PetscInt_FMT "_%d)", vertex, rank));
1352           }
1353           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ";\n"));
1354           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1355         }
1356       }
1357     }
1358     for (c = cStart; c < cEnd; ++c) {
1359       double             ccoords[3] = {0.0, 0.0, 0.0};
1360       PetscBool          isLabeled  = PETSC_FALSE;
1361       PetscScalar       *cellCoords = NULL;
1362       const PetscScalar *array;
1363       PetscInt           numCoords, cdim, d;
1364       PetscBool          isDG;
1365 
1366       if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1367       PetscCall(DMGetCoordinateDim(dm, &cdim));
1368       PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1369       PetscCheck(!(numCoords % cdim), PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "coordinate dim %" PetscInt_FMT " does not divide numCoords %" PetscInt_FMT, cdim, numCoords);
1370       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1371       for (p = 0; p < numCoords / cdim; ++p) {
1372         for (d = 0; d < cdim; ++d) {
1373           tcoords[d] = (double)(scale * PetscRealPart(cellCoords[p * cdim + d]));
1374           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1375         }
1376         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1377         if (cdim == 3) {
1378           PetscReal tmp = tcoords[1];
1379           tcoords[1]    = tcoords[2];
1380           tcoords[2]    = -tmp;
1381         }
1382         for (d = 0; d < dim; ++d) ccoords[d] += tcoords[d];
1383       }
1384       for (d = 0; d < cdim; ++d) ccoords[d] /= (numCoords / cdim);
1385       PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1386       for (d = 0; d < cdim; ++d) {
1387         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1388         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)ccoords[d]));
1389       }
1390       if (drawHasse) color = colors[depth % numColors];
1391       else color = colors[rank % numColors];
1392       for (l = 0; l < numLabels; ++l) {
1393         PetscInt val;
1394         PetscCall(DMGetLabelValue(dm, names[l], c, &val));
1395         if (val >= 0) {
1396           color     = lcolors[l % numLColors];
1397           isLabeled = PETSC_TRUE;
1398           break;
1399         }
1400       }
1401       if (drawNumbers[dim]) {
1402         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", c, rank, color, c));
1403       } else if (drawColors[dim]) {
1404         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", c, rank, !isLabeled ? 1 : 2, color));
1405       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", c, rank));
1406     }
1407     if (drawHasse) {
1408       int height = 0;
1409 
1410       color = colors[depth % numColors];
1411       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Cells\n"));
1412       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\c in {\\cStart,...,\\cEnd}\n"));
1413       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1414       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\c_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\cShift+\\c-\\cStart,%d) {\\c};\n", rank, color, height++));
1415       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1416 
1417       if (depth > 2) {
1418         color = colors[1 % numColors];
1419         PetscCall(PetscViewerASCIIPrintf(viewer, "%% Faces\n"));
1420         PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\f in {\\fStart,...,\\fEnd}\n"));
1421         PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1422         PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\f_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\fShift+\\f-\\fStart,%d) {\\f};\n", rank, color, height++));
1423         PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1424       }
1425 
1426       color = colors[1 % numColors];
1427       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Edges\n"));
1428       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\e in {\\eStart,...,\\eEnd}\n"));
1429       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1430       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\e_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\eShift+\\e-\\eStart,%d) {\\e};\n", rank, color, height++));
1431       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1432 
1433       color = colors[0 % numColors];
1434       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Vertices\n"));
1435       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\v in {\\vStart,...,\\vEnd}\n"));
1436       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1437       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\v_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\vShift+\\v-\\vStart,%d) {\\v};\n", rank, color, height++));
1438       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1439 
1440       for (p = pStart; p < pEnd; ++p) {
1441         const PetscInt *cone;
1442         PetscInt        coneSize, cp;
1443 
1444         PetscCall(DMPlexGetCone(dm, p, &cone));
1445         PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
1446         for (cp = 0; cp < coneSize; ++cp) PetscCall(PetscViewerASCIIPrintf(viewer, "\\draw[->, shorten >=1pt] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", cone[cp], rank, p, rank));
1447       }
1448     }
1449     PetscCall(PetscViewerFlush(viewer));
1450     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
1451     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{tikzpicture}\n"));
1452     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{document}\n"));
1453     for (l = 0; l < numLabels; ++l) PetscCall(PetscFree(names[l]));
1454     for (c = 0; c < numColors; ++c) PetscCall(PetscFree(colors[c]));
1455     for (c = 0; c < numLColors; ++c) PetscCall(PetscFree(lcolors[c]));
1456     PetscCall(PetscFree3(names, colors, lcolors));
1457     PetscCall(PetscBTDestroy(&wp));
1458   } else if (format == PETSC_VIEWER_LOAD_BALANCE) {
1459     Vec                    cown, acown;
1460     VecScatter             sct;
1461     ISLocalToGlobalMapping g2l;
1462     IS                     gid, acis;
1463     MPI_Comm               comm, ncomm = MPI_COMM_NULL;
1464     MPI_Group              ggroup, ngroup;
1465     PetscScalar           *array, nid;
1466     const PetscInt        *idxs;
1467     PetscInt              *idxs2, *start, *adjacency, *work;
1468     PetscInt64             lm[3], gm[3];
1469     PetscInt               i, c, cStart, cEnd, cum, numVertices, ect, ectn, cellHeight;
1470     PetscMPIInt            d1, d2, rank;
1471 
1472     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1473     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1474 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1475     PetscCallMPI(MPI_Comm_split_type(comm, MPI_COMM_TYPE_SHARED, rank, MPI_INFO_NULL, &ncomm));
1476 #endif
1477     if (ncomm != MPI_COMM_NULL) {
1478       PetscCallMPI(MPI_Comm_group(comm, &ggroup));
1479       PetscCallMPI(MPI_Comm_group(ncomm, &ngroup));
1480       d1 = 0;
1481       PetscCallMPI(MPI_Group_translate_ranks(ngroup, 1, &d1, ggroup, &d2));
1482       nid = d2;
1483       PetscCallMPI(MPI_Group_free(&ggroup));
1484       PetscCallMPI(MPI_Group_free(&ngroup));
1485       PetscCallMPI(MPI_Comm_free(&ncomm));
1486     } else nid = 0.0;
1487 
1488     /* Get connectivity */
1489     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1490     PetscCall(DMPlexCreatePartitionerGraph(dm, cellHeight, &numVertices, &start, &adjacency, &gid));
1491 
1492     /* filter overlapped local cells */
1493     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
1494     PetscCall(ISGetIndices(gid, &idxs));
1495     PetscCall(ISGetLocalSize(gid, &cum));
1496     PetscCall(PetscMalloc1(cum, &idxs2));
1497     for (c = cStart, cum = 0; c < cEnd; c++) {
1498       if (idxs[c - cStart] < 0) continue;
1499       idxs2[cum++] = idxs[c - cStart];
1500     }
1501     PetscCall(ISRestoreIndices(gid, &idxs));
1502     PetscCheck(numVertices == cum, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Unexpected %" PetscInt_FMT " != %" PetscInt_FMT, numVertices, cum);
1503     PetscCall(ISDestroy(&gid));
1504     PetscCall(ISCreateGeneral(comm, numVertices, idxs2, PETSC_OWN_POINTER, &gid));
1505 
1506     /* support for node-aware cell locality */
1507     PetscCall(ISCreateGeneral(comm, start[numVertices], adjacency, PETSC_USE_POINTER, &acis));
1508     PetscCall(VecCreateSeq(PETSC_COMM_SELF, start[numVertices], &acown));
1509     PetscCall(VecCreateMPI(comm, numVertices, PETSC_DECIDE, &cown));
1510     PetscCall(VecGetArray(cown, &array));
1511     for (c = 0; c < numVertices; c++) array[c] = nid;
1512     PetscCall(VecRestoreArray(cown, &array));
1513     PetscCall(VecScatterCreate(cown, acis, acown, NULL, &sct));
1514     PetscCall(VecScatterBegin(sct, cown, acown, INSERT_VALUES, SCATTER_FORWARD));
1515     PetscCall(VecScatterEnd(sct, cown, acown, INSERT_VALUES, SCATTER_FORWARD));
1516     PetscCall(ISDestroy(&acis));
1517     PetscCall(VecScatterDestroy(&sct));
1518     PetscCall(VecDestroy(&cown));
1519 
1520     /* compute edgeCut */
1521     for (c = 0, cum = 0; c < numVertices; c++) cum = PetscMax(cum, start[c + 1] - start[c]);
1522     PetscCall(PetscMalloc1(cum, &work));
1523     PetscCall(ISLocalToGlobalMappingCreateIS(gid, &g2l));
1524     PetscCall(ISLocalToGlobalMappingSetType(g2l, ISLOCALTOGLOBALMAPPINGHASH));
1525     PetscCall(ISDestroy(&gid));
1526     PetscCall(VecGetArray(acown, &array));
1527     for (c = 0, ect = 0, ectn = 0; c < numVertices; c++) {
1528       PetscInt totl;
1529 
1530       totl = start[c + 1] - start[c];
1531       PetscCall(ISGlobalToLocalMappingApply(g2l, IS_GTOLM_MASK, totl, adjacency + start[c], NULL, work));
1532       for (i = 0; i < totl; i++) {
1533         if (work[i] < 0) {
1534           ect += 1;
1535           ectn += (array[i + start[c]] != nid) ? 0 : 1;
1536         }
1537       }
1538     }
1539     PetscCall(PetscFree(work));
1540     PetscCall(VecRestoreArray(acown, &array));
1541     lm[0] = numVertices > 0 ? numVertices : PETSC_MAX_INT;
1542     lm[1] = -numVertices;
1543     PetscCall(MPIU_Allreduce(lm, gm, 2, MPIU_INT64, MPI_MIN, comm));
1544     PetscCall(PetscViewerASCIIPrintf(viewer, "  Cell balance: %.2f (max %" PetscInt_FMT ", min %" PetscInt_FMT, -((double)gm[1]) / ((double)gm[0]), -(PetscInt)gm[1], (PetscInt)gm[0]));
1545     lm[0] = ect;                     /* edgeCut */
1546     lm[1] = ectn;                    /* node-aware edgeCut */
1547     lm[2] = numVertices > 0 ? 0 : 1; /* empty processes */
1548     PetscCall(MPIU_Allreduce(lm, gm, 3, MPIU_INT64, MPI_SUM, comm));
1549     PetscCall(PetscViewerASCIIPrintf(viewer, ", empty %" PetscInt_FMT ")\n", (PetscInt)gm[2]));
1550 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1551     PetscCall(PetscViewerASCIIPrintf(viewer, "  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n", (PetscInt)(gm[0] / 2), gm[0] ? ((double)gm[1]) / ((double)gm[0]) : 1.));
1552 #else
1553     PetscCall(PetscViewerASCIIPrintf(viewer, "  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n", (PetscInt)(gm[0] / 2), 0.0));
1554 #endif
1555     PetscCall(ISLocalToGlobalMappingDestroy(&g2l));
1556     PetscCall(PetscFree(start));
1557     PetscCall(PetscFree(adjacency));
1558     PetscCall(VecDestroy(&acown));
1559   } else {
1560     const char    *name;
1561     PetscInt      *sizes, *hybsizes, *ghostsizes;
1562     PetscInt       locDepth, depth, cellHeight, dim, d;
1563     PetscInt       pStart, pEnd, p, gcStart, gcEnd, gcNum;
1564     PetscInt       numLabels, l, maxSize = 17;
1565     DMPolytopeType ct0 = DM_POLYTOPE_UNKNOWN;
1566     MPI_Comm       comm;
1567     PetscMPIInt    size, rank;
1568 
1569     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1570     PetscCallMPI(MPI_Comm_size(comm, &size));
1571     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1572     PetscCall(DMGetDimension(dm, &dim));
1573     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1574     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1575     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
1576     else PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
1577     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
1578     PetscCall(DMPlexGetDepth(dm, &locDepth));
1579     PetscCall(MPIU_Allreduce(&locDepth, &depth, 1, MPIU_INT, MPI_MAX, comm));
1580     PetscCall(DMPlexGetCellTypeStratum(dm, DM_POLYTOPE_FV_GHOST, &gcStart, &gcEnd));
1581     gcNum = gcEnd - gcStart;
1582     if (size < maxSize) PetscCall(PetscCalloc3(size, &sizes, size, &hybsizes, size, &ghostsizes));
1583     else PetscCall(PetscCalloc3(3, &sizes, 3, &hybsizes, 3, &ghostsizes));
1584     for (d = 0; d <= depth; d++) {
1585       PetscInt Nc[2] = {0, 0}, ict;
1586 
1587       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
1588       if (pStart < pEnd) PetscCall(DMPlexGetCellType(dm, pStart, &ct0));
1589       ict = ct0;
1590       PetscCallMPI(MPI_Bcast(&ict, 1, MPIU_INT, 0, comm));
1591       ct0 = (DMPolytopeType)ict;
1592       for (p = pStart; p < pEnd; ++p) {
1593         DMPolytopeType ct;
1594 
1595         PetscCall(DMPlexGetCellType(dm, p, &ct));
1596         if (ct == ct0) ++Nc[0];
1597         else ++Nc[1];
1598       }
1599       if (size < maxSize) {
1600         PetscCallMPI(MPI_Gather(&Nc[0], 1, MPIU_INT, sizes, 1, MPIU_INT, 0, comm));
1601         PetscCallMPI(MPI_Gather(&Nc[1], 1, MPIU_INT, hybsizes, 1, MPIU_INT, 0, comm));
1602         if (d == depth) PetscCallMPI(MPI_Gather(&gcNum, 1, MPIU_INT, ghostsizes, 1, MPIU_INT, 0, comm));
1603         PetscCall(PetscViewerASCIIPrintf(viewer, "  Number of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1604         for (p = 0; p < size; ++p) {
1605           if (rank == 0) {
1606             PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT, sizes[p] + hybsizes[p]));
1607             if (hybsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT ")", hybsizes[p]));
1608             if (ghostsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "]", ghostsizes[p]));
1609           }
1610         }
1611       } else {
1612         PetscInt locMinMax[2];
1613 
1614         locMinMax[0] = Nc[0] + Nc[1];
1615         locMinMax[1] = Nc[0] + Nc[1];
1616         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, sizes));
1617         locMinMax[0] = Nc[1];
1618         locMinMax[1] = Nc[1];
1619         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, hybsizes));
1620         if (d == depth) {
1621           locMinMax[0] = gcNum;
1622           locMinMax[1] = gcNum;
1623           PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, ghostsizes));
1624         }
1625         PetscCall(PetscViewerASCIIPrintf(viewer, "  Min/Max of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1626         PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT "/%" PetscInt_FMT, sizes[0], sizes[1]));
1627         if (hybsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT "/%" PetscInt_FMT ")", hybsizes[0], hybsizes[1]));
1628         if (ghostsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "/%" PetscInt_FMT "]", ghostsizes[0], ghostsizes[1]));
1629       }
1630       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
1631     }
1632     PetscCall(PetscFree3(sizes, hybsizes, ghostsizes));
1633     {
1634       const PetscReal *maxCell;
1635       const PetscReal *L;
1636       PetscBool        localized;
1637 
1638       PetscCall(DMGetPeriodicity(dm, &maxCell, NULL, &L));
1639       PetscCall(DMGetCoordinatesLocalized(dm, &localized));
1640       if (L || localized) {
1641         PetscCall(PetscViewerASCIIPrintf(viewer, "Periodic mesh"));
1642         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1643         if (L) {
1644           PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
1645           for (d = 0; d < dim; ++d) {
1646             if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1647             PetscCall(PetscViewerASCIIPrintf(viewer, "%s", L[d] > 0.0 ? "PERIODIC" : "NONE"));
1648           }
1649           PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
1650         }
1651         PetscCall(PetscViewerASCIIPrintf(viewer, " coordinates %s\n", localized ? "localized" : "not localized"));
1652         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1653       }
1654     }
1655     PetscCall(DMGetNumLabels(dm, &numLabels));
1656     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
1657     for (l = 0; l < numLabels; ++l) {
1658       DMLabel     label;
1659       const char *name;
1660       PetscInt   *values;
1661       PetscInt    numValues, v;
1662 
1663       PetscCall(DMGetLabelName(dm, l, &name));
1664       PetscCall(DMGetLabel(dm, name, &label));
1665       PetscCall(DMLabelGetNumValues(label, &numValues));
1666       PetscCall(PetscViewerASCIIPrintf(viewer, "  %s: %" PetscInt_FMT " strata with value/size (", name, numValues));
1667 
1668       { // Extract array of DMLabel values so it can be sorted
1669         IS              is_values;
1670         const PetscInt *is_values_local = NULL;
1671 
1672         PetscCall(DMLabelGetValueIS(label, &is_values));
1673         PetscCall(ISGetIndices(is_values, &is_values_local));
1674         PetscCall(PetscMalloc1(numValues, &values));
1675         PetscCall(PetscArraycpy(values, is_values_local, numValues));
1676         PetscCall(PetscSortInt(numValues, values));
1677         PetscCall(ISRestoreIndices(is_values, &is_values_local));
1678         PetscCall(ISDestroy(&is_values));
1679       }
1680       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1681       for (v = 0; v < numValues; ++v) {
1682         PetscInt size;
1683 
1684         PetscCall(DMLabelGetStratumSize(label, values[v], &size));
1685         if (v > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1686         PetscCall(PetscViewerASCIIPrintf(viewer, "%" PetscInt_FMT " (%" PetscInt_FMT ")", values[v], size));
1687       }
1688       PetscCall(PetscViewerASCIIPrintf(viewer, ")\n"));
1689       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1690       PetscCall(PetscFree(values));
1691     }
1692     {
1693       char    **labelNames;
1694       PetscInt  Nl = numLabels;
1695       PetscBool flg;
1696 
1697       PetscCall(PetscMalloc1(Nl, &labelNames));
1698       PetscCall(PetscOptionsGetStringArray(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_plex_view_labels", labelNames, &Nl, &flg));
1699       for (l = 0; l < Nl; ++l) {
1700         DMLabel label;
1701 
1702         PetscCall(DMHasLabel(dm, labelNames[l], &flg));
1703         if (flg) {
1704           PetscCall(DMGetLabel(dm, labelNames[l], &label));
1705           PetscCall(DMLabelView(label, viewer));
1706         }
1707         PetscCall(PetscFree(labelNames[l]));
1708       }
1709       PetscCall(PetscFree(labelNames));
1710     }
1711     /* If no fields are specified, people do not want to see adjacency */
1712     if (dm->Nf) {
1713       PetscInt f;
1714 
1715       for (f = 0; f < dm->Nf; ++f) {
1716         const char *name;
1717 
1718         PetscCall(PetscObjectGetName(dm->fields[f].disc, &name));
1719         if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Field %s:\n", name));
1720         PetscCall(PetscViewerASCIIPushTab(viewer));
1721         if (dm->fields[f].label) PetscCall(DMLabelView(dm->fields[f].label, viewer));
1722         if (dm->fields[f].adjacency[0]) {
1723           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM++\n"));
1724           else PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM\n"));
1725         } else {
1726           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FEM\n"));
1727           else PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FUNKY\n"));
1728         }
1729         PetscCall(PetscViewerASCIIPopTab(viewer));
1730       }
1731     }
1732     PetscCall(DMGetCoarseDM(dm, &cdm));
1733     if (cdm) {
1734       PetscCall(PetscViewerASCIIPushTab(viewer));
1735       PetscCall(PetscViewerASCIIPrintf(viewer, "Defined by transform from:\n"));
1736       PetscCall(DMPlexView_Ascii(cdm, viewer));
1737       PetscCall(PetscViewerASCIIPopTab(viewer));
1738     }
1739   }
1740   PetscFunctionReturn(PETSC_SUCCESS);
1741 }
1742 
1743 static PetscErrorCode DMPlexDrawCell(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[])
1744 {
1745   DMPolytopeType ct;
1746   PetscMPIInt    rank;
1747   PetscInt       cdim;
1748 
1749   PetscFunctionBegin;
1750   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1751   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1752   PetscCall(DMGetCoordinateDim(dm, &cdim));
1753   switch (ct) {
1754   case DM_POLYTOPE_SEGMENT:
1755   case DM_POLYTOPE_POINT_PRISM_TENSOR:
1756     switch (cdim) {
1757     case 1: {
1758       const PetscReal y  = 0.5;  /* TODO Put it in the middle of the viewport */
1759       const PetscReal dy = 0.05; /* TODO Make it a fraction of the total length */
1760 
1761       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y, PetscRealPart(coords[1]), y, PETSC_DRAW_BLACK));
1762       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y + dy, PetscRealPart(coords[0]), y - dy, PETSC_DRAW_BLACK));
1763       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[1]), y + dy, PetscRealPart(coords[1]), y - dy, PETSC_DRAW_BLACK));
1764     } break;
1765     case 2: {
1766       const PetscReal dx = (PetscRealPart(coords[3]) - PetscRealPart(coords[1]));
1767       const PetscReal dy = (PetscRealPart(coords[2]) - PetscRealPart(coords[0]));
1768       const PetscReal l  = 0.1 / PetscSqrtReal(dx * dx + dy * dy);
1769 
1770       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1771       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));
1772       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));
1773     } break;
1774     default:
1775       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of dimension %" PetscInt_FMT, cdim);
1776     }
1777     break;
1778   case DM_POLYTOPE_TRIANGLE:
1779     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));
1780     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1781     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1782     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1783     break;
1784   case DM_POLYTOPE_QUADRILATERAL:
1785     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));
1786     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));
1787     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1788     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1789     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_BLACK));
1790     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1791     break;
1792   case DM_POLYTOPE_SEG_PRISM_TENSOR:
1793     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));
1794     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));
1795     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1796     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_BLACK));
1797     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1798     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1799     break;
1800   case DM_POLYTOPE_FV_GHOST:
1801     break;
1802   default:
1803     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1804   }
1805   PetscFunctionReturn(PETSC_SUCCESS);
1806 }
1807 
1808 static PetscErrorCode DrawPolygon_Private(DM dm, PetscDraw draw, PetscInt cell, PetscInt Nv, const PetscReal refVertices[], const PetscScalar coords[], PetscInt edgeDiv, PetscReal refCoords[], PetscReal edgeCoords[])
1809 {
1810   PetscReal   centroid[2] = {0., 0.};
1811   PetscMPIInt rank;
1812   PetscInt    fillColor;
1813 
1814   PetscFunctionBegin;
1815   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1816   fillColor = PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2;
1817   for (PetscInt v = 0; v < Nv; ++v) {
1818     centroid[0] += PetscRealPart(coords[v * 2 + 0]) / Nv;
1819     centroid[1] += PetscRealPart(coords[v * 2 + 1]) / Nv;
1820   }
1821   for (PetscInt e = 0; e < Nv; ++e) {
1822     refCoords[0] = refVertices[e * 2 + 0];
1823     refCoords[1] = refVertices[e * 2 + 1];
1824     for (PetscInt d = 1; d <= edgeDiv; ++d) {
1825       refCoords[d * 2 + 0] = refCoords[0] + (refVertices[(e + 1) % Nv * 2 + 0] - refCoords[0]) * d / edgeDiv;
1826       refCoords[d * 2 + 1] = refCoords[1] + (refVertices[(e + 1) % Nv * 2 + 1] - refCoords[1]) * d / edgeDiv;
1827     }
1828     PetscCall(DMPlexReferenceToCoordinates(dm, cell, edgeDiv + 1, refCoords, edgeCoords));
1829     for (PetscInt d = 0; d < edgeDiv; ++d) {
1830       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));
1831       PetscCall(PetscDrawLine(draw, edgeCoords[d * 2 + 0], edgeCoords[d * 2 + 1], edgeCoords[(d + 1) * 2 + 0], edgeCoords[(d + 1) * 2 + 1], PETSC_DRAW_BLACK));
1832     }
1833   }
1834   PetscFunctionReturn(PETSC_SUCCESS);
1835 }
1836 
1837 static PetscErrorCode DMPlexDrawCellHighOrder(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[], PetscInt edgeDiv, PetscReal refCoords[], PetscReal edgeCoords[])
1838 {
1839   DMPolytopeType ct;
1840 
1841   PetscFunctionBegin;
1842   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1843   switch (ct) {
1844   case DM_POLYTOPE_TRIANGLE: {
1845     PetscReal refVertices[6] = {-1., -1., 1., -1., -1., 1.};
1846 
1847     PetscCall(DrawPolygon_Private(dm, draw, cell, 3, refVertices, coords, edgeDiv, refCoords, edgeCoords));
1848   } break;
1849   case DM_POLYTOPE_QUADRILATERAL: {
1850     PetscReal refVertices[8] = {-1., -1., 1., -1., 1., 1., -1., 1.};
1851 
1852     PetscCall(DrawPolygon_Private(dm, draw, cell, 4, refVertices, coords, edgeDiv, refCoords, edgeCoords));
1853   } break;
1854   default:
1855     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1856   }
1857   PetscFunctionReturn(PETSC_SUCCESS);
1858 }
1859 
1860 static PetscErrorCode DMPlexView_Draw(DM dm, PetscViewer viewer)
1861 {
1862   PetscDraw    draw;
1863   DM           cdm;
1864   PetscSection coordSection;
1865   Vec          coordinates;
1866   PetscReal    xyl[3], xyr[3];
1867   PetscReal   *refCoords, *edgeCoords;
1868   PetscBool    isnull, drawAffine;
1869   PetscInt     dim, vStart, vEnd, cStart, cEnd, c, cDegree, edgeDiv;
1870 
1871   PetscFunctionBegin;
1872   PetscCall(DMGetCoordinateDim(dm, &dim));
1873   PetscCheck(dim <= 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT, dim);
1874   PetscCall(DMGetCoordinateDegree_Internal(dm, &cDegree));
1875   drawAffine = cDegree > 1 ? PETSC_FALSE : PETSC_TRUE;
1876   edgeDiv    = cDegree + 1;
1877   PetscCall(PetscOptionsGetBool(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_view_draw_affine", &drawAffine, NULL));
1878   if (!drawAffine) PetscCall(PetscMalloc2((edgeDiv + 1) * dim, &refCoords, (edgeDiv + 1) * dim, &edgeCoords));
1879   PetscCall(DMGetCoordinateDM(dm, &cdm));
1880   PetscCall(DMGetLocalSection(cdm, &coordSection));
1881   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1882   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1883   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1884 
1885   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
1886   PetscCall(PetscDrawIsNull(draw, &isnull));
1887   if (isnull) PetscFunctionReturn(PETSC_SUCCESS);
1888   PetscCall(PetscDrawSetTitle(draw, "Mesh"));
1889 
1890   PetscCall(DMGetBoundingBox(dm, xyl, xyr));
1891   PetscCall(PetscDrawSetCoordinates(draw, xyl[0], xyl[1], xyr[0], xyr[1]));
1892   PetscCall(PetscDrawClear(draw));
1893 
1894   for (c = cStart; c < cEnd; ++c) {
1895     PetscScalar       *coords = NULL;
1896     const PetscScalar *coords_arr;
1897     PetscInt           numCoords;
1898     PetscBool          isDG;
1899 
1900     PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
1901     if (drawAffine) PetscCall(DMPlexDrawCell(dm, draw, c, coords));
1902     else PetscCall(DMPlexDrawCellHighOrder(dm, draw, c, coords, edgeDiv, refCoords, edgeCoords));
1903     PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
1904   }
1905   if (!drawAffine) PetscCall(PetscFree2(refCoords, edgeCoords));
1906   PetscCall(PetscDrawFlush(draw));
1907   PetscCall(PetscDrawPause(draw));
1908   PetscCall(PetscDrawSave(draw));
1909   PetscFunctionReturn(PETSC_SUCCESS);
1910 }
1911 
1912 static PetscErrorCode DMPlexCreateHighOrderSurrogate_Internal(DM dm, DM *hdm)
1913 {
1914   DM           odm = dm, rdm = dm, cdm;
1915   PetscFE      fe;
1916   PetscSpace   sp;
1917   PetscClassId id;
1918   PetscInt     degree;
1919   PetscBool    hoView = PETSC_TRUE;
1920 
1921   PetscFunctionBegin;
1922   PetscObjectOptionsBegin((PetscObject)dm);
1923   PetscCall(PetscOptionsBool("-dm_plex_high_order_view", "Subsample to view meshes with high order coordinates", "DMPlexCreateHighOrderSurrogate_Internal", hoView, &hoView, NULL));
1924   PetscOptionsEnd();
1925   PetscCall(PetscObjectReference((PetscObject)dm));
1926   *hdm = dm;
1927   if (!hoView) PetscFunctionReturn(PETSC_SUCCESS);
1928   PetscCall(DMGetCoordinateDM(dm, &cdm));
1929   PetscCall(DMGetField(cdm, 0, NULL, (PetscObject *)&fe));
1930   PetscCall(PetscObjectGetClassId((PetscObject)fe, &id));
1931   if (id != PETSCFE_CLASSID) PetscFunctionReturn(PETSC_SUCCESS);
1932   PetscCall(PetscFEGetBasisSpace(fe, &sp));
1933   PetscCall(PetscSpaceGetDegree(sp, &degree, NULL));
1934   for (PetscInt r = 0, rd = PetscCeilReal(((PetscReal)degree) / 2.); r < (PetscInt)PetscCeilReal(PetscLog2Real(degree)); ++r, rd = PetscCeilReal(((PetscReal)rd) / 2.)) {
1935     DM  cdm, rcdm;
1936     Mat In;
1937     Vec cl, rcl;
1938 
1939     PetscCall(DMRefine(odm, PetscObjectComm((PetscObject)odm), &rdm));
1940     PetscCall(DMPlexCreateCoordinateSpace(rdm, rd, PETSC_FALSE, NULL));
1941     PetscCall(PetscObjectSetName((PetscObject)rdm, "Refined Mesh with Linear Coordinates"));
1942     PetscCall(DMGetCoordinateDM(odm, &cdm));
1943     PetscCall(DMGetCoordinateDM(rdm, &rcdm));
1944     PetscCall(DMGetCoordinatesLocal(odm, &cl));
1945     PetscCall(DMGetCoordinatesLocal(rdm, &rcl));
1946     PetscCall(DMSetCoarseDM(rcdm, cdm));
1947     PetscCall(DMCreateInterpolation(cdm, rcdm, &In, NULL));
1948     PetscCall(MatMult(In, cl, rcl));
1949     PetscCall(MatDestroy(&In));
1950     PetscCall(DMSetCoordinatesLocal(rdm, rcl));
1951     PetscCall(DMDestroy(&odm));
1952     odm = rdm;
1953   }
1954   *hdm = rdm;
1955   PetscFunctionReturn(PETSC_SUCCESS);
1956 }
1957 
1958 #if defined(PETSC_HAVE_EXODUSII)
1959   #include <exodusII.h>
1960   #include <petscviewerexodusii.h>
1961 #endif
1962 
1963 PetscErrorCode DMView_Plex(DM dm, PetscViewer viewer)
1964 {
1965   PetscBool iascii, ishdf5, isvtk, isdraw, flg, isglvis, isexodus, iscgns;
1966   char      name[PETSC_MAX_PATH_LEN];
1967 
1968   PetscFunctionBegin;
1969   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1970   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1971   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &iascii));
1972   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
1973   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1974   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
1975   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
1976   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodus));
1977   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
1978   if (iascii) {
1979     PetscViewerFormat format;
1980     PetscCall(PetscViewerGetFormat(viewer, &format));
1981     if (format == PETSC_VIEWER_ASCII_GLVIS) PetscCall(DMPlexView_GLVis(dm, viewer));
1982     else PetscCall(DMPlexView_Ascii(dm, viewer));
1983   } else if (ishdf5) {
1984 #if defined(PETSC_HAVE_HDF5)
1985     PetscCall(DMPlexView_HDF5_Internal(dm, viewer));
1986 #else
1987     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1988 #endif
1989   } else if (isvtk) {
1990     PetscCall(DMPlexVTKWriteAll((PetscObject)dm, viewer));
1991   } else if (isdraw) {
1992     DM hdm;
1993 
1994     PetscCall(DMPlexCreateHighOrderSurrogate_Internal(dm, &hdm));
1995     PetscCall(DMPlexView_Draw(hdm, viewer));
1996     PetscCall(DMDestroy(&hdm));
1997   } else if (isglvis) {
1998     PetscCall(DMPlexView_GLVis(dm, viewer));
1999 #if defined(PETSC_HAVE_EXODUSII)
2000   } else if (isexodus) {
2001     /*
2002       exodusII requires that all sets be part of exactly one cell set.
2003       If the dm does not have a "Cell Sets" label defined, we create one
2004       with ID 1, containing all cells.
2005       Note that if the Cell Sets label is defined but does not cover all cells,
2006       we may still have a problem. This should probably be checked here or in the viewer;
2007     */
2008     PetscInt numCS;
2009     PetscCall(DMGetLabelSize(dm, "Cell Sets", &numCS));
2010     if (!numCS) {
2011       PetscInt cStart, cEnd, c;
2012       PetscCall(DMCreateLabel(dm, "Cell Sets"));
2013       PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
2014       for (c = cStart; c < cEnd; ++c) PetscCall(DMSetLabelValue(dm, "Cell Sets", c, 1));
2015     }
2016     PetscCall(DMView_PlexExodusII(dm, viewer));
2017 #endif
2018 #if defined(PETSC_HAVE_CGNS)
2019   } else if (iscgns) {
2020     PetscCall(DMView_PlexCGNS(dm, viewer));
2021 #endif
2022   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex writing", ((PetscObject)viewer)->type_name);
2023   /* Optionally view the partition */
2024   PetscCall(PetscOptionsHasName(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_partition_view", &flg));
2025   if (flg) {
2026     Vec ranks;
2027     PetscCall(DMPlexCreateRankField(dm, &ranks));
2028     PetscCall(VecView(ranks, viewer));
2029     PetscCall(VecDestroy(&ranks));
2030   }
2031   /* Optionally view a label */
2032   PetscCall(PetscOptionsGetString(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_label_view", name, sizeof(name), &flg));
2033   if (flg) {
2034     DMLabel label;
2035     Vec     val;
2036 
2037     PetscCall(DMGetLabel(dm, name, &label));
2038     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Label %s provided to -dm_label_view does not exist in this DM", name);
2039     PetscCall(DMPlexCreateLabelField(dm, label, &val));
2040     PetscCall(VecView(val, viewer));
2041     PetscCall(VecDestroy(&val));
2042   }
2043   PetscFunctionReturn(PETSC_SUCCESS);
2044 }
2045 
2046 /*@
2047   DMPlexTopologyView - Saves a `DMPLEX` topology into a file
2048 
2049   Collective
2050 
2051   Input Parameters:
2052 + dm     - The `DM` whose topology is to be saved
2053 - viewer - The `PetscViewer` to save it in
2054 
2055   Level: advanced
2056 
2057 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexTopologyLoad()`, `PetscViewer`
2058 @*/
2059 PetscErrorCode DMPlexTopologyView(DM dm, PetscViewer viewer)
2060 {
2061   PetscBool ishdf5;
2062 
2063   PetscFunctionBegin;
2064   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2065   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2066   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2067   PetscCall(PetscLogEventBegin(DMPLEX_TopologyView, viewer, 0, 0, 0));
2068   if (ishdf5) {
2069 #if defined(PETSC_HAVE_HDF5)
2070     PetscViewerFormat format;
2071     PetscCall(PetscViewerGetFormat(viewer, &format));
2072     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2073       IS globalPointNumbering;
2074 
2075       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
2076       PetscCall(DMPlexTopologyView_HDF5_Internal(dm, globalPointNumbering, viewer));
2077       PetscCall(ISDestroy(&globalPointNumbering));
2078     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
2079 #else
2080     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2081 #endif
2082   }
2083   PetscCall(PetscLogEventEnd(DMPLEX_TopologyView, viewer, 0, 0, 0));
2084   PetscFunctionReturn(PETSC_SUCCESS);
2085 }
2086 
2087 /*@
2088   DMPlexCoordinatesView - Saves `DMPLEX` coordinates into a file
2089 
2090   Collective
2091 
2092   Input Parameters:
2093 + dm     - The `DM` whose coordinates are to be saved
2094 - viewer - The `PetscViewer` for saving
2095 
2096   Level: advanced
2097 
2098 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexLabelsView()`, `DMPlexCoordinatesLoad()`, `PetscViewer`
2099 @*/
2100 PetscErrorCode DMPlexCoordinatesView(DM dm, PetscViewer viewer)
2101 {
2102   PetscBool ishdf5;
2103 
2104   PetscFunctionBegin;
2105   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2106   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2107   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2108   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
2109   if (ishdf5) {
2110 #if defined(PETSC_HAVE_HDF5)
2111     PetscViewerFormat format;
2112     PetscCall(PetscViewerGetFormat(viewer, &format));
2113     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2114       PetscCall(DMPlexCoordinatesView_HDF5_Internal(dm, viewer));
2115     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
2116 #else
2117     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2118 #endif
2119   }
2120   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
2121   PetscFunctionReturn(PETSC_SUCCESS);
2122 }
2123 
2124 /*@
2125   DMPlexLabelsView - Saves `DMPLEX` labels into a file
2126 
2127   Collective
2128 
2129   Input Parameters:
2130 + dm     - The `DM` whose labels are to be saved
2131 - viewer - The `PetscViewer` for saving
2132 
2133   Level: advanced
2134 
2135 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsLoad()`, `PetscViewer`
2136 @*/
2137 PetscErrorCode DMPlexLabelsView(DM dm, PetscViewer viewer)
2138 {
2139   PetscBool ishdf5;
2140 
2141   PetscFunctionBegin;
2142   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2143   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2144   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2145   PetscCall(PetscLogEventBegin(DMPLEX_LabelsView, viewer, 0, 0, 0));
2146   if (ishdf5) {
2147 #if defined(PETSC_HAVE_HDF5)
2148     IS                globalPointNumbering;
2149     PetscViewerFormat format;
2150 
2151     PetscCall(PetscViewerGetFormat(viewer, &format));
2152     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2153       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
2154       PetscCall(DMPlexLabelsView_HDF5_Internal(dm, globalPointNumbering, viewer));
2155       PetscCall(ISDestroy(&globalPointNumbering));
2156     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2157 #else
2158     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2159 #endif
2160   }
2161   PetscCall(PetscLogEventEnd(DMPLEX_LabelsView, viewer, 0, 0, 0));
2162   PetscFunctionReturn(PETSC_SUCCESS);
2163 }
2164 
2165 /*@
2166   DMPlexSectionView - Saves a section associated with a `DMPLEX`
2167 
2168   Collective
2169 
2170   Input Parameters:
2171 + dm        - The `DM` that contains the topology on which the section to be saved is defined
2172 . viewer    - The `PetscViewer` for saving
2173 - sectiondm - The `DM` that contains the section to be saved, can be `NULL`
2174 
2175   Level: advanced
2176 
2177   Notes:
2178   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.
2179 
2180   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.
2181 
2182 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`, `PetscSectionView()`, `DMPlexSectionLoad()`, `PetscViewer`
2183 @*/
2184 PetscErrorCode DMPlexSectionView(DM dm, PetscViewer viewer, DM sectiondm)
2185 {
2186   PetscBool ishdf5;
2187 
2188   PetscFunctionBegin;
2189   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2190   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2191   if (!sectiondm) sectiondm = dm;
2192   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2193   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2194   PetscCall(PetscLogEventBegin(DMPLEX_SectionView, viewer, 0, 0, 0));
2195   if (ishdf5) {
2196 #if defined(PETSC_HAVE_HDF5)
2197     PetscCall(DMPlexSectionView_HDF5_Internal(dm, viewer, sectiondm));
2198 #else
2199     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2200 #endif
2201   }
2202   PetscCall(PetscLogEventEnd(DMPLEX_SectionView, viewer, 0, 0, 0));
2203   PetscFunctionReturn(PETSC_SUCCESS);
2204 }
2205 
2206 /*@
2207   DMPlexGlobalVectorView - Saves a global vector
2208 
2209   Collective
2210 
2211   Input Parameters:
2212 + dm        - The `DM` that represents the topology
2213 . viewer    - The `PetscViewer` to save data with
2214 . sectiondm - The `DM` that contains the global section on which vec is defined, can be `NULL`
2215 - vec       - The global vector to be saved
2216 
2217   Level: advanced
2218 
2219   Notes:
2220   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.
2221 
2222   Calling sequence:
2223 .vb
2224        DMCreate(PETSC_COMM_WORLD, &dm);
2225        DMSetType(dm, DMPLEX);
2226        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2227        DMClone(dm, &sectiondm);
2228        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2229        PetscSectionCreate(PETSC_COMM_WORLD, &section);
2230        DMPlexGetChart(sectiondm, &pStart, &pEnd);
2231        PetscSectionSetChart(section, pStart, pEnd);
2232        PetscSectionSetUp(section);
2233        DMSetLocalSection(sectiondm, section);
2234        PetscSectionDestroy(&section);
2235        DMGetGlobalVector(sectiondm, &vec);
2236        PetscObjectSetName((PetscObject)vec, "vec_name");
2237        DMPlexTopologyView(dm, viewer);
2238        DMPlexSectionView(dm, viewer, sectiondm);
2239        DMPlexGlobalVectorView(dm, viewer, sectiondm, vec);
2240        DMRestoreGlobalVector(sectiondm, &vec);
2241        DMDestroy(&sectiondm);
2242        DMDestroy(&dm);
2243 .ve
2244 
2245 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexLocalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2246 @*/
2247 PetscErrorCode DMPlexGlobalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2248 {
2249   PetscBool ishdf5;
2250 
2251   PetscFunctionBegin;
2252   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2253   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2254   if (!sectiondm) sectiondm = dm;
2255   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2256   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2257   /* Check consistency */
2258   {
2259     PetscSection section;
2260     PetscBool    includesConstraints;
2261     PetscInt     m, m1;
2262 
2263     PetscCall(VecGetLocalSize(vec, &m1));
2264     PetscCall(DMGetGlobalSection(sectiondm, &section));
2265     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2266     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2267     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2268     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2269   }
2270   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2271   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorView, viewer, 0, 0, 0));
2272   if (ishdf5) {
2273 #if defined(PETSC_HAVE_HDF5)
2274     PetscCall(DMPlexGlobalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2275 #else
2276     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2277 #endif
2278   }
2279   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorView, viewer, 0, 0, 0));
2280   PetscFunctionReturn(PETSC_SUCCESS);
2281 }
2282 
2283 /*@
2284   DMPlexLocalVectorView - Saves a local vector
2285 
2286   Collective
2287 
2288   Input Parameters:
2289 + dm        - The `DM` that represents the topology
2290 . viewer    - The `PetscViewer` to save data with
2291 . sectiondm - The `DM` that contains the local section on which `vec` is defined, can be `NULL`
2292 - vec       - The local vector to be saved
2293 
2294   Level: advanced
2295 
2296   Note:
2297   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.
2298 
2299   Calling sequence:
2300 .vb
2301        DMCreate(PETSC_COMM_WORLD, &dm);
2302        DMSetType(dm, DMPLEX);
2303        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2304        DMClone(dm, &sectiondm);
2305        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2306        PetscSectionCreate(PETSC_COMM_WORLD, &section);
2307        DMPlexGetChart(sectiondm, &pStart, &pEnd);
2308        PetscSectionSetChart(section, pStart, pEnd);
2309        PetscSectionSetUp(section);
2310        DMSetLocalSection(sectiondm, section);
2311        DMGetLocalVector(sectiondm, &vec);
2312        PetscObjectSetName((PetscObject)vec, "vec_name");
2313        DMPlexTopologyView(dm, viewer);
2314        DMPlexSectionView(dm, viewer, sectiondm);
2315        DMPlexLocalVectorView(dm, viewer, sectiondm, vec);
2316        DMRestoreLocalVector(sectiondm, &vec);
2317        DMDestroy(&sectiondm);
2318        DMDestroy(&dm);
2319 .ve
2320 
2321 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexGlobalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2322 @*/
2323 PetscErrorCode DMPlexLocalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2324 {
2325   PetscBool ishdf5;
2326 
2327   PetscFunctionBegin;
2328   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2329   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2330   if (!sectiondm) sectiondm = dm;
2331   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2332   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2333   /* Check consistency */
2334   {
2335     PetscSection section;
2336     PetscBool    includesConstraints;
2337     PetscInt     m, m1;
2338 
2339     PetscCall(VecGetLocalSize(vec, &m1));
2340     PetscCall(DMGetLocalSection(sectiondm, &section));
2341     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2342     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2343     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2344     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2345   }
2346   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2347   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorView, viewer, 0, 0, 0));
2348   if (ishdf5) {
2349 #if defined(PETSC_HAVE_HDF5)
2350     PetscCall(DMPlexLocalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2351 #else
2352     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2353 #endif
2354   }
2355   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorView, viewer, 0, 0, 0));
2356   PetscFunctionReturn(PETSC_SUCCESS);
2357 }
2358 
2359 PetscErrorCode DMLoad_Plex(DM dm, PetscViewer viewer)
2360 {
2361   PetscBool ishdf5;
2362 
2363   PetscFunctionBegin;
2364   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2365   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2366   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2367   if (ishdf5) {
2368 #if defined(PETSC_HAVE_HDF5)
2369     PetscViewerFormat format;
2370     PetscCall(PetscViewerGetFormat(viewer, &format));
2371     if (format == PETSC_VIEWER_HDF5_XDMF || format == PETSC_VIEWER_HDF5_VIZ) {
2372       PetscCall(DMPlexLoad_HDF5_Xdmf_Internal(dm, viewer));
2373     } else if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2374       PetscCall(DMPlexLoad_HDF5_Internal(dm, viewer));
2375     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2376     PetscFunctionReturn(PETSC_SUCCESS);
2377 #else
2378     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2379 #endif
2380   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex loading", ((PetscObject)viewer)->type_name);
2381 }
2382 
2383 /*@
2384   DMPlexTopologyLoad - Loads a topology into a `DMPLEX`
2385 
2386   Collective
2387 
2388   Input Parameters:
2389 + dm     - The `DM` into which the topology is loaded
2390 - viewer - The `PetscViewer` for the saved topology
2391 
2392   Output Parameter:
2393 . 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;
2394   `NULL` if unneeded
2395 
2396   Level: advanced
2397 
2398 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2399           `PetscViewer`, `PetscSF`
2400 @*/
2401 PetscErrorCode DMPlexTopologyLoad(DM dm, PetscViewer viewer, PetscSF *globalToLocalPointSF)
2402 {
2403   PetscBool ishdf5;
2404 
2405   PetscFunctionBegin;
2406   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2407   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2408   if (globalToLocalPointSF) PetscAssertPointer(globalToLocalPointSF, 3);
2409   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2410   PetscCall(PetscLogEventBegin(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2411   if (ishdf5) {
2412 #if defined(PETSC_HAVE_HDF5)
2413     PetscViewerFormat format;
2414     PetscCall(PetscViewerGetFormat(viewer, &format));
2415     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2416       PetscCall(DMPlexTopologyLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2417     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2418 #else
2419     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2420 #endif
2421   }
2422   PetscCall(PetscLogEventEnd(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2423   PetscFunctionReturn(PETSC_SUCCESS);
2424 }
2425 
2426 /*@
2427   DMPlexCoordinatesLoad - Loads coordinates into a `DMPLEX`
2428 
2429   Collective
2430 
2431   Input Parameters:
2432 + dm                   - The `DM` into which the coordinates are loaded
2433 . viewer               - The `PetscViewer` for the saved coordinates
2434 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad()` when loading dm from viewer
2435 
2436   Level: advanced
2437 
2438 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2439           `PetscSF`, `PetscViewer`
2440 @*/
2441 PetscErrorCode DMPlexCoordinatesLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2442 {
2443   PetscBool ishdf5;
2444 
2445   PetscFunctionBegin;
2446   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2447   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2448   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2449   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2450   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesLoad, viewer, 0, 0, 0));
2451   if (ishdf5) {
2452 #if defined(PETSC_HAVE_HDF5)
2453     PetscViewerFormat format;
2454     PetscCall(PetscViewerGetFormat(viewer, &format));
2455     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2456       PetscCall(DMPlexCoordinatesLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2457     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2458 #else
2459     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2460 #endif
2461   }
2462   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesLoad, viewer, 0, 0, 0));
2463   PetscFunctionReturn(PETSC_SUCCESS);
2464 }
2465 
2466 /*@
2467   DMPlexLabelsLoad - Loads labels into a `DMPLEX`
2468 
2469   Collective
2470 
2471   Input Parameters:
2472 + dm                   - The `DM` into which the labels are loaded
2473 . viewer               - The `PetscViewer` for the saved labels
2474 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad()` when loading `dm` from viewer
2475 
2476   Level: advanced
2477 
2478   Note:
2479   The `PetscSF` argument must not be `NULL` if the `DM` is distributed, otherwise an error occurs.
2480 
2481 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2482           `PetscSF`, `PetscViewer`
2483 @*/
2484 PetscErrorCode DMPlexLabelsLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2485 {
2486   PetscBool ishdf5;
2487 
2488   PetscFunctionBegin;
2489   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2490   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2491   if (globalToLocalPointSF) PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2492   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2493   PetscCall(PetscLogEventBegin(DMPLEX_LabelsLoad, viewer, 0, 0, 0));
2494   if (ishdf5) {
2495 #if defined(PETSC_HAVE_HDF5)
2496     PetscViewerFormat format;
2497 
2498     PetscCall(PetscViewerGetFormat(viewer, &format));
2499     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2500       PetscCall(DMPlexLabelsLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2501     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2502 #else
2503     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2504 #endif
2505   }
2506   PetscCall(PetscLogEventEnd(DMPLEX_LabelsLoad, viewer, 0, 0, 0));
2507   PetscFunctionReturn(PETSC_SUCCESS);
2508 }
2509 
2510 /*@
2511   DMPlexSectionLoad - Loads section into a `DMPLEX`
2512 
2513   Collective
2514 
2515   Input Parameters:
2516 + dm                   - The `DM` that represents the topology
2517 . viewer               - The `PetscViewer` that represents the on-disk section (sectionA)
2518 . sectiondm            - The `DM` into which the on-disk section (sectionA) is migrated, can be `NULL`
2519 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad(`) when loading dm from viewer
2520 
2521   Output Parameters:
2522 + 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)
2523 - 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)
2524 
2525   Level: advanced
2526 
2527   Notes:
2528   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.
2529 
2530   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.
2531 
2532   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.
2533 
2534   Example using 2 processes:
2535 .vb
2536   NX (number of points on dm): 4
2537   sectionA                   : the on-disk section
2538   vecA                       : a vector associated with sectionA
2539   sectionB                   : sectiondm's local section constructed in this function
2540   vecB (local)               : a vector associated with sectiondm's local section
2541   vecB (global)              : a vector associated with sectiondm's global section
2542 
2543                                      rank 0    rank 1
2544   vecA (global)                  : [.0 .4 .1 | .2 .3]        <- to be loaded in DMPlexGlobalVectorLoad() or DMPlexLocalVectorLoad()
2545   sectionA->atlasOff             :       0 2 | 1             <- loaded in PetscSectionLoad()
2546   sectionA->atlasDof             :       1 3 | 1             <- loaded in PetscSectionLoad()
2547   sectionA's global point numbers:       0 2 | 3             <- loaded in DMPlexSectionLoad()
2548   [0, NX)                        :       0 1 | 2 3           <- conceptual partition used in globalToLocalPointSF
2549   sectionB's global point numbers:     0 1 3 | 3 2           <- associated with [0, NX) by globalToLocalPointSF
2550   sectionB->atlasDof             :     1 0 1 | 1 3
2551   sectionB->atlasOff (no perm)   :     0 1 1 | 0 1
2552   vecB (local)                   :   [.0 .4] | [.4 .1 .2 .3] <- to be constructed by calling DMPlexLocalVectorLoad() with localDofSF
2553   vecB (global)                  :    [.0 .4 | .1 .2 .3]     <- to be constructed by calling DMPlexGlobalVectorLoad() with globalDofSF
2554 .ve
2555   where "|" represents a partition of loaded data, and global point 3 is assumed to be owned by rank 0.
2556 
2557 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`, `PetscSectionLoad()`, `DMPlexSectionView()`, `PetscSF`, `PetscViewer`
2558 @*/
2559 PetscErrorCode DMPlexSectionLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF globalToLocalPointSF, PetscSF *globalDofSF, PetscSF *localDofSF)
2560 {
2561   PetscBool ishdf5;
2562 
2563   PetscFunctionBegin;
2564   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2565   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2566   if (!sectiondm) sectiondm = dm;
2567   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2568   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 4);
2569   if (globalDofSF) PetscAssertPointer(globalDofSF, 5);
2570   if (localDofSF) PetscAssertPointer(localDofSF, 6);
2571   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2572   PetscCall(PetscLogEventBegin(DMPLEX_SectionLoad, viewer, 0, 0, 0));
2573   if (ishdf5) {
2574 #if defined(PETSC_HAVE_HDF5)
2575     PetscCall(DMPlexSectionLoad_HDF5_Internal(dm, viewer, sectiondm, globalToLocalPointSF, globalDofSF, localDofSF));
2576 #else
2577     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2578 #endif
2579   }
2580   PetscCall(PetscLogEventEnd(DMPLEX_SectionLoad, viewer, 0, 0, 0));
2581   PetscFunctionReturn(PETSC_SUCCESS);
2582 }
2583 
2584 /*@
2585   DMPlexGlobalVectorLoad - Loads on-disk vector data into a global vector
2586 
2587   Collective
2588 
2589   Input Parameters:
2590 + dm        - The `DM` that represents the topology
2591 . viewer    - The `PetscViewer` that represents the on-disk vector data
2592 . sectiondm - The `DM` that contains the global section on which vec is defined, can be `NULL`
2593 . sf        - The `PetscSF` that migrates the on-disk vector data into vec
2594 - vec       - The global vector to set values of
2595 
2596   Level: advanced
2597 
2598   Notes:
2599   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.
2600 
2601   Calling sequence:
2602 .vb
2603        DMCreate(PETSC_COMM_WORLD, &dm);
2604        DMSetType(dm, DMPLEX);
2605        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2606        DMPlexTopologyLoad(dm, viewer, &sfX);
2607        DMClone(dm, &sectiondm);
2608        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2609        DMPlexSectionLoad(dm, viewer, sectiondm, sfX, &gsf, NULL);
2610        DMGetGlobalVector(sectiondm, &vec);
2611        PetscObjectSetName((PetscObject)vec, "vec_name");
2612        DMPlexGlobalVectorLoad(dm, viewer, sectiondm, gsf, vec);
2613        DMRestoreGlobalVector(sectiondm, &vec);
2614        PetscSFDestroy(&gsf);
2615        PetscSFDestroy(&sfX);
2616        DMDestroy(&sectiondm);
2617        DMDestroy(&dm);
2618 .ve
2619 
2620 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexLocalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`,
2621           `PetscSF`, `PetscViewer`
2622 @*/
2623 PetscErrorCode DMPlexGlobalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2624 {
2625   PetscBool ishdf5;
2626 
2627   PetscFunctionBegin;
2628   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2629   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2630   if (!sectiondm) sectiondm = dm;
2631   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2632   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2633   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2634   /* Check consistency */
2635   {
2636     PetscSection section;
2637     PetscBool    includesConstraints;
2638     PetscInt     m, m1;
2639 
2640     PetscCall(VecGetLocalSize(vec, &m1));
2641     PetscCall(DMGetGlobalSection(sectiondm, &section));
2642     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2643     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2644     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2645     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2646   }
2647   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2648   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorLoad, viewer, 0, 0, 0));
2649   if (ishdf5) {
2650 #if defined(PETSC_HAVE_HDF5)
2651     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2652 #else
2653     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2654 #endif
2655   }
2656   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorLoad, viewer, 0, 0, 0));
2657   PetscFunctionReturn(PETSC_SUCCESS);
2658 }
2659 
2660 /*@
2661   DMPlexLocalVectorLoad - Loads on-disk vector data into a local vector
2662 
2663   Collective
2664 
2665   Input Parameters:
2666 + dm        - The `DM` that represents the topology
2667 . viewer    - The `PetscViewer` that represents the on-disk vector data
2668 . sectiondm - The `DM` that contains the local section on which vec is defined, can be `NULL`
2669 . sf        - The `PetscSF` that migrates the on-disk vector data into vec
2670 - vec       - The local vector to set values of
2671 
2672   Level: advanced
2673 
2674   Notes:
2675   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.
2676 
2677   Calling sequence:
2678 .vb
2679        DMCreate(PETSC_COMM_WORLD, &dm);
2680        DMSetType(dm, DMPLEX);
2681        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2682        DMPlexTopologyLoad(dm, viewer, &sfX);
2683        DMClone(dm, &sectiondm);
2684        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2685        DMPlexSectionLoad(dm, viewer, sectiondm, sfX, NULL, &lsf);
2686        DMGetLocalVector(sectiondm, &vec);
2687        PetscObjectSetName((PetscObject)vec, "vec_name");
2688        DMPlexLocalVectorLoad(dm, viewer, sectiondm, lsf, vec);
2689        DMRestoreLocalVector(sectiondm, &vec);
2690        PetscSFDestroy(&lsf);
2691        PetscSFDestroy(&sfX);
2692        DMDestroy(&sectiondm);
2693        DMDestroy(&dm);
2694 .ve
2695 
2696 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`,
2697           `PetscSF`, `PetscViewer`
2698 @*/
2699 PetscErrorCode DMPlexLocalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2700 {
2701   PetscBool ishdf5;
2702 
2703   PetscFunctionBegin;
2704   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2705   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2706   if (!sectiondm) sectiondm = dm;
2707   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2708   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2709   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2710   /* Check consistency */
2711   {
2712     PetscSection section;
2713     PetscBool    includesConstraints;
2714     PetscInt     m, m1;
2715 
2716     PetscCall(VecGetLocalSize(vec, &m1));
2717     PetscCall(DMGetLocalSection(sectiondm, &section));
2718     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2719     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2720     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2721     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2722   }
2723   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2724   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorLoad, viewer, 0, 0, 0));
2725   if (ishdf5) {
2726 #if defined(PETSC_HAVE_HDF5)
2727     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2728 #else
2729     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2730 #endif
2731   }
2732   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorLoad, viewer, 0, 0, 0));
2733   PetscFunctionReturn(PETSC_SUCCESS);
2734 }
2735 
2736 PetscErrorCode DMDestroy_Plex(DM dm)
2737 {
2738   DM_Plex *mesh = (DM_Plex *)dm->data;
2739 
2740   PetscFunctionBegin;
2741   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMSetUpGLVisViewer_C", NULL));
2742   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertBoundaryValues_C", NULL));
2743   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMCreateNeumannOverlap_C", NULL));
2744   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMInterpolateSolution_C", NULL));
2745   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertTimeDerivativeBoundaryValues_C", NULL));
2746   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", NULL));
2747   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeGetDefault_C", NULL));
2748   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeSetDefault_C", NULL));
2749   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "MatComputeNeumannOverlap_C", NULL));
2750   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderGetDefault_C", NULL));
2751   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderSetDefault_C", NULL));
2752   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionGetDefault_C", NULL));
2753   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionSetDefault_C", NULL));
2754   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionGetType_C", NULL));
2755   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionSetType_C", NULL));
2756   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", NULL));
2757   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexSetOverlap_C", NULL));
2758   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetUseCeed_C", NULL));
2759   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexSetUseCeed_C", NULL));
2760   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMGetIsoperiodicPointSF_C", NULL));
2761   if (--mesh->refct > 0) PetscFunctionReturn(PETSC_SUCCESS);
2762   PetscCall(PetscSectionDestroy(&mesh->coneSection));
2763   PetscCall(PetscFree(mesh->cones));
2764   PetscCall(PetscFree(mesh->coneOrientations));
2765   PetscCall(PetscSectionDestroy(&mesh->supportSection));
2766   PetscCall(PetscSectionDestroy(&mesh->subdomainSection));
2767   PetscCall(PetscFree(mesh->supports));
2768   PetscCall(PetscFree(mesh->cellTypes));
2769   PetscCall(DMPlexTransformDestroy(&mesh->tr));
2770   PetscCall(PetscFree(mesh->tetgenOpts));
2771   PetscCall(PetscFree(mesh->triangleOpts));
2772   PetscCall(PetscFree(mesh->transformType));
2773   PetscCall(PetscFree(mesh->distributionName));
2774   PetscCall(PetscPartitionerDestroy(&mesh->partitioner));
2775   PetscCall(DMLabelDestroy(&mesh->subpointMap));
2776   PetscCall(ISDestroy(&mesh->subpointIS));
2777   PetscCall(ISDestroy(&mesh->globalVertexNumbers));
2778   PetscCall(ISDestroy(&mesh->globalCellNumbers));
2779   if (mesh->periodic.face_sfs) {
2780     for (PetscInt i = 0; i < mesh->periodic.num_face_sfs; i++) PetscCall(PetscSFDestroy(&mesh->periodic.face_sfs[i]));
2781     PetscCall(PetscFree(mesh->periodic.face_sfs));
2782   }
2783   PetscCall(PetscSFDestroy(&mesh->periodic.composed_sf));
2784   if (mesh->periodic.periodic_points) {
2785     for (PetscInt i = 0; i < mesh->periodic.num_face_sfs; i++) PetscCall(ISDestroy(&mesh->periodic.periodic_points[i]));
2786     PetscCall(PetscFree(mesh->periodic.periodic_points));
2787   }
2788   if (mesh->periodic.transform) PetscCall(PetscFree(mesh->periodic.transform));
2789   PetscCall(PetscSectionDestroy(&mesh->anchorSection));
2790   PetscCall(ISDestroy(&mesh->anchorIS));
2791   PetscCall(PetscSectionDestroy(&mesh->parentSection));
2792   PetscCall(PetscFree(mesh->parents));
2793   PetscCall(PetscFree(mesh->childIDs));
2794   PetscCall(PetscSectionDestroy(&mesh->childSection));
2795   PetscCall(PetscFree(mesh->children));
2796   PetscCall(DMDestroy(&mesh->referenceTree));
2797   PetscCall(PetscGridHashDestroy(&mesh->lbox));
2798   PetscCall(PetscFree(mesh->neighbors));
2799   if (mesh->metricCtx) PetscCall(PetscFree(mesh->metricCtx));
2800   if (mesh->nonempty_comm != MPI_COMM_NULL && mesh->nonempty_comm != MPI_COMM_SELF) PetscCallMPI(MPI_Comm_free(&mesh->nonempty_comm));
2801   /* This was originally freed in DMDestroy(), but that prevents reference counting of backend objects */
2802   PetscCall(PetscFree(mesh));
2803   PetscFunctionReturn(PETSC_SUCCESS);
2804 }
2805 
2806 PetscErrorCode DMCreateMatrix_Plex(DM dm, Mat *J)
2807 {
2808   PetscSection           sectionGlobal, sectionLocal;
2809   PetscInt               bs = -1, mbs;
2810   PetscInt               localSize, localStart = 0;
2811   PetscBool              isShell, isBlock, isSeqBlock, isMPIBlock, isSymBlock, isSymSeqBlock, isSymMPIBlock, isMatIS;
2812   MatType                mtype;
2813   ISLocalToGlobalMapping ltog;
2814 
2815   PetscFunctionBegin;
2816   PetscCall(MatInitializePackage());
2817   mtype = dm->mattype;
2818   PetscCall(DMGetLocalSection(dm, &sectionLocal));
2819   PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
2820   /* PetscCall(PetscSectionGetStorageSize(sectionGlobal, &localSize)); */
2821   PetscCall(PetscSectionGetConstrainedStorageSize(sectionGlobal, &localSize));
2822   PetscCallMPI(MPI_Exscan(&localSize, &localStart, 1, MPIU_INT, MPI_SUM, PetscObjectComm((PetscObject)dm)));
2823   PetscCall(MatCreate(PetscObjectComm((PetscObject)dm), J));
2824   PetscCall(MatSetSizes(*J, localSize, localSize, PETSC_DETERMINE, PETSC_DETERMINE));
2825   PetscCall(MatSetType(*J, mtype));
2826   PetscCall(MatSetFromOptions(*J));
2827   PetscCall(MatGetBlockSize(*J, &mbs));
2828   if (mbs > 1) bs = mbs;
2829   PetscCall(PetscStrcmp(mtype, MATSHELL, &isShell));
2830   PetscCall(PetscStrcmp(mtype, MATBAIJ, &isBlock));
2831   PetscCall(PetscStrcmp(mtype, MATSEQBAIJ, &isSeqBlock));
2832   PetscCall(PetscStrcmp(mtype, MATMPIBAIJ, &isMPIBlock));
2833   PetscCall(PetscStrcmp(mtype, MATSBAIJ, &isSymBlock));
2834   PetscCall(PetscStrcmp(mtype, MATSEQSBAIJ, &isSymSeqBlock));
2835   PetscCall(PetscStrcmp(mtype, MATMPISBAIJ, &isSymMPIBlock));
2836   PetscCall(PetscStrcmp(mtype, MATIS, &isMatIS));
2837   if (!isShell) {
2838     // There are three states with pblocks, since block starts can have no dofs:
2839     // UNKNOWN) New Block:   An open block has been signalled by pblocks[p] == 1
2840     // TRUE)    Block Start: The first entry in a block has been added
2841     // FALSE)   Block Add:   An additional block entry has been added, since pblocks[p] == 0
2842     PetscBT         blst;
2843     PetscBool3      bstate     = PETSC_BOOL3_UNKNOWN;
2844     PetscBool       fillMatrix = (PetscBool)(!dm->prealloc_only && !isMatIS);
2845     const PetscInt *perm       = NULL;
2846     PetscInt       *dnz, *onz, *dnzu, *onzu, bsLocal[2], bsMinMax[2], *pblocks;
2847     PetscInt        pStart, pEnd, dof, cdof, num_fields;
2848 
2849     PetscCall(DMGetLocalToGlobalMapping(dm, &ltog));
2850     PetscCall(PetscSectionGetBlockStarts(sectionLocal, &blst));
2851     if (sectionLocal->perm) PetscCall(ISGetIndices(sectionLocal->perm, &perm));
2852 
2853     PetscCall(PetscCalloc1(localSize, &pblocks));
2854     PetscCall(PetscSectionGetChart(sectionGlobal, &pStart, &pEnd));
2855     PetscCall(PetscSectionGetNumFields(sectionGlobal, &num_fields));
2856     // We need to process in the permuted order to get block sizes right
2857     for (PetscInt point = pStart; point < pEnd; ++point) {
2858       const PetscInt p = perm ? perm[point] : point;
2859 
2860       switch (dm->blocking_type) {
2861       case DM_BLOCKING_TOPOLOGICAL_POINT: { // One block per topological point
2862         PetscInt bdof, offset;
2863 
2864         PetscCall(PetscSectionGetDof(sectionGlobal, p, &dof));
2865         PetscCall(PetscSectionGetOffset(sectionGlobal, p, &offset));
2866         PetscCall(PetscSectionGetConstraintDof(sectionGlobal, p, &cdof));
2867         if (blst && PetscBTLookup(blst, p)) bstate = PETSC_BOOL3_UNKNOWN;
2868         if (dof > 0) {
2869           // State change
2870           if (bstate == PETSC_BOOL3_UNKNOWN) bstate = PETSC_BOOL3_TRUE;
2871           else if (bstate == PETSC_BOOL3_TRUE && blst && !PetscBTLookup(blst, p)) bstate = PETSC_BOOL3_FALSE;
2872 
2873           for (PetscInt i = 0; i < dof - cdof; ++i) pblocks[offset - localStart + i] = dof - cdof;
2874           // Signal block concatenation
2875           if (bstate == PETSC_BOOL3_FALSE && dof - cdof) pblocks[offset - localStart] = -(dof - cdof);
2876         }
2877         dof  = dof < 0 ? -(dof + 1) : dof;
2878         bdof = cdof && (dof - cdof) ? 1 : dof;
2879         if (dof) {
2880           if (bs < 0) {
2881             bs = bdof;
2882           } else if (bs != bdof) {
2883             bs = 1;
2884           }
2885         }
2886       } break;
2887       case DM_BLOCKING_FIELD_NODE: {
2888         for (PetscInt field = 0; field < num_fields; field++) {
2889           PetscInt num_comp, bdof, offset;
2890           PetscCall(PetscSectionGetFieldComponents(sectionGlobal, field, &num_comp));
2891           PetscCall(PetscSectionGetFieldDof(sectionGlobal, p, field, &dof));
2892           if (dof < 0) continue;
2893           PetscCall(PetscSectionGetFieldOffset(sectionGlobal, p, field, &offset));
2894           PetscCall(PetscSectionGetFieldConstraintDof(sectionGlobal, p, field, &cdof));
2895           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);
2896           PetscInt num_nodes = dof / num_comp;
2897           for (PetscInt i = 0; i < dof - cdof; i++) pblocks[offset - localStart + i] = (dof - cdof) / num_nodes;
2898           // Handle possibly constant block size (unlikely)
2899           bdof = cdof && (dof - cdof) ? 1 : dof;
2900           if (dof) {
2901             if (bs < 0) {
2902               bs = bdof;
2903             } else if (bs != bdof) {
2904               bs = 1;
2905             }
2906           }
2907         }
2908       } break;
2909       }
2910     }
2911     if (sectionLocal->perm) PetscCall(ISRestoreIndices(sectionLocal->perm, &perm));
2912     /* Must have same blocksize on all procs (some might have no points) */
2913     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs;
2914     bsLocal[1] = bs;
2915     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
2916     if (bsMinMax[0] != bsMinMax[1]) bs = 1;
2917     else bs = bsMinMax[0];
2918     bs = PetscMax(1, bs);
2919     PetscCall(MatSetLocalToGlobalMapping(*J, ltog, ltog));
2920     if (dm->prealloc_skip) { // User will likely use MatSetPreallocationCOO(), but still set structural parameters
2921       PetscCall(MatSetBlockSize(*J, bs));
2922       PetscCall(MatSetUp(*J));
2923     } else {
2924       PetscCall(PetscCalloc4(localSize / bs, &dnz, localSize / bs, &onz, localSize / bs, &dnzu, localSize / bs, &onzu));
2925       PetscCall(DMPlexPreallocateOperator(dm, bs, dnz, onz, dnzu, onzu, *J, fillMatrix));
2926       PetscCall(PetscFree4(dnz, onz, dnzu, onzu));
2927     }
2928     if (pblocks) { // Consolidate blocks
2929       PetscInt nblocks = 0;
2930       pblocks[0]       = PetscAbs(pblocks[0]);
2931       for (PetscInt i = 0; i < localSize; i += PetscMax(1, pblocks[i])) {
2932         if (pblocks[i] == 0) continue;
2933         // Negative block size indicates the blocks should be concatenated
2934         if (pblocks[i] < 0) {
2935           pblocks[i] = -pblocks[i];
2936           pblocks[nblocks - 1] += pblocks[i];
2937         } else {
2938           pblocks[nblocks++] = pblocks[i]; // nblocks always <= i
2939         }
2940         for (PetscInt j = 1; j < pblocks[i]; j++)
2941           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);
2942       }
2943       PetscCall(MatSetVariableBlockSizes(*J, nblocks, pblocks));
2944     }
2945     PetscCall(PetscFree(pblocks));
2946   }
2947   PetscCall(MatSetDM(*J, dm));
2948   PetscFunctionReturn(PETSC_SUCCESS);
2949 }
2950 
2951 /*@
2952   DMPlexGetSubdomainSection - Returns the section associated with the subdomain
2953 
2954   Not Collective
2955 
2956   Input Parameter:
2957 . dm - The `DMPLEX`
2958 
2959   Output Parameter:
2960 . subsection - The subdomain section
2961 
2962   Level: developer
2963 
2964 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `PetscSection`
2965 @*/
2966 PetscErrorCode DMPlexGetSubdomainSection(DM dm, PetscSection *subsection)
2967 {
2968   DM_Plex *mesh = (DM_Plex *)dm->data;
2969 
2970   PetscFunctionBegin;
2971   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2972   if (!mesh->subdomainSection) {
2973     PetscSection section;
2974     PetscSF      sf;
2975 
2976     PetscCall(PetscSFCreate(PETSC_COMM_SELF, &sf));
2977     PetscCall(DMGetLocalSection(dm, &section));
2978     PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_TRUE, PETSC_FALSE, PETSC_TRUE, &mesh->subdomainSection));
2979     PetscCall(PetscSFDestroy(&sf));
2980   }
2981   *subsection = mesh->subdomainSection;
2982   PetscFunctionReturn(PETSC_SUCCESS);
2983 }
2984 
2985 /*@
2986   DMPlexGetChart - Return the interval for all mesh points [`pStart`, `pEnd`)
2987 
2988   Not Collective
2989 
2990   Input Parameter:
2991 . dm - The `DMPLEX`
2992 
2993   Output Parameters:
2994 + pStart - The first mesh point
2995 - pEnd   - The upper bound for mesh points
2996 
2997   Level: beginner
2998 
2999 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetChart()`
3000 @*/
3001 PetscErrorCode DMPlexGetChart(DM dm, PetscInt *pStart, PetscInt *pEnd)
3002 {
3003   DM_Plex *mesh = (DM_Plex *)dm->data;
3004 
3005   PetscFunctionBegin;
3006   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3007   if (mesh->tr) PetscCall(DMPlexTransformGetChart(mesh->tr, pStart, pEnd));
3008   else PetscCall(PetscSectionGetChart(mesh->coneSection, pStart, pEnd));
3009   PetscFunctionReturn(PETSC_SUCCESS);
3010 }
3011 
3012 /*@
3013   DMPlexSetChart - Set the interval for all mesh points [`pStart`, `pEnd`)
3014 
3015   Not Collective
3016 
3017   Input Parameters:
3018 + dm     - The `DMPLEX`
3019 . pStart - The first mesh point
3020 - pEnd   - The upper bound for mesh points
3021 
3022   Level: beginner
3023 
3024 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetChart()`
3025 @*/
3026 PetscErrorCode DMPlexSetChart(DM dm, PetscInt pStart, PetscInt pEnd)
3027 {
3028   DM_Plex *mesh = (DM_Plex *)dm->data;
3029 
3030   PetscFunctionBegin;
3031   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3032   PetscCall(PetscSectionSetChart(mesh->coneSection, pStart, pEnd));
3033   PetscCall(PetscSectionSetChart(mesh->supportSection, pStart, pEnd));
3034   PetscCall(PetscFree(mesh->cellTypes));
3035   PetscFunctionReturn(PETSC_SUCCESS);
3036 }
3037 
3038 /*@
3039   DMPlexGetConeSize - Return the number of in-edges for this point in the DAG
3040 
3041   Not Collective
3042 
3043   Input Parameters:
3044 + dm - The `DMPLEX`
3045 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3046 
3047   Output Parameter:
3048 . size - The cone size for point `p`
3049 
3050   Level: beginner
3051 
3052 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
3053 @*/
3054 PetscErrorCode DMPlexGetConeSize(DM dm, PetscInt p, PetscInt *size)
3055 {
3056   DM_Plex *mesh = (DM_Plex *)dm->data;
3057 
3058   PetscFunctionBegin;
3059   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3060   PetscAssertPointer(size, 3);
3061   if (mesh->tr) PetscCall(DMPlexTransformGetConeSize(mesh->tr, p, size));
3062   else PetscCall(PetscSectionGetDof(mesh->coneSection, p, size));
3063   PetscFunctionReturn(PETSC_SUCCESS);
3064 }
3065 
3066 /*@
3067   DMPlexSetConeSize - Set the number of in-edges for this point in the DAG
3068 
3069   Not Collective
3070 
3071   Input Parameters:
3072 + dm   - The `DMPLEX`
3073 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3074 - size - The cone size for point `p`
3075 
3076   Level: beginner
3077 
3078   Note:
3079   This should be called after `DMPlexSetChart()`.
3080 
3081 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetCone()`, `DMPlexCreate()`, `DMPlexGetConeSize()`, `DMPlexSetChart()`
3082 @*/
3083 PetscErrorCode DMPlexSetConeSize(DM dm, PetscInt p, PetscInt size)
3084 {
3085   DM_Plex *mesh = (DM_Plex *)dm->data;
3086 
3087   PetscFunctionBegin;
3088   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3089   PetscCheck(!mesh->tr, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Cannot call DMPlexSetConeSize() on a mesh with a transform defined.");
3090   PetscCall(PetscSectionSetDof(mesh->coneSection, p, size));
3091   PetscFunctionReturn(PETSC_SUCCESS);
3092 }
3093 
3094 /*@C
3095   DMPlexGetCone - Return the points on the in-edges for this point in the DAG
3096 
3097   Not Collective
3098 
3099   Input Parameters:
3100 + dm - The `DMPLEX`
3101 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3102 
3103   Output Parameter:
3104 . cone - An array of points which are on the in-edges for point `p`, the length of `cone` is the result of `DMPlexGetConeSize()`
3105 
3106   Level: beginner
3107 
3108   Fortran Notes:
3109   `cone` must be declared with
3110 .vb
3111   PetscInt, pointer :: cone(:)
3112 .ve
3113 
3114   You must also call `DMPlexRestoreCone()` after you finish using the array.
3115   `DMPlexRestoreCone()` is not needed/available in C.
3116 
3117 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSize()`, `DMPlexSetCone()`, `DMPlexGetConeTuple()`, `DMPlexSetChart()`, `DMPlexRestoreCone()`
3118 @*/
3119 PetscErrorCode DMPlexGetCone(DM dm, PetscInt p, const PetscInt *cone[])
3120 {
3121   DM_Plex *mesh = (DM_Plex *)dm->data;
3122   PetscInt off;
3123 
3124   PetscFunctionBegin;
3125   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3126   PetscAssertPointer(cone, 3);
3127   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3128   *cone = PetscSafePointerPlusOffset(mesh->cones, off);
3129   PetscFunctionReturn(PETSC_SUCCESS);
3130 }
3131 
3132 /*@
3133   DMPlexGetConeTuple - Return the points on the in-edges of several points in the DAG
3134 
3135   Not Collective
3136 
3137   Input Parameters:
3138 + dm - The `DMPLEX`
3139 - p  - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3140 
3141   Output Parameters:
3142 + pConesSection - `PetscSection` describing the layout of `pCones`
3143 - pCones        - An `IS` containing the points which are on the in-edges for the point set `p`
3144 
3145   Level: intermediate
3146 
3147 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeRecursive()`, `DMPlexSetChart()`, `PetscSection`, `IS`
3148 @*/
3149 PetscErrorCode DMPlexGetConeTuple(DM dm, IS p, PetscSection *pConesSection, IS *pCones)
3150 {
3151   PetscSection cs, newcs;
3152   PetscInt    *cones;
3153   PetscInt    *newarr = NULL;
3154   PetscInt     n;
3155 
3156   PetscFunctionBegin;
3157   PetscCall(DMPlexGetCones(dm, &cones));
3158   PetscCall(DMPlexGetConeSection(dm, &cs));
3159   PetscCall(PetscSectionExtractDofsFromArray(cs, MPIU_INT, cones, p, &newcs, pCones ? ((void **)&newarr) : NULL));
3160   if (pConesSection) *pConesSection = newcs;
3161   if (pCones) {
3162     PetscCall(PetscSectionGetStorageSize(newcs, &n));
3163     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)p), n, newarr, PETSC_OWN_POINTER, pCones));
3164   }
3165   PetscFunctionReturn(PETSC_SUCCESS);
3166 }
3167 
3168 /*@
3169   DMPlexGetConeRecursiveVertices - Expand each given point into its cone points and do that recursively until we end up just with vertices.
3170 
3171   Not Collective
3172 
3173   Input Parameters:
3174 + dm     - The `DMPLEX`
3175 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3176 
3177   Output Parameter:
3178 . expandedPoints - An `IS` containing the of vertices recursively expanded from input points
3179 
3180   Level: advanced
3181 
3182   Notes:
3183   Like `DMPlexGetConeRecursive()` but returns only the 0-depth `IS` (i.e. vertices only) and no sections.
3184 
3185   There is no corresponding Restore function, just call `ISDestroy()` on the returned `IS` to deallocate.
3186 
3187 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexRestoreConeRecursive()`,
3188           `DMPlexGetDepth()`, `IS`
3189 @*/
3190 PetscErrorCode DMPlexGetConeRecursiveVertices(DM dm, IS points, IS *expandedPoints)
3191 {
3192   IS      *expandedPointsAll;
3193   PetscInt depth;
3194 
3195   PetscFunctionBegin;
3196   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3197   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
3198   PetscAssertPointer(expandedPoints, 3);
3199   PetscCall(DMPlexGetConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
3200   *expandedPoints = expandedPointsAll[0];
3201   PetscCall(PetscObjectReference((PetscObject)expandedPointsAll[0]));
3202   PetscCall(DMPlexRestoreConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
3203   PetscFunctionReturn(PETSC_SUCCESS);
3204 }
3205 
3206 /*@
3207   DMPlexGetConeRecursive - Expand each given point into its cone points and do that recursively until we end up just with vertices
3208   (DAG points of depth 0, i.e., without cones).
3209 
3210   Not Collective
3211 
3212   Input Parameters:
3213 + dm     - The `DMPLEX`
3214 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3215 
3216   Output Parameters:
3217 + depth          - (optional) Size of the output arrays, equal to `DMPLEX` depth, returned by `DMPlexGetDepth()`
3218 . expandedPoints - (optional) An array of index sets with recursively expanded cones
3219 - sections       - (optional) An array of sections which describe mappings from points to their cone points
3220 
3221   Level: advanced
3222 
3223   Notes:
3224   Like `DMPlexGetConeTuple()` but recursive.
3225 
3226   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.
3227   For example, for d=0 it contains only vertices, for d=1 it can contain vertices and edges, etc.
3228 
3229   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\:
3230   (1) DAG points in `expandedPoints`[d+1] with `depth` d+1 to their cone points in `expandedPoints`[d];
3231   (2) DAG points in `expandedPoints`[d+1] with `depth` in [0,d] to the same points in `expandedPoints`[d].
3232 
3233 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexRestoreConeRecursive()`, `DMPlexGetConeRecursiveVertices()`,
3234           `DMPlexGetDepth()`, `PetscSection`, `IS`
3235 @*/
3236 PetscErrorCode DMPlexGetConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
3237 {
3238   const PetscInt *arr0 = NULL, *cone = NULL;
3239   PetscInt       *arr = NULL, *newarr = NULL;
3240   PetscInt        d, depth_, i, n, newn, cn, co, start, end;
3241   IS             *expandedPoints_;
3242   PetscSection   *sections_;
3243 
3244   PetscFunctionBegin;
3245   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3246   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
3247   if (depth) PetscAssertPointer(depth, 3);
3248   if (expandedPoints) PetscAssertPointer(expandedPoints, 4);
3249   if (sections) PetscAssertPointer(sections, 5);
3250   PetscCall(ISGetLocalSize(points, &n));
3251   PetscCall(ISGetIndices(points, &arr0));
3252   PetscCall(DMPlexGetDepth(dm, &depth_));
3253   PetscCall(PetscCalloc1(depth_, &expandedPoints_));
3254   PetscCall(PetscCalloc1(depth_, &sections_));
3255   arr = (PetscInt *)arr0; /* this is ok because first generation of arr is not modified */
3256   for (d = depth_ - 1; d >= 0; d--) {
3257     PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &sections_[d]));
3258     PetscCall(PetscSectionSetChart(sections_[d], 0, n));
3259     for (i = 0; i < n; i++) {
3260       PetscCall(DMPlexGetDepthStratum(dm, d + 1, &start, &end));
3261       if (arr[i] >= start && arr[i] < end) {
3262         PetscCall(DMPlexGetConeSize(dm, arr[i], &cn));
3263         PetscCall(PetscSectionSetDof(sections_[d], i, cn));
3264       } else {
3265         PetscCall(PetscSectionSetDof(sections_[d], i, 1));
3266       }
3267     }
3268     PetscCall(PetscSectionSetUp(sections_[d]));
3269     PetscCall(PetscSectionGetStorageSize(sections_[d], &newn));
3270     PetscCall(PetscMalloc1(newn, &newarr));
3271     for (i = 0; i < n; i++) {
3272       PetscCall(PetscSectionGetDof(sections_[d], i, &cn));
3273       PetscCall(PetscSectionGetOffset(sections_[d], i, &co));
3274       if (cn > 1) {
3275         PetscCall(DMPlexGetCone(dm, arr[i], &cone));
3276         PetscCall(PetscMemcpy(&newarr[co], cone, cn * sizeof(PetscInt)));
3277       } else {
3278         newarr[co] = arr[i];
3279       }
3280     }
3281     PetscCall(ISCreateGeneral(PETSC_COMM_SELF, newn, newarr, PETSC_OWN_POINTER, &expandedPoints_[d]));
3282     arr = newarr;
3283     n   = newn;
3284   }
3285   PetscCall(ISRestoreIndices(points, &arr0));
3286   *depth = depth_;
3287   if (expandedPoints) *expandedPoints = expandedPoints_;
3288   else {
3289     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&expandedPoints_[d]));
3290     PetscCall(PetscFree(expandedPoints_));
3291   }
3292   if (sections) *sections = sections_;
3293   else {
3294     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&sections_[d]));
3295     PetscCall(PetscFree(sections_));
3296   }
3297   PetscFunctionReturn(PETSC_SUCCESS);
3298 }
3299 
3300 /*@
3301   DMPlexRestoreConeRecursive - Deallocates arrays created by `DMPlexGetConeRecursive()`
3302 
3303   Not Collective
3304 
3305   Input Parameters:
3306 + dm     - The `DMPLEX`
3307 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3308 
3309   Output Parameters:
3310 + depth          - (optional) Size of the output arrays, equal to `DMPLEX` depth, returned by `DMPlexGetDepth()`
3311 . expandedPoints - (optional) An array of recursively expanded cones
3312 - sections       - (optional) An array of sections which describe mappings from points to their cone points
3313 
3314   Level: advanced
3315 
3316   Note:
3317   See `DMPlexGetConeRecursive()`
3318 
3319 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexGetConeRecursiveVertices()`,
3320           `DMPlexGetDepth()`, `IS`, `PetscSection`
3321 @*/
3322 PetscErrorCode DMPlexRestoreConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
3323 {
3324   PetscInt d, depth_;
3325 
3326   PetscFunctionBegin;
3327   PetscCall(DMPlexGetDepth(dm, &depth_));
3328   PetscCheck(!depth || *depth == depth_, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "depth changed since last call to DMPlexGetConeRecursive");
3329   if (depth) *depth = 0;
3330   if (expandedPoints) {
3331     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&((*expandedPoints)[d])));
3332     PetscCall(PetscFree(*expandedPoints));
3333   }
3334   if (sections) {
3335     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&((*sections)[d])));
3336     PetscCall(PetscFree(*sections));
3337   }
3338   PetscFunctionReturn(PETSC_SUCCESS);
3339 }
3340 
3341 /*@
3342   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
3343 
3344   Not Collective
3345 
3346   Input Parameters:
3347 + dm   - The `DMPLEX`
3348 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3349 - cone - An array of points which are on the in-edges for point `p`, its length must have been previously provided with `DMPlexSetConeSize()`
3350 
3351   Level: beginner
3352 
3353   Note:
3354   This should be called after all calls to `DMPlexSetConeSize()` and `DMSetUp()`.
3355 
3356 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`, `DMPlexSetSupport()`, `DMPlexSetSupportSize()`
3357 @*/
3358 PetscErrorCode DMPlexSetCone(DM dm, PetscInt p, const PetscInt cone[])
3359 {
3360   DM_Plex *mesh = (DM_Plex *)dm->data;
3361   PetscInt dof, off, c;
3362 
3363   PetscFunctionBegin;
3364   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3365   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3366   if (dof) PetscAssertPointer(cone, 3);
3367   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3368   if (PetscDefined(USE_DEBUG)) {
3369     PetscInt pStart, pEnd;
3370     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3371     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);
3372     for (c = 0; c < dof; ++c) {
3373       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);
3374       mesh->cones[off + c] = cone[c];
3375     }
3376   } else {
3377     for (c = 0; c < dof; ++c) mesh->cones[off + c] = cone[c];
3378   }
3379   PetscFunctionReturn(PETSC_SUCCESS);
3380 }
3381 
3382 /*@C
3383   DMPlexGetConeOrientation - Return the orientations on the in-edges for this point in the DAG
3384 
3385   Not Collective
3386 
3387   Input Parameters:
3388 + dm - The `DMPLEX`
3389 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3390 
3391   Output Parameter:
3392 . coneOrientation - An array of orientations which are on the in-edges for point `p`. An orientation is an
3393                     integer giving the prescription for cone traversal. Its length is given by the result of `DMPlexSetConeSize()`
3394 
3395   Level: beginner
3396 
3397   Note:
3398   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3399   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3400   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3401   with the identity.
3402 
3403   Fortran Notes:
3404   You must call `DMPlexRestoreConeOrientation()` after you finish using the returned array.
3405   `DMPlexRestoreConeOrientation()` is not needed/available in C.
3406 
3407 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetConeSize()`, `DMPolytopeTypeComposeOrientation()`, `DMPolytopeTypeComposeOrientationInv()`,
3408           `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetCone()`, `DMPlexSetChart()`
3409 @*/
3410 PetscErrorCode DMPlexGetConeOrientation(DM dm, PetscInt p, const PetscInt *coneOrientation[])
3411 {
3412   DM_Plex *mesh = (DM_Plex *)dm->data;
3413   PetscInt off;
3414 
3415   PetscFunctionBegin;
3416   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3417   if (PetscDefined(USE_DEBUG)) {
3418     PetscInt dof;
3419     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3420     if (dof) PetscAssertPointer(coneOrientation, 3);
3421   }
3422   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3423 
3424   *coneOrientation = &mesh->coneOrientations[off];
3425   PetscFunctionReturn(PETSC_SUCCESS);
3426 }
3427 
3428 /*@
3429   DMPlexSetConeOrientation - Set the orientations on the in-edges for this point in the DAG
3430 
3431   Not Collective
3432 
3433   Input Parameters:
3434 + dm              - The `DMPLEX`
3435 . p               - The point, which must lie in the chart set with `DMPlexSetChart()`
3436 - coneOrientation - An array of orientations. Its length is given by the result of `DMPlexSetConeSize()`
3437 
3438   Level: beginner
3439 
3440   Notes:
3441   This should be called after all calls to `DMPlexSetConeSize()` and `DMSetUp()`.
3442 
3443   The meaning of coneOrientation is detailed in `DMPlexGetConeOrientation()`.
3444 
3445 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetConeOrientation()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3446 @*/
3447 PetscErrorCode DMPlexSetConeOrientation(DM dm, PetscInt p, const PetscInt coneOrientation[])
3448 {
3449   DM_Plex *mesh = (DM_Plex *)dm->data;
3450   PetscInt pStart, pEnd;
3451   PetscInt dof, off, c;
3452 
3453   PetscFunctionBegin;
3454   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3455   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3456   if (dof) PetscAssertPointer(coneOrientation, 3);
3457   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3458   if (PetscDefined(USE_DEBUG)) {
3459     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3460     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);
3461     for (c = 0; c < dof; ++c) {
3462       PetscInt cdof, o = coneOrientation[c];
3463 
3464       PetscCall(PetscSectionGetDof(mesh->coneSection, mesh->cones[off + c], &cdof));
3465       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);
3466       mesh->coneOrientations[off + c] = o;
3467     }
3468   } else {
3469     for (c = 0; c < dof; ++c) mesh->coneOrientations[off + c] = coneOrientation[c];
3470   }
3471   PetscFunctionReturn(PETSC_SUCCESS);
3472 }
3473 
3474 /*@
3475   DMPlexInsertCone - Insert a point into the in-edges for the point p in the DAG
3476 
3477   Not Collective
3478 
3479   Input Parameters:
3480 + dm        - The `DMPLEX`
3481 . p         - The point, which must lie in the chart set with `DMPlexSetChart()`
3482 . conePos   - The local index in the cone where the point should be put
3483 - conePoint - The mesh point to insert
3484 
3485   Level: beginner
3486 
3487 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3488 @*/
3489 PetscErrorCode DMPlexInsertCone(DM dm, PetscInt p, PetscInt conePos, PetscInt conePoint)
3490 {
3491   DM_Plex *mesh = (DM_Plex *)dm->data;
3492   PetscInt pStart, pEnd;
3493   PetscInt dof, off;
3494 
3495   PetscFunctionBegin;
3496   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3497   if (PetscDefined(USE_DEBUG)) {
3498     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3499     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);
3500     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);
3501     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3502     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);
3503   }
3504   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3505   mesh->cones[off + conePos] = conePoint;
3506   PetscFunctionReturn(PETSC_SUCCESS);
3507 }
3508 
3509 /*@
3510   DMPlexInsertConeOrientation - Insert a point orientation for the in-edge for the point p in the DAG
3511 
3512   Not Collective
3513 
3514   Input Parameters:
3515 + dm              - The `DMPLEX`
3516 . p               - The point, which must lie in the chart set with `DMPlexSetChart()`
3517 . conePos         - The local index in the cone where the point should be put
3518 - coneOrientation - The point orientation to insert
3519 
3520   Level: beginner
3521 
3522   Note:
3523   The meaning of coneOrientation values is detailed in `DMPlexGetConeOrientation()`.
3524 
3525 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3526 @*/
3527 PetscErrorCode DMPlexInsertConeOrientation(DM dm, PetscInt p, PetscInt conePos, PetscInt coneOrientation)
3528 {
3529   DM_Plex *mesh = (DM_Plex *)dm->data;
3530   PetscInt pStart, pEnd;
3531   PetscInt dof, off;
3532 
3533   PetscFunctionBegin;
3534   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3535   if (PetscDefined(USE_DEBUG)) {
3536     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3537     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);
3538     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3539     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);
3540   }
3541   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3542   mesh->coneOrientations[off + conePos] = coneOrientation;
3543   PetscFunctionReturn(PETSC_SUCCESS);
3544 }
3545 
3546 /*@C
3547   DMPlexGetOrientedCone - Return the points and orientations on the in-edges for this point in the DAG
3548 
3549   Not collective
3550 
3551   Input Parameters:
3552 + dm - The DMPlex
3553 - p  - The point, which must lie in the chart set with DMPlexSetChart()
3554 
3555   Output Parameters:
3556 + cone - An array of points which are on the in-edges for point `p`
3557 - ornt - An array of orientations which are on the in-edges for point `p`. An orientation is an
3558          integer giving the prescription for cone traversal.
3559 
3560   Level: beginner
3561 
3562   Notes:
3563   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3564   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3565   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3566   with the identity.
3567 
3568   You must also call `DMPlexRestoreOrientedCone()` after you finish using the returned array.
3569 
3570   Fortran Notes:
3571   `cone` and `ornt` must be declared with
3572 .vb
3573   PetscInt, pointer :: cone(:)
3574   PetscInt, pointer :: ornt(:)
3575 .ve
3576 
3577 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreOrientedCone()`, `DMPlexGetConeSize()`, `DMPlexGetCone()`, `DMPlexGetChart()`
3578 @*/
3579 PetscErrorCode DMPlexGetOrientedCone(DM dm, PetscInt p, const PetscInt *cone[], const PetscInt *ornt[])
3580 {
3581   DM_Plex *mesh = (DM_Plex *)dm->data;
3582 
3583   PetscFunctionBegin;
3584   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3585   if (mesh->tr) {
3586     PetscCall(DMPlexTransformGetCone(mesh->tr, p, cone, ornt));
3587   } else {
3588     PetscInt off;
3589     if (PetscDefined(USE_DEBUG)) {
3590       PetscInt dof;
3591       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3592       if (dof) {
3593         if (cone) PetscAssertPointer(cone, 3);
3594         if (ornt) PetscAssertPointer(ornt, 4);
3595       }
3596     }
3597     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3598     if (cone) *cone = PetscSafePointerPlusOffset(mesh->cones, off);
3599     if (ornt) *ornt = PetscSafePointerPlusOffset(mesh->coneOrientations, off);
3600   }
3601   PetscFunctionReturn(PETSC_SUCCESS);
3602 }
3603 
3604 /*@C
3605   DMPlexRestoreOrientedCone - Restore the points and orientations on the in-edges for this point in the DAG obtained with `DMPlexGetOrientedCone()`
3606 
3607   Not Collective
3608 
3609   Input Parameters:
3610 + dm   - The DMPlex
3611 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3612 . cone - An array of points which are on the in-edges for point p
3613 - ornt - An array of orientations which are on the in-edges for point `p`. An orientation is an
3614          integer giving the prescription for cone traversal.
3615 
3616   Level: beginner
3617 
3618 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetOrientedCone()`, `DMPlexGetConeSize()`, `DMPlexGetCone()`, `DMPlexGetChart()`
3619 @*/
3620 PetscErrorCode DMPlexRestoreOrientedCone(DM dm, PetscInt p, const PetscInt *cone[], const PetscInt *ornt[])
3621 {
3622   DM_Plex *mesh = (DM_Plex *)dm->data;
3623 
3624   PetscFunctionBegin;
3625   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3626   if (mesh->tr) PetscCall(DMPlexTransformRestoreCone(mesh->tr, p, cone, ornt));
3627   PetscFunctionReturn(PETSC_SUCCESS);
3628 }
3629 
3630 /*@
3631   DMPlexGetSupportSize - Return the number of out-edges for this point in the DAG
3632 
3633   Not Collective
3634 
3635   Input Parameters:
3636 + dm - The `DMPLEX`
3637 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3638 
3639   Output Parameter:
3640 . size - The support size for point `p`
3641 
3642   Level: beginner
3643 
3644 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`, `DMPlexGetConeSize()`
3645 @*/
3646 PetscErrorCode DMPlexGetSupportSize(DM dm, PetscInt p, PetscInt *size)
3647 {
3648   DM_Plex *mesh = (DM_Plex *)dm->data;
3649 
3650   PetscFunctionBegin;
3651   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3652   PetscAssertPointer(size, 3);
3653   PetscCall(PetscSectionGetDof(mesh->supportSection, p, size));
3654   PetscFunctionReturn(PETSC_SUCCESS);
3655 }
3656 
3657 /*@
3658   DMPlexSetSupportSize - Set the number of out-edges for this point in the DAG
3659 
3660   Not Collective
3661 
3662   Input Parameters:
3663 + dm   - The `DMPLEX`
3664 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3665 - size - The support size for point `p`
3666 
3667   Level: beginner
3668 
3669   Note:
3670   This should be called after `DMPlexSetChart()`.
3671 
3672 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetSupportSize()`, `DMPlexSetChart()`
3673 @*/
3674 PetscErrorCode DMPlexSetSupportSize(DM dm, PetscInt p, PetscInt size)
3675 {
3676   DM_Plex *mesh = (DM_Plex *)dm->data;
3677 
3678   PetscFunctionBegin;
3679   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3680   PetscCall(PetscSectionSetDof(mesh->supportSection, p, size));
3681   PetscFunctionReturn(PETSC_SUCCESS);
3682 }
3683 
3684 /*@C
3685   DMPlexGetSupport - Return the points on the out-edges for this point in the DAG
3686 
3687   Not Collective
3688 
3689   Input Parameters:
3690 + dm - The `DMPLEX`
3691 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3692 
3693   Output Parameter:
3694 . support - An array of points which are on the out-edges for point `p`, its length is that obtained from `DMPlexGetSupportSize()`
3695 
3696   Level: beginner
3697 
3698   Fortran Notes:
3699   `support` must be declared with
3700 .vb
3701   PetscInt, pointer :: support(:)
3702 .ve
3703 
3704   You must also call `DMPlexRestoreSupport()` after you finish using the returned array.
3705   `DMPlexRestoreSupport()` is not needed/available in C.
3706 
3707 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSupportSize()`, `DMPlexSetSupport()`, `DMPlexGetCone()`, `DMPlexSetChart()`
3708 @*/
3709 PetscErrorCode DMPlexGetSupport(DM dm, PetscInt p, const PetscInt *support[])
3710 {
3711   DM_Plex *mesh = (DM_Plex *)dm->data;
3712   PetscInt off;
3713 
3714   PetscFunctionBegin;
3715   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3716   PetscAssertPointer(support, 3);
3717   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3718   *support = PetscSafePointerPlusOffset(mesh->supports, off);
3719   PetscFunctionReturn(PETSC_SUCCESS);
3720 }
3721 
3722 /*@
3723   DMPlexSetSupport - Set the points on the out-edges for this point in the DAG, that is the list of points that this point covers
3724 
3725   Not Collective
3726 
3727   Input Parameters:
3728 + dm      - The `DMPLEX`
3729 . p       - The point, which must lie in the chart set with `DMPlexSetChart()`
3730 - support - An array of points which are on the out-edges for point `p`, its length is that obtained from `DMPlexGetSupportSize()`
3731 
3732   Level: beginner
3733 
3734   Note:
3735   This should be called after all calls to `DMPlexSetSupportSize()` and `DMSetUp()`.
3736 
3737 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetCone()`, `DMPlexSetConeSize()`, `DMPlexCreate()`, `DMPlexGetSupport()`, `DMPlexSetChart()`, `DMPlexSetSupportSize()`, `DMSetUp()`
3738 @*/
3739 PetscErrorCode DMPlexSetSupport(DM dm, PetscInt p, const PetscInt support[])
3740 {
3741   DM_Plex *mesh = (DM_Plex *)dm->data;
3742   PetscInt pStart, pEnd;
3743   PetscInt dof, off, c;
3744 
3745   PetscFunctionBegin;
3746   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3747   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3748   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3749   if (dof) PetscAssertPointer(support, 3);
3750   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3751   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);
3752   for (c = 0; c < dof; ++c) {
3753     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);
3754     mesh->supports[off + c] = support[c];
3755   }
3756   PetscFunctionReturn(PETSC_SUCCESS);
3757 }
3758 
3759 /*@
3760   DMPlexInsertSupport - Insert a point into the out-edges for the point p in the DAG
3761 
3762   Not Collective
3763 
3764   Input Parameters:
3765 + dm           - The `DMPLEX`
3766 . p            - The point, which must lie in the chart set with `DMPlexSetChart()`
3767 . supportPos   - The local index in the cone where the point should be put
3768 - supportPoint - The mesh point to insert
3769 
3770   Level: beginner
3771 
3772 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3773 @*/
3774 PetscErrorCode DMPlexInsertSupport(DM dm, PetscInt p, PetscInt supportPos, PetscInt supportPoint)
3775 {
3776   DM_Plex *mesh = (DM_Plex *)dm->data;
3777   PetscInt pStart, pEnd;
3778   PetscInt dof, off;
3779 
3780   PetscFunctionBegin;
3781   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3782   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3783   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3784   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3785   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);
3786   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);
3787   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);
3788   mesh->supports[off + supportPos] = supportPoint;
3789   PetscFunctionReturn(PETSC_SUCCESS);
3790 }
3791 
3792 /* Converts an orientation o in the current numbering to the previous scheme used in Plex */
3793 PetscInt DMPolytopeConvertNewOrientation_Internal(DMPolytopeType ct, PetscInt o)
3794 {
3795   switch (ct) {
3796   case DM_POLYTOPE_SEGMENT:
3797     if (o == -1) return -2;
3798     break;
3799   case DM_POLYTOPE_TRIANGLE:
3800     if (o == -3) return -1;
3801     if (o == -2) return -3;
3802     if (o == -1) return -2;
3803     break;
3804   case DM_POLYTOPE_QUADRILATERAL:
3805     if (o == -4) return -2;
3806     if (o == -3) return -1;
3807     if (o == -2) return -4;
3808     if (o == -1) return -3;
3809     break;
3810   default:
3811     return o;
3812   }
3813   return o;
3814 }
3815 
3816 /* Converts an orientation o in the previous scheme used in Plex to the current numbering */
3817 PetscInt DMPolytopeConvertOldOrientation_Internal(DMPolytopeType ct, PetscInt o)
3818 {
3819   switch (ct) {
3820   case DM_POLYTOPE_SEGMENT:
3821     if ((o == -2) || (o == 1)) return -1;
3822     if (o == -1) return 0;
3823     break;
3824   case DM_POLYTOPE_TRIANGLE:
3825     if (o == -3) return -2;
3826     if (o == -2) return -1;
3827     if (o == -1) return -3;
3828     break;
3829   case DM_POLYTOPE_QUADRILATERAL:
3830     if (o == -4) return -2;
3831     if (o == -3) return -1;
3832     if (o == -2) return -4;
3833     if (o == -1) return -3;
3834     break;
3835   default:
3836     return o;
3837   }
3838   return o;
3839 }
3840 
3841 /* Takes in a mesh whose orientations are in the previous scheme and converts them all to the current numbering */
3842 PetscErrorCode DMPlexConvertOldOrientations_Internal(DM dm)
3843 {
3844   PetscInt pStart, pEnd, p;
3845 
3846   PetscFunctionBegin;
3847   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3848   for (p = pStart; p < pEnd; ++p) {
3849     const PetscInt *cone, *ornt;
3850     PetscInt        coneSize, c;
3851 
3852     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
3853     PetscCall(DMPlexGetCone(dm, p, &cone));
3854     PetscCall(DMPlexGetConeOrientation(dm, p, &ornt));
3855     for (c = 0; c < coneSize; ++c) {
3856       DMPolytopeType ct;
3857       const PetscInt o = ornt[c];
3858 
3859       PetscCall(DMPlexGetCellType(dm, cone[c], &ct));
3860       switch (ct) {
3861       case DM_POLYTOPE_SEGMENT:
3862         if ((o == -2) || (o == 1)) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3863         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, 0));
3864         break;
3865       case DM_POLYTOPE_TRIANGLE:
3866         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3867         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3868         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3869         break;
3870       case DM_POLYTOPE_QUADRILATERAL:
3871         if (o == -4) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3872         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3873         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -4));
3874         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3875         break;
3876       default:
3877         break;
3878       }
3879     }
3880   }
3881   PetscFunctionReturn(PETSC_SUCCESS);
3882 }
3883 
3884 static inline PetscErrorCode DMPlexGetTransitiveClosure_Hot_Private(DM dm, PetscInt p, PetscBool useCone, PetscInt *size, const PetscInt *arr[], const PetscInt *ornt[])
3885 {
3886   DM_Plex *mesh = (DM_Plex *)dm->data;
3887 
3888   PetscFunctionBeginHot;
3889   if (PetscDefined(USE_DEBUG) || mesh->tr) {
3890     if (useCone) {
3891       PetscCall(DMPlexGetConeSize(dm, p, size));
3892       PetscCall(DMPlexGetOrientedCone(dm, p, arr, ornt));
3893     } else {
3894       PetscCall(DMPlexGetSupportSize(dm, p, size));
3895       PetscCall(DMPlexGetSupport(dm, p, arr));
3896     }
3897   } else {
3898     if (useCone) {
3899       const PetscSection s   = mesh->coneSection;
3900       const PetscInt     ps  = p - s->pStart;
3901       const PetscInt     off = s->atlasOff[ps];
3902 
3903       *size = s->atlasDof[ps];
3904       *arr  = mesh->cones + off;
3905       *ornt = mesh->coneOrientations + off;
3906     } else {
3907       const PetscSection s   = mesh->supportSection;
3908       const PetscInt     ps  = p - s->pStart;
3909       const PetscInt     off = s->atlasOff[ps];
3910 
3911       *size = s->atlasDof[ps];
3912       *arr  = mesh->supports + off;
3913     }
3914   }
3915   PetscFunctionReturn(PETSC_SUCCESS);
3916 }
3917 
3918 static inline PetscErrorCode DMPlexRestoreTransitiveClosure_Hot_Private(DM dm, PetscInt p, PetscBool useCone, PetscInt *size, const PetscInt *arr[], const PetscInt *ornt[])
3919 {
3920   DM_Plex *mesh = (DM_Plex *)dm->data;
3921 
3922   PetscFunctionBeginHot;
3923   if (PetscDefined(USE_DEBUG) || mesh->tr) {
3924     if (useCone) PetscCall(DMPlexRestoreOrientedCone(dm, p, arr, ornt));
3925   }
3926   PetscFunctionReturn(PETSC_SUCCESS);
3927 }
3928 
3929 static PetscErrorCode DMPlexGetTransitiveClosure_Depth1_Private(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3930 {
3931   DMPolytopeType  ct = DM_POLYTOPE_UNKNOWN;
3932   PetscInt       *closure;
3933   const PetscInt *tmp = NULL, *tmpO = NULL;
3934   PetscInt        off = 0, tmpSize, t;
3935 
3936   PetscFunctionBeginHot;
3937   if (ornt) {
3938     PetscCall(DMPlexGetCellType(dm, p, &ct));
3939     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;
3940   }
3941   if (*points) {
3942     closure = *points;
3943   } else {
3944     PetscInt maxConeSize, maxSupportSize;
3945     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3946     PetscCall(DMGetWorkArray(dm, 2 * (PetscMax(maxConeSize, maxSupportSize) + 1), MPIU_INT, &closure));
3947   }
3948   PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, p, useCone, &tmpSize, &tmp, &tmpO));
3949   if (ct == DM_POLYTOPE_UNKNOWN) {
3950     closure[off++] = p;
3951     closure[off++] = 0;
3952     for (t = 0; t < tmpSize; ++t) {
3953       closure[off++] = tmp[t];
3954       closure[off++] = tmpO ? tmpO[t] : 0;
3955     }
3956   } else {
3957     const PetscInt *arr = DMPolytopeTypeGetArrangement(ct, ornt);
3958 
3959     /* We assume that cells with a valid type have faces with a valid type */
3960     closure[off++] = p;
3961     closure[off++] = ornt;
3962     for (t = 0; t < tmpSize; ++t) {
3963       DMPolytopeType ft;
3964 
3965       PetscCall(DMPlexGetCellType(dm, tmp[t], &ft));
3966       closure[off++] = tmp[arr[t]];
3967       closure[off++] = tmpO ? DMPolytopeTypeComposeOrientation(ft, ornt, tmpO[t]) : 0;
3968     }
3969   }
3970   PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, p, useCone, &tmpSize, &tmp, &tmpO));
3971   if (numPoints) *numPoints = tmpSize + 1;
3972   if (points) *points = closure;
3973   PetscFunctionReturn(PETSC_SUCCESS);
3974 }
3975 
3976 /* We need a special tensor version because we want to allow duplicate points in the endcaps for hybrid cells */
3977 static PetscErrorCode DMPlexTransitiveClosure_Tensor_Internal(DM dm, PetscInt point, DMPolytopeType ct, PetscInt o, PetscBool useCone, PetscInt *numPoints, PetscInt **points)
3978 {
3979   const PetscInt *arr = DMPolytopeTypeGetArrangement(ct, o);
3980   const PetscInt *cone, *ornt;
3981   PetscInt       *pts, *closure = NULL;
3982   DMPolytopeType  ft;
3983   PetscInt        maxConeSize, maxSupportSize, coneSeries, supportSeries, maxSize;
3984   PetscInt        dim, coneSize, c, d, clSize, cl;
3985 
3986   PetscFunctionBeginHot;
3987   PetscCall(DMGetDimension(dm, &dim));
3988   PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, point, PETSC_TRUE, &coneSize, &cone, &ornt));
3989   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3990   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, dim + 1) - 1) / (maxConeSize - 1)) : dim + 1;
3991   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, dim + 1) - 1) / (maxSupportSize - 1)) : dim + 1;
3992   maxSize       = PetscMax(coneSeries, supportSeries);
3993   if (*points) {
3994     pts = *points;
3995   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &pts));
3996   c        = 0;
3997   pts[c++] = point;
3998   pts[c++] = o;
3999   PetscCall(DMPlexGetCellType(dm, cone[arr[0 * 2 + 0]], &ft));
4000   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[0 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[0 * 2 + 1], ornt[0]), useCone, &clSize, &closure));
4001   for (cl = 0; cl < clSize * 2; cl += 2) {
4002     pts[c++] = closure[cl];
4003     pts[c++] = closure[cl + 1];
4004   }
4005   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[1 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[1 * 2 + 1], ornt[1]), useCone, &clSize, &closure));
4006   for (cl = 0; cl < clSize * 2; cl += 2) {
4007     pts[c++] = closure[cl];
4008     pts[c++] = closure[cl + 1];
4009   }
4010   PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[0], useCone, &clSize, &closure));
4011   for (d = 2; d < coneSize; ++d) {
4012     PetscCall(DMPlexGetCellType(dm, cone[arr[d * 2 + 0]], &ft));
4013     pts[c++] = cone[arr[d * 2 + 0]];
4014     pts[c++] = DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]);
4015   }
4016   PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, point, PETSC_TRUE, &coneSize, &cone, &ornt));
4017   if (dim >= 3) {
4018     for (d = 2; d < coneSize; ++d) {
4019       const PetscInt  fpoint = cone[arr[d * 2 + 0]];
4020       const PetscInt *fcone, *fornt;
4021       PetscInt        fconeSize, fc, i;
4022 
4023       PetscCall(DMPlexGetCellType(dm, fpoint, &ft));
4024       const PetscInt *farr = DMPolytopeTypeGetArrangement(ft, DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]));
4025       PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, fpoint, PETSC_TRUE, &fconeSize, &fcone, &fornt));
4026       for (fc = 0; fc < fconeSize; ++fc) {
4027         const PetscInt cp = fcone[farr[fc * 2 + 0]];
4028         const PetscInt co = farr[fc * 2 + 1];
4029 
4030         for (i = 0; i < c; i += 2)
4031           if (pts[i] == cp) break;
4032         if (i == c) {
4033           PetscCall(DMPlexGetCellType(dm, cp, &ft));
4034           pts[c++] = cp;
4035           pts[c++] = DMPolytopeTypeComposeOrientation(ft, co, fornt[farr[fc * 2 + 0]]);
4036         }
4037       }
4038       PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, fpoint, PETSC_TRUE, &fconeSize, &fcone, &fornt));
4039     }
4040   }
4041   *numPoints = c / 2;
4042   *points    = pts;
4043   PetscFunctionReturn(PETSC_SUCCESS);
4044 }
4045 
4046 PetscErrorCode DMPlexGetTransitiveClosure_Internal(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
4047 {
4048   DMPolytopeType ct;
4049   PetscInt      *closure, *fifo;
4050   PetscInt       closureSize = 0, fifoStart = 0, fifoSize = 0;
4051   PetscInt       maxConeSize, maxSupportSize, coneSeries, supportSeries;
4052   PetscInt       depth, maxSize;
4053 
4054   PetscFunctionBeginHot;
4055   PetscCall(DMPlexGetDepth(dm, &depth));
4056   if (depth == 1) {
4057     PetscCall(DMPlexGetTransitiveClosure_Depth1_Private(dm, p, ornt, useCone, numPoints, points));
4058     PetscFunctionReturn(PETSC_SUCCESS);
4059   }
4060   PetscCall(DMPlexGetCellType(dm, p, &ct));
4061   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;
4062   if (DMPolytopeTypeIsHybrid(ct) && ct != DM_POLYTOPE_POINT_PRISM_TENSOR) {
4063     PetscCall(DMPlexTransitiveClosure_Tensor_Internal(dm, p, ct, ornt, useCone, numPoints, points));
4064     PetscFunctionReturn(PETSC_SUCCESS);
4065   }
4066   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
4067   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, depth + 1) - 1) / (maxConeSize - 1)) : depth + 1;
4068   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, depth + 1) - 1) / (maxSupportSize - 1)) : depth + 1;
4069   maxSize       = PetscMax(coneSeries, supportSeries);
4070   PetscCall(DMGetWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
4071   if (*points) {
4072     closure = *points;
4073   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &closure));
4074   closure[closureSize++] = p;
4075   closure[closureSize++] = ornt;
4076   fifo[fifoSize++]       = p;
4077   fifo[fifoSize++]       = ornt;
4078   fifo[fifoSize++]       = ct;
4079   /* Should kick out early when depth is reached, rather than checking all vertices for empty cones */
4080   while (fifoSize - fifoStart) {
4081     const PetscInt       q    = fifo[fifoStart++];
4082     const PetscInt       o    = fifo[fifoStart++];
4083     const DMPolytopeType qt   = (DMPolytopeType)fifo[fifoStart++];
4084     const PetscInt      *qarr = DMPolytopeTypeGetArrangement(qt, o);
4085     const PetscInt      *tmp, *tmpO = NULL;
4086     PetscInt             tmpSize, t;
4087 
4088     if (PetscDefined(USE_DEBUG)) {
4089       PetscInt nO = DMPolytopeTypeGetNumArrangements(qt) / 2;
4090       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);
4091     }
4092     PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, q, useCone, &tmpSize, &tmp, &tmpO));
4093     for (t = 0; t < tmpSize; ++t) {
4094       const PetscInt ip = useCone && qarr ? qarr[t * 2] : t;
4095       const PetscInt io = useCone && qarr ? qarr[t * 2 + 1] : 0;
4096       const PetscInt cp = tmp[ip];
4097       PetscCall(DMPlexGetCellType(dm, cp, &ct));
4098       const PetscInt co = tmpO ? DMPolytopeTypeComposeOrientation(ct, io, tmpO[ip]) : 0;
4099       PetscInt       c;
4100 
4101       /* Check for duplicate */
4102       for (c = 0; c < closureSize; c += 2) {
4103         if (closure[c] == cp) break;
4104       }
4105       if (c == closureSize) {
4106         closure[closureSize++] = cp;
4107         closure[closureSize++] = co;
4108         fifo[fifoSize++]       = cp;
4109         fifo[fifoSize++]       = co;
4110         fifo[fifoSize++]       = ct;
4111       }
4112     }
4113     PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, q, useCone, &tmpSize, &tmp, &tmpO));
4114   }
4115   PetscCall(DMRestoreWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
4116   if (numPoints) *numPoints = closureSize / 2;
4117   if (points) *points = closure;
4118   PetscFunctionReturn(PETSC_SUCCESS);
4119 }
4120 
4121 /*@C
4122   DMPlexGetTransitiveClosure - Return the points on the transitive closure of the in-edges or out-edges for this point in the DAG
4123 
4124   Not Collective
4125 
4126   Input Parameters:
4127 + dm      - The `DMPLEX`
4128 . p       - The mesh point
4129 - useCone - `PETSC_TRUE` for the closure, otherwise return the star
4130 
4131   Input/Output Parameter:
4132 . points - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...];
4133            if *points is `NULL` on input, internal storage will be returned, use `DMPlexRestoreTransitiveClosure()`,
4134            otherwise the provided array is used to hold the values
4135 
4136   Output Parameter:
4137 . numPoints - The number of points in the closure, so `points` is of size 2*`numPoints`
4138 
4139   Level: beginner
4140 
4141   Note:
4142   If using internal storage (points is `NULL` on input), each call overwrites the last output.
4143 
4144   Fortran Notes:
4145   `points` must be declared with
4146 .vb
4147   PetscInt, pointer :: points(:)
4148 .ve
4149   and is always allocated by the function.
4150 
4151   The `numPoints` argument is not present in the Fortran binding.
4152 
4153 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
4154 @*/
4155 PetscErrorCode DMPlexGetTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
4156 {
4157   PetscFunctionBeginHot;
4158   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4159   if (numPoints) PetscAssertPointer(numPoints, 4);
4160   if (points) PetscAssertPointer(points, 5);
4161   if (PetscDefined(USE_DEBUG)) {
4162     PetscInt pStart, pEnd;
4163     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4164     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);
4165   }
4166   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, p, 0, useCone, numPoints, points));
4167   PetscFunctionReturn(PETSC_SUCCESS);
4168 }
4169 
4170 /*@C
4171   DMPlexRestoreTransitiveClosure - Restore the array of points on the transitive closure of the in-edges or out-edges for this point in the DAG
4172 
4173   Not Collective
4174 
4175   Input Parameters:
4176 + dm        - The `DMPLEX`
4177 . p         - The mesh point
4178 . useCone   - `PETSC_TRUE` for the closure, otherwise return the star
4179 . numPoints - The number of points in the closure, so points[] is of size 2*`numPoints`
4180 - points    - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...]
4181 
4182   Level: beginner
4183 
4184   Note:
4185   If not using internal storage (points is not `NULL` on input), this call is unnecessary
4186 
4187 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
4188 @*/
4189 PetscErrorCode DMPlexRestoreTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
4190 {
4191   PetscFunctionBeginHot;
4192   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4193   if (numPoints) *numPoints = 0;
4194   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, points));
4195   PetscFunctionReturn(PETSC_SUCCESS);
4196 }
4197 
4198 /*@
4199   DMPlexGetMaxSizes - Return the maximum number of in-edges (cone) and out-edges (support) for any point in the DAG
4200 
4201   Not Collective
4202 
4203   Input Parameter:
4204 . dm - The `DMPLEX`
4205 
4206   Output Parameters:
4207 + maxConeSize    - The maximum number of in-edges
4208 - maxSupportSize - The maximum number of out-edges
4209 
4210   Level: beginner
4211 
4212 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
4213 @*/
4214 PetscErrorCode DMPlexGetMaxSizes(DM dm, PetscInt *maxConeSize, PetscInt *maxSupportSize)
4215 {
4216   DM_Plex *mesh = (DM_Plex *)dm->data;
4217 
4218   PetscFunctionBegin;
4219   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4220   if (maxConeSize) PetscCall(PetscSectionGetMaxDof(mesh->coneSection, maxConeSize));
4221   if (maxSupportSize) PetscCall(PetscSectionGetMaxDof(mesh->supportSection, maxSupportSize));
4222   PetscFunctionReturn(PETSC_SUCCESS);
4223 }
4224 
4225 PetscErrorCode DMSetUp_Plex(DM dm)
4226 {
4227   DM_Plex *mesh = (DM_Plex *)dm->data;
4228   PetscInt size, maxSupportSize;
4229 
4230   PetscFunctionBegin;
4231   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4232   PetscCall(PetscSectionSetUp(mesh->coneSection));
4233   PetscCall(PetscSectionGetStorageSize(mesh->coneSection, &size));
4234   PetscCall(PetscMalloc1(size, &mesh->cones));
4235   PetscCall(PetscCalloc1(size, &mesh->coneOrientations));
4236   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
4237   if (maxSupportSize) {
4238     PetscCall(PetscSectionSetUp(mesh->supportSection));
4239     PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &size));
4240     PetscCall(PetscMalloc1(size, &mesh->supports));
4241   }
4242   PetscFunctionReturn(PETSC_SUCCESS);
4243 }
4244 
4245 PetscErrorCode DMCreateSubDM_Plex(DM dm, PetscInt numFields, const PetscInt fields[], IS *is, DM *subdm)
4246 {
4247   PetscFunctionBegin;
4248   if (subdm) PetscCall(DMClone(dm, subdm));
4249   PetscCall(DMCreateSectionSubDM(dm, numFields, fields, NULL, NULL, is, subdm));
4250   if (subdm) (*subdm)->useNatural = dm->useNatural;
4251   if (dm->useNatural && dm->sfMigration) {
4252     PetscSF sfNatural;
4253 
4254     (*subdm)->sfMigration = dm->sfMigration;
4255     PetscCall(PetscObjectReference((PetscObject)dm->sfMigration));
4256     PetscCall(DMPlexCreateGlobalToNaturalSF(*subdm, NULL, (*subdm)->sfMigration, &sfNatural));
4257     (*subdm)->sfNatural = sfNatural;
4258   }
4259   PetscFunctionReturn(PETSC_SUCCESS);
4260 }
4261 
4262 PetscErrorCode DMCreateSuperDM_Plex(DM dms[], PetscInt len, IS **is, DM *superdm)
4263 {
4264   PetscInt i = 0;
4265 
4266   PetscFunctionBegin;
4267   PetscCall(DMClone(dms[0], superdm));
4268   PetscCall(DMCreateSectionSuperDM(dms, len, is, superdm));
4269   (*superdm)->useNatural = PETSC_FALSE;
4270   for (i = 0; i < len; i++) {
4271     if (dms[i]->useNatural && dms[i]->sfMigration) {
4272       PetscSF sfNatural;
4273 
4274       (*superdm)->sfMigration = dms[i]->sfMigration;
4275       PetscCall(PetscObjectReference((PetscObject)dms[i]->sfMigration));
4276       (*superdm)->useNatural = PETSC_TRUE;
4277       PetscCall(DMPlexCreateGlobalToNaturalSF(*superdm, NULL, (*superdm)->sfMigration, &sfNatural));
4278       (*superdm)->sfNatural = sfNatural;
4279       break;
4280     }
4281   }
4282   PetscFunctionReturn(PETSC_SUCCESS);
4283 }
4284 
4285 /*@
4286   DMPlexSymmetrize - Create support (out-edge) information from cone (in-edge) information
4287 
4288   Not Collective
4289 
4290   Input Parameter:
4291 . dm - The `DMPLEX`
4292 
4293   Level: beginner
4294 
4295   Note:
4296   This should be called after all calls to `DMPlexSetCone()`
4297 
4298 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMPlexSetCone()`
4299 @*/
4300 PetscErrorCode DMPlexSymmetrize(DM dm)
4301 {
4302   DM_Plex  *mesh = (DM_Plex *)dm->data;
4303   PetscInt *offsets;
4304   PetscInt  supportSize;
4305   PetscInt  pStart, pEnd, p;
4306 
4307   PetscFunctionBegin;
4308   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4309   PetscCheck(!mesh->supports, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONGSTATE, "Supports were already setup in this DMPlex");
4310   PetscCall(PetscLogEventBegin(DMPLEX_Symmetrize, dm, 0, 0, 0));
4311   /* Calculate support sizes */
4312   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4313   for (p = pStart; p < pEnd; ++p) {
4314     PetscInt dof, off, c;
4315 
4316     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
4317     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
4318     for (c = off; c < off + dof; ++c) PetscCall(PetscSectionAddDof(mesh->supportSection, mesh->cones[c], 1));
4319   }
4320   PetscCall(PetscSectionSetUp(mesh->supportSection));
4321   /* Calculate supports */
4322   PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &supportSize));
4323   PetscCall(PetscMalloc1(supportSize, &mesh->supports));
4324   PetscCall(PetscCalloc1(pEnd - pStart, &offsets));
4325   for (p = pStart; p < pEnd; ++p) {
4326     PetscInt dof, off, c;
4327 
4328     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
4329     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
4330     for (c = off; c < off + dof; ++c) {
4331       const PetscInt q = mesh->cones[c];
4332       PetscInt       offS;
4333 
4334       PetscCall(PetscSectionGetOffset(mesh->supportSection, q, &offS));
4335 
4336       mesh->supports[offS + offsets[q]] = p;
4337       ++offsets[q];
4338     }
4339   }
4340   PetscCall(PetscFree(offsets));
4341   PetscCall(PetscLogEventEnd(DMPLEX_Symmetrize, dm, 0, 0, 0));
4342   PetscFunctionReturn(PETSC_SUCCESS);
4343 }
4344 
4345 static PetscErrorCode DMPlexCreateDepthStratum(DM dm, DMLabel label, PetscInt depth, PetscInt pStart, PetscInt pEnd)
4346 {
4347   IS stratumIS;
4348 
4349   PetscFunctionBegin;
4350   if (pStart >= pEnd) PetscFunctionReturn(PETSC_SUCCESS);
4351   if (PetscDefined(USE_DEBUG)) {
4352     PetscInt  qStart, qEnd, numLevels, level;
4353     PetscBool overlap = PETSC_FALSE;
4354     PetscCall(DMLabelGetNumValues(label, &numLevels));
4355     for (level = 0; level < numLevels; level++) {
4356       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4357       if ((pStart >= qStart && pStart < qEnd) || (pEnd > qStart && pEnd <= qEnd)) {
4358         overlap = PETSC_TRUE;
4359         break;
4360       }
4361     }
4362     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);
4363   }
4364   PetscCall(ISCreateStride(PETSC_COMM_SELF, pEnd - pStart, pStart, 1, &stratumIS));
4365   PetscCall(DMLabelSetStratumIS(label, depth, stratumIS));
4366   PetscCall(ISDestroy(&stratumIS));
4367   PetscFunctionReturn(PETSC_SUCCESS);
4368 }
4369 
4370 static PetscErrorCode DMPlexStratify_CellType_Private(DM dm, DMLabel label)
4371 {
4372   PetscInt *pMin, *pMax;
4373   PetscInt  pStart, pEnd;
4374   PetscInt  dmin = PETSC_MAX_INT, dmax = PETSC_MIN_INT;
4375 
4376   PetscFunctionBegin;
4377   {
4378     DMLabel label2;
4379 
4380     PetscCall(DMPlexGetCellTypeLabel(dm, &label2));
4381     PetscCall(PetscObjectViewFromOptions((PetscObject)label2, NULL, "-ct_view"));
4382   }
4383   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4384   for (PetscInt p = pStart; p < pEnd; ++p) {
4385     DMPolytopeType ct;
4386 
4387     PetscCall(DMPlexGetCellType(dm, p, &ct));
4388     dmin = PetscMin(DMPolytopeTypeGetDim(ct), dmin);
4389     dmax = PetscMax(DMPolytopeTypeGetDim(ct), dmax);
4390   }
4391   PetscCall(PetscMalloc2(dmax + 1, &pMin, dmax + 1, &pMax));
4392   for (PetscInt d = dmin; d <= dmax; ++d) {
4393     pMin[d] = PETSC_MAX_INT;
4394     pMax[d] = PETSC_MIN_INT;
4395   }
4396   for (PetscInt p = pStart; p < pEnd; ++p) {
4397     DMPolytopeType ct;
4398     PetscInt       d;
4399 
4400     PetscCall(DMPlexGetCellType(dm, p, &ct));
4401     d       = DMPolytopeTypeGetDim(ct);
4402     pMin[d] = PetscMin(p, pMin[d]);
4403     pMax[d] = PetscMax(p, pMax[d]);
4404   }
4405   for (PetscInt d = dmin; d <= dmax; ++d) {
4406     if (pMin[d] > pMax[d]) continue;
4407     PetscCall(DMPlexCreateDepthStratum(dm, label, d, pMin[d], pMax[d] + 1));
4408   }
4409   PetscCall(PetscFree2(pMin, pMax));
4410   PetscFunctionReturn(PETSC_SUCCESS);
4411 }
4412 
4413 static PetscErrorCode DMPlexStratify_Topological_Private(DM dm, DMLabel label)
4414 {
4415   PetscInt pStart, pEnd;
4416   PetscInt numRoots = 0, numLeaves = 0;
4417 
4418   PetscFunctionBegin;
4419   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4420   {
4421     /* Initialize roots and count leaves */
4422     PetscInt sMin = PETSC_MAX_INT;
4423     PetscInt sMax = PETSC_MIN_INT;
4424     PetscInt coneSize, supportSize;
4425 
4426     for (PetscInt p = pStart; p < pEnd; ++p) {
4427       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4428       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
4429       if (!coneSize && supportSize) {
4430         sMin = PetscMin(p, sMin);
4431         sMax = PetscMax(p, sMax);
4432         ++numRoots;
4433       } else if (!supportSize && coneSize) {
4434         ++numLeaves;
4435       } else if (!supportSize && !coneSize) {
4436         /* Isolated points */
4437         sMin = PetscMin(p, sMin);
4438         sMax = PetscMax(p, sMax);
4439       }
4440     }
4441     PetscCall(DMPlexCreateDepthStratum(dm, label, 0, sMin, sMax + 1));
4442   }
4443 
4444   if (numRoots + numLeaves == (pEnd - pStart)) {
4445     PetscInt sMin = PETSC_MAX_INT;
4446     PetscInt sMax = PETSC_MIN_INT;
4447     PetscInt coneSize, supportSize;
4448 
4449     for (PetscInt p = pStart; p < pEnd; ++p) {
4450       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4451       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
4452       if (!supportSize && coneSize) {
4453         sMin = PetscMin(p, sMin);
4454         sMax = PetscMax(p, sMax);
4455       }
4456     }
4457     PetscCall(DMPlexCreateDepthStratum(dm, label, 1, sMin, sMax + 1));
4458   } else {
4459     PetscInt level = 0;
4460     PetscInt qStart, qEnd;
4461 
4462     PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4463     while (qEnd > qStart) {
4464       PetscInt sMin = PETSC_MAX_INT;
4465       PetscInt sMax = PETSC_MIN_INT;
4466 
4467       for (PetscInt q = qStart; q < qEnd; ++q) {
4468         const PetscInt *support;
4469         PetscInt        supportSize;
4470 
4471         PetscCall(DMPlexGetSupportSize(dm, q, &supportSize));
4472         PetscCall(DMPlexGetSupport(dm, q, &support));
4473         for (PetscInt s = 0; s < supportSize; ++s) {
4474           sMin = PetscMin(support[s], sMin);
4475           sMax = PetscMax(support[s], sMax);
4476         }
4477       }
4478       PetscCall(DMLabelGetNumValues(label, &level));
4479       PetscCall(DMPlexCreateDepthStratum(dm, label, level, sMin, sMax + 1));
4480       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4481     }
4482   }
4483   PetscFunctionReturn(PETSC_SUCCESS);
4484 }
4485 
4486 /*@
4487   DMPlexStratify - Computes the strata for all points in the `DMPLEX`
4488 
4489   Collective
4490 
4491   Input Parameter:
4492 . dm - The `DMPLEX`
4493 
4494   Level: beginner
4495 
4496   Notes:
4497   The strata group all points of the same grade, and this function calculates the strata. This
4498   grade can be seen as the height (or depth) of the point in the DAG.
4499 
4500   The DAG for most topologies is a graded poset (https://en.wikipedia.org/wiki/Graded_poset), and
4501   can be illustrated by a Hasse Diagram (https://en.wikipedia.org/wiki/Hasse_diagram).
4502   Concretely, `DMPlexStratify()` creates a new label named "depth" containing the depth in the DAG of each point. For cell-vertex
4503   meshes, vertices are depth 0 and cells are depth 1. For fully interpolated meshes, depth 0 for vertices, 1 for edges, and so on
4504   until cells have depth equal to the dimension of the mesh. The depth label can be accessed through `DMPlexGetDepthLabel()` or `DMPlexGetDepthStratum()`, or
4505   manually via `DMGetLabel()`.  The height is defined implicitly by height = maxDimension - depth, and can be accessed
4506   via `DMPlexGetHeightStratum()`.  For example, cells have height 0 and faces have height 1.
4507 
4508   The depth of a point is calculated by executing a breadth-first search (BFS) on the DAG. This could produce surprising results
4509   if run on a partially interpolated mesh, meaning one that had some edges and faces, but not others. For example, suppose that
4510   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
4511   to interpolate only that one (e0), so that
4512 .vb
4513   cone(c0) = {e0, v2}
4514   cone(e0) = {v0, v1}
4515 .ve
4516   If `DMPlexStratify()` is run on this mesh, it will give depths
4517 .vb
4518    depth 0 = {v0, v1, v2}
4519    depth 1 = {e0, c0}
4520 .ve
4521   where the triangle has been given depth 1, instead of 2, because it is reachable from vertex v2.
4522 
4523   `DMPlexStratify()` should be called after all calls to `DMPlexSymmetrize()`
4524 
4525 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexComputeCellTypes()`
4526 @*/
4527 PetscErrorCode DMPlexStratify(DM dm)
4528 {
4529   DM_Plex  *mesh = (DM_Plex *)dm->data;
4530   DMLabel   label;
4531   PetscBool flg = PETSC_FALSE;
4532 
4533   PetscFunctionBegin;
4534   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4535   PetscCall(PetscLogEventBegin(DMPLEX_Stratify, dm, 0, 0, 0));
4536 
4537   // Create depth label
4538   PetscCall(DMRemoveLabel(dm, "depth", NULL));
4539   PetscCall(DMCreateLabel(dm, "depth"));
4540   PetscCall(DMPlexGetDepthLabel(dm, &label));
4541 
4542   PetscCall(PetscOptionsGetBool(NULL, dm->hdr.prefix, "-dm_plex_stratify_celltype", &flg, NULL));
4543   if (flg) PetscCall(DMPlexStratify_CellType_Private(dm, label));
4544   else PetscCall(DMPlexStratify_Topological_Private(dm, label));
4545 
4546   { /* just in case there is an empty process */
4547     PetscInt numValues, maxValues = 0, v;
4548 
4549     PetscCall(DMLabelGetNumValues(label, &numValues));
4550     PetscCall(MPIU_Allreduce(&numValues, &maxValues, 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
4551     for (v = numValues; v < maxValues; v++) PetscCall(DMLabelAddStratum(label, v));
4552   }
4553   PetscCall(PetscObjectStateGet((PetscObject)label, &mesh->depthState));
4554   PetscCall(PetscLogEventEnd(DMPLEX_Stratify, dm, 0, 0, 0));
4555   PetscFunctionReturn(PETSC_SUCCESS);
4556 }
4557 
4558 PetscErrorCode DMPlexComputeCellType_Internal(DM dm, PetscInt p, PetscInt pdepth, DMPolytopeType *pt)
4559 {
4560   DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4561   PetscInt       dim, depth, pheight, coneSize;
4562 
4563   PetscFunctionBeginHot;
4564   PetscCall(DMGetDimension(dm, &dim));
4565   PetscCall(DMPlexGetDepth(dm, &depth));
4566   PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4567   pheight = depth - pdepth;
4568   if (depth <= 1) {
4569     switch (pdepth) {
4570     case 0:
4571       ct = DM_POLYTOPE_POINT;
4572       break;
4573     case 1:
4574       switch (coneSize) {
4575       case 2:
4576         ct = DM_POLYTOPE_SEGMENT;
4577         break;
4578       case 3:
4579         ct = DM_POLYTOPE_TRIANGLE;
4580         break;
4581       case 4:
4582         switch (dim) {
4583         case 2:
4584           ct = DM_POLYTOPE_QUADRILATERAL;
4585           break;
4586         case 3:
4587           ct = DM_POLYTOPE_TETRAHEDRON;
4588           break;
4589         default:
4590           break;
4591         }
4592         break;
4593       case 5:
4594         ct = DM_POLYTOPE_PYRAMID;
4595         break;
4596       case 6:
4597         ct = DM_POLYTOPE_TRI_PRISM_TENSOR;
4598         break;
4599       case 8:
4600         ct = DM_POLYTOPE_HEXAHEDRON;
4601         break;
4602       default:
4603         break;
4604       }
4605     }
4606   } else {
4607     if (pdepth == 0) {
4608       ct = DM_POLYTOPE_POINT;
4609     } else if (pheight == 0) {
4610       switch (dim) {
4611       case 1:
4612         switch (coneSize) {
4613         case 2:
4614           ct = DM_POLYTOPE_SEGMENT;
4615           break;
4616         default:
4617           break;
4618         }
4619         break;
4620       case 2:
4621         switch (coneSize) {
4622         case 3:
4623           ct = DM_POLYTOPE_TRIANGLE;
4624           break;
4625         case 4:
4626           ct = DM_POLYTOPE_QUADRILATERAL;
4627           break;
4628         default:
4629           break;
4630         }
4631         break;
4632       case 3:
4633         switch (coneSize) {
4634         case 4:
4635           ct = DM_POLYTOPE_TETRAHEDRON;
4636           break;
4637         case 5: {
4638           const PetscInt *cone;
4639           PetscInt        faceConeSize;
4640 
4641           PetscCall(DMPlexGetCone(dm, p, &cone));
4642           PetscCall(DMPlexGetConeSize(dm, cone[0], &faceConeSize));
4643           switch (faceConeSize) {
4644           case 3:
4645             ct = DM_POLYTOPE_TRI_PRISM_TENSOR;
4646             break;
4647           case 4:
4648             ct = DM_POLYTOPE_PYRAMID;
4649             break;
4650           }
4651         } break;
4652         case 6:
4653           ct = DM_POLYTOPE_HEXAHEDRON;
4654           break;
4655         default:
4656           break;
4657         }
4658         break;
4659       default:
4660         break;
4661       }
4662     } else if (pheight > 0) {
4663       switch (coneSize) {
4664       case 2:
4665         ct = DM_POLYTOPE_SEGMENT;
4666         break;
4667       case 3:
4668         ct = DM_POLYTOPE_TRIANGLE;
4669         break;
4670       case 4:
4671         ct = DM_POLYTOPE_QUADRILATERAL;
4672         break;
4673       default:
4674         break;
4675       }
4676     }
4677   }
4678   *pt = ct;
4679   PetscFunctionReturn(PETSC_SUCCESS);
4680 }
4681 
4682 /*@
4683   DMPlexComputeCellTypes - Infer the polytope type of every cell using its dimension and cone size.
4684 
4685   Collective
4686 
4687   Input Parameter:
4688 . dm - The `DMPLEX`
4689 
4690   Level: developer
4691 
4692   Note:
4693   This function is normally called automatically when a cell type is requested. It creates an
4694   internal `DMLabel` named "celltype" which can be directly accessed using `DMGetLabel()`. A user may disable
4695   automatic creation by creating the label manually, using `DMCreateLabel`(dm, "celltype").
4696 
4697   `DMPlexComputeCellTypes()` should be called after all calls to `DMPlexSymmetrize()` and `DMPlexStratify()`
4698 
4699 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexStratify()`, `DMGetLabel()`, `DMCreateLabel()`
4700 @*/
4701 PetscErrorCode DMPlexComputeCellTypes(DM dm)
4702 {
4703   DM_Plex *mesh;
4704   DMLabel  ctLabel;
4705   PetscInt pStart, pEnd, p;
4706 
4707   PetscFunctionBegin;
4708   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4709   mesh = (DM_Plex *)dm->data;
4710   PetscCall(DMCreateLabel(dm, "celltype"));
4711   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
4712   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4713   PetscCall(PetscFree(mesh->cellTypes));
4714   PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
4715   for (p = pStart; p < pEnd; ++p) {
4716     DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4717     PetscInt       pdepth;
4718 
4719     PetscCall(DMPlexGetPointDepth(dm, p, &pdepth));
4720     PetscCall(DMPlexComputeCellType_Internal(dm, p, pdepth, &ct));
4721     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]);
4722     PetscCall(DMLabelSetValue(ctLabel, p, ct));
4723     mesh->cellTypes[p - pStart].value_as_uint8 = ct;
4724   }
4725   PetscCall(PetscObjectStateGet((PetscObject)ctLabel, &mesh->celltypeState));
4726   PetscCall(PetscObjectViewFromOptions((PetscObject)ctLabel, NULL, "-dm_plex_celltypes_view"));
4727   PetscFunctionReturn(PETSC_SUCCESS);
4728 }
4729 
4730 /*@C
4731   DMPlexGetJoin - Get an array for the join of the set of points
4732 
4733   Not Collective
4734 
4735   Input Parameters:
4736 + dm        - The `DMPLEX` object
4737 . numPoints - The number of input points for the join
4738 - points    - The input points
4739 
4740   Output Parameters:
4741 + numCoveredPoints - The number of points in the join
4742 - coveredPoints    - The points in the join
4743 
4744   Level: intermediate
4745 
4746   Note:
4747   Currently, this is restricted to a single level join
4748 
4749   Fortran Notes:
4750   `converedPoints` must be declared with
4751 .vb
4752   PetscInt, pointer :: coveredPints(:)
4753 .ve
4754 
4755   The `numCoveredPoints` argument is not present in the Fortran binding.
4756 
4757 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4758 @*/
4759 PetscErrorCode DMPlexGetJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt *coveredPoints[])
4760 {
4761   DM_Plex  *mesh = (DM_Plex *)dm->data;
4762   PetscInt *join[2];
4763   PetscInt  joinSize, i = 0;
4764   PetscInt  dof, off, p, c, m;
4765   PetscInt  maxSupportSize;
4766 
4767   PetscFunctionBegin;
4768   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4769   PetscAssertPointer(points, 3);
4770   PetscAssertPointer(numCoveredPoints, 4);
4771   PetscAssertPointer(coveredPoints, 5);
4772   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
4773   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[0]));
4774   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[1]));
4775   /* Copy in support of first point */
4776   PetscCall(PetscSectionGetDof(mesh->supportSection, points[0], &dof));
4777   PetscCall(PetscSectionGetOffset(mesh->supportSection, points[0], &off));
4778   for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = mesh->supports[off + joinSize];
4779   /* Check each successive support */
4780   for (p = 1; p < numPoints; ++p) {
4781     PetscInt newJoinSize = 0;
4782 
4783     PetscCall(PetscSectionGetDof(mesh->supportSection, points[p], &dof));
4784     PetscCall(PetscSectionGetOffset(mesh->supportSection, points[p], &off));
4785     for (c = 0; c < dof; ++c) {
4786       const PetscInt point = mesh->supports[off + c];
4787 
4788       for (m = 0; m < joinSize; ++m) {
4789         if (point == join[i][m]) {
4790           join[1 - i][newJoinSize++] = point;
4791           break;
4792         }
4793       }
4794     }
4795     joinSize = newJoinSize;
4796     i        = 1 - i;
4797   }
4798   *numCoveredPoints = joinSize;
4799   *coveredPoints    = join[i];
4800   PetscCall(DMRestoreWorkArray(dm, maxSupportSize, MPIU_INT, &join[1 - i]));
4801   PetscFunctionReturn(PETSC_SUCCESS);
4802 }
4803 
4804 /*@C
4805   DMPlexRestoreJoin - Restore an array for the join of the set of points obtained with `DMPlexGetJoin()`
4806 
4807   Not Collective
4808 
4809   Input Parameters:
4810 + dm        - The `DMPLEX` object
4811 . numPoints - The number of input points for the join
4812 - points    - The input points
4813 
4814   Output Parameters:
4815 + numCoveredPoints - The number of points in the join
4816 - coveredPoints    - The points in the join
4817 
4818   Level: intermediate
4819 
4820   Fortran Notes:
4821   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4822 
4823 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetJoin()`, `DMPlexGetFullJoin()`, `DMPlexGetMeet()`
4824 @*/
4825 PetscErrorCode DMPlexRestoreJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt *coveredPoints[])
4826 {
4827   PetscFunctionBegin;
4828   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4829   if (points) PetscAssertPointer(points, 3);
4830   if (numCoveredPoints) PetscAssertPointer(numCoveredPoints, 4);
4831   PetscAssertPointer(coveredPoints, 5);
4832   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
4833   if (numCoveredPoints) *numCoveredPoints = 0;
4834   PetscFunctionReturn(PETSC_SUCCESS);
4835 }
4836 
4837 /*@C
4838   DMPlexGetFullJoin - Get an array for the join of the set of points
4839 
4840   Not Collective
4841 
4842   Input Parameters:
4843 + dm        - The `DMPLEX` object
4844 . numPoints - The number of input points for the join
4845 - points    - The input points, its length is `numPoints`
4846 
4847   Output Parameters:
4848 + numCoveredPoints - The number of points in the join
4849 - coveredPoints    - The points in the join, its length is `numCoveredPoints`
4850 
4851   Level: intermediate
4852 
4853   Fortran Notes:
4854   `points` and `converedPoints` must be declared with
4855 .vb
4856   PetscInt, pointer :: points(:)
4857   PetscInt, pointer :: coveredPints(:)
4858 .ve
4859 
4860   The `numCoveredPoints` argument is not present in the Fortran binding.
4861 
4862 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetJoin()`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4863 @*/
4864 PetscErrorCode DMPlexGetFullJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt *coveredPoints[])
4865 {
4866   PetscInt *offsets, **closures;
4867   PetscInt *join[2];
4868   PetscInt  depth = 0, maxSize, joinSize = 0, i = 0;
4869   PetscInt  p, d, c, m, ms;
4870 
4871   PetscFunctionBegin;
4872   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4873   PetscAssertPointer(points, 3);
4874   PetscAssertPointer(numCoveredPoints, 4);
4875   PetscAssertPointer(coveredPoints, 5);
4876 
4877   PetscCall(DMPlexGetDepth(dm, &depth));
4878   PetscCall(PetscCalloc1(numPoints, &closures));
4879   PetscCall(DMGetWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
4880   PetscCall(DMPlexGetMaxSizes(dm, NULL, &ms));
4881   maxSize = (ms > 1) ? ((PetscPowInt(ms, depth + 1) - 1) / (ms - 1)) : depth + 1;
4882   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[0]));
4883   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[1]));
4884 
4885   for (p = 0; p < numPoints; ++p) {
4886     PetscInt closureSize;
4887 
4888     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_FALSE, &closureSize, &closures[p]));
4889 
4890     offsets[p * (depth + 2) + 0] = 0;
4891     for (d = 0; d < depth + 1; ++d) {
4892       PetscInt pStart, pEnd, i;
4893 
4894       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
4895       for (i = offsets[p * (depth + 2) + d]; i < closureSize; ++i) {
4896         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
4897           offsets[p * (depth + 2) + d + 1] = i;
4898           break;
4899         }
4900       }
4901       if (i == closureSize) offsets[p * (depth + 2) + d + 1] = i;
4902     }
4903     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);
4904   }
4905   for (d = 0; d < depth + 1; ++d) {
4906     PetscInt dof;
4907 
4908     /* Copy in support of first point */
4909     dof = offsets[d + 1] - offsets[d];
4910     for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = closures[0][(offsets[d] + joinSize) * 2];
4911     /* Check each successive cone */
4912     for (p = 1; p < numPoints && joinSize; ++p) {
4913       PetscInt newJoinSize = 0;
4914 
4915       dof = offsets[p * (depth + 2) + d + 1] - offsets[p * (depth + 2) + d];
4916       for (c = 0; c < dof; ++c) {
4917         const PetscInt point = closures[p][(offsets[p * (depth + 2) + d] + c) * 2];
4918 
4919         for (m = 0; m < joinSize; ++m) {
4920           if (point == join[i][m]) {
4921             join[1 - i][newJoinSize++] = point;
4922             break;
4923           }
4924         }
4925       }
4926       joinSize = newJoinSize;
4927       i        = 1 - i;
4928     }
4929     if (joinSize) break;
4930   }
4931   *numCoveredPoints = joinSize;
4932   *coveredPoints    = join[i];
4933   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_FALSE, NULL, &closures[p]));
4934   PetscCall(PetscFree(closures));
4935   PetscCall(DMRestoreWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
4936   PetscCall(DMRestoreWorkArray(dm, ms, MPIU_INT, &join[1 - i]));
4937   PetscFunctionReturn(PETSC_SUCCESS);
4938 }
4939 
4940 /*@C
4941   DMPlexGetMeet - Get an array for the meet of the set of points
4942 
4943   Not Collective
4944 
4945   Input Parameters:
4946 + dm        - The `DMPLEX` object
4947 . numPoints - The number of input points for the meet
4948 - points    - The input points, of length `numPoints`
4949 
4950   Output Parameters:
4951 + numCoveringPoints - The number of points in the meet
4952 - coveringPoints    - The points in the meet, of length `numCoveringPoints`
4953 
4954   Level: intermediate
4955 
4956   Note:
4957   Currently, this is restricted to a single level meet
4958 
4959   Fortran Notes:
4960   `coveringPoints` must be declared with
4961 .vb
4962   PetscInt, pointer :: coveringPoints(:)
4963 .ve
4964 
4965   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4966 
4967 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
4968 @*/
4969 PetscErrorCode DMPlexGetMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveringPoints, const PetscInt *coveringPoints[])
4970 {
4971   DM_Plex  *mesh = (DM_Plex *)dm->data;
4972   PetscInt *meet[2];
4973   PetscInt  meetSize, i = 0;
4974   PetscInt  dof, off, p, c, m;
4975   PetscInt  maxConeSize;
4976 
4977   PetscFunctionBegin;
4978   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4979   PetscAssertPointer(points, 3);
4980   PetscAssertPointer(numCoveringPoints, 4);
4981   PetscAssertPointer(coveringPoints, 5);
4982   PetscCall(PetscSectionGetMaxDof(mesh->coneSection, &maxConeSize));
4983   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[0]));
4984   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[1]));
4985   /* Copy in cone of first point */
4986   PetscCall(PetscSectionGetDof(mesh->coneSection, points[0], &dof));
4987   PetscCall(PetscSectionGetOffset(mesh->coneSection, points[0], &off));
4988   for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = mesh->cones[off + meetSize];
4989   /* Check each successive cone */
4990   for (p = 1; p < numPoints; ++p) {
4991     PetscInt newMeetSize = 0;
4992 
4993     PetscCall(PetscSectionGetDof(mesh->coneSection, points[p], &dof));
4994     PetscCall(PetscSectionGetOffset(mesh->coneSection, points[p], &off));
4995     for (c = 0; c < dof; ++c) {
4996       const PetscInt point = mesh->cones[off + c];
4997 
4998       for (m = 0; m < meetSize; ++m) {
4999         if (point == meet[i][m]) {
5000           meet[1 - i][newMeetSize++] = point;
5001           break;
5002         }
5003       }
5004     }
5005     meetSize = newMeetSize;
5006     i        = 1 - i;
5007   }
5008   *numCoveringPoints = meetSize;
5009   *coveringPoints    = meet[i];
5010   PetscCall(DMRestoreWorkArray(dm, maxConeSize, MPIU_INT, &meet[1 - i]));
5011   PetscFunctionReturn(PETSC_SUCCESS);
5012 }
5013 
5014 /*@C
5015   DMPlexRestoreMeet - Restore an array for the meet of the set of points obtained with `DMPlexGetMeet()`
5016 
5017   Not Collective
5018 
5019   Input Parameters:
5020 + dm        - The `DMPLEX` object
5021 . numPoints - The number of input points for the meet
5022 - points    - The input points
5023 
5024   Output Parameters:
5025 + numCoveredPoints - The number of points in the meet
5026 - coveredPoints    - The points in the meet
5027 
5028   Level: intermediate
5029 
5030   Fortran Notes:
5031   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
5032 
5033 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetMeet()`, `DMPlexGetFullMeet()`, `DMPlexGetJoin()`
5034 @*/
5035 PetscErrorCode DMPlexRestoreMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt *coveredPoints[])
5036 {
5037   PetscFunctionBegin;
5038   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5039   if (points) PetscAssertPointer(points, 3);
5040   if (numCoveredPoints) PetscAssertPointer(numCoveredPoints, 4);
5041   PetscAssertPointer(coveredPoints, 5);
5042   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
5043   if (numCoveredPoints) *numCoveredPoints = 0;
5044   PetscFunctionReturn(PETSC_SUCCESS);
5045 }
5046 
5047 /*@C
5048   DMPlexGetFullMeet - Get an array for the meet of the set of points
5049 
5050   Not Collective
5051 
5052   Input Parameters:
5053 + dm        - The `DMPLEX` object
5054 . numPoints - The number of input points for the meet
5055 - points    - The input points, of length  `numPoints`
5056 
5057   Output Parameters:
5058 + numCoveredPoints - The number of points in the meet
5059 - coveredPoints    - The points in the meet, of length  `numCoveredPoints`
5060 
5061   Level: intermediate
5062 
5063   Fortran Notes:
5064   `points` and `coveredPoints` must be declared with
5065 .vb
5066   PetscInt, pointer :: points(:)
5067   PetscInt, pointer :: coveredPoints(:)
5068 .ve
5069 
5070   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
5071 
5072 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetMeet()`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
5073 @*/
5074 PetscErrorCode DMPlexGetFullMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt *coveredPoints[])
5075 {
5076   PetscInt *offsets, **closures;
5077   PetscInt *meet[2];
5078   PetscInt  height = 0, maxSize, meetSize = 0, i = 0;
5079   PetscInt  p, h, c, m, mc;
5080 
5081   PetscFunctionBegin;
5082   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5083   PetscAssertPointer(points, 3);
5084   PetscAssertPointer(numCoveredPoints, 4);
5085   PetscAssertPointer(coveredPoints, 5);
5086 
5087   PetscCall(DMPlexGetDepth(dm, &height));
5088   PetscCall(PetscMalloc1(numPoints, &closures));
5089   PetscCall(DMGetWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
5090   PetscCall(DMPlexGetMaxSizes(dm, &mc, NULL));
5091   maxSize = (mc > 1) ? ((PetscPowInt(mc, height + 1) - 1) / (mc - 1)) : height + 1;
5092   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[0]));
5093   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[1]));
5094 
5095   for (p = 0; p < numPoints; ++p) {
5096     PetscInt closureSize;
5097 
5098     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_TRUE, &closureSize, &closures[p]));
5099 
5100     offsets[p * (height + 2) + 0] = 0;
5101     for (h = 0; h < height + 1; ++h) {
5102       PetscInt pStart, pEnd, i;
5103 
5104       PetscCall(DMPlexGetHeightStratum(dm, h, &pStart, &pEnd));
5105       for (i = offsets[p * (height + 2) + h]; i < closureSize; ++i) {
5106         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
5107           offsets[p * (height + 2) + h + 1] = i;
5108           break;
5109         }
5110       }
5111       if (i == closureSize) offsets[p * (height + 2) + h + 1] = i;
5112     }
5113     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);
5114   }
5115   for (h = 0; h < height + 1; ++h) {
5116     PetscInt dof;
5117 
5118     /* Copy in cone of first point */
5119     dof = offsets[h + 1] - offsets[h];
5120     for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = closures[0][(offsets[h] + meetSize) * 2];
5121     /* Check each successive cone */
5122     for (p = 1; p < numPoints && meetSize; ++p) {
5123       PetscInt newMeetSize = 0;
5124 
5125       dof = offsets[p * (height + 2) + h + 1] - offsets[p * (height + 2) + h];
5126       for (c = 0; c < dof; ++c) {
5127         const PetscInt point = closures[p][(offsets[p * (height + 2) + h] + c) * 2];
5128 
5129         for (m = 0; m < meetSize; ++m) {
5130           if (point == meet[i][m]) {
5131             meet[1 - i][newMeetSize++] = point;
5132             break;
5133           }
5134         }
5135       }
5136       meetSize = newMeetSize;
5137       i        = 1 - i;
5138     }
5139     if (meetSize) break;
5140   }
5141   *numCoveredPoints = meetSize;
5142   *coveredPoints    = meet[i];
5143   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_TRUE, NULL, &closures[p]));
5144   PetscCall(PetscFree(closures));
5145   PetscCall(DMRestoreWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
5146   PetscCall(DMRestoreWorkArray(dm, mc, MPIU_INT, &meet[1 - i]));
5147   PetscFunctionReturn(PETSC_SUCCESS);
5148 }
5149 
5150 /*@
5151   DMPlexEqual - Determine if two `DM` have the same topology
5152 
5153   Not Collective
5154 
5155   Input Parameters:
5156 + dmA - A `DMPLEX` object
5157 - dmB - A `DMPLEX` object
5158 
5159   Output Parameter:
5160 . equal - `PETSC_TRUE` if the topologies are identical
5161 
5162   Level: intermediate
5163 
5164   Note:
5165   We are not solving graph isomorphism, so we do not permute.
5166 
5167 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCone()`
5168 @*/
5169 PetscErrorCode DMPlexEqual(DM dmA, DM dmB, PetscBool *equal)
5170 {
5171   PetscInt depth, depthB, pStart, pEnd, pStartB, pEndB, p;
5172 
5173   PetscFunctionBegin;
5174   PetscValidHeaderSpecific(dmA, DM_CLASSID, 1);
5175   PetscValidHeaderSpecific(dmB, DM_CLASSID, 2);
5176   PetscAssertPointer(equal, 3);
5177 
5178   *equal = PETSC_FALSE;
5179   PetscCall(DMPlexGetDepth(dmA, &depth));
5180   PetscCall(DMPlexGetDepth(dmB, &depthB));
5181   if (depth != depthB) PetscFunctionReturn(PETSC_SUCCESS);
5182   PetscCall(DMPlexGetChart(dmA, &pStart, &pEnd));
5183   PetscCall(DMPlexGetChart(dmB, &pStartB, &pEndB));
5184   if ((pStart != pStartB) || (pEnd != pEndB)) PetscFunctionReturn(PETSC_SUCCESS);
5185   for (p = pStart; p < pEnd; ++p) {
5186     const PetscInt *cone, *coneB, *ornt, *orntB, *support, *supportB;
5187     PetscInt        coneSize, coneSizeB, c, supportSize, supportSizeB, s;
5188 
5189     PetscCall(DMPlexGetConeSize(dmA, p, &coneSize));
5190     PetscCall(DMPlexGetCone(dmA, p, &cone));
5191     PetscCall(DMPlexGetConeOrientation(dmA, p, &ornt));
5192     PetscCall(DMPlexGetConeSize(dmB, p, &coneSizeB));
5193     PetscCall(DMPlexGetCone(dmB, p, &coneB));
5194     PetscCall(DMPlexGetConeOrientation(dmB, p, &orntB));
5195     if (coneSize != coneSizeB) PetscFunctionReturn(PETSC_SUCCESS);
5196     for (c = 0; c < coneSize; ++c) {
5197       if (cone[c] != coneB[c]) PetscFunctionReturn(PETSC_SUCCESS);
5198       if (ornt[c] != orntB[c]) PetscFunctionReturn(PETSC_SUCCESS);
5199     }
5200     PetscCall(DMPlexGetSupportSize(dmA, p, &supportSize));
5201     PetscCall(DMPlexGetSupport(dmA, p, &support));
5202     PetscCall(DMPlexGetSupportSize(dmB, p, &supportSizeB));
5203     PetscCall(DMPlexGetSupport(dmB, p, &supportB));
5204     if (supportSize != supportSizeB) PetscFunctionReturn(PETSC_SUCCESS);
5205     for (s = 0; s < supportSize; ++s) {
5206       if (support[s] != supportB[s]) PetscFunctionReturn(PETSC_SUCCESS);
5207     }
5208   }
5209   *equal = PETSC_TRUE;
5210   PetscFunctionReturn(PETSC_SUCCESS);
5211 }
5212 
5213 /*@
5214   DMPlexGetNumFaceVertices - Returns the number of vertices on a face
5215 
5216   Not Collective
5217 
5218   Input Parameters:
5219 + dm         - The `DMPLEX`
5220 . cellDim    - The cell dimension
5221 - numCorners - The number of vertices on a cell
5222 
5223   Output Parameter:
5224 . numFaceVertices - The number of vertices on a face
5225 
5226   Level: developer
5227 
5228   Note:
5229   Of course this can only work for a restricted set of symmetric shapes
5230 
5231 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCone()`
5232 @*/
5233 PetscErrorCode DMPlexGetNumFaceVertices(DM dm, PetscInt cellDim, PetscInt numCorners, PetscInt *numFaceVertices)
5234 {
5235   MPI_Comm comm;
5236 
5237   PetscFunctionBegin;
5238   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
5239   PetscAssertPointer(numFaceVertices, 4);
5240   switch (cellDim) {
5241   case 0:
5242     *numFaceVertices = 0;
5243     break;
5244   case 1:
5245     *numFaceVertices = 1;
5246     break;
5247   case 2:
5248     switch (numCorners) {
5249     case 3:                 /* triangle */
5250       *numFaceVertices = 2; /* Edge has 2 vertices */
5251       break;
5252     case 4:                 /* quadrilateral */
5253       *numFaceVertices = 2; /* Edge has 2 vertices */
5254       break;
5255     case 6:                 /* quadratic triangle, tri and quad cohesive Lagrange cells */
5256       *numFaceVertices = 3; /* Edge has 3 vertices */
5257       break;
5258     case 9:                 /* quadratic quadrilateral, quadratic quad cohesive Lagrange cells */
5259       *numFaceVertices = 3; /* Edge has 3 vertices */
5260       break;
5261     default:
5262       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
5263     }
5264     break;
5265   case 3:
5266     switch (numCorners) {
5267     case 4:                 /* tetradehdron */
5268       *numFaceVertices = 3; /* Face has 3 vertices */
5269       break;
5270     case 6:                 /* tet cohesive cells */
5271       *numFaceVertices = 4; /* Face has 4 vertices */
5272       break;
5273     case 8:                 /* hexahedron */
5274       *numFaceVertices = 4; /* Face has 4 vertices */
5275       break;
5276     case 9:                 /* tet cohesive Lagrange cells */
5277       *numFaceVertices = 6; /* Face has 6 vertices */
5278       break;
5279     case 10:                /* quadratic tetrahedron */
5280       *numFaceVertices = 6; /* Face has 6 vertices */
5281       break;
5282     case 12:                /* hex cohesive Lagrange cells */
5283       *numFaceVertices = 6; /* Face has 6 vertices */
5284       break;
5285     case 18:                /* quadratic tet cohesive Lagrange cells */
5286       *numFaceVertices = 6; /* Face has 6 vertices */
5287       break;
5288     case 27:                /* quadratic hexahedron, quadratic hex cohesive Lagrange cells */
5289       *numFaceVertices = 9; /* Face has 9 vertices */
5290       break;
5291     default:
5292       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
5293     }
5294     break;
5295   default:
5296     SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid cell dimension %" PetscInt_FMT, cellDim);
5297   }
5298   PetscFunctionReturn(PETSC_SUCCESS);
5299 }
5300 
5301 /*@
5302   DMPlexGetDepthLabel - Get the `DMLabel` recording the depth of each point
5303 
5304   Not Collective
5305 
5306   Input Parameter:
5307 . dm - The `DMPLEX` object
5308 
5309   Output Parameter:
5310 . depthLabel - The `DMLabel` recording point depth
5311 
5312   Level: developer
5313 
5314 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepth()`, `DMPlexGetHeightStratum()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`,
5315 @*/
5316 PetscErrorCode DMPlexGetDepthLabel(DM dm, DMLabel *depthLabel)
5317 {
5318   PetscFunctionBegin;
5319   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5320   PetscAssertPointer(depthLabel, 2);
5321   *depthLabel = dm->depthLabel;
5322   PetscFunctionReturn(PETSC_SUCCESS);
5323 }
5324 
5325 /*@
5326   DMPlexGetDepth - Get the depth of the DAG representing this mesh
5327 
5328   Not Collective
5329 
5330   Input Parameter:
5331 . dm - The `DMPLEX` object
5332 
5333   Output Parameter:
5334 . depth - The number of strata (breadth first levels) in the DAG
5335 
5336   Level: developer
5337 
5338   Notes:
5339   This returns maximum of point depths over all points, i.e. maximum value of the label returned by `DMPlexGetDepthLabel()`.
5340 
5341   The point depth is described more in detail in `DMPlexGetDepthStratum()`.
5342 
5343   An empty mesh gives -1.
5344 
5345 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepthLabel()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`
5346 @*/
5347 PetscErrorCode DMPlexGetDepth(DM dm, PetscInt *depth)
5348 {
5349   DM_Plex *mesh = (DM_Plex *)dm->data;
5350   DMLabel  label;
5351   PetscInt d = -1;
5352 
5353   PetscFunctionBegin;
5354   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5355   PetscAssertPointer(depth, 2);
5356   if (mesh->tr) {
5357     PetscCall(DMPlexTransformGetDepth(mesh->tr, depth));
5358   } else {
5359     PetscCall(DMPlexGetDepthLabel(dm, &label));
5360     // Allow missing depths
5361     if (label) PetscCall(DMLabelGetValueBounds(label, NULL, &d));
5362     *depth = d;
5363   }
5364   PetscFunctionReturn(PETSC_SUCCESS);
5365 }
5366 
5367 /*@
5368   DMPlexGetDepthStratum - Get the bounds [`start`, `end`) for all points at a certain depth.
5369 
5370   Not Collective
5371 
5372   Input Parameters:
5373 + dm    - The `DMPLEX` object
5374 - depth - The requested depth
5375 
5376   Output Parameters:
5377 + start - The first point at this `depth`
5378 - end   - One beyond the last point at this `depth`
5379 
5380   Level: developer
5381 
5382   Notes:
5383   Depth indexing is related to topological dimension.  Depth stratum 0 contains the lowest topological dimension points,
5384   often "vertices".  If the mesh is "interpolated" (see `DMPlexInterpolate()`), then depth stratum 1 contains the next
5385   higher dimension, e.g., "edges".
5386 
5387 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetHeightStratum()`, `DMPlexGetCellTypeStratum()`, `DMPlexGetDepth()`, `DMPlexGetDepthLabel()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`, `DMPlexInterpolate()`
5388 @*/
5389 PetscErrorCode DMPlexGetDepthStratum(DM dm, PetscInt depth, PetscInt *start, PetscInt *end)
5390 {
5391   DM_Plex *mesh = (DM_Plex *)dm->data;
5392   DMLabel  label;
5393   PetscInt pStart, pEnd;
5394 
5395   PetscFunctionBegin;
5396   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5397   if (start) {
5398     PetscAssertPointer(start, 3);
5399     *start = 0;
5400   }
5401   if (end) {
5402     PetscAssertPointer(end, 4);
5403     *end = 0;
5404   }
5405   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
5406   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
5407   if (depth < 0) {
5408     if (start) *start = pStart;
5409     if (end) *end = pEnd;
5410     PetscFunctionReturn(PETSC_SUCCESS);
5411   }
5412   if (mesh->tr) {
5413     PetscCall(DMPlexTransformGetDepthStratum(mesh->tr, depth, start, end));
5414   } else {
5415     PetscCall(DMPlexGetDepthLabel(dm, &label));
5416     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named depth was found");
5417     PetscCall(DMLabelGetStratumBounds(label, depth, start, end));
5418   }
5419   PetscFunctionReturn(PETSC_SUCCESS);
5420 }
5421 
5422 /*@
5423   DMPlexGetHeightStratum - Get the bounds [`start`, `end`) for all points at a certain height.
5424 
5425   Not Collective
5426 
5427   Input Parameters:
5428 + dm     - The `DMPLEX` object
5429 - height - The requested height
5430 
5431   Output Parameters:
5432 + start - The first point at this `height`
5433 - end   - One beyond the last point at this `height`
5434 
5435   Level: developer
5436 
5437   Notes:
5438   Height indexing is related to topological codimension.  Height stratum 0 contains the highest topological dimension
5439   points, often called "cells" or "elements".  If the mesh is "interpolated" (see `DMPlexInterpolate()`), then height
5440   stratum 1 contains the boundary of these "cells", often called "faces" or "facets".
5441 
5442 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepthStratum()`, `DMPlexGetCellTypeStratum()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
5443 @*/
5444 PetscErrorCode DMPlexGetHeightStratum(DM dm, PetscInt height, PetscInt *start, PetscInt *end)
5445 {
5446   DMLabel  label;
5447   PetscInt depth, pStart, pEnd;
5448 
5449   PetscFunctionBegin;
5450   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5451   if (start) {
5452     PetscAssertPointer(start, 3);
5453     *start = 0;
5454   }
5455   if (end) {
5456     PetscAssertPointer(end, 4);
5457     *end = 0;
5458   }
5459   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
5460   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
5461   if (height < 0) {
5462     if (start) *start = pStart;
5463     if (end) *end = pEnd;
5464     PetscFunctionReturn(PETSC_SUCCESS);
5465   }
5466   PetscCall(DMPlexGetDepthLabel(dm, &label));
5467   if (label) PetscCall(DMLabelGetNumValues(label, &depth));
5468   else PetscCall(DMGetDimension(dm, &depth));
5469   PetscCheck(depth >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Depth not yet computed");
5470   PetscCall(DMPlexGetDepthStratum(dm, depth - 1 - height, start, end));
5471   PetscFunctionReturn(PETSC_SUCCESS);
5472 }
5473 
5474 /*@
5475   DMPlexGetPointDepth - Get the `depth` of a given point
5476 
5477   Not Collective
5478 
5479   Input Parameters:
5480 + dm    - The `DMPLEX` object
5481 - point - The point
5482 
5483   Output Parameter:
5484 . depth - The depth of the `point`
5485 
5486   Level: intermediate
5487 
5488 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
5489 @*/
5490 PetscErrorCode DMPlexGetPointDepth(DM dm, PetscInt point, PetscInt *depth)
5491 {
5492   PetscFunctionBegin;
5493   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5494   PetscAssertPointer(depth, 3);
5495   PetscCall(DMLabelGetValue(dm->depthLabel, point, depth));
5496   PetscFunctionReturn(PETSC_SUCCESS);
5497 }
5498 
5499 /*@
5500   DMPlexGetPointHeight - Get the `height` of a given point
5501 
5502   Not Collective
5503 
5504   Input Parameters:
5505 + dm    - The `DMPLEX` object
5506 - point - The point
5507 
5508   Output Parameter:
5509 . height - The height of the `point`
5510 
5511   Level: intermediate
5512 
5513 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointDepth()`
5514 @*/
5515 PetscErrorCode DMPlexGetPointHeight(DM dm, PetscInt point, PetscInt *height)
5516 {
5517   PetscInt n, pDepth;
5518 
5519   PetscFunctionBegin;
5520   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5521   PetscAssertPointer(height, 3);
5522   PetscCall(DMLabelGetNumValues(dm->depthLabel, &n));
5523   PetscCall(DMLabelGetValue(dm->depthLabel, point, &pDepth));
5524   *height = n - 1 - pDepth; /* DAG depth is n-1 */
5525   PetscFunctionReturn(PETSC_SUCCESS);
5526 }
5527 
5528 /*@
5529   DMPlexGetCellTypeLabel - Get the `DMLabel` recording the polytope type of each cell
5530 
5531   Not Collective
5532 
5533   Input Parameter:
5534 . dm - The `DMPLEX` object
5535 
5536   Output Parameter:
5537 . celltypeLabel - The `DMLabel` recording cell polytope type
5538 
5539   Level: developer
5540 
5541   Note:
5542   This function will trigger automatica computation of cell types. This can be disabled by calling
5543   `DMCreateLabel`(dm, "celltype") beforehand.
5544 
5545 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMCreateLabel()`
5546 @*/
5547 PetscErrorCode DMPlexGetCellTypeLabel(DM dm, DMLabel *celltypeLabel)
5548 {
5549   PetscFunctionBegin;
5550   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5551   PetscAssertPointer(celltypeLabel, 2);
5552   if (!dm->celltypeLabel) PetscCall(DMPlexComputeCellTypes(dm));
5553   *celltypeLabel = dm->celltypeLabel;
5554   PetscFunctionReturn(PETSC_SUCCESS);
5555 }
5556 
5557 /*@
5558   DMPlexGetCellType - Get the polytope type of a given cell
5559 
5560   Not Collective
5561 
5562   Input Parameters:
5563 + dm   - The `DMPLEX` object
5564 - cell - The cell
5565 
5566   Output Parameter:
5567 . celltype - The polytope type of the cell
5568 
5569   Level: intermediate
5570 
5571 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPolytopeType`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`
5572 @*/
5573 PetscErrorCode DMPlexGetCellType(DM dm, PetscInt cell, DMPolytopeType *celltype)
5574 {
5575   DM_Plex *mesh = (DM_Plex *)dm->data;
5576   DMLabel  label;
5577   PetscInt ct;
5578 
5579   PetscFunctionBegin;
5580   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5581   PetscAssertPointer(celltype, 3);
5582   if (mesh->tr) {
5583     PetscCall(DMPlexTransformGetCellType(mesh->tr, cell, celltype));
5584   } else {
5585     PetscInt pStart, pEnd;
5586 
5587     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, NULL));
5588     if (!mesh->cellTypes) { /* XXX remove? optimize? */
5589       PetscCall(PetscSectionGetChart(mesh->coneSection, NULL, &pEnd));
5590       PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
5591       PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5592       for (PetscInt p = pStart; p < pEnd; p++) {
5593         PetscCall(DMLabelGetValue(label, p, &ct));
5594         mesh->cellTypes[p - pStart].value_as_uint8 = (DMPolytopeType)ct;
5595       }
5596     }
5597     *celltype = (DMPolytopeType)mesh->cellTypes[cell - pStart].value_as_uint8;
5598     if (PetscDefined(USE_DEBUG)) {
5599       PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5600       PetscCall(DMLabelGetValue(label, cell, &ct));
5601       PetscCheck(ct >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Cell %" PetscInt_FMT " has not been assigned a cell type", cell);
5602       PetscCheck(ct == (PetscInt)*celltype, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid cellType for %" PetscInt_FMT ": %d != %" PetscInt_FMT, cell, (int)*celltype, ct);
5603     }
5604   }
5605   PetscFunctionReturn(PETSC_SUCCESS);
5606 }
5607 
5608 /*@
5609   DMPlexSetCellType - Set the polytope type of a given cell
5610 
5611   Not Collective
5612 
5613   Input Parameters:
5614 + dm       - The `DMPLEX` object
5615 . cell     - The cell
5616 - celltype - The polytope type of the cell
5617 
5618   Level: advanced
5619 
5620   Note:
5621   By default, cell types will be automatically computed using `DMPlexComputeCellTypes()` before this function
5622   is executed. This function will override the computed type. However, if automatic classification will not succeed
5623   and a user wants to manually specify all types, the classification must be disabled by calling
5624   DMCreateLabel(dm, "celltype") before getting or setting any cell types.
5625 
5626 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexComputeCellTypes()`, `DMCreateLabel()`
5627 @*/
5628 PetscErrorCode DMPlexSetCellType(DM dm, PetscInt cell, DMPolytopeType celltype)
5629 {
5630   DM_Plex *mesh = (DM_Plex *)dm->data;
5631   DMLabel  label;
5632   PetscInt pStart, pEnd;
5633 
5634   PetscFunctionBegin;
5635   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5636   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
5637   PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5638   PetscCall(DMLabelSetValue(label, cell, celltype));
5639   if (!mesh->cellTypes) PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
5640   mesh->cellTypes[cell - pStart].value_as_uint8 = celltype;
5641   PetscFunctionReturn(PETSC_SUCCESS);
5642 }
5643 
5644 PetscErrorCode DMCreateCoordinateDM_Plex(DM dm, DM *cdm)
5645 {
5646   PetscSection section;
5647   PetscInt     maxHeight;
5648   const char  *prefix;
5649 
5650   PetscFunctionBegin;
5651   PetscCall(DMClone(dm, cdm));
5652   PetscCall(PetscObjectGetOptionsPrefix((PetscObject)dm, &prefix));
5653   PetscCall(PetscObjectSetOptionsPrefix((PetscObject)*cdm, prefix));
5654   PetscCall(PetscObjectAppendOptionsPrefix((PetscObject)*cdm, "cdm_"));
5655   PetscCall(DMPlexGetMaxProjectionHeight(dm, &maxHeight));
5656   PetscCall(DMPlexSetMaxProjectionHeight(*cdm, maxHeight));
5657   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
5658   PetscCall(DMSetLocalSection(*cdm, section));
5659   PetscCall(PetscSectionDestroy(&section));
5660 
5661   PetscCall(DMSetNumFields(*cdm, 1));
5662   PetscCall(DMCreateDS(*cdm));
5663   (*cdm)->cloneOpts = PETSC_TRUE;
5664   if (dm->setfromoptionscalled) PetscCall(DMSetFromOptions(*cdm));
5665   PetscFunctionReturn(PETSC_SUCCESS);
5666 }
5667 
5668 PetscErrorCode DMCreateCoordinateField_Plex(DM dm, DMField *field)
5669 {
5670   Vec coordsLocal, cellCoordsLocal;
5671   DM  coordsDM, cellCoordsDM;
5672 
5673   PetscFunctionBegin;
5674   *field = NULL;
5675   PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
5676   PetscCall(DMGetCoordinateDM(dm, &coordsDM));
5677   PetscCall(DMGetCellCoordinatesLocal(dm, &cellCoordsLocal));
5678   PetscCall(DMGetCellCoordinateDM(dm, &cellCoordsDM));
5679   if (coordsLocal && coordsDM) {
5680     if (cellCoordsLocal && cellCoordsDM) PetscCall(DMFieldCreateDSWithDG(coordsDM, cellCoordsDM, 0, coordsLocal, cellCoordsLocal, field));
5681     else PetscCall(DMFieldCreateDS(coordsDM, 0, coordsLocal, field));
5682   }
5683   PetscFunctionReturn(PETSC_SUCCESS);
5684 }
5685 
5686 /*@
5687   DMPlexGetConeSection - Return a section which describes the layout of cone data
5688 
5689   Not Collective
5690 
5691   Input Parameter:
5692 . dm - The `DMPLEX` object
5693 
5694   Output Parameter:
5695 . section - The `PetscSection` object
5696 
5697   Level: developer
5698 
5699 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSupportSection()`, `DMPlexGetCones()`, `DMPlexGetConeOrientations()`, `PetscSection`
5700 @*/
5701 PetscErrorCode DMPlexGetConeSection(DM dm, PetscSection *section)
5702 {
5703   DM_Plex *mesh = (DM_Plex *)dm->data;
5704 
5705   PetscFunctionBegin;
5706   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5707   if (section) *section = mesh->coneSection;
5708   PetscFunctionReturn(PETSC_SUCCESS);
5709 }
5710 
5711 /*@
5712   DMPlexGetSupportSection - Return a section which describes the layout of support data
5713 
5714   Not Collective
5715 
5716   Input Parameter:
5717 . dm - The `DMPLEX` object
5718 
5719   Output Parameter:
5720 . section - The `PetscSection` object
5721 
5722   Level: developer
5723 
5724 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `PetscSection`
5725 @*/
5726 PetscErrorCode DMPlexGetSupportSection(DM dm, PetscSection *section)
5727 {
5728   DM_Plex *mesh = (DM_Plex *)dm->data;
5729 
5730   PetscFunctionBegin;
5731   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5732   if (section) *section = mesh->supportSection;
5733   PetscFunctionReturn(PETSC_SUCCESS);
5734 }
5735 
5736 /*@C
5737   DMPlexGetCones - Return cone data
5738 
5739   Not Collective
5740 
5741   Input Parameter:
5742 . dm - The `DMPLEX` object
5743 
5744   Output Parameter:
5745 . cones - The cone for each point
5746 
5747   Level: developer
5748 
5749 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`
5750 @*/
5751 PetscErrorCode DMPlexGetCones(DM dm, PetscInt *cones[])
5752 {
5753   DM_Plex *mesh = (DM_Plex *)dm->data;
5754 
5755   PetscFunctionBegin;
5756   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5757   if (cones) *cones = mesh->cones;
5758   PetscFunctionReturn(PETSC_SUCCESS);
5759 }
5760 
5761 /*@C
5762   DMPlexGetConeOrientations - Return cone orientation data
5763 
5764   Not Collective
5765 
5766   Input Parameter:
5767 . dm - The `DMPLEX` object
5768 
5769   Output Parameter:
5770 . coneOrientations - The array of cone orientations for all points
5771 
5772   Level: developer
5773 
5774   Notes:
5775   The `PetscSection` returned by `DMPlexGetConeSection()` partitions coneOrientations into cone orientations of particular points
5776   as returned by `DMPlexGetConeOrientation()`.
5777 
5778   The meaning of coneOrientations values is detailed in `DMPlexGetConeOrientation()`.
5779 
5780 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `DMPlexGetConeOrientation()`, `PetscSection`
5781 @*/
5782 PetscErrorCode DMPlexGetConeOrientations(DM dm, PetscInt *coneOrientations[])
5783 {
5784   DM_Plex *mesh = (DM_Plex *)dm->data;
5785 
5786   PetscFunctionBegin;
5787   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5788   if (coneOrientations) *coneOrientations = mesh->coneOrientations;
5789   PetscFunctionReturn(PETSC_SUCCESS);
5790 }
5791 
5792 /******************************** FEM Support **********************************/
5793 
5794 PetscErrorCode DMPlexGetAllCells_Internal(DM plex, IS *cellIS)
5795 {
5796   PetscInt depth;
5797 
5798   PetscFunctionBegin;
5799   PetscCall(DMPlexGetDepth(plex, &depth));
5800   PetscCall(DMGetStratumIS(plex, "dim", depth, cellIS));
5801   if (!*cellIS) PetscCall(DMGetStratumIS(plex, "depth", depth, cellIS));
5802   PetscFunctionReturn(PETSC_SUCCESS);
5803 }
5804 
5805 PetscErrorCode DMPlexGetAllFaces_Internal(DM plex, IS *faceIS)
5806 {
5807   PetscInt depth;
5808 
5809   PetscFunctionBegin;
5810   PetscCall(DMPlexGetDepth(plex, &depth));
5811   PetscCall(DMGetStratumIS(plex, "dim", depth - 1, faceIS));
5812   if (!*faceIS) PetscCall(DMGetStratumIS(plex, "depth", depth - 1, faceIS));
5813   PetscFunctionReturn(PETSC_SUCCESS);
5814 }
5815 
5816 /*
5817  Returns number of components and tensor degree for the field.  For interpolated meshes, line should be a point
5818  representing a line in the section.
5819 */
5820 static PetscErrorCode PetscSectionFieldGetTensorDegree_Private(DM dm, PetscSection section, PetscInt field, PetscInt line, PetscInt *Nc, PetscInt *k, PetscBool *continuous, PetscBool *tensor)
5821 {
5822   PetscObject  obj;
5823   PetscClassId id;
5824   PetscFE      fe = NULL;
5825 
5826   PetscFunctionBeginHot;
5827   PetscCall(PetscSectionGetFieldComponents(section, field, Nc));
5828   PetscCall(DMGetField(dm, field, NULL, &obj));
5829   PetscCall(PetscObjectGetClassId(obj, &id));
5830   if (id == PETSCFE_CLASSID) fe = (PetscFE)obj;
5831 
5832   if (!fe) {
5833     /* Assume the full interpolated mesh is in the chart; lines in particular */
5834     /* An order k SEM disc has k-1 dofs on an edge */
5835     PetscCall(PetscSectionGetFieldDof(section, line, field, k));
5836     *k = *k / *Nc + 1;
5837   } else {
5838     PetscInt       dual_space_size, dim;
5839     PetscDualSpace dsp;
5840 
5841     PetscCall(DMGetDimension(dm, &dim));
5842     PetscCall(PetscFEGetDualSpace(fe, &dsp));
5843     PetscCall(PetscDualSpaceGetDimension(dsp, &dual_space_size));
5844     *k = (PetscInt)PetscCeilReal(PetscPowReal(dual_space_size / *Nc, 1.0 / dim)) - 1;
5845     PetscCall(PetscDualSpaceLagrangeGetContinuity(dsp, continuous));
5846     PetscCall(PetscDualSpaceLagrangeGetTensor(dsp, tensor));
5847   }
5848   PetscFunctionReturn(PETSC_SUCCESS);
5849 }
5850 
5851 static PetscErrorCode GetFieldSize_Private(PetscInt dim, PetscInt k, PetscBool tensor, PetscInt *dof)
5852 {
5853   PetscFunctionBeginHot;
5854   if (tensor) {
5855     *dof = PetscPowInt(k + 1, dim);
5856   } else {
5857     switch (dim) {
5858     case 1:
5859       *dof = k + 1;
5860       break;
5861     case 2:
5862       *dof = ((k + 1) * (k + 2)) / 2;
5863       break;
5864     case 3:
5865       *dof = ((k + 1) * (k + 2) * (k + 3)) / 6;
5866       break;
5867     default:
5868       *dof = 0;
5869     }
5870   }
5871   PetscFunctionReturn(PETSC_SUCCESS);
5872 }
5873 
5874 /*@
5875   DMPlexSetClosurePermutationTensor - Create a permutation from the default (BFS) point ordering in the closure, to a
5876   lexicographic ordering over the tensor product cell (i.e., line, quad, hex, etc.), and set this permutation in the
5877   section provided (or the section of the `DM`).
5878 
5879   Input Parameters:
5880 + dm      - The `DM`
5881 . point   - Either a cell (highest dim point) or an edge (dim 1 point), or `PETSC_DETERMINE`
5882 - section - The `PetscSection` to reorder, or `NULL` for the default section
5883 
5884   Example:
5885   A typical interpolated single-quad mesh might order points as
5886 .vb
5887   [c0, v1, v2, v3, v4, e5, e6, e7, e8]
5888 
5889   v4 -- e6 -- v3
5890   |           |
5891   e7    c0    e8
5892   |           |
5893   v1 -- e5 -- v2
5894 .ve
5895 
5896   (There is no significance to the ordering described here.)  The default section for a Q3 quad might typically assign
5897   dofs in the order of points, e.g.,
5898 .vb
5899     c0 -> [0,1,2,3]
5900     v1 -> [4]
5901     ...
5902     e5 -> [8, 9]
5903 .ve
5904 
5905   which corresponds to the dofs
5906 .vb
5907     6   10  11  7
5908     13  2   3   15
5909     12  0   1   14
5910     4   8   9   5
5911 .ve
5912 
5913   The closure in BFS ordering works through height strata (cells, edges, vertices) to produce the ordering
5914 .vb
5915   0 1 2 3 8 9 14 15 11 10 13 12 4 5 7 6
5916 .ve
5917 
5918   After calling DMPlexSetClosurePermutationTensor(), the closure will be ordered lexicographically,
5919 .vb
5920    4 8 9 5 12 0 1 14 13 2 3 15 6 10 11 7
5921 .ve
5922 
5923   Level: developer
5924 
5925   Notes:
5926   The point is used to determine the number of dofs/field on an edge. For SEM, this is related to the polynomial
5927   degree of the basis.
5928 
5929   This is required to run with libCEED.
5930 
5931 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMGetLocalSection()`, `PetscSectionSetClosurePermutation()`, `DMSetGlobalSection()`
5932 @*/
5933 PetscErrorCode DMPlexSetClosurePermutationTensor(DM dm, PetscInt point, PetscSection section)
5934 {
5935   DMLabel   label;
5936   PetscInt  dim, depth = -1, eStart = -1, Nf;
5937   PetscBool continuous = PETSC_TRUE, tensor = PETSC_TRUE;
5938 
5939   PetscFunctionBegin;
5940   PetscCall(DMGetDimension(dm, &dim));
5941   if (dim < 1) PetscFunctionReturn(PETSC_SUCCESS);
5942   if (point < 0) {
5943     PetscInt sStart, sEnd;
5944 
5945     PetscCall(DMPlexGetDepthStratum(dm, 1, &sStart, &sEnd));
5946     point = sEnd - sStart ? sStart : point;
5947   }
5948   PetscCall(DMPlexGetDepthLabel(dm, &label));
5949   if (point >= 0) PetscCall(DMLabelGetValue(label, point, &depth));
5950   if (!section) PetscCall(DMGetLocalSection(dm, &section));
5951   if (depth == 1) {
5952     eStart = point;
5953   } else if (depth == dim) {
5954     const PetscInt *cone;
5955 
5956     PetscCall(DMPlexGetCone(dm, point, &cone));
5957     if (dim == 2) eStart = cone[0];
5958     else if (dim == 3) {
5959       const PetscInt *cone2;
5960       PetscCall(DMPlexGetCone(dm, cone[0], &cone2));
5961       eStart = cone2[0];
5962     } 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);
5963   } 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);
5964 
5965   PetscCall(PetscSectionGetNumFields(section, &Nf));
5966   for (PetscInt d = 1; d <= dim; d++) {
5967     PetscInt  k, f, Nc, c, i, j, size = 0, offset = 0, foffset = 0;
5968     PetscInt *perm;
5969 
5970     for (f = 0; f < Nf; ++f) {
5971       PetscInt dof;
5972 
5973       PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
5974       PetscCheck(dim == 1 || tensor || !continuous, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Continuous field %" PetscInt_FMT " must have a tensor product discretization", f);
5975       if (!continuous && d < dim) continue;
5976       PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
5977       size += dof * Nc;
5978     }
5979     PetscCall(PetscMalloc1(size, &perm));
5980     for (f = 0; f < Nf; ++f) {
5981       switch (d) {
5982       case 1:
5983         PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
5984         if (!continuous && d < dim) continue;
5985         /*
5986          Original ordering is [ edge of length k-1; vtx0; vtx1 ]
5987          We want              [ vtx0; edge of length k-1; vtx1 ]
5988          */
5989         if (continuous) {
5990           for (c = 0; c < Nc; c++, offset++) perm[offset] = (k - 1) * Nc + c + foffset;
5991           for (i = 0; i < k - 1; i++)
5992             for (c = 0; c < Nc; c++, offset++) perm[offset] = i * Nc + c + foffset;
5993           for (c = 0; c < Nc; c++, offset++) perm[offset] = k * Nc + c + foffset;
5994           foffset = offset;
5995         } else {
5996           PetscInt dof;
5997 
5998           PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
5999           for (i = 0; i < dof * Nc; ++i, ++offset) perm[offset] = i + foffset;
6000           foffset = offset;
6001         }
6002         break;
6003       case 2:
6004         /* The original quad closure is oriented clockwise, {f, e_b, e_r, e_t, e_l, v_lb, v_rb, v_tr, v_tl} */
6005         PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
6006         if (!continuous && d < dim) continue;
6007         /* The SEM order is
6008 
6009          v_lb, {e_b}, v_rb,
6010          e^{(k-1)-i}_l, {f^{i*(k-1)}}, e^i_r,
6011          v_lt, reverse {e_t}, v_rt
6012          */
6013         if (continuous) {
6014           const PetscInt of   = 0;
6015           const PetscInt oeb  = of + PetscSqr(k - 1);
6016           const PetscInt oer  = oeb + (k - 1);
6017           const PetscInt oet  = oer + (k - 1);
6018           const PetscInt oel  = oet + (k - 1);
6019           const PetscInt ovlb = oel + (k - 1);
6020           const PetscInt ovrb = ovlb + 1;
6021           const PetscInt ovrt = ovrb + 1;
6022           const PetscInt ovlt = ovrt + 1;
6023           PetscInt       o;
6024 
6025           /* bottom */
6026           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlb * Nc + c + foffset;
6027           for (o = oeb; o < oer; ++o)
6028             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6029           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrb * Nc + c + foffset;
6030           /* middle */
6031           for (i = 0; i < k - 1; ++i) {
6032             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oel + (k - 2) - i) * Nc + c + foffset;
6033             for (o = of + (k - 1) * i; o < of + (k - 1) * (i + 1); ++o)
6034               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6035             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oer + i) * Nc + c + foffset;
6036           }
6037           /* top */
6038           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlt * Nc + c + foffset;
6039           for (o = oel - 1; o >= oet; --o)
6040             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6041           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrt * Nc + c + foffset;
6042           foffset = offset;
6043         } else {
6044           PetscInt dof;
6045 
6046           PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
6047           for (i = 0; i < dof * Nc; ++i, ++offset) perm[offset] = i + foffset;
6048           foffset = offset;
6049         }
6050         break;
6051       case 3:
6052         /* The original hex closure is
6053 
6054          {c,
6055          f_b, f_t, f_f, f_b, f_r, f_l,
6056          e_bl, e_bb, e_br, e_bf,  e_tf, e_tr, e_tb, e_tl,  e_rf, e_lf, e_lb, e_rb,
6057          v_blf, v_blb, v_brb, v_brf, v_tlf, v_trf, v_trb, v_tlb}
6058          */
6059         PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
6060         if (!continuous && d < dim) continue;
6061         /* The SEM order is
6062          Bottom Slice
6063          v_blf, {e^{(k-1)-n}_bf}, v_brf,
6064          e^{i}_bl, f^{n*(k-1)+(k-1)-i}_b, e^{(k-1)-i}_br,
6065          v_blb, {e_bb}, v_brb,
6066 
6067          Middle Slice (j)
6068          {e^{(k-1)-j}_lf}, {f^{j*(k-1)+n}_f}, e^j_rf,
6069          f^{i*(k-1)+j}_l, {c^{(j*(k-1) + i)*(k-1)+n}_t}, f^{j*(k-1)+i}_r,
6070          e^j_lb, {f^{j*(k-1)+(k-1)-n}_b}, e^{(k-1)-j}_rb,
6071 
6072          Top Slice
6073          v_tlf, {e_tf}, v_trf,
6074          e^{(k-1)-i}_tl, {f^{i*(k-1)}_t}, e^{i}_tr,
6075          v_tlb, {e^{(k-1)-n}_tb}, v_trb,
6076          */
6077         if (continuous) {
6078           const PetscInt oc    = 0;
6079           const PetscInt ofb   = oc + PetscSqr(k - 1) * (k - 1);
6080           const PetscInt oft   = ofb + PetscSqr(k - 1);
6081           const PetscInt off   = oft + PetscSqr(k - 1);
6082           const PetscInt ofk   = off + PetscSqr(k - 1);
6083           const PetscInt ofr   = ofk + PetscSqr(k - 1);
6084           const PetscInt ofl   = ofr + PetscSqr(k - 1);
6085           const PetscInt oebl  = ofl + PetscSqr(k - 1);
6086           const PetscInt oebb  = oebl + (k - 1);
6087           const PetscInt oebr  = oebb + (k - 1);
6088           const PetscInt oebf  = oebr + (k - 1);
6089           const PetscInt oetf  = oebf + (k - 1);
6090           const PetscInt oetr  = oetf + (k - 1);
6091           const PetscInt oetb  = oetr + (k - 1);
6092           const PetscInt oetl  = oetb + (k - 1);
6093           const PetscInt oerf  = oetl + (k - 1);
6094           const PetscInt oelf  = oerf + (k - 1);
6095           const PetscInt oelb  = oelf + (k - 1);
6096           const PetscInt oerb  = oelb + (k - 1);
6097           const PetscInt ovblf = oerb + (k - 1);
6098           const PetscInt ovblb = ovblf + 1;
6099           const PetscInt ovbrb = ovblb + 1;
6100           const PetscInt ovbrf = ovbrb + 1;
6101           const PetscInt ovtlf = ovbrf + 1;
6102           const PetscInt ovtrf = ovtlf + 1;
6103           const PetscInt ovtrb = ovtrf + 1;
6104           const PetscInt ovtlb = ovtrb + 1;
6105           PetscInt       o, n;
6106 
6107           /* Bottom Slice */
6108           /*   bottom */
6109           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblf * Nc + c + foffset;
6110           for (o = oetf - 1; o >= oebf; --o)
6111             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6112           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrf * Nc + c + foffset;
6113           /*   middle */
6114           for (i = 0; i < k - 1; ++i) {
6115             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebl + i) * Nc + c + foffset;
6116             for (n = 0; n < k - 1; ++n) {
6117               o = ofb + n * (k - 1) + i;
6118               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6119             }
6120             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebr + (k - 2) - i) * Nc + c + foffset;
6121           }
6122           /*   top */
6123           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblb * Nc + c + foffset;
6124           for (o = oebb; o < oebr; ++o)
6125             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6126           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrb * Nc + c + foffset;
6127 
6128           /* Middle Slice */
6129           for (j = 0; j < k - 1; ++j) {
6130             /*   bottom */
6131             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelf + (k - 2) - j) * Nc + c + foffset;
6132             for (o = off + j * (k - 1); o < off + (j + 1) * (k - 1); ++o)
6133               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6134             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerf + j) * Nc + c + foffset;
6135             /*   middle */
6136             for (i = 0; i < k - 1; ++i) {
6137               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofl + i * (k - 1) + j) * Nc + c + foffset;
6138               for (n = 0; n < k - 1; ++n)
6139                 for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oc + (j * (k - 1) + i) * (k - 1) + n) * Nc + c + foffset;
6140               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofr + j * (k - 1) + i) * Nc + c + foffset;
6141             }
6142             /*   top */
6143             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelb + j) * Nc + c + foffset;
6144             for (o = ofk + j * (k - 1) + (k - 2); o >= ofk + j * (k - 1); --o)
6145               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6146             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerb + (k - 2) - j) * Nc + c + foffset;
6147           }
6148 
6149           /* Top Slice */
6150           /*   bottom */
6151           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlf * Nc + c + foffset;
6152           for (o = oetf; o < oetr; ++o)
6153             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6154           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrf * Nc + c + foffset;
6155           /*   middle */
6156           for (i = 0; i < k - 1; ++i) {
6157             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetl + (k - 2) - i) * Nc + c + foffset;
6158             for (n = 0; n < k - 1; ++n)
6159               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oft + i * (k - 1) + n) * Nc + c + foffset;
6160             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetr + i) * Nc + c + foffset;
6161           }
6162           /*   top */
6163           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlb * Nc + c + foffset;
6164           for (o = oetl - 1; o >= oetb; --o)
6165             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6166           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrb * Nc + c + foffset;
6167 
6168           foffset = offset;
6169         } else {
6170           PetscInt dof;
6171 
6172           PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
6173           for (i = 0; i < dof * Nc; ++i, ++offset) perm[offset] = i + foffset;
6174           foffset = offset;
6175         }
6176         break;
6177       default:
6178         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "No spectral ordering for dimension %" PetscInt_FMT, d);
6179       }
6180     }
6181     PetscCheck(offset == size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Number of permutation entries %" PetscInt_FMT " != %" PetscInt_FMT, offset, size);
6182     /* Check permutation */
6183     {
6184       PetscInt *check;
6185 
6186       PetscCall(PetscMalloc1(size, &check));
6187       for (i = 0; i < size; ++i) {
6188         check[i] = -1;
6189         PetscCheck(perm[i] >= 0 && perm[i] < size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid permutation index p[%" PetscInt_FMT "] = %" PetscInt_FMT, i, perm[i]);
6190       }
6191       for (i = 0; i < size; ++i) check[perm[i]] = i;
6192       for (i = 0; i < size; ++i) PetscCheck(check[i] >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Missing permutation index %" PetscInt_FMT, i);
6193       PetscCall(PetscFree(check));
6194     }
6195     PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size, PETSC_OWN_POINTER, perm));
6196     if (d == dim) { // Add permutation for localized (in case this is a coordinate DM)
6197       PetscInt *loc_perm;
6198       PetscCall(PetscMalloc1(size * 2, &loc_perm));
6199       for (PetscInt i = 0; i < size; i++) {
6200         loc_perm[i]        = perm[i];
6201         loc_perm[size + i] = size + perm[i];
6202       }
6203       PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size * 2, PETSC_OWN_POINTER, loc_perm));
6204     }
6205   }
6206   PetscFunctionReturn(PETSC_SUCCESS);
6207 }
6208 
6209 PetscErrorCode DMPlexGetPointDualSpaceFEM(DM dm, PetscInt point, PetscInt field, PetscDualSpace *dspace)
6210 {
6211   PetscDS  prob;
6212   PetscInt depth, Nf, h;
6213   DMLabel  label;
6214 
6215   PetscFunctionBeginHot;
6216   PetscCall(DMGetDS(dm, &prob));
6217   Nf      = prob->Nf;
6218   label   = dm->depthLabel;
6219   *dspace = NULL;
6220   if (field < Nf) {
6221     PetscObject disc = prob->disc[field];
6222 
6223     if (disc->classid == PETSCFE_CLASSID) {
6224       PetscDualSpace dsp;
6225 
6226       PetscCall(PetscFEGetDualSpace((PetscFE)disc, &dsp));
6227       PetscCall(DMLabelGetNumValues(label, &depth));
6228       PetscCall(DMLabelGetValue(label, point, &h));
6229       h = depth - 1 - h;
6230       if (h) {
6231         PetscCall(PetscDualSpaceGetHeightSubspace(dsp, h, dspace));
6232       } else {
6233         *dspace = dsp;
6234       }
6235     }
6236   }
6237   PetscFunctionReturn(PETSC_SUCCESS);
6238 }
6239 
6240 static inline PetscErrorCode DMPlexVecGetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6241 {
6242   PetscScalar       *array;
6243   const PetscScalar *vArray;
6244   const PetscInt    *cone, *coneO;
6245   PetscInt           pStart, pEnd, p, numPoints, size = 0, offset = 0;
6246 
6247   PetscFunctionBeginHot;
6248   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6249   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
6250   PetscCall(DMPlexGetCone(dm, point, &cone));
6251   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
6252   if (!values || !*values) {
6253     if ((point >= pStart) && (point < pEnd)) {
6254       PetscInt dof;
6255 
6256       PetscCall(PetscSectionGetDof(section, point, &dof));
6257       size += dof;
6258     }
6259     for (p = 0; p < numPoints; ++p) {
6260       const PetscInt cp = cone[p];
6261       PetscInt       dof;
6262 
6263       if ((cp < pStart) || (cp >= pEnd)) continue;
6264       PetscCall(PetscSectionGetDof(section, cp, &dof));
6265       size += dof;
6266     }
6267     if (!values) {
6268       if (csize) *csize = size;
6269       PetscFunctionReturn(PETSC_SUCCESS);
6270     }
6271     PetscCall(DMGetWorkArray(dm, size, MPIU_SCALAR, &array));
6272   } else {
6273     array = *values;
6274   }
6275   size = 0;
6276   PetscCall(VecGetArrayRead(v, &vArray));
6277   if ((point >= pStart) && (point < pEnd)) {
6278     PetscInt           dof, off, d;
6279     const PetscScalar *varr;
6280 
6281     PetscCall(PetscSectionGetDof(section, point, &dof));
6282     PetscCall(PetscSectionGetOffset(section, point, &off));
6283     varr = PetscSafePointerPlusOffset(vArray, off);
6284     for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
6285     size += dof;
6286   }
6287   for (p = 0; p < numPoints; ++p) {
6288     const PetscInt     cp = cone[p];
6289     PetscInt           o  = coneO[p];
6290     PetscInt           dof, off, d;
6291     const PetscScalar *varr;
6292 
6293     if ((cp < pStart) || (cp >= pEnd)) continue;
6294     PetscCall(PetscSectionGetDof(section, cp, &dof));
6295     PetscCall(PetscSectionGetOffset(section, cp, &off));
6296     varr = PetscSafePointerPlusOffset(vArray, off);
6297     if (o >= 0) {
6298       for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
6299     } else {
6300       for (d = dof - 1; d >= 0; --d, ++offset) array[offset] = varr[d];
6301     }
6302     size += dof;
6303   }
6304   PetscCall(VecRestoreArrayRead(v, &vArray));
6305   if (!*values) {
6306     if (csize) *csize = size;
6307     *values = array;
6308   } else {
6309     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
6310     *csize = size;
6311   }
6312   PetscFunctionReturn(PETSC_SUCCESS);
6313 }
6314 
6315 /* Compress out points not in the section */
6316 static inline PetscErrorCode CompressPoints_Private(PetscSection section, PetscInt *numPoints, PetscInt points[])
6317 {
6318   const PetscInt np = *numPoints;
6319   PetscInt       pStart, pEnd, p, q;
6320 
6321   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6322   for (p = 0, q = 0; p < np; ++p) {
6323     const PetscInt r = points[p * 2];
6324     if ((r >= pStart) && (r < pEnd)) {
6325       points[q * 2]     = r;
6326       points[q * 2 + 1] = points[p * 2 + 1];
6327       ++q;
6328     }
6329   }
6330   *numPoints = q;
6331   return PETSC_SUCCESS;
6332 }
6333 
6334 /* Compressed closure does not apply closure permutation */
6335 PetscErrorCode DMPlexGetCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt ornt, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
6336 {
6337   const PetscInt *cla = NULL;
6338   PetscInt        np, *pts = NULL;
6339 
6340   PetscFunctionBeginHot;
6341   PetscCall(PetscSectionGetClosureIndex(section, (PetscObject)dm, clSec, clPoints));
6342   if (!ornt && *clPoints) {
6343     PetscInt dof, off;
6344 
6345     PetscCall(PetscSectionGetDof(*clSec, point, &dof));
6346     PetscCall(PetscSectionGetOffset(*clSec, point, &off));
6347     PetscCall(ISGetIndices(*clPoints, &cla));
6348     np  = dof / 2;
6349     pts = PetscSafePointerPlusOffset((PetscInt *)cla, off);
6350   } else {
6351     PetscCall(DMPlexGetTransitiveClosure_Internal(dm, point, ornt, PETSC_TRUE, &np, &pts));
6352     PetscCall(CompressPoints_Private(section, &np, pts));
6353   }
6354   *numPoints = np;
6355   *points    = pts;
6356   *clp       = cla;
6357   PetscFunctionReturn(PETSC_SUCCESS);
6358 }
6359 
6360 PetscErrorCode DMPlexRestoreCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
6361 {
6362   PetscFunctionBeginHot;
6363   if (!*clPoints) {
6364     PetscCall(DMPlexRestoreTransitiveClosure(dm, point, PETSC_TRUE, numPoints, points));
6365   } else {
6366     PetscCall(ISRestoreIndices(*clPoints, clp));
6367   }
6368   *numPoints = 0;
6369   *points    = NULL;
6370   *clSec     = NULL;
6371   *clPoints  = NULL;
6372   *clp       = NULL;
6373   PetscFunctionReturn(PETSC_SUCCESS);
6374 }
6375 
6376 static inline PetscErrorCode DMPlexVecGetClosure_Static(DM dm, PetscSection section, PetscInt numPoints, const PetscInt points[], const PetscInt clperm[], const PetscScalar vArray[], PetscInt *size, PetscScalar array[])
6377 {
6378   PetscInt            offset = 0, p;
6379   const PetscInt    **perms  = NULL;
6380   const PetscScalar **flips  = NULL;
6381 
6382   PetscFunctionBeginHot;
6383   *size = 0;
6384   PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
6385   for (p = 0; p < numPoints; p++) {
6386     const PetscInt     point = points[2 * p];
6387     const PetscInt    *perm  = perms ? perms[p] : NULL;
6388     const PetscScalar *flip  = flips ? flips[p] : NULL;
6389     PetscInt           dof, off, d;
6390     const PetscScalar *varr;
6391 
6392     PetscCall(PetscSectionGetDof(section, point, &dof));
6393     PetscCall(PetscSectionGetOffset(section, point, &off));
6394     varr = PetscSafePointerPlusOffset(vArray, off);
6395     if (clperm) {
6396       if (perm) {
6397         for (d = 0; d < dof; d++) array[clperm[offset + perm[d]]] = varr[d];
6398       } else {
6399         for (d = 0; d < dof; d++) array[clperm[offset + d]] = varr[d];
6400       }
6401       if (flip) {
6402         for (d = 0; d < dof; d++) array[clperm[offset + d]] *= flip[d];
6403       }
6404     } else {
6405       if (perm) {
6406         for (d = 0; d < dof; d++) array[offset + perm[d]] = varr[d];
6407       } else {
6408         for (d = 0; d < dof; d++) array[offset + d] = varr[d];
6409       }
6410       if (flip) {
6411         for (d = 0; d < dof; d++) array[offset + d] *= flip[d];
6412       }
6413     }
6414     offset += dof;
6415   }
6416   PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
6417   *size = offset;
6418   PetscFunctionReturn(PETSC_SUCCESS);
6419 }
6420 
6421 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[])
6422 {
6423   PetscInt offset = 0, f;
6424 
6425   PetscFunctionBeginHot;
6426   *size = 0;
6427   for (f = 0; f < numFields; ++f) {
6428     PetscInt            p;
6429     const PetscInt    **perms = NULL;
6430     const PetscScalar **flips = NULL;
6431 
6432     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6433     for (p = 0; p < numPoints; p++) {
6434       const PetscInt     point = points[2 * p];
6435       PetscInt           fdof, foff, b;
6436       const PetscScalar *varr;
6437       const PetscInt    *perm = perms ? perms[p] : NULL;
6438       const PetscScalar *flip = flips ? flips[p] : NULL;
6439 
6440       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6441       PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6442       varr = &vArray[foff];
6443       if (clperm) {
6444         if (perm) {
6445           for (b = 0; b < fdof; b++) array[clperm[offset + perm[b]]] = varr[b];
6446         } else {
6447           for (b = 0; b < fdof; b++) array[clperm[offset + b]] = varr[b];
6448         }
6449         if (flip) {
6450           for (b = 0; b < fdof; b++) array[clperm[offset + b]] *= flip[b];
6451         }
6452       } else {
6453         if (perm) {
6454           for (b = 0; b < fdof; b++) array[offset + perm[b]] = varr[b];
6455         } else {
6456           for (b = 0; b < fdof; b++) array[offset + b] = varr[b];
6457         }
6458         if (flip) {
6459           for (b = 0; b < fdof; b++) array[offset + b] *= flip[b];
6460         }
6461       }
6462       offset += fdof;
6463     }
6464     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6465   }
6466   *size = offset;
6467   PetscFunctionReturn(PETSC_SUCCESS);
6468 }
6469 
6470 PetscErrorCode DMPlexVecGetOrientedClosure_Internal(DM dm, PetscSection section, PetscBool useClPerm, Vec v, PetscInt point, PetscInt ornt, PetscInt *csize, PetscScalar *values[])
6471 {
6472   PetscSection    clSection;
6473   IS              clPoints;
6474   PetscInt       *points = NULL;
6475   const PetscInt *clp, *perm = NULL;
6476   PetscInt        depth, numFields, numPoints, asize;
6477 
6478   PetscFunctionBeginHot;
6479   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6480   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6481   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6482   PetscValidHeaderSpecific(v, VEC_CLASSID, 4);
6483   PetscCall(DMPlexGetDepth(dm, &depth));
6484   PetscCall(PetscSectionGetNumFields(section, &numFields));
6485   if (depth == 1 && numFields < 2) {
6486     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6487     PetscFunctionReturn(PETSC_SUCCESS);
6488   }
6489   /* Get points */
6490   PetscCall(DMPlexGetCompressedClosure(dm, section, point, ornt, &numPoints, &points, &clSection, &clPoints, &clp));
6491   /* Get sizes */
6492   asize = 0;
6493   for (PetscInt p = 0; p < numPoints * 2; p += 2) {
6494     PetscInt dof;
6495     PetscCall(PetscSectionGetDof(section, points[p], &dof));
6496     asize += dof;
6497   }
6498   if (values) {
6499     const PetscScalar *vArray;
6500     PetscInt           size;
6501 
6502     if (*values) {
6503       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);
6504     } else PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, values));
6505     if (useClPerm) PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, asize, &perm));
6506     PetscCall(VecGetArrayRead(v, &vArray));
6507     /* Get values */
6508     if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, numPoints, points, numFields, perm, vArray, &size, *values));
6509     else PetscCall(DMPlexVecGetClosure_Static(dm, section, numPoints, points, perm, vArray, &size, *values));
6510     PetscCheck(asize == size, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Section size %" PetscInt_FMT " does not match Vec closure size %" PetscInt_FMT, asize, size);
6511     /* Cleanup array */
6512     PetscCall(VecRestoreArrayRead(v, &vArray));
6513   }
6514   if (csize) *csize = asize;
6515   /* Cleanup points */
6516   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6517   PetscFunctionReturn(PETSC_SUCCESS);
6518 }
6519 
6520 /*@C
6521   DMPlexVecGetClosure - Get an array of the values on the closure of 'point'
6522 
6523   Not collective
6524 
6525   Input Parameters:
6526 + dm      - The `DM`
6527 . section - The section describing the layout in `v`, or `NULL` to use the default section
6528 . v       - The local vector
6529 - point   - The point in the `DM`
6530 
6531   Input/Output Parameters:
6532 + csize  - The size of the input values array, or `NULL`; on output the number of values in the closure
6533 - values - An array to use for the values, or *values = `NULL` to have it allocated automatically;
6534            if the user provided `NULL`, it is a borrowed array and should not be freed, use  `DMPlexVecRestoreClosure()` to return it
6535 
6536   Level: intermediate
6537 
6538   Notes:
6539   `DMPlexVecGetClosure()`/`DMPlexVecRestoreClosure()` only allocates the values array if it set to `NULL` in the
6540   calling function. This is because `DMPlexVecGetClosure()` is typically called in the inner loop of a `Vec` or `Mat`
6541   assembly function, and a user may already have allocated storage for this operation.
6542 
6543   A typical use could be
6544 .vb
6545    values = NULL;
6546    PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
6547    for (cl = 0; cl < clSize; ++cl) {
6548      <Compute on closure>
6549    }
6550    PetscCall(DMPlexVecRestoreClosure(dm, NULL, v, p, &clSize, &values));
6551 .ve
6552   or
6553 .vb
6554    PetscMalloc1(clMaxSize, &values);
6555    for (p = pStart; p < pEnd; ++p) {
6556      clSize = clMaxSize;
6557      PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
6558      for (cl = 0; cl < clSize; ++cl) {
6559        <Compute on closure>
6560      }
6561    }
6562    PetscFree(values);
6563 .ve
6564 
6565   Fortran Notes:
6566   The `csize` argument is not present in the Fortran binding.
6567 
6568   `values` must be declared with
6569 .vb
6570   PetscScalar,dimension(:),pointer   :: values
6571 .ve
6572   and it will be allocated internally by PETSc to hold the values returned
6573 
6574 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecRestoreClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6575 @*/
6576 PetscErrorCode DMPlexVecGetClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6577 {
6578   PetscFunctionBeginHot;
6579   PetscCall(DMPlexVecGetOrientedClosure_Internal(dm, section, PETSC_TRUE, v, point, 0, csize, values));
6580   PetscFunctionReturn(PETSC_SUCCESS);
6581 }
6582 
6583 PetscErrorCode DMPlexVecGetClosureAtDepth_Internal(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt depth, PetscInt *csize, PetscScalar *values[])
6584 {
6585   DMLabel            depthLabel;
6586   PetscSection       clSection;
6587   IS                 clPoints;
6588   PetscScalar       *array;
6589   const PetscScalar *vArray;
6590   PetscInt          *points = NULL;
6591   const PetscInt    *clp, *perm = NULL;
6592   PetscInt           mdepth, numFields, numPoints, Np = 0, p, clsize, size;
6593 
6594   PetscFunctionBeginHot;
6595   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6596   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6597   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6598   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6599   PetscCall(DMPlexGetDepth(dm, &mdepth));
6600   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
6601   PetscCall(PetscSectionGetNumFields(section, &numFields));
6602   if (mdepth == 1 && numFields < 2) {
6603     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6604     PetscFunctionReturn(PETSC_SUCCESS);
6605   }
6606   /* Get points */
6607   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
6608   for (clsize = 0, p = 0; p < Np; p++) {
6609     PetscInt dof;
6610     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
6611     clsize += dof;
6612   }
6613   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &perm));
6614   /* Filter points */
6615   for (p = 0; p < numPoints * 2; p += 2) {
6616     PetscInt dep;
6617 
6618     PetscCall(DMLabelGetValue(depthLabel, points[p], &dep));
6619     if (dep != depth) continue;
6620     points[Np * 2 + 0] = points[p];
6621     points[Np * 2 + 1] = points[p + 1];
6622     ++Np;
6623   }
6624   /* Get array */
6625   if (!values || !*values) {
6626     PetscInt asize = 0, dof;
6627 
6628     for (p = 0; p < Np * 2; p += 2) {
6629       PetscCall(PetscSectionGetDof(section, points[p], &dof));
6630       asize += dof;
6631     }
6632     if (!values) {
6633       PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6634       if (csize) *csize = asize;
6635       PetscFunctionReturn(PETSC_SUCCESS);
6636     }
6637     PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, &array));
6638   } else {
6639     array = *values;
6640   }
6641   PetscCall(VecGetArrayRead(v, &vArray));
6642   /* Get values */
6643   if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, Np, points, numFields, perm, vArray, &size, array));
6644   else PetscCall(DMPlexVecGetClosure_Static(dm, section, Np, points, perm, vArray, &size, array));
6645   /* Cleanup points */
6646   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6647   /* Cleanup array */
6648   PetscCall(VecRestoreArrayRead(v, &vArray));
6649   if (!*values) {
6650     if (csize) *csize = size;
6651     *values = array;
6652   } else {
6653     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
6654     *csize = size;
6655   }
6656   PetscFunctionReturn(PETSC_SUCCESS);
6657 }
6658 
6659 /*@C
6660   DMPlexVecRestoreClosure - Restore the array of the values on the closure of 'point' obtained with `DMPlexVecGetClosure()`
6661 
6662   Not collective
6663 
6664   Input Parameters:
6665 + dm      - The `DM`
6666 . section - The section describing the layout in `v`, or `NULL` to use the default section
6667 . v       - The local vector
6668 . point   - The point in the `DM`
6669 . csize   - The number of values in the closure, or `NULL`
6670 - values  - The array of values
6671 
6672   Level: intermediate
6673 
6674   Note:
6675   The array values are discarded and not copied back into `v`. In order to copy values back to `v`, use `DMPlexVecSetClosure()`
6676 
6677   Fortran Note:
6678   The `csize` argument is not present in the Fortran binding since it is internal to the array.
6679 
6680 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6681 @*/
6682 PetscErrorCode DMPlexVecRestoreClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6683 {
6684   PetscInt size = 0;
6685 
6686   PetscFunctionBegin;
6687   /* Should work without recalculating size */
6688   PetscCall(DMRestoreWorkArray(dm, size, MPIU_SCALAR, (void *)values));
6689   *values = NULL;
6690   PetscFunctionReturn(PETSC_SUCCESS);
6691 }
6692 
6693 static inline void add(PetscScalar *x, PetscScalar y)
6694 {
6695   *x += y;
6696 }
6697 static inline void insert(PetscScalar *x, PetscScalar y)
6698 {
6699   *x = y;
6700 }
6701 
6702 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[])
6703 {
6704   PetscInt        cdof;  /* The number of constraints on this point */
6705   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6706   PetscScalar    *a;
6707   PetscInt        off, cind = 0, k;
6708 
6709   PetscFunctionBegin;
6710   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6711   PetscCall(PetscSectionGetOffset(section, point, &off));
6712   a = &array[off];
6713   if (!cdof || setBC) {
6714     if (clperm) {
6715       if (perm) {
6716         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6717       } else {
6718         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6719       }
6720     } else {
6721       if (perm) {
6722         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6723       } else {
6724         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6725       }
6726     }
6727   } else {
6728     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6729     if (clperm) {
6730       if (perm) {
6731         for (k = 0; k < dof; ++k) {
6732           if ((cind < cdof) && (k == cdofs[cind])) {
6733             ++cind;
6734             continue;
6735           }
6736           fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6737         }
6738       } else {
6739         for (k = 0; k < dof; ++k) {
6740           if ((cind < cdof) && (k == cdofs[cind])) {
6741             ++cind;
6742             continue;
6743           }
6744           fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6745         }
6746       }
6747     } else {
6748       if (perm) {
6749         for (k = 0; k < dof; ++k) {
6750           if ((cind < cdof) && (k == cdofs[cind])) {
6751             ++cind;
6752             continue;
6753           }
6754           fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6755         }
6756       } else {
6757         for (k = 0; k < dof; ++k) {
6758           if ((cind < cdof) && (k == cdofs[cind])) {
6759             ++cind;
6760             continue;
6761           }
6762           fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6763         }
6764       }
6765     }
6766   }
6767   PetscFunctionReturn(PETSC_SUCCESS);
6768 }
6769 
6770 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[])
6771 {
6772   PetscInt        cdof;  /* The number of constraints on this point */
6773   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6774   PetscScalar    *a;
6775   PetscInt        off, cind = 0, k;
6776 
6777   PetscFunctionBegin;
6778   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6779   PetscCall(PetscSectionGetOffset(section, point, &off));
6780   a = &array[off];
6781   if (cdof) {
6782     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6783     if (clperm) {
6784       if (perm) {
6785         for (k = 0; k < dof; ++k) {
6786           if ((cind < cdof) && (k == cdofs[cind])) {
6787             fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6788             cind++;
6789           }
6790         }
6791       } else {
6792         for (k = 0; k < dof; ++k) {
6793           if ((cind < cdof) && (k == cdofs[cind])) {
6794             fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6795             cind++;
6796           }
6797         }
6798       }
6799     } else {
6800       if (perm) {
6801         for (k = 0; k < dof; ++k) {
6802           if ((cind < cdof) && (k == cdofs[cind])) {
6803             fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6804             cind++;
6805           }
6806         }
6807       } else {
6808         for (k = 0; k < dof; ++k) {
6809           if ((cind < cdof) && (k == cdofs[cind])) {
6810             fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6811             cind++;
6812           }
6813         }
6814       }
6815     }
6816   }
6817   PetscFunctionReturn(PETSC_SUCCESS);
6818 }
6819 
6820 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[])
6821 {
6822   PetscScalar    *a;
6823   PetscInt        fdof, foff, fcdof, foffset = *offset;
6824   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6825   PetscInt        cind = 0, b;
6826 
6827   PetscFunctionBegin;
6828   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6829   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6830   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6831   a = &array[foff];
6832   if (!fcdof || setBC) {
6833     if (clperm) {
6834       if (perm) {
6835         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6836       } else {
6837         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6838       }
6839     } else {
6840       if (perm) {
6841         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6842       } else {
6843         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6844       }
6845     }
6846   } else {
6847     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6848     if (clperm) {
6849       if (perm) {
6850         for (b = 0; b < fdof; b++) {
6851           if ((cind < fcdof) && (b == fcdofs[cind])) {
6852             ++cind;
6853             continue;
6854           }
6855           fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6856         }
6857       } else {
6858         for (b = 0; b < fdof; b++) {
6859           if ((cind < fcdof) && (b == fcdofs[cind])) {
6860             ++cind;
6861             continue;
6862           }
6863           fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6864         }
6865       }
6866     } else {
6867       if (perm) {
6868         for (b = 0; b < fdof; b++) {
6869           if ((cind < fcdof) && (b == fcdofs[cind])) {
6870             ++cind;
6871             continue;
6872           }
6873           fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6874         }
6875       } else {
6876         for (b = 0; b < fdof; b++) {
6877           if ((cind < fcdof) && (b == fcdofs[cind])) {
6878             ++cind;
6879             continue;
6880           }
6881           fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6882         }
6883       }
6884     }
6885   }
6886   *offset += fdof;
6887   PetscFunctionReturn(PETSC_SUCCESS);
6888 }
6889 
6890 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[])
6891 {
6892   PetscScalar    *a;
6893   PetscInt        fdof, foff, fcdof, foffset = *offset;
6894   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6895   PetscInt        Nc, cind = 0, ncind = 0, b;
6896   PetscBool       ncSet, fcSet;
6897 
6898   PetscFunctionBegin;
6899   PetscCall(PetscSectionGetFieldComponents(section, f, &Nc));
6900   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6901   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6902   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6903   a = &array[foff];
6904   if (fcdof) {
6905     /* We just override fcdof and fcdofs with Ncc and comps */
6906     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6907     if (clperm) {
6908       if (perm) {
6909         if (comps) {
6910           for (b = 0; b < fdof; b++) {
6911             ncSet = fcSet = PETSC_FALSE;
6912             if (b % Nc == comps[ncind]) {
6913               ncind = (ncind + 1) % Ncc;
6914               ncSet = PETSC_TRUE;
6915             }
6916             if ((cind < fcdof) && (b == fcdofs[cind])) {
6917               ++cind;
6918               fcSet = PETSC_TRUE;
6919             }
6920             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6921           }
6922         } else {
6923           for (b = 0; b < fdof; b++) {
6924             if ((cind < fcdof) && (b == fcdofs[cind])) {
6925               fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6926               ++cind;
6927             }
6928           }
6929         }
6930       } else {
6931         if (comps) {
6932           for (b = 0; b < fdof; b++) {
6933             ncSet = fcSet = PETSC_FALSE;
6934             if (b % Nc == comps[ncind]) {
6935               ncind = (ncind + 1) % Ncc;
6936               ncSet = PETSC_TRUE;
6937             }
6938             if ((cind < fcdof) && (b == fcdofs[cind])) {
6939               ++cind;
6940               fcSet = PETSC_TRUE;
6941             }
6942             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6943           }
6944         } else {
6945           for (b = 0; b < fdof; b++) {
6946             if ((cind < fcdof) && (b == fcdofs[cind])) {
6947               fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6948               ++cind;
6949             }
6950           }
6951         }
6952       }
6953     } else {
6954       if (perm) {
6955         if (comps) {
6956           for (b = 0; b < fdof; b++) {
6957             ncSet = fcSet = PETSC_FALSE;
6958             if (b % Nc == comps[ncind]) {
6959               ncind = (ncind + 1) % Ncc;
6960               ncSet = PETSC_TRUE;
6961             }
6962             if ((cind < fcdof) && (b == fcdofs[cind])) {
6963               ++cind;
6964               fcSet = PETSC_TRUE;
6965             }
6966             if (ncSet && fcSet) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6967           }
6968         } else {
6969           for (b = 0; b < fdof; b++) {
6970             if ((cind < fcdof) && (b == fcdofs[cind])) {
6971               fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6972               ++cind;
6973             }
6974           }
6975         }
6976       } else {
6977         if (comps) {
6978           for (b = 0; b < fdof; b++) {
6979             ncSet = fcSet = PETSC_FALSE;
6980             if (b % Nc == comps[ncind]) {
6981               ncind = (ncind + 1) % Ncc;
6982               ncSet = PETSC_TRUE;
6983             }
6984             if ((cind < fcdof) && (b == fcdofs[cind])) {
6985               ++cind;
6986               fcSet = PETSC_TRUE;
6987             }
6988             if (ncSet && fcSet) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6989           }
6990         } else {
6991           for (b = 0; b < fdof; b++) {
6992             if ((cind < fcdof) && (b == fcdofs[cind])) {
6993               fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6994               ++cind;
6995             }
6996           }
6997         }
6998       }
6999     }
7000   }
7001   *offset += fdof;
7002   PetscFunctionReturn(PETSC_SUCCESS);
7003 }
7004 
7005 static inline PetscErrorCode DMPlexVecSetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
7006 {
7007   PetscScalar    *array;
7008   const PetscInt *cone, *coneO;
7009   PetscInt        pStart, pEnd, p, numPoints, off, dof;
7010 
7011   PetscFunctionBeginHot;
7012   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
7013   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
7014   PetscCall(DMPlexGetCone(dm, point, &cone));
7015   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
7016   PetscCall(VecGetArray(v, &array));
7017   for (p = 0, off = 0; p <= numPoints; ++p, off += dof) {
7018     const PetscInt cp = !p ? point : cone[p - 1];
7019     const PetscInt o  = !p ? 0 : coneO[p - 1];
7020 
7021     if ((cp < pStart) || (cp >= pEnd)) {
7022       dof = 0;
7023       continue;
7024     }
7025     PetscCall(PetscSectionGetDof(section, cp, &dof));
7026     /* ADD_VALUES */
7027     {
7028       const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
7029       PetscScalar    *a;
7030       PetscInt        cdof, coff, cind = 0, k;
7031 
7032       PetscCall(PetscSectionGetConstraintDof(section, cp, &cdof));
7033       PetscCall(PetscSectionGetOffset(section, cp, &coff));
7034       a = &array[coff];
7035       if (!cdof) {
7036         if (o >= 0) {
7037           for (k = 0; k < dof; ++k) a[k] += values[off + k];
7038         } else {
7039           for (k = 0; k < dof; ++k) a[k] += values[off + dof - k - 1];
7040         }
7041       } else {
7042         PetscCall(PetscSectionGetConstraintIndices(section, cp, &cdofs));
7043         if (o >= 0) {
7044           for (k = 0; k < dof; ++k) {
7045             if ((cind < cdof) && (k == cdofs[cind])) {
7046               ++cind;
7047               continue;
7048             }
7049             a[k] += values[off + k];
7050           }
7051         } else {
7052           for (k = 0; k < dof; ++k) {
7053             if ((cind < cdof) && (k == cdofs[cind])) {
7054               ++cind;
7055               continue;
7056             }
7057             a[k] += values[off + dof - k - 1];
7058           }
7059         }
7060       }
7061     }
7062   }
7063   PetscCall(VecRestoreArray(v, &array));
7064   PetscFunctionReturn(PETSC_SUCCESS);
7065 }
7066 
7067 /*@C
7068   DMPlexVecSetClosure - Set an array of the values on the closure of `point`
7069 
7070   Not collective
7071 
7072   Input Parameters:
7073 + dm      - The `DM`
7074 . section - The section describing the layout in `v`, or `NULL` to use the default section
7075 . v       - The local vector
7076 . point   - The point in the `DM`
7077 . values  - The array of values
7078 - mode    - The insert mode. One of `INSERT_ALL_VALUES`, `ADD_ALL_VALUES`, `INSERT_VALUES`, `ADD_VALUES`, `INSERT_BC_VALUES`, and `ADD_BC_VALUES`,
7079             where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions.
7080 
7081   Level: intermediate
7082 
7083   Note:
7084   Usually the input arrays were obtained with `DMPlexVecGetClosure()`
7085 
7086   Fortran Note:
7087   `values` must be declared with
7088 .vb
7089   PetscScalar,dimension(:),pointer   :: values
7090 .ve
7091 
7092 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`
7093 @*/
7094 PetscErrorCode DMPlexVecSetClosure(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
7095 {
7096   PetscSection    clSection;
7097   IS              clPoints;
7098   PetscScalar    *array;
7099   PetscInt       *points = NULL;
7100   const PetscInt *clp, *clperm = NULL;
7101   PetscInt        depth, numFields, numPoints, p, clsize;
7102 
7103   PetscFunctionBeginHot;
7104   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7105   if (!section) PetscCall(DMGetLocalSection(dm, &section));
7106   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7107   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
7108   PetscCall(DMPlexGetDepth(dm, &depth));
7109   PetscCall(PetscSectionGetNumFields(section, &numFields));
7110   if (depth == 1 && numFields < 2 && mode == ADD_VALUES) {
7111     PetscCall(DMPlexVecSetClosure_Depth1_Static(dm, section, v, point, values, mode));
7112     PetscFunctionReturn(PETSC_SUCCESS);
7113   }
7114   /* Get points */
7115   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
7116   for (clsize = 0, p = 0; p < numPoints; p++) {
7117     PetscInt dof;
7118     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
7119     clsize += dof;
7120   }
7121   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
7122   /* Get array */
7123   PetscCall(VecGetArray(v, &array));
7124   /* Get values */
7125   if (numFields > 0) {
7126     PetscInt offset = 0, f;
7127     for (f = 0; f < numFields; ++f) {
7128       const PetscInt    **perms = NULL;
7129       const PetscScalar **flips = NULL;
7130 
7131       PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7132       switch (mode) {
7133       case INSERT_VALUES:
7134         for (p = 0; p < numPoints; p++) {
7135           const PetscInt     point = points[2 * p];
7136           const PetscInt    *perm  = perms ? perms[p] : NULL;
7137           const PetscScalar *flip  = flips ? flips[p] : NULL;
7138           PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, clperm, values, &offset, array));
7139         }
7140         break;
7141       case INSERT_ALL_VALUES:
7142         for (p = 0; p < numPoints; p++) {
7143           const PetscInt     point = points[2 * p];
7144           const PetscInt    *perm  = perms ? perms[p] : NULL;
7145           const PetscScalar *flip  = flips ? flips[p] : NULL;
7146           PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, clperm, values, &offset, array));
7147         }
7148         break;
7149       case INSERT_BC_VALUES:
7150         for (p = 0; p < numPoints; p++) {
7151           const PetscInt     point = points[2 * p];
7152           const PetscInt    *perm  = perms ? perms[p] : NULL;
7153           const PetscScalar *flip  = flips ? flips[p] : NULL;
7154           PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, insert, clperm, values, &offset, array));
7155         }
7156         break;
7157       case ADD_VALUES:
7158         for (p = 0; p < numPoints; p++) {
7159           const PetscInt     point = points[2 * p];
7160           const PetscInt    *perm  = perms ? perms[p] : NULL;
7161           const PetscScalar *flip  = flips ? flips[p] : NULL;
7162           PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, clperm, values, &offset, array));
7163         }
7164         break;
7165       case ADD_ALL_VALUES:
7166         for (p = 0; p < numPoints; p++) {
7167           const PetscInt     point = points[2 * p];
7168           const PetscInt    *perm  = perms ? perms[p] : NULL;
7169           const PetscScalar *flip  = flips ? flips[p] : NULL;
7170           PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, clperm, values, &offset, array));
7171         }
7172         break;
7173       case ADD_BC_VALUES:
7174         for (p = 0; p < numPoints; p++) {
7175           const PetscInt     point = points[2 * p];
7176           const PetscInt    *perm  = perms ? perms[p] : NULL;
7177           const PetscScalar *flip  = flips ? flips[p] : NULL;
7178           PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, add, clperm, values, &offset, array));
7179         }
7180         break;
7181       default:
7182         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
7183       }
7184       PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7185     }
7186   } else {
7187     PetscInt            dof, off;
7188     const PetscInt    **perms = NULL;
7189     const PetscScalar **flips = NULL;
7190 
7191     PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
7192     switch (mode) {
7193     case INSERT_VALUES:
7194       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7195         const PetscInt     point = points[2 * p];
7196         const PetscInt    *perm  = perms ? perms[p] : NULL;
7197         const PetscScalar *flip  = flips ? flips[p] : NULL;
7198         PetscCall(PetscSectionGetDof(section, point, &dof));
7199         PetscCall(updatePoint_private(section, point, dof, insert, PETSC_FALSE, perm, flip, clperm, values, off, array));
7200       }
7201       break;
7202     case INSERT_ALL_VALUES:
7203       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7204         const PetscInt     point = points[2 * p];
7205         const PetscInt    *perm  = perms ? perms[p] : NULL;
7206         const PetscScalar *flip  = flips ? flips[p] : NULL;
7207         PetscCall(PetscSectionGetDof(section, point, &dof));
7208         PetscCall(updatePoint_private(section, point, dof, insert, PETSC_TRUE, perm, flip, clperm, values, off, array));
7209       }
7210       break;
7211     case INSERT_BC_VALUES:
7212       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7213         const PetscInt     point = points[2 * p];
7214         const PetscInt    *perm  = perms ? perms[p] : NULL;
7215         const PetscScalar *flip  = flips ? flips[p] : NULL;
7216         PetscCall(PetscSectionGetDof(section, point, &dof));
7217         PetscCall(updatePointBC_private(section, point, dof, insert, perm, flip, clperm, values, off, array));
7218       }
7219       break;
7220     case ADD_VALUES:
7221       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7222         const PetscInt     point = points[2 * p];
7223         const PetscInt    *perm  = perms ? perms[p] : NULL;
7224         const PetscScalar *flip  = flips ? flips[p] : NULL;
7225         PetscCall(PetscSectionGetDof(section, point, &dof));
7226         PetscCall(updatePoint_private(section, point, dof, add, PETSC_FALSE, perm, flip, clperm, values, off, array));
7227       }
7228       break;
7229     case ADD_ALL_VALUES:
7230       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7231         const PetscInt     point = points[2 * p];
7232         const PetscInt    *perm  = perms ? perms[p] : NULL;
7233         const PetscScalar *flip  = flips ? flips[p] : NULL;
7234         PetscCall(PetscSectionGetDof(section, point, &dof));
7235         PetscCall(updatePoint_private(section, point, dof, add, PETSC_TRUE, perm, flip, clperm, values, off, array));
7236       }
7237       break;
7238     case ADD_BC_VALUES:
7239       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7240         const PetscInt     point = points[2 * p];
7241         const PetscInt    *perm  = perms ? perms[p] : NULL;
7242         const PetscScalar *flip  = flips ? flips[p] : NULL;
7243         PetscCall(PetscSectionGetDof(section, point, &dof));
7244         PetscCall(updatePointBC_private(section, point, dof, add, perm, flip, clperm, values, off, array));
7245       }
7246       break;
7247     default:
7248       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
7249     }
7250     PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
7251   }
7252   /* Cleanup points */
7253   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
7254   /* Cleanup array */
7255   PetscCall(VecRestoreArray(v, &array));
7256   PetscFunctionReturn(PETSC_SUCCESS);
7257 }
7258 
7259 /* Check whether the given point is in the label. If not, update the offset to skip this point */
7260 static inline PetscErrorCode CheckPoint_Private(DMLabel label, PetscInt labelId, PetscSection section, PetscInt point, PetscInt f, PetscInt *offset, PetscBool *contains)
7261 {
7262   PetscFunctionBegin;
7263   *contains = PETSC_TRUE;
7264   if (label) {
7265     PetscInt fdof;
7266 
7267     PetscCall(DMLabelStratumHasPoint(label, labelId, point, contains));
7268     if (!*contains) {
7269       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7270       *offset += fdof;
7271       PetscFunctionReturn(PETSC_SUCCESS);
7272     }
7273   }
7274   PetscFunctionReturn(PETSC_SUCCESS);
7275 }
7276 
7277 /* Unlike DMPlexVecSetClosure(), this uses plex-native closure permutation, not a user-specified permutation such as DMPlexSetClosurePermutationTensor(). */
7278 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)
7279 {
7280   PetscSection    clSection;
7281   IS              clPoints;
7282   PetscScalar    *array;
7283   PetscInt       *points = NULL;
7284   const PetscInt *clp;
7285   PetscInt        numFields, numPoints, p;
7286   PetscInt        offset = 0, f;
7287 
7288   PetscFunctionBeginHot;
7289   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7290   if (!section) PetscCall(DMGetLocalSection(dm, &section));
7291   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7292   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
7293   PetscCall(PetscSectionGetNumFields(section, &numFields));
7294   /* Get points */
7295   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
7296   /* Get array */
7297   PetscCall(VecGetArray(v, &array));
7298   /* Get values */
7299   for (f = 0; f < numFields; ++f) {
7300     const PetscInt    **perms = NULL;
7301     const PetscScalar **flips = NULL;
7302     PetscBool           contains;
7303 
7304     if (!fieldActive[f]) {
7305       for (p = 0; p < numPoints * 2; p += 2) {
7306         PetscInt fdof;
7307         PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
7308         offset += fdof;
7309       }
7310       continue;
7311     }
7312     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7313     switch (mode) {
7314     case INSERT_VALUES:
7315       for (p = 0; p < numPoints; p++) {
7316         const PetscInt     point = points[2 * p];
7317         const PetscInt    *perm  = perms ? perms[p] : NULL;
7318         const PetscScalar *flip  = flips ? flips[p] : NULL;
7319         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7320         if (!contains) continue;
7321         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, NULL, values, &offset, array));
7322       }
7323       break;
7324     case INSERT_ALL_VALUES:
7325       for (p = 0; p < numPoints; p++) {
7326         const PetscInt     point = points[2 * p];
7327         const PetscInt    *perm  = perms ? perms[p] : NULL;
7328         const PetscScalar *flip  = flips ? flips[p] : NULL;
7329         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7330         if (!contains) continue;
7331         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, NULL, values, &offset, array));
7332       }
7333       break;
7334     case INSERT_BC_VALUES:
7335       for (p = 0; p < numPoints; p++) {
7336         const PetscInt     point = points[2 * p];
7337         const PetscInt    *perm  = perms ? perms[p] : NULL;
7338         const PetscScalar *flip  = flips ? flips[p] : NULL;
7339         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7340         if (!contains) continue;
7341         PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, Ncc, comps, insert, NULL, values, &offset, array));
7342       }
7343       break;
7344     case ADD_VALUES:
7345       for (p = 0; p < numPoints; p++) {
7346         const PetscInt     point = points[2 * p];
7347         const PetscInt    *perm  = perms ? perms[p] : NULL;
7348         const PetscScalar *flip  = flips ? flips[p] : NULL;
7349         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7350         if (!contains) continue;
7351         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, NULL, values, &offset, array));
7352       }
7353       break;
7354     case ADD_ALL_VALUES:
7355       for (p = 0; p < numPoints; p++) {
7356         const PetscInt     point = points[2 * p];
7357         const PetscInt    *perm  = perms ? perms[p] : NULL;
7358         const PetscScalar *flip  = flips ? flips[p] : NULL;
7359         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7360         if (!contains) continue;
7361         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, NULL, values, &offset, array));
7362       }
7363       break;
7364     default:
7365       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
7366     }
7367     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7368   }
7369   /* Cleanup points */
7370   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
7371   /* Cleanup array */
7372   PetscCall(VecRestoreArray(v, &array));
7373   PetscFunctionReturn(PETSC_SUCCESS);
7374 }
7375 
7376 static PetscErrorCode DMPlexPrintMatSetValues(PetscViewer viewer, Mat A, PetscInt point, PetscInt numRIndices, const PetscInt rindices[], PetscInt numCIndices, const PetscInt cindices[], const PetscScalar values[])
7377 {
7378   PetscMPIInt rank;
7379   PetscInt    i, j;
7380 
7381   PetscFunctionBegin;
7382   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7383   PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat for point %" PetscInt_FMT "\n", rank, point));
7384   for (i = 0; i < numRIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat row indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, rindices[i]));
7385   for (i = 0; i < numCIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat col indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, cindices[i]));
7386   numCIndices = numCIndices ? numCIndices : numRIndices;
7387   if (!values) PetscFunctionReturn(PETSC_SUCCESS);
7388   for (i = 0; i < numRIndices; i++) {
7389     PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]", rank));
7390     for (j = 0; j < numCIndices; j++) {
7391 #if defined(PETSC_USE_COMPLEX)
7392       PetscCall(PetscViewerASCIIPrintf(viewer, " (%g,%g)", (double)PetscRealPart(values[i * numCIndices + j]), (double)PetscImaginaryPart(values[i * numCIndices + j])));
7393 #else
7394       PetscCall(PetscViewerASCIIPrintf(viewer, " %g", (double)values[i * numCIndices + j]));
7395 #endif
7396     }
7397     PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
7398   }
7399   PetscFunctionReturn(PETSC_SUCCESS);
7400 }
7401 
7402 /*
7403   DMPlexGetIndicesPoint_Internal - Add the indices for dofs on a point to an index array
7404 
7405   Input Parameters:
7406 + section - The section for this data layout
7407 . islocal - Is the section (and thus indices being requested) local or global?
7408 . point   - The point contributing dofs with these indices
7409 . off     - The global offset of this point
7410 . loff    - The local offset of each field
7411 . setBC   - The flag determining whether to include indices of boundary values
7412 . perm    - A permutation of the dofs on this point, or NULL
7413 - indperm - A permutation of the entire indices array, or NULL
7414 
7415   Output Parameter:
7416 . indices - Indices for dofs on this point
7417 
7418   Level: developer
7419 
7420   Note: The indices could be local or global, depending on the value of 'off'.
7421 */
7422 PetscErrorCode DMPlexGetIndicesPoint_Internal(PetscSection section, PetscBool islocal, PetscInt point, PetscInt off, PetscInt *loff, PetscBool setBC, const PetscInt perm[], const PetscInt indperm[], PetscInt indices[])
7423 {
7424   PetscInt        dof;   /* The number of unknowns on this point */
7425   PetscInt        cdof;  /* The number of constraints on this point */
7426   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
7427   PetscInt        cind = 0, k;
7428 
7429   PetscFunctionBegin;
7430   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
7431   PetscCall(PetscSectionGetDof(section, point, &dof));
7432   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
7433   if (!cdof || setBC) {
7434     for (k = 0; k < dof; ++k) {
7435       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
7436       const PetscInt ind    = indperm ? indperm[preind] : preind;
7437 
7438       indices[ind] = off + k;
7439     }
7440   } else {
7441     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
7442     for (k = 0; k < dof; ++k) {
7443       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
7444       const PetscInt ind    = indperm ? indperm[preind] : preind;
7445 
7446       if ((cind < cdof) && (k == cdofs[cind])) {
7447         /* Insert check for returning constrained indices */
7448         indices[ind] = -(off + k + 1);
7449         ++cind;
7450       } else {
7451         indices[ind] = off + k - (islocal ? 0 : cind);
7452       }
7453     }
7454   }
7455   *loff += dof;
7456   PetscFunctionReturn(PETSC_SUCCESS);
7457 }
7458 
7459 /*
7460  DMPlexGetIndicesPointFields_Internal - gets section indices for a point in its canonical ordering.
7461 
7462  Input Parameters:
7463 + section - a section (global or local)
7464 - islocal - `PETSC_TRUE` if requesting local indices (i.e., section is local); `PETSC_FALSE` for global
7465 . point - point within section
7466 . off - The offset of this point in the (local or global) indexed space - should match islocal and (usually) the section
7467 . foffs - array of length numFields containing the offset in canonical point ordering (the location in indices) of each field
7468 . setBC - identify constrained (boundary condition) points via involution.
7469 . perms - perms[f][permsoff][:] is a permutation of dofs within each field
7470 . permsoff - offset
7471 - indperm - index permutation
7472 
7473  Output Parameter:
7474 . foffs - each entry is incremented by the number of (unconstrained if setBC=FALSE) dofs in that field
7475 . indices - array to hold indices (as defined by section) of each dof associated with point
7476 
7477  Notes:
7478  If section is local and setBC=true, there is no distinction between constrained and unconstrained dofs.
7479  If section is local and setBC=false, the indices for constrained points are the involution -(i+1) of their position
7480  in the local vector.
7481 
7482  If section is global and setBC=false, the indices for constrained points are negative (and their value is not
7483  significant).  It is invalid to call with a global section and setBC=true.
7484 
7485  Developer Note:
7486  The section is only used for field layout, so islocal is technically a statement about the offset (off).  At some point
7487  in the future, global sections may have fields set, in which case we could pass the global section and obtain the
7488  offset could be obtained from the section instead of passing it explicitly as we do now.
7489 
7490  Example:
7491  Suppose a point contains one field with three components, and for which the unconstrained indices are {10, 11, 12}.
7492  When the middle component is constrained, we get the array {10, -12, 12} for (islocal=TRUE, setBC=FALSE).
7493  Note that -12 is the involution of 11, so the user can involute negative indices to recover local indices.
7494  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.
7495 
7496  Level: developer
7497 */
7498 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[])
7499 {
7500   PetscInt numFields, foff, f;
7501 
7502   PetscFunctionBegin;
7503   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
7504   PetscCall(PetscSectionGetNumFields(section, &numFields));
7505   for (f = 0, foff = 0; f < numFields; ++f) {
7506     PetscInt        fdof, cfdof;
7507     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7508     PetscInt        cind = 0, b;
7509     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7510 
7511     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7512     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7513     if (!cfdof || setBC) {
7514       for (b = 0; b < fdof; ++b) {
7515         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7516         const PetscInt ind    = indperm ? indperm[preind] : preind;
7517 
7518         indices[ind] = off + foff + b;
7519       }
7520     } else {
7521       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7522       for (b = 0; b < fdof; ++b) {
7523         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7524         const PetscInt ind    = indperm ? indperm[preind] : preind;
7525 
7526         if ((cind < cfdof) && (b == fcdofs[cind])) {
7527           indices[ind] = -(off + foff + b + 1);
7528           ++cind;
7529         } else {
7530           indices[ind] = off + foff + b - (islocal ? 0 : cind);
7531         }
7532       }
7533     }
7534     foff += (setBC || islocal ? fdof : (fdof - cfdof));
7535     foffs[f] += fdof;
7536   }
7537   PetscFunctionReturn(PETSC_SUCCESS);
7538 }
7539 
7540 /*
7541   This version believes the globalSection offsets for each field, rather than just the point offset
7542 
7543  . foffs - The offset into 'indices' for each field, since it is segregated by field
7544 
7545  Notes:
7546  The semantics of this function relate to that of setBC=FALSE in DMPlexGetIndicesPointFields_Internal.
7547  Since this function uses global indices, setBC=TRUE would be invalid, so no such argument exists.
7548 */
7549 static PetscErrorCode DMPlexGetIndicesPointFieldsSplit_Internal(PetscSection section, PetscSection globalSection, PetscInt point, PetscInt foffs[], const PetscInt ***perms, PetscInt permsoff, const PetscInt indperm[], PetscInt indices[])
7550 {
7551   PetscInt numFields, foff, f;
7552 
7553   PetscFunctionBegin;
7554   PetscCall(PetscSectionGetNumFields(section, &numFields));
7555   for (f = 0; f < numFields; ++f) {
7556     PetscInt        fdof, cfdof;
7557     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7558     PetscInt        cind = 0, b;
7559     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7560 
7561     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7562     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7563     PetscCall(PetscSectionGetFieldOffset(globalSection, point, f, &foff));
7564     if (!cfdof) {
7565       for (b = 0; b < fdof; ++b) {
7566         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7567         const PetscInt ind    = indperm ? indperm[preind] : preind;
7568 
7569         indices[ind] = foff + b;
7570       }
7571     } else {
7572       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7573       for (b = 0; b < fdof; ++b) {
7574         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7575         const PetscInt ind    = indperm ? indperm[preind] : preind;
7576 
7577         if ((cind < cfdof) && (b == fcdofs[cind])) {
7578           indices[ind] = -(foff + b + 1);
7579           ++cind;
7580         } else {
7581           indices[ind] = foff + b - cind;
7582         }
7583       }
7584     }
7585     foffs[f] += fdof;
7586   }
7587   PetscFunctionReturn(PETSC_SUCCESS);
7588 }
7589 
7590 static PetscErrorCode DMPlexAnchorsGetSubMatIndices(PetscInt nPoints, const PetscInt pnts[], PetscSection section, PetscSection cSec, PetscInt tmpIndices[], PetscInt fieldOffsets[], PetscInt indices[], const PetscInt ***perms)
7591 {
7592   PetscInt numFields, sStart, sEnd, cStart, cEnd;
7593 
7594   PetscFunctionBegin;
7595   PetscCall(PetscSectionGetNumFields(section, &numFields));
7596   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
7597   PetscCall(PetscSectionGetChart(cSec, &cStart, &cEnd));
7598   for (PetscInt p = 0; p < nPoints; p++) {
7599     PetscInt     b       = pnts[2 * p];
7600     PetscInt     bSecDof = 0, bOff;
7601     PetscInt     cSecDof = 0;
7602     PetscSection indices_section;
7603 
7604     if (b >= sStart && b < sEnd) PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7605     if (!bSecDof) continue;
7606     if (b >= cStart && b < cEnd) PetscCall(PetscSectionGetDof(cSec, b, &cSecDof));
7607     indices_section = cSecDof > 0 ? cSec : section;
7608     if (numFields) {
7609       PetscInt fStart[32], fEnd[32];
7610 
7611       fStart[0] = 0;
7612       fEnd[0]   = 0;
7613       for (PetscInt f = 0; f < numFields; f++) {
7614         PetscInt fDof = 0;
7615 
7616         PetscCall(PetscSectionGetFieldDof(indices_section, b, f, &fDof));
7617         fStart[f + 1] = fStart[f] + fDof;
7618         fEnd[f + 1]   = fStart[f + 1];
7619       }
7620       PetscCall(PetscSectionGetOffset(indices_section, b, &bOff));
7621       // only apply permutations on one side
7622       PetscCall(DMPlexGetIndicesPointFields_Internal(indices_section, PETSC_TRUE, b, bOff, fEnd, PETSC_TRUE, perms, perms ? p : -1, NULL, tmpIndices));
7623       for (PetscInt f = 0; f < numFields; f++) {
7624         for (PetscInt i = fStart[f]; i < fEnd[f]; i++) { indices[fieldOffsets[f]++] = (cSecDof > 0) ? tmpIndices[i] : -(tmpIndices[i] + 1); }
7625       }
7626     } else {
7627       PetscInt bEnd = 0;
7628 
7629       PetscCall(PetscSectionGetOffset(indices_section, b, &bOff));
7630       PetscCall(DMPlexGetIndicesPoint_Internal(indices_section, PETSC_TRUE, b, bOff, &bEnd, PETSC_TRUE, (perms && perms[0]) ? perms[0][p] : NULL, NULL, tmpIndices));
7631 
7632       for (PetscInt i = 0; i < bEnd; i++) indices[fieldOffsets[0]++] = (cSecDof > 0) ? tmpIndices[i] : -(tmpIndices[i] + 1);
7633     }
7634   }
7635   PetscFunctionReturn(PETSC_SUCCESS);
7636 }
7637 
7638 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[])
7639 {
7640   Mat             cMat;
7641   PetscSection    aSec, cSec;
7642   IS              aIS;
7643   PetscInt        aStart = -1, aEnd = -1;
7644   PetscInt        sStart = -1, sEnd = -1;
7645   PetscInt        cStart = -1, cEnd = -1;
7646   const PetscInt *anchors;
7647   PetscInt        numFields, p;
7648   PetscInt        newNumPoints = 0, newNumIndices = 0;
7649   PetscInt       *newPoints, *indices, *newIndices, *tmpIndices, *tmpNewIndices;
7650   PetscInt        oldOffsets[32];
7651   PetscInt        newOffsets[32];
7652   PetscInt        oldOffsetsCopy[32];
7653   PetscInt        newOffsetsCopy[32];
7654   PetscScalar    *modMat         = NULL;
7655   PetscBool       anyConstrained = PETSC_FALSE;
7656 
7657   PetscFunctionBegin;
7658   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7659   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7660   PetscCall(PetscSectionGetNumFields(section, &numFields));
7661 
7662   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
7663   /* if there are point-to-point constraints */
7664   if (aSec) {
7665     PetscCall(PetscArrayzero(newOffsets, 32));
7666     PetscCall(PetscArrayzero(oldOffsets, 32));
7667     PetscCall(ISGetIndices(aIS, &anchors));
7668     PetscCall(PetscSectionGetChart(aSec, &aStart, &aEnd));
7669     PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
7670     /* figure out how many points are going to be in the new element matrix
7671      * (we allow double counting, because it's all just going to be summed
7672      * into the global matrix anyway) */
7673     for (p = 0; p < 2 * numPoints; p += 2) {
7674       PetscInt b    = points[p];
7675       PetscInt bDof = 0, bSecDof = 0;
7676 
7677       if (b >= sStart && b < sEnd) PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7678       if (!bSecDof) continue;
7679 
7680       for (PetscInt f = 0; f < numFields; f++) {
7681         PetscInt fDof = 0;
7682 
7683         PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7684         oldOffsets[f + 1] += fDof;
7685       }
7686       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7687       if (bDof) {
7688         /* this point is constrained */
7689         /* it is going to be replaced by its anchors */
7690         PetscInt bOff, q;
7691 
7692         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7693         for (q = 0; q < bDof; q++) {
7694           PetscInt a    = anchors[bOff + q];
7695           PetscInt aDof = 0;
7696 
7697           if (a >= sStart && a < sEnd) PetscCall(PetscSectionGetDof(section, a, &aDof));
7698           if (aDof) {
7699             anyConstrained = PETSC_TRUE;
7700             newNumPoints += 1;
7701           }
7702           newNumIndices += aDof;
7703           for (PetscInt f = 0; f < numFields; ++f) {
7704             PetscInt fDof = 0;
7705 
7706             if (a >= sStart && a < sEnd) PetscCall(PetscSectionGetFieldDof(section, a, f, &fDof));
7707             newOffsets[f + 1] += fDof;
7708           }
7709         }
7710       } else {
7711         /* this point is not constrained */
7712         newNumPoints++;
7713         newNumIndices += bSecDof;
7714         for (PetscInt f = 0; f < numFields; ++f) {
7715           PetscInt fDof;
7716 
7717           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7718           newOffsets[f + 1] += fDof;
7719         }
7720       }
7721     }
7722   }
7723   if (!anyConstrained) {
7724     if (outNumPoints) *outNumPoints = 0;
7725     if (outNumIndices) *outNumIndices = 0;
7726     if (outPoints) *outPoints = NULL;
7727     if (outMat) *outMat = NULL;
7728     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7729     PetscFunctionReturn(PETSC_SUCCESS);
7730   }
7731 
7732   if (outNumPoints) *outNumPoints = newNumPoints;
7733   if (outNumIndices) *outNumIndices = newNumIndices;
7734 
7735   for (PetscInt f = 0; f < numFields; ++f) newOffsets[f + 1] += newOffsets[f];
7736   for (PetscInt f = 0; f < numFields; ++f) oldOffsets[f + 1] += oldOffsets[f];
7737 
7738   if (!outPoints && !outMat) {
7739     if (offsets) {
7740       for (PetscInt f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7741     }
7742     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7743     PetscFunctionReturn(PETSC_SUCCESS);
7744   }
7745 
7746   PetscCheck(!numFields || newOffsets[numFields] == newNumIndices, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, newOffsets[numFields], newNumIndices);
7747   PetscCheck(!numFields || oldOffsets[numFields] == numIndices, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, oldOffsets[numFields], numIndices);
7748 
7749   PetscCall(DMGetDefaultConstraints(dm, &cSec, &cMat, NULL));
7750   PetscCall(PetscSectionGetChart(cSec, &cStart, &cEnd));
7751 
7752   /* output arrays */
7753   PetscCall(DMGetWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7754   PetscCall(PetscArrayzero(newPoints, 2 * newNumPoints));
7755 
7756   // get the new Points
7757   for (PetscInt p = 0, newP = 0; p < numPoints; p++) {
7758     PetscInt b    = points[2 * p];
7759     PetscInt bDof = 0, bSecDof = 0, bOff;
7760 
7761     if (b >= sStart && b < sEnd) PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7762     if (!bSecDof) continue;
7763     if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7764     if (bDof) {
7765       PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7766       for (PetscInt q = 0; q < bDof; q++) {
7767         PetscInt a = anchors[bOff + q], aDof = 0;
7768 
7769         if (a >= sStart && a < sEnd) PetscCall(PetscSectionGetDof(section, a, &aDof));
7770         if (aDof) {
7771           newPoints[2 * newP]     = a;
7772           newPoints[2 * newP + 1] = 0; // orientations are accounted for in constructing the matrix, newly added points are in default orientation
7773           newP++;
7774         }
7775       }
7776     } else {
7777       newPoints[2 * newP]     = b;
7778       newPoints[2 * newP + 1] = points[2 * p + 1];
7779       newP++;
7780     }
7781   }
7782 
7783   if (outMat) {
7784     PetscScalar *tmpMat;
7785     PetscCall(PetscArraycpy(oldOffsetsCopy, oldOffsets, 32));
7786     PetscCall(PetscArraycpy(newOffsetsCopy, newOffsets, 32));
7787 
7788     PetscCall(DMGetWorkArray(dm, numIndices, MPIU_INT, &indices));
7789     PetscCall(DMGetWorkArray(dm, numIndices, MPIU_INT, &tmpIndices));
7790     PetscCall(DMGetWorkArray(dm, newNumIndices, MPIU_INT, &newIndices));
7791     PetscCall(DMGetWorkArray(dm, newNumIndices, MPIU_INT, &tmpNewIndices));
7792 
7793     for (PetscInt i = 0; i < numIndices; i++) indices[i] = -1;
7794     for (PetscInt i = 0; i < newNumIndices; i++) newIndices[i] = -1;
7795 
7796     PetscCall(DMPlexAnchorsGetSubMatIndices(numPoints, points, section, cSec, tmpIndices, oldOffsetsCopy, indices, perms));
7797     PetscCall(DMPlexAnchorsGetSubMatIndices(newNumPoints, newPoints, section, section, tmpNewIndices, newOffsetsCopy, newIndices, NULL));
7798 
7799     PetscCall(DMGetWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &modMat));
7800     PetscCall(DMGetWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &tmpMat));
7801     PetscCall(PetscArrayzero(modMat, newNumIndices * numIndices));
7802     // for each field, insert the anchor modification into modMat
7803     for (PetscInt f = 0; f < PetscMax(1, numFields); f++) {
7804       PetscInt fStart    = oldOffsets[f];
7805       PetscInt fNewStart = newOffsets[f];
7806       for (PetscInt p = 0, newP = 0, o = fStart, oNew = fNewStart; p < numPoints; p++) {
7807         PetscInt b    = points[2 * p];
7808         PetscInt bDof = 0, bSecDof = 0, bOff;
7809 
7810         if (b >= sStart && b < sEnd) {
7811           if (numFields) {
7812             PetscCall(PetscSectionGetFieldDof(section, b, f, &bSecDof));
7813           } else {
7814             PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7815           }
7816         }
7817         if (!bSecDof) continue;
7818         if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7819         if (bDof) {
7820           PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7821           for (PetscInt q = 0; q < bDof; q++, newP++) {
7822             PetscInt a = anchors[bOff + q], aDof = 0;
7823 
7824             if (a >= sStart && a < sEnd) {
7825               if (numFields) {
7826                 PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
7827               } else {
7828                 PetscCall(PetscSectionGetDof(section, a, &aDof));
7829               }
7830             }
7831             if (aDof) {
7832               PetscCall(MatGetValues(cMat, bSecDof, &indices[o], aDof, &newIndices[oNew], tmpMat));
7833               for (PetscInt d = 0; d < bSecDof; d++) {
7834                 for (PetscInt e = 0; e < aDof; e++) modMat[(o + d) * newNumIndices + oNew + e] = tmpMat[d * aDof + e];
7835               }
7836             }
7837             oNew += aDof;
7838           }
7839         } else {
7840           // Insert the identity matrix in this block
7841           for (PetscInt d = 0; d < bSecDof; d++) modMat[(o + d) * newNumIndices + oNew + d] = 1;
7842           oNew += bSecDof;
7843           newP++;
7844         }
7845         o += bSecDof;
7846       }
7847     }
7848 
7849     *outMat = modMat;
7850 
7851     PetscCall(DMRestoreWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &tmpMat));
7852     PetscCall(DMRestoreWorkArray(dm, newNumIndices, MPIU_INT, &tmpNewIndices));
7853     PetscCall(DMRestoreWorkArray(dm, newNumIndices, MPIU_INT, &newIndices));
7854     PetscCall(DMRestoreWorkArray(dm, numIndices, MPIU_INT, &tmpIndices));
7855     PetscCall(DMRestoreWorkArray(dm, numIndices, MPIU_INT, &indices));
7856   }
7857   PetscCall(ISRestoreIndices(aIS, &anchors));
7858 
7859   /* output */
7860   if (outPoints) {
7861     *outPoints = newPoints;
7862   } else {
7863     PetscCall(DMRestoreWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7864   }
7865   for (PetscInt f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7866   PetscFunctionReturn(PETSC_SUCCESS);
7867 }
7868 
7869 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)
7870 {
7871   PetscScalar *modMat        = NULL;
7872   PetscInt     newNumIndices = -1;
7873 
7874   PetscFunctionBegin;
7875   /* 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.
7876      modMat is that matrix C */
7877   PetscCall(DMPlexAnchorsGetSubMatModification(dm, section, numPoints, numIndices, points, perms, outNumPoints, &newNumIndices, outPoints, offsets, outValues ? &modMat : NULL));
7878   if (outNumIndices) *outNumIndices = newNumIndices;
7879   if (modMat) {
7880     const PetscScalar *newValues = values;
7881 
7882     if (multiplyRight) {
7883       PetscScalar *newNewValues = NULL;
7884       PetscBLASInt M            = newNumIndices;
7885       PetscBLASInt N            = numRows;
7886       PetscBLASInt K            = numIndices;
7887       PetscScalar  a = 1.0, b = 0.0;
7888 
7889       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);
7890 
7891       PetscCall(DMGetWorkArray(dm, numRows * newNumIndices, MPIU_SCALAR, &newNewValues));
7892       // row-major to column-major conversion, right multiplication becomes left multiplication
7893       PetscCallBLAS("BLASgemm", BLASgemm_("N", "N", &M, &N, &K, &a, modMat, &M, newValues, &K, &b, newNewValues, &M));
7894 
7895       numCols   = newNumIndices;
7896       newValues = newNewValues;
7897     }
7898 
7899     if (multiplyLeft) {
7900       PetscScalar *newNewValues = NULL;
7901       PetscBLASInt M            = numCols;
7902       PetscBLASInt N            = newNumIndices;
7903       PetscBLASInt K            = numIndices;
7904       PetscScalar  a = 1.0, b = 0.0;
7905 
7906       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);
7907 
7908       PetscCall(DMGetWorkArray(dm, newNumIndices * numCols, MPIU_SCALAR, &newNewValues));
7909       // row-major to column-major conversion, left multiplication becomes right multiplication
7910       PetscCallBLAS("BLASgemm", BLASgemm_("N", "T", &M, &N, &K, &a, newValues, &M, modMat, &N, &b, newNewValues, &M));
7911       if (newValues != values) PetscCall(DMRestoreWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &newValues));
7912       newValues = newNewValues;
7913     }
7914     *outValues = (PetscScalar *)newValues;
7915     PetscCall(DMRestoreWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &modMat));
7916   }
7917   PetscFunctionReturn(PETSC_SUCCESS);
7918 }
7919 
7920 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)
7921 {
7922   PetscFunctionBegin;
7923   PetscCall(DMPlexAnchorsModifyMat_Internal(dm, section, numPoints, numIndices, points, perms, numIndices, numIndices, values, outNumPoints, outNumIndices, outPoints, outValues, offsets, PETSC_TRUE, multiplyLeft));
7924   PetscFunctionReturn(PETSC_SUCCESS);
7925 }
7926 
7927 static PetscErrorCode DMPlexGetClosureIndicesSize_Internal(DM dm, PetscSection section, PetscInt point, PetscInt *closureSize)
7928 {
7929   /* Closure ordering */
7930   PetscSection    clSection;
7931   IS              clPoints;
7932   const PetscInt *clp;
7933   PetscInt       *points;
7934   PetscInt        Ncl, Ni = 0;
7935 
7936   PetscFunctionBeginHot;
7937   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &Ncl, &points, &clSection, &clPoints, &clp));
7938   for (PetscInt p = 0; p < Ncl * 2; p += 2) {
7939     PetscInt dof;
7940 
7941     PetscCall(PetscSectionGetDof(section, points[p], &dof));
7942     Ni += dof;
7943   }
7944   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7945   *closureSize = Ni;
7946   PetscFunctionReturn(PETSC_SUCCESS);
7947 }
7948 
7949 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)
7950 {
7951   /* Closure ordering */
7952   PetscSection    clSection;
7953   IS              clPoints;
7954   const PetscInt *clp;
7955   PetscInt       *points;
7956   const PetscInt *clperm = NULL;
7957   /* Dof permutation and sign flips */
7958   const PetscInt    **perms[32] = {NULL};
7959   const PetscScalar **flips[32] = {NULL};
7960   PetscScalar        *valCopy   = NULL;
7961   /* Hanging node constraints */
7962   PetscInt    *pointsC = NULL;
7963   PetscScalar *valuesC = NULL;
7964   PetscInt     NclC, NiC;
7965 
7966   PetscInt *idx;
7967   PetscInt  Nf, Ncl, Ni = 0, offsets[32], p, f;
7968   PetscBool isLocal = (section == idxSection) ? PETSC_TRUE : PETSC_FALSE;
7969   PetscInt  idxStart, idxEnd;
7970   PetscInt  nRows, nCols;
7971 
7972   PetscFunctionBeginHot;
7973   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7974   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7975   PetscValidHeaderSpecific(idxSection, PETSC_SECTION_CLASSID, 3);
7976   PetscAssertPointer(numRows, 6);
7977   PetscAssertPointer(numCols, 7);
7978   if (indices) PetscAssertPointer(indices, 8);
7979   if (outOffsets) PetscAssertPointer(outOffsets, 9);
7980   if (values) PetscAssertPointer(values, 10);
7981   PetscCall(PetscSectionGetNumFields(section, &Nf));
7982   PetscCheck(Nf <= 31, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", Nf);
7983   PetscCall(PetscArrayzero(offsets, 32));
7984   /* 1) Get points in closure */
7985   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &Ncl, &points, &clSection, &clPoints, &clp));
7986   if (useClPerm) {
7987     PetscInt depth, clsize;
7988     PetscCall(DMPlexGetPointDepth(dm, point, &depth));
7989     for (clsize = 0, p = 0; p < Ncl; p++) {
7990       PetscInt dof;
7991       PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
7992       clsize += dof;
7993     }
7994     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
7995   }
7996   /* 2) Get number of indices on these points and field offsets from section */
7997   for (p = 0; p < Ncl * 2; p += 2) {
7998     PetscInt dof, fdof;
7999 
8000     PetscCall(PetscSectionGetDof(section, points[p], &dof));
8001     for (f = 0; f < Nf; ++f) {
8002       PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
8003       offsets[f + 1] += fdof;
8004     }
8005     Ni += dof;
8006   }
8007   if (*numRows == -1) *numRows = Ni;
8008   if (*numCols == -1) *numCols = Ni;
8009   nRows = *numRows;
8010   nCols = *numCols;
8011   for (f = 1; f < Nf; ++f) offsets[f + 1] += offsets[f];
8012   PetscCheck(!Nf || offsets[Nf] == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, offsets[Nf], Ni);
8013   /* 3) Get symmetries and sign flips. Apply sign flips to values if passed in (only works for square values matrix) */
8014   if (multiplyRight) PetscCheck(nCols == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Expected %" PetscInt_FMT " columns, got %" PetscInt_FMT, Ni, nCols);
8015   if (multiplyLeft) PetscCheck(nRows == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Expected %" PetscInt_FMT " rows, got %" PetscInt_FMT, Ni, nRows);
8016   for (f = 0; f < PetscMax(1, Nf); ++f) {
8017     if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
8018     else PetscCall(PetscSectionGetPointSyms(section, Ncl, points, &perms[f], &flips[f]));
8019     /* may need to apply sign changes to the element matrix */
8020     if (values && flips[f]) {
8021       PetscInt foffset = offsets[f];
8022 
8023       for (p = 0; p < Ncl; ++p) {
8024         PetscInt           pnt  = points[2 * p], fdof;
8025         const PetscScalar *flip = flips[f] ? flips[f][p] : NULL;
8026 
8027         if (!Nf) PetscCall(PetscSectionGetDof(section, pnt, &fdof));
8028         else PetscCall(PetscSectionGetFieldDof(section, pnt, f, &fdof));
8029         if (flip) {
8030           PetscInt i, j, k;
8031 
8032           if (!valCopy) {
8033             PetscCall(DMGetWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
8034             for (j = 0; j < Ni * Ni; ++j) valCopy[j] = (*values)[j];
8035             *values = valCopy;
8036           }
8037           for (i = 0; i < fdof; ++i) {
8038             PetscScalar fval = flip[i];
8039 
8040             if (multiplyRight) {
8041               for (k = 0; k < nRows; ++k) { valCopy[Ni * k + (foffset + i)] *= fval; }
8042             }
8043             if (multiplyLeft) {
8044               for (k = 0; k < nCols; ++k) { valCopy[nCols * (foffset + i) + k] *= fval; }
8045             }
8046           }
8047         }
8048         foffset += fdof;
8049       }
8050     }
8051   }
8052   /* 4) Apply hanging node constraints. Get new symmetries and replace all storage with constrained storage */
8053   PetscCall(DMPlexAnchorsModifyMat_Internal(dm, section, Ncl, Ni, points, perms, nRows, nCols, values ? *values : NULL, &NclC, &NiC, &pointsC, values ? &valuesC : NULL, offsets, multiplyRight, multiplyLeft));
8054   if (NclC) {
8055     if (multiplyRight) { *numCols = nCols = NiC; }
8056     if (multiplyLeft) { *numRows = nRows = NiC; }
8057     if (valCopy) PetscCall(DMRestoreWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
8058     for (f = 0; f < PetscMax(1, Nf); ++f) {
8059       if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
8060       else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
8061     }
8062     for (f = 0; f < PetscMax(1, Nf); ++f) {
8063       if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, NclC, pointsC, &perms[f], &flips[f]));
8064       else PetscCall(PetscSectionGetPointSyms(section, NclC, pointsC, &perms[f], &flips[f]));
8065     }
8066     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
8067     Ncl    = NclC;
8068     Ni     = NiC;
8069     points = pointsC;
8070     if (values) *values = valuesC;
8071   }
8072   /* 5) Calculate indices */
8073   PetscCall(DMGetWorkArray(dm, Ni, MPIU_INT, &idx));
8074   PetscCall(PetscSectionGetChart(idxSection, &idxStart, &idxEnd));
8075   if (Nf) {
8076     PetscInt  idxOff;
8077     PetscBool useFieldOffsets;
8078 
8079     if (outOffsets) {
8080       for (f = 0; f <= Nf; f++) outOffsets[f] = offsets[f];
8081     }
8082     PetscCall(PetscSectionGetUseFieldOffsets(idxSection, &useFieldOffsets));
8083     if (useFieldOffsets) {
8084       for (p = 0; p < Ncl; ++p) {
8085         const PetscInt pnt = points[p * 2];
8086 
8087         PetscCall(DMPlexGetIndicesPointFieldsSplit_Internal(section, idxSection, pnt, offsets, perms, p, clperm, idx));
8088       }
8089     } else {
8090       for (p = 0; p < Ncl; ++p) {
8091         const PetscInt pnt = points[p * 2];
8092 
8093         if (pnt < idxStart || pnt >= idxEnd) continue;
8094         PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
8095         /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
8096          * not (at the time of this writing) have fields set. They probably should, in which case we would pass the
8097          * global section. */
8098         PetscCall(DMPlexGetIndicesPointFields_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, offsets, PETSC_FALSE, perms, p, clperm, idx));
8099       }
8100     }
8101   } else {
8102     PetscInt off = 0, idxOff;
8103 
8104     for (p = 0; p < Ncl; ++p) {
8105       const PetscInt  pnt  = points[p * 2];
8106       const PetscInt *perm = perms[0] ? perms[0][p] : NULL;
8107 
8108       if (pnt < idxStart || pnt >= idxEnd) continue;
8109       PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
8110       /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
8111        * not (at the time of this writing) have fields set. They probably should, in which case we would pass the global section. */
8112       PetscCall(DMPlexGetIndicesPoint_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, &off, PETSC_FALSE, perm, clperm, idx));
8113     }
8114   }
8115   /* 6) Cleanup */
8116   for (f = 0; f < PetscMax(1, Nf); ++f) {
8117     if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
8118     else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
8119   }
8120   if (NclC) {
8121     PetscCall(DMRestoreWorkArray(dm, NclC * 2, MPIU_INT, &pointsC));
8122   } else {
8123     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
8124   }
8125 
8126   if (indices) *indices = idx;
8127   PetscFunctionReturn(PETSC_SUCCESS);
8128 }
8129 
8130 /*@C
8131   DMPlexGetClosureIndices - Gets the global dof indices associated with the closure of the given point within the provided sections.
8132 
8133   Not collective
8134 
8135   Input Parameters:
8136 + dm         - The `DM`
8137 . section    - The `PetscSection` describing the points (a local section)
8138 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
8139 . point      - The point defining the closure
8140 - useClPerm  - Use the closure point permutation if available
8141 
8142   Output Parameters:
8143 + numIndices - The number of dof indices in the closure of point with the input sections
8144 . indices    - The dof indices
8145 . outOffsets - Array to write the field offsets into, or `NULL`
8146 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or `NULL`
8147 
8148   Level: advanced
8149 
8150   Notes:
8151   Call `DMPlexRestoreClosureIndices()` to free allocated memory
8152 
8153   If `idxSection` is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
8154   of those indices is not significant.  If `idxSection` is local, the constrained dofs will yield the involution -(idx+1)
8155   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
8156   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when `idxSection` == section, otherwise global
8157   indices (with the above semantics) are implied.
8158 
8159 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`,
8160           `PetscSection`, `DMGetGlobalSection()`
8161 @*/
8162 PetscErrorCode DMPlexGetClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
8163 {
8164   PetscInt numRows = -1, numCols = -1;
8165 
8166   PetscFunctionBeginHot;
8167   PetscCall(DMPlexGetClosureIndices_Internal(dm, section, idxSection, point, useClPerm, &numRows, &numCols, indices, outOffsets, values, PETSC_TRUE, PETSC_TRUE));
8168   PetscCheck(numRows == numCols, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Symmetric matrix transformation produces rectangular dimensions (%" PetscInt_FMT ", %" PetscInt_FMT ")", numRows, numCols);
8169   *numIndices = numRows;
8170   PetscFunctionReturn(PETSC_SUCCESS);
8171 }
8172 
8173 /*@C
8174   DMPlexRestoreClosureIndices - Restores the global dof indices associated with the closure of the given point within the provided sections.
8175 
8176   Not collective
8177 
8178   Input Parameters:
8179 + dm         - The `DM`
8180 . section    - The `PetscSection` describing the points (a local section)
8181 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
8182 . point      - The point defining the closure
8183 - useClPerm  - Use the closure point permutation if available
8184 
8185   Output Parameters:
8186 + numIndices - The number of dof indices in the closure of point with the input sections
8187 . indices    - The dof indices
8188 . outOffsets - Array to write the field offsets into, or `NULL`
8189 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or `NULL`
8190 
8191   Level: advanced
8192 
8193   Notes:
8194   If values were modified, the user is responsible for calling `DMRestoreWorkArray`(dm, 0, `MPIU_SCALAR`, &values).
8195 
8196   If idxSection is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
8197   of those indices is not significant.  If idxSection is local, the constrained dofs will yield the involution -(idx+1)
8198   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
8199   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when idxSection == section, otherwise global
8200   indices (with the above semantics) are implied.
8201 
8202 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`, `DMGetGlobalSection()`
8203 @*/
8204 PetscErrorCode DMPlexRestoreClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
8205 {
8206   PetscFunctionBegin;
8207   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8208   PetscAssertPointer(indices, 7);
8209   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, indices));
8210   PetscFunctionReturn(PETSC_SUCCESS);
8211 }
8212 
8213 PetscErrorCode DMPlexMatSetClosure_Internal(DM dm, PetscSection section, PetscSection globalSection, PetscBool useClPerm, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8214 {
8215   DM_Plex           *mesh = (DM_Plex *)dm->data;
8216   PetscInt          *indices;
8217   PetscInt           numIndices;
8218   const PetscScalar *valuesOrig = values;
8219   PetscErrorCode     ierr;
8220 
8221   PetscFunctionBegin;
8222   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8223   if (!section) PetscCall(DMGetLocalSection(dm, &section));
8224   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
8225   if (!globalSection) PetscCall(DMGetGlobalSection(dm, &globalSection));
8226   PetscValidHeaderSpecific(globalSection, PETSC_SECTION_CLASSID, 3);
8227   PetscValidHeaderSpecific(A, MAT_CLASSID, 5);
8228 
8229   PetscCall(DMPlexGetClosureIndices(dm, section, globalSection, point, useClPerm, &numIndices, &indices, NULL, (PetscScalar **)&values));
8230 
8231   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndices, indices, 0, NULL, values));
8232   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8233   ierr = MatSetValues(A, numIndices, indices, numIndices, indices, values, mode);
8234   if (ierr) {
8235     PetscMPIInt rank;
8236 
8237     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8238     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8239     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndices, indices, 0, NULL, values));
8240     PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
8241     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
8242     SETERRQ(PetscObjectComm((PetscObject)dm), ierr, "Not possible to set matrix values");
8243   }
8244   if (mesh->printFEM > 1) {
8245     PetscInt i;
8246     PetscCall(PetscPrintf(PETSC_COMM_SELF, "  Indices:"));
8247     for (i = 0; i < numIndices; ++i) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, indices[i]));
8248     PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8249   }
8250 
8251   PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
8252   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
8253   PetscFunctionReturn(PETSC_SUCCESS);
8254 }
8255 
8256 /*@C
8257   DMPlexMatSetClosure - Set an array of the values on the closure of 'point'
8258 
8259   Not collective
8260 
8261   Input Parameters:
8262 + dm            - The `DM`
8263 . section       - The section describing the layout in `v`, or `NULL` to use the default section
8264 . globalSection - The section describing the layout in `v`, or `NULL` to use the default global section
8265 . A             - The matrix
8266 . point         - The point in the `DM`
8267 . values        - The array of values
8268 - mode          - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
8269 
8270   Level: intermediate
8271 
8272 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosureGeneral()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
8273 @*/
8274 PetscErrorCode DMPlexMatSetClosure(DM dm, PetscSection section, PetscSection globalSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8275 {
8276   PetscFunctionBegin;
8277   PetscCall(DMPlexMatSetClosure_Internal(dm, section, globalSection, PETSC_TRUE, A, point, values, mode));
8278   PetscFunctionReturn(PETSC_SUCCESS);
8279 }
8280 
8281 /*@C
8282   DMPlexMatSetClosureGeneral - Set an array of the values on the closure of 'point' using a different row and column section
8283 
8284   Not collective
8285 
8286   Input Parameters:
8287 + dmRow            - The `DM` for the row fields
8288 . sectionRow       - The section describing the layout, or `NULL` to use the default section in `dmRow`
8289 . useRowPerm       - The flag to use the closure permutation of the `dmRow` if available
8290 . globalSectionRow - The section describing the layout, or `NULL` to use the default global section in `dmRow`
8291 . dmCol            - The `DM` for the column fields
8292 . sectionCol       - The section describing the layout, or `NULL` to use the default section in `dmCol`
8293 . useColPerm       - The flag to use the closure permutation of the `dmCol` if available
8294 . globalSectionCol - The section describing the layout, or `NULL` to use the default global section in `dmCol`
8295 . A                - The matrix
8296 . point            - The point in the `DM`
8297 . values           - The array of values
8298 - mode             - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
8299 
8300   Level: intermediate
8301 
8302 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosure()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
8303 @*/
8304 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)
8305 {
8306   DM_Plex           *mesh = (DM_Plex *)dmRow->data;
8307   PetscInt          *indicesRow, *indicesCol;
8308   PetscInt           numIndicesRow = -1, numIndicesCol = -1;
8309   const PetscScalar *valuesV0 = values, *valuesV1, *valuesV2;
8310 
8311   PetscErrorCode ierr;
8312 
8313   PetscFunctionBegin;
8314   PetscValidHeaderSpecific(dmRow, DM_CLASSID, 1);
8315   if (!sectionRow) PetscCall(DMGetLocalSection(dmRow, &sectionRow));
8316   PetscValidHeaderSpecific(sectionRow, PETSC_SECTION_CLASSID, 2);
8317   if (!globalSectionRow) PetscCall(DMGetGlobalSection(dmRow, &globalSectionRow));
8318   PetscValidHeaderSpecific(globalSectionRow, PETSC_SECTION_CLASSID, 3);
8319   PetscValidHeaderSpecific(dmCol, DM_CLASSID, 5);
8320   if (!sectionCol) PetscCall(DMGetLocalSection(dmCol, &sectionCol));
8321   PetscValidHeaderSpecific(sectionCol, PETSC_SECTION_CLASSID, 6);
8322   if (!globalSectionCol) PetscCall(DMGetGlobalSection(dmCol, &globalSectionCol));
8323   PetscValidHeaderSpecific(globalSectionCol, PETSC_SECTION_CLASSID, 7);
8324   PetscValidHeaderSpecific(A, MAT_CLASSID, 9);
8325 
8326   PetscCall(DMPlexGetClosureIndicesSize_Internal(dmRow, sectionRow, point, &numIndicesRow));
8327   PetscCall(DMPlexGetClosureIndicesSize_Internal(dmCol, sectionCol, point, &numIndicesCol));
8328   valuesV1 = valuesV0;
8329   PetscCall(DMPlexGetClosureIndices_Internal(dmRow, sectionRow, globalSectionRow, point, useRowPerm, &numIndicesRow, &numIndicesCol, &indicesRow, NULL, (PetscScalar **)&valuesV1, PETSC_FALSE, PETSC_TRUE));
8330   valuesV2 = valuesV1;
8331   PetscCall(DMPlexGetClosureIndices_Internal(dmCol, sectionCol, globalSectionCol, point, useColPerm, &numIndicesRow, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&valuesV2, PETSC_TRUE, PETSC_FALSE));
8332 
8333   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, valuesV2));
8334   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8335   ierr = MatSetValues(A, numIndicesRow, indicesRow, numIndicesCol, indicesCol, valuesV2, mode);
8336   if (ierr) {
8337     PetscMPIInt rank;
8338 
8339     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8340     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8341     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
8342     PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesRow, NULL, (PetscScalar **)&valuesV2));
8343     PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&valuesV1));
8344     if (valuesV2 != valuesV1) PetscCall(DMRestoreWorkArray(dmCol, 0, MPIU_SCALAR, &valuesV2));
8345     if (valuesV1 != valuesV0) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &valuesV1));
8346   }
8347 
8348   PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, useColPerm, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&valuesV2));
8349   PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, useRowPerm, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&valuesV1));
8350   if (valuesV2 != valuesV1) PetscCall(DMRestoreWorkArray(dmCol, 0, MPIU_SCALAR, &valuesV2));
8351   if (valuesV1 != valuesV0) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &valuesV1));
8352   PetscFunctionReturn(PETSC_SUCCESS);
8353 }
8354 
8355 PetscErrorCode DMPlexMatSetClosureRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8356 {
8357   DM_Plex        *mesh    = (DM_Plex *)dmf->data;
8358   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
8359   PetscInt       *cpoints = NULL;
8360   PetscInt       *findices, *cindices;
8361   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
8362   PetscInt        foffsets[32], coffsets[32];
8363   DMPolytopeType  ct;
8364   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
8365   PetscErrorCode  ierr;
8366 
8367   PetscFunctionBegin;
8368   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
8369   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
8370   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
8371   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
8372   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
8373   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
8374   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
8375   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
8376   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
8377   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
8378   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
8379   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
8380   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
8381   PetscCall(PetscArrayzero(foffsets, 32));
8382   PetscCall(PetscArrayzero(coffsets, 32));
8383   /* Column indices */
8384   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8385   maxFPoints = numCPoints;
8386   /* Compress out points not in the section */
8387   /*   TODO: Squeeze out points with 0 dof as well */
8388   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
8389   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
8390     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
8391       cpoints[q * 2]     = cpoints[p];
8392       cpoints[q * 2 + 1] = cpoints[p + 1];
8393       ++q;
8394     }
8395   }
8396   numCPoints = q;
8397   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
8398     PetscInt fdof;
8399 
8400     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
8401     if (!dof) continue;
8402     for (f = 0; f < numFields; ++f) {
8403       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
8404       coffsets[f + 1] += fdof;
8405     }
8406     numCIndices += dof;
8407   }
8408   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
8409   /* Row indices */
8410   PetscCall(DMPlexGetCellType(dmc, point, &ct));
8411   {
8412     DMPlexTransform tr;
8413     DMPolytopeType *rct;
8414     PetscInt       *rsize, *rcone, *rornt, Nt;
8415 
8416     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8417     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8418     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8419     numSubcells = rsize[Nt - 1];
8420     PetscCall(DMPlexTransformDestroy(&tr));
8421   }
8422   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8423   for (r = 0, q = 0; r < numSubcells; ++r) {
8424     /* TODO Map from coarse to fine cells */
8425     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8426     /* Compress out points not in the section */
8427     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8428     for (p = 0; p < numFPoints * 2; p += 2) {
8429       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8430         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8431         if (!dof) continue;
8432         for (s = 0; s < q; ++s)
8433           if (fpoints[p] == ftotpoints[s * 2]) break;
8434         if (s < q) continue;
8435         ftotpoints[q * 2]     = fpoints[p];
8436         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8437         ++q;
8438       }
8439     }
8440     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8441   }
8442   numFPoints = q;
8443   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8444     PetscInt fdof;
8445 
8446     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8447     if (!dof) continue;
8448     for (f = 0; f < numFields; ++f) {
8449       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8450       foffsets[f + 1] += fdof;
8451     }
8452     numFIndices += dof;
8453   }
8454   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8455 
8456   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8457   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8458   PetscCall(DMGetWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8459   PetscCall(DMGetWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8460   if (numFields) {
8461     const PetscInt **permsF[32] = {NULL};
8462     const PetscInt **permsC[32] = {NULL};
8463 
8464     for (f = 0; f < numFields; f++) {
8465       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8466       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8467     }
8468     for (p = 0; p < numFPoints; p++) {
8469       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8470       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8471     }
8472     for (p = 0; p < numCPoints; p++) {
8473       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8474       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8475     }
8476     for (f = 0; f < numFields; f++) {
8477       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8478       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8479     }
8480   } else {
8481     const PetscInt **permsF = NULL;
8482     const PetscInt **permsC = NULL;
8483 
8484     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8485     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8486     for (p = 0, off = 0; p < numFPoints; p++) {
8487       const PetscInt *perm = permsF ? permsF[p] : NULL;
8488 
8489       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8490       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8491     }
8492     for (p = 0, off = 0; p < numCPoints; p++) {
8493       const PetscInt *perm = permsC ? permsC[p] : NULL;
8494 
8495       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8496       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8497     }
8498     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8499     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8500   }
8501   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8502   /* TODO: flips */
8503   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8504   ierr = MatSetValues(A, numFIndices, findices, numCIndices, cindices, values, mode);
8505   if (ierr) {
8506     PetscMPIInt rank;
8507 
8508     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8509     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8510     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8511     PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8512     PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8513   }
8514   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8515   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8516   PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8517   PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8518   PetscFunctionReturn(PETSC_SUCCESS);
8519 }
8520 
8521 PetscErrorCode DMPlexMatGetClosureIndicesRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, PetscInt point, PetscInt cindices[], PetscInt findices[])
8522 {
8523   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
8524   PetscInt       *cpoints      = NULL;
8525   PetscInt        foffsets[32] = {0}, coffsets[32] = {0};
8526   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
8527   DMPolytopeType  ct;
8528   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
8529 
8530   PetscFunctionBegin;
8531   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
8532   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
8533   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
8534   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
8535   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
8536   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
8537   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
8538   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
8539   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
8540   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
8541   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
8542   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
8543   /* Column indices */
8544   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8545   maxFPoints = numCPoints;
8546   /* Compress out points not in the section */
8547   /*   TODO: Squeeze out points with 0 dof as well */
8548   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
8549   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
8550     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
8551       cpoints[q * 2]     = cpoints[p];
8552       cpoints[q * 2 + 1] = cpoints[p + 1];
8553       ++q;
8554     }
8555   }
8556   numCPoints = q;
8557   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
8558     PetscInt fdof;
8559 
8560     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
8561     if (!dof) continue;
8562     for (f = 0; f < numFields; ++f) {
8563       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
8564       coffsets[f + 1] += fdof;
8565     }
8566     numCIndices += dof;
8567   }
8568   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
8569   /* Row indices */
8570   PetscCall(DMPlexGetCellType(dmc, point, &ct));
8571   {
8572     DMPlexTransform tr;
8573     DMPolytopeType *rct;
8574     PetscInt       *rsize, *rcone, *rornt, Nt;
8575 
8576     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8577     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8578     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8579     numSubcells = rsize[Nt - 1];
8580     PetscCall(DMPlexTransformDestroy(&tr));
8581   }
8582   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8583   for (r = 0, q = 0; r < numSubcells; ++r) {
8584     /* TODO Map from coarse to fine cells */
8585     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8586     /* Compress out points not in the section */
8587     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8588     for (p = 0; p < numFPoints * 2; p += 2) {
8589       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8590         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8591         if (!dof) continue;
8592         for (s = 0; s < q; ++s)
8593           if (fpoints[p] == ftotpoints[s * 2]) break;
8594         if (s < q) continue;
8595         ftotpoints[q * 2]     = fpoints[p];
8596         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8597         ++q;
8598       }
8599     }
8600     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8601   }
8602   numFPoints = q;
8603   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8604     PetscInt fdof;
8605 
8606     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8607     if (!dof) continue;
8608     for (f = 0; f < numFields; ++f) {
8609       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8610       foffsets[f + 1] += fdof;
8611     }
8612     numFIndices += dof;
8613   }
8614   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8615 
8616   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8617   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8618   if (numFields) {
8619     const PetscInt **permsF[32] = {NULL};
8620     const PetscInt **permsC[32] = {NULL};
8621 
8622     for (f = 0; f < numFields; f++) {
8623       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8624       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8625     }
8626     for (p = 0; p < numFPoints; p++) {
8627       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8628       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8629     }
8630     for (p = 0; p < numCPoints; p++) {
8631       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8632       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8633     }
8634     for (f = 0; f < numFields; f++) {
8635       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8636       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8637     }
8638   } else {
8639     const PetscInt **permsF = NULL;
8640     const PetscInt **permsC = NULL;
8641 
8642     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8643     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8644     for (p = 0, off = 0; p < numFPoints; p++) {
8645       const PetscInt *perm = permsF ? permsF[p] : NULL;
8646 
8647       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8648       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8649     }
8650     for (p = 0, off = 0; p < numCPoints; p++) {
8651       const PetscInt *perm = permsC ? permsC[p] : NULL;
8652 
8653       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8654       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8655     }
8656     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8657     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8658   }
8659   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8660   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8661   PetscFunctionReturn(PETSC_SUCCESS);
8662 }
8663 
8664 /*@
8665   DMPlexGetVTKCellHeight - Returns the height in the DAG used to determine which points are cells (normally 0)
8666 
8667   Input Parameter:
8668 . dm - The `DMPLEX` object
8669 
8670   Output Parameter:
8671 . cellHeight - The height of a cell
8672 
8673   Level: developer
8674 
8675 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetVTKCellHeight()`
8676 @*/
8677 PetscErrorCode DMPlexGetVTKCellHeight(DM dm, PetscInt *cellHeight)
8678 {
8679   DM_Plex *mesh = (DM_Plex *)dm->data;
8680 
8681   PetscFunctionBegin;
8682   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8683   PetscAssertPointer(cellHeight, 2);
8684   *cellHeight = mesh->vtkCellHeight;
8685   PetscFunctionReturn(PETSC_SUCCESS);
8686 }
8687 
8688 /*@
8689   DMPlexSetVTKCellHeight - Sets the height in the DAG used to determine which points are cells (normally 0)
8690 
8691   Input Parameters:
8692 + dm         - The `DMPLEX` object
8693 - cellHeight - The height of a cell
8694 
8695   Level: developer
8696 
8697 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetVTKCellHeight()`
8698 @*/
8699 PetscErrorCode DMPlexSetVTKCellHeight(DM dm, PetscInt cellHeight)
8700 {
8701   DM_Plex *mesh = (DM_Plex *)dm->data;
8702 
8703   PetscFunctionBegin;
8704   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8705   mesh->vtkCellHeight = cellHeight;
8706   PetscFunctionReturn(PETSC_SUCCESS);
8707 }
8708 
8709 /*@
8710   DMPlexGetCellTypeStratum - Get the range of cells of a given celltype
8711 
8712   Input Parameters:
8713 + dm - The `DMPLEX` object
8714 - ct - The `DMPolytopeType` of the cell
8715 
8716   Output Parameters:
8717 + start - The first cell of this type, or `NULL`
8718 - end   - The upper bound on this celltype, or `NULL`
8719 
8720   Level: advanced
8721 
8722 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexConstructGhostCells()`, `DMPlexGetDepthStratum()`, `DMPlexGetHeightStratum()`
8723 @*/
8724 PetscErrorCode DMPlexGetCellTypeStratum(DM dm, DMPolytopeType ct, PetscInt *start, PetscInt *end)
8725 {
8726   DM_Plex *mesh = (DM_Plex *)dm->data;
8727   DMLabel  label;
8728   PetscInt pStart, pEnd;
8729 
8730   PetscFunctionBegin;
8731   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8732   if (start) {
8733     PetscAssertPointer(start, 3);
8734     *start = 0;
8735   }
8736   if (end) {
8737     PetscAssertPointer(end, 4);
8738     *end = 0;
8739   }
8740   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8741   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
8742   if (mesh->tr) {
8743     PetscCall(DMPlexTransformGetCellTypeStratum(mesh->tr, ct, start, end));
8744   } else {
8745     PetscCall(DMPlexGetCellTypeLabel(dm, &label));
8746     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named celltype was found");
8747     PetscCall(DMLabelGetStratumBounds(label, ct, start, end));
8748   }
8749   PetscFunctionReturn(PETSC_SUCCESS);
8750 }
8751 
8752 PetscErrorCode DMPlexCreateNumbering_Plex(DM dm, PetscInt pStart, PetscInt pEnd, PetscInt shift, PetscInt *globalSize, PetscSF sf, IS *numbering)
8753 {
8754   PetscSection section, globalSection;
8755   PetscInt    *numbers, p;
8756 
8757   PetscFunctionBegin;
8758   if (PetscDefined(USE_DEBUG)) PetscCall(DMPlexCheckPointSF(dm, sf, PETSC_TRUE));
8759   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
8760   PetscCall(PetscSectionSetChart(section, pStart, pEnd));
8761   for (p = pStart; p < pEnd; ++p) PetscCall(PetscSectionSetDof(section, p, 1));
8762   PetscCall(PetscSectionSetUp(section));
8763   PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &globalSection));
8764   PetscCall(PetscMalloc1(pEnd - pStart, &numbers));
8765   for (p = pStart; p < pEnd; ++p) {
8766     PetscCall(PetscSectionGetOffset(globalSection, p, &numbers[p - pStart]));
8767     if (numbers[p - pStart] < 0) numbers[p - pStart] -= shift;
8768     else numbers[p - pStart] += shift;
8769   }
8770   PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), pEnd - pStart, numbers, PETSC_OWN_POINTER, numbering));
8771   if (globalSize) {
8772     PetscLayout layout;
8773     PetscCall(PetscSectionGetPointLayout(PetscObjectComm((PetscObject)dm), globalSection, &layout));
8774     PetscCall(PetscLayoutGetSize(layout, globalSize));
8775     PetscCall(PetscLayoutDestroy(&layout));
8776   }
8777   PetscCall(PetscSectionDestroy(&section));
8778   PetscCall(PetscSectionDestroy(&globalSection));
8779   PetscFunctionReturn(PETSC_SUCCESS);
8780 }
8781 
8782 /*@
8783   DMPlexCreateCellNumbering - Get a global cell numbering for all cells on this process
8784 
8785   Input Parameters:
8786 + dm         - The `DMPLEX` object
8787 - includeAll - Whether to include all cells, or just the simplex and box cells
8788 
8789   Output Parameter:
8790 . globalCellNumbers - Global cell numbers for all cells on this process
8791 
8792   Level: developer
8793 
8794 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`, `DMPlexGetVertexNumbering()`
8795 @*/
8796 PetscErrorCode DMPlexCreateCellNumbering(DM dm, PetscBool includeAll, IS *globalCellNumbers)
8797 {
8798   PetscInt cellHeight, cStart, cEnd;
8799 
8800   PetscFunctionBegin;
8801   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8802   if (includeAll) PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8803   else PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd));
8804   PetscCall(DMPlexCreateNumbering_Plex(dm, cStart, cEnd, 0, NULL, dm->sf, globalCellNumbers));
8805   PetscFunctionReturn(PETSC_SUCCESS);
8806 }
8807 
8808 /*@
8809   DMPlexGetCellNumbering - Get a global cell numbering for all cells on this process
8810 
8811   Input Parameter:
8812 . dm - The `DMPLEX` object
8813 
8814   Output Parameter:
8815 . globalCellNumbers - Global cell numbers for all cells on this process
8816 
8817   Level: developer
8818 
8819 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreateCellNumbering()`, `DMPlexGetVertexNumbering()`
8820 @*/
8821 PetscErrorCode DMPlexGetCellNumbering(DM dm, IS *globalCellNumbers)
8822 {
8823   DM_Plex *mesh = (DM_Plex *)dm->data;
8824 
8825   PetscFunctionBegin;
8826   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8827   if (!mesh->globalCellNumbers) PetscCall(DMPlexCreateCellNumbering(dm, PETSC_FALSE, &mesh->globalCellNumbers));
8828   *globalCellNumbers = mesh->globalCellNumbers;
8829   PetscFunctionReturn(PETSC_SUCCESS);
8830 }
8831 
8832 PetscErrorCode DMPlexCreateVertexNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalVertexNumbers)
8833 {
8834   PetscInt vStart, vEnd;
8835 
8836   PetscFunctionBegin;
8837   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8838   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8839   PetscCall(DMPlexCreateNumbering_Plex(dm, vStart, vEnd, 0, NULL, dm->sf, globalVertexNumbers));
8840   PetscFunctionReturn(PETSC_SUCCESS);
8841 }
8842 
8843 /*@
8844   DMPlexGetVertexNumbering - Get a global vertex numbering for all vertices on this process
8845 
8846   Input Parameter:
8847 . dm - The `DMPLEX` object
8848 
8849   Output Parameter:
8850 . globalVertexNumbers - Global vertex numbers for all vertices on this process
8851 
8852   Level: developer
8853 
8854 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
8855 @*/
8856 PetscErrorCode DMPlexGetVertexNumbering(DM dm, IS *globalVertexNumbers)
8857 {
8858   DM_Plex *mesh = (DM_Plex *)dm->data;
8859 
8860   PetscFunctionBegin;
8861   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8862   if (!mesh->globalVertexNumbers) PetscCall(DMPlexCreateVertexNumbering_Internal(dm, PETSC_FALSE, &mesh->globalVertexNumbers));
8863   *globalVertexNumbers = mesh->globalVertexNumbers;
8864   PetscFunctionReturn(PETSC_SUCCESS);
8865 }
8866 
8867 /*@
8868   DMPlexCreatePointNumbering - Create a global numbering for all points.
8869 
8870   Collective
8871 
8872   Input Parameter:
8873 . dm - The `DMPLEX` object
8874 
8875   Output Parameter:
8876 . globalPointNumbers - Global numbers for all points on this process
8877 
8878   Level: developer
8879 
8880   Notes:
8881   The point numbering `IS` is parallel, with local portion indexed by local points (see `DMGetLocalSection()`). The global
8882   points are taken as stratified, with each MPI rank owning a contiguous subset of each stratum. In the IS, owned points
8883   will have their non-negative value while points owned by different ranks will be involuted -(idx+1). As an example,
8884   consider a parallel mesh in which the first two elements and first two vertices are owned by rank 0.
8885 
8886   The partitioned mesh is
8887   ```
8888   (2)--0--(3)--1--(4)    (1)--0--(2)
8889   ```
8890   and its global numbering is
8891   ```
8892   (3)--0--(4)--1--(5)--2--(6)
8893   ```
8894   Then the global numbering is provided as
8895   ```
8896   [0] Number of indices in set 5
8897   [0] 0 0
8898   [0] 1 1
8899   [0] 2 3
8900   [0] 3 4
8901   [0] 4 -6
8902   [1] Number of indices in set 3
8903   [1] 0 2
8904   [1] 1 5
8905   [1] 2 6
8906   ```
8907 
8908 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
8909 @*/
8910 PetscErrorCode DMPlexCreatePointNumbering(DM dm, IS *globalPointNumbers)
8911 {
8912   IS        nums[4];
8913   PetscInt  depths[4], gdepths[4], starts[4];
8914   PetscInt  depth, d, shift = 0;
8915   PetscBool empty = PETSC_FALSE;
8916 
8917   PetscFunctionBegin;
8918   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8919   PetscCall(DMPlexGetDepth(dm, &depth));
8920   // For unstratified meshes use dim instead of depth
8921   if (depth < 0) PetscCall(DMGetDimension(dm, &depth));
8922   // If any stratum is empty, we must mark all empty
8923   for (d = 0; d <= depth; ++d) {
8924     PetscInt end;
8925 
8926     depths[d] = depth - d;
8927     PetscCall(DMPlexGetDepthStratum(dm, depths[d], &starts[d], &end));
8928     if (!(starts[d] - end)) empty = PETSC_TRUE;
8929   }
8930   if (empty)
8931     for (d = 0; d <= depth; ++d) {
8932       depths[d] = -1;
8933       starts[d] = -1;
8934     }
8935   else PetscCall(PetscSortIntWithArray(depth + 1, starts, depths));
8936   PetscCall(MPIU_Allreduce(depths, gdepths, depth + 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
8937   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]);
8938   // Note here that 'shift' is collective, so that the numbering is stratified by depth
8939   for (d = 0; d <= depth; ++d) {
8940     PetscInt pStart, pEnd, gsize;
8941 
8942     PetscCall(DMPlexGetDepthStratum(dm, gdepths[d], &pStart, &pEnd));
8943     PetscCall(DMPlexCreateNumbering_Plex(dm, pStart, pEnd, shift, &gsize, dm->sf, &nums[d]));
8944     shift += gsize;
8945   }
8946   PetscCall(ISConcatenate(PETSC_COMM_SELF, depth + 1, nums, globalPointNumbers));
8947   for (d = 0; d <= depth; ++d) PetscCall(ISDestroy(&nums[d]));
8948   PetscFunctionReturn(PETSC_SUCCESS);
8949 }
8950 
8951 /*@
8952   DMPlexCreateEdgeNumbering - Create a global numbering for edges.
8953 
8954   Collective
8955 
8956   Input Parameter:
8957 . dm - The `DMPLEX` object
8958 
8959   Output Parameter:
8960 . globalEdgeNumbers - Global numbers for all edges on this process
8961 
8962   Level: developer
8963 
8964   Notes:
8965   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).
8966 
8967 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`, `DMPlexGetVertexNumbering()`, `DMPlexCreatePointNumbering()`
8968 @*/
8969 PetscErrorCode DMPlexCreateEdgeNumbering(DM dm, IS *globalEdgeNumbers)
8970 {
8971   PetscSF  sf;
8972   PetscInt eStart, eEnd;
8973 
8974   PetscFunctionBegin;
8975   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8976   PetscCall(DMGetPointSF(dm, &sf));
8977   PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
8978   PetscCall(DMPlexCreateNumbering_Plex(dm, eStart, eEnd, 0, NULL, sf, globalEdgeNumbers));
8979   PetscFunctionReturn(PETSC_SUCCESS);
8980 }
8981 
8982 /*@
8983   DMPlexCreateRankField - Create a cell field whose value is the rank of the owner
8984 
8985   Input Parameter:
8986 . dm - The `DMPLEX` object
8987 
8988   Output Parameter:
8989 . ranks - The rank field
8990 
8991   Options Database Key:
8992 . -dm_partition_view - Adds the rank field into the `DM` output from `-dm_view` using the same viewer
8993 
8994   Level: intermediate
8995 
8996 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`
8997 @*/
8998 PetscErrorCode DMPlexCreateRankField(DM dm, Vec *ranks)
8999 {
9000   DM             rdm;
9001   PetscFE        fe;
9002   PetscScalar   *r;
9003   PetscMPIInt    rank;
9004   DMPolytopeType ct;
9005   PetscInt       dim, cStart, cEnd, c;
9006   PetscBool      simplex;
9007 
9008   PetscFunctionBeginUser;
9009   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9010   PetscAssertPointer(ranks, 2);
9011   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
9012   PetscCall(DMClone(dm, &rdm));
9013   PetscCall(DMGetDimension(rdm, &dim));
9014   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
9015   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
9016   simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct) + 1 ? PETSC_TRUE : PETSC_FALSE;
9017   PetscCall(PetscFECreateDefault(PETSC_COMM_SELF, dim, 1, simplex, "PETSc___rank_", -1, &fe));
9018   PetscCall(PetscObjectSetName((PetscObject)fe, "rank"));
9019   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
9020   PetscCall(PetscFEDestroy(&fe));
9021   PetscCall(DMCreateDS(rdm));
9022   PetscCall(DMCreateGlobalVector(rdm, ranks));
9023   PetscCall(PetscObjectSetName((PetscObject)*ranks, "partition"));
9024   PetscCall(VecGetArray(*ranks, &r));
9025   for (c = cStart; c < cEnd; ++c) {
9026     PetscScalar *lr;
9027 
9028     PetscCall(DMPlexPointGlobalRef(rdm, c, r, &lr));
9029     if (lr) *lr = rank;
9030   }
9031   PetscCall(VecRestoreArray(*ranks, &r));
9032   PetscCall(DMDestroy(&rdm));
9033   PetscFunctionReturn(PETSC_SUCCESS);
9034 }
9035 
9036 /*@
9037   DMPlexCreateLabelField - Create a field whose value is the label value for that point
9038 
9039   Input Parameters:
9040 + dm    - The `DMPLEX`
9041 - label - The `DMLabel`
9042 
9043   Output Parameter:
9044 . val - The label value field
9045 
9046   Options Database Key:
9047 . -dm_label_view - Adds the label value field into the `DM` output from `-dm_view` using the same viewer
9048 
9049   Level: intermediate
9050 
9051 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`
9052 @*/
9053 PetscErrorCode DMPlexCreateLabelField(DM dm, DMLabel label, Vec *val)
9054 {
9055   DM             rdm, plex;
9056   Vec            lval;
9057   PetscSection   section;
9058   PetscFE        fe;
9059   PetscScalar   *v;
9060   PetscInt       dim, pStart, pEnd, p, cStart;
9061   DMPolytopeType ct;
9062   char           name[PETSC_MAX_PATH_LEN];
9063   const char    *lname, *prefix;
9064 
9065   PetscFunctionBeginUser;
9066   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9067   PetscAssertPointer(label, 2);
9068   PetscAssertPointer(val, 3);
9069   PetscCall(DMClone(dm, &rdm));
9070   PetscCall(DMConvert(rdm, DMPLEX, &plex));
9071   PetscCall(DMPlexGetHeightStratum(plex, 0, &cStart, NULL));
9072   PetscCall(DMPlexGetCellType(plex, cStart, &ct));
9073   PetscCall(DMDestroy(&plex));
9074   PetscCall(DMGetDimension(rdm, &dim));
9075   PetscCall(DMGetOptionsPrefix(dm, &prefix));
9076   PetscCall(PetscObjectGetName((PetscObject)label, &lname));
9077   PetscCall(PetscSNPrintf(name, sizeof(name), "%s%s_", prefix ? prefix : "", lname));
9078   PetscCall(PetscFECreateByCell(PETSC_COMM_SELF, dim, 1, ct, name, -1, &fe));
9079   PetscCall(PetscObjectSetName((PetscObject)fe, ""));
9080   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
9081   PetscCall(PetscFEDestroy(&fe));
9082   PetscCall(DMCreateDS(rdm));
9083   PetscCall(DMCreateGlobalVector(rdm, val));
9084   PetscCall(DMCreateLocalVector(rdm, &lval));
9085   PetscCall(PetscObjectSetName((PetscObject)*val, lname));
9086   PetscCall(DMGetLocalSection(rdm, &section));
9087   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
9088   PetscCall(VecGetArray(lval, &v));
9089   for (p = pStart; p < pEnd; ++p) {
9090     PetscInt cval, dof, off;
9091 
9092     PetscCall(PetscSectionGetDof(section, p, &dof));
9093     if (!dof) continue;
9094     PetscCall(DMLabelGetValue(label, p, &cval));
9095     PetscCall(PetscSectionGetOffset(section, p, &off));
9096     for (PetscInt d = 0; d < dof; d++) v[off + d] = cval;
9097   }
9098   PetscCall(VecRestoreArray(lval, &v));
9099   PetscCall(DMLocalToGlobal(rdm, lval, INSERT_VALUES, *val));
9100   PetscCall(VecDestroy(&lval));
9101   PetscCall(DMDestroy(&rdm));
9102   PetscFunctionReturn(PETSC_SUCCESS);
9103 }
9104 
9105 /*@
9106   DMPlexCheckSymmetry - Check that the adjacency information in the mesh is symmetric.
9107 
9108   Input Parameter:
9109 . dm - The `DMPLEX` object
9110 
9111   Level: developer
9112 
9113   Notes:
9114   This is a useful diagnostic when creating meshes programmatically.
9115 
9116   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9117 
9118 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9119 @*/
9120 PetscErrorCode DMPlexCheckSymmetry(DM dm)
9121 {
9122   PetscSection    coneSection, supportSection;
9123   const PetscInt *cone, *support;
9124   PetscInt        coneSize, c, supportSize, s;
9125   PetscInt        pStart, pEnd, p, pp, csize, ssize;
9126   PetscBool       storagecheck = PETSC_TRUE;
9127 
9128   PetscFunctionBegin;
9129   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9130   PetscCall(DMViewFromOptions(dm, NULL, "-sym_dm_view"));
9131   PetscCall(DMPlexGetConeSection(dm, &coneSection));
9132   PetscCall(DMPlexGetSupportSection(dm, &supportSection));
9133   /* Check that point p is found in the support of its cone points, and vice versa */
9134   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
9135   for (p = pStart; p < pEnd; ++p) {
9136     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
9137     PetscCall(DMPlexGetCone(dm, p, &cone));
9138     for (c = 0; c < coneSize; ++c) {
9139       PetscBool dup = PETSC_FALSE;
9140       PetscInt  d;
9141       for (d = c - 1; d >= 0; --d) {
9142         if (cone[c] == cone[d]) {
9143           dup = PETSC_TRUE;
9144           break;
9145         }
9146       }
9147       PetscCall(DMPlexGetSupportSize(dm, cone[c], &supportSize));
9148       PetscCall(DMPlexGetSupport(dm, cone[c], &support));
9149       for (s = 0; s < supportSize; ++s) {
9150         if (support[s] == p) break;
9151       }
9152       if ((s >= supportSize) || (dup && (support[s + 1] != p))) {
9153         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", p));
9154         for (s = 0; s < coneSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[s]));
9155         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9156         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", cone[c]));
9157         for (s = 0; s < supportSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[s]));
9158         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9159         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]);
9160         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in support of cone point %" PetscInt_FMT, p, cone[c]);
9161       }
9162     }
9163     PetscCall(DMPlexGetTreeParent(dm, p, &pp, NULL));
9164     if (p != pp) {
9165       storagecheck = PETSC_FALSE;
9166       continue;
9167     }
9168     PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
9169     PetscCall(DMPlexGetSupport(dm, p, &support));
9170     for (s = 0; s < supportSize; ++s) {
9171       PetscCall(DMPlexGetConeSize(dm, support[s], &coneSize));
9172       PetscCall(DMPlexGetCone(dm, support[s], &cone));
9173       for (c = 0; c < coneSize; ++c) {
9174         PetscCall(DMPlexGetTreeParent(dm, cone[c], &pp, NULL));
9175         if (cone[c] != pp) {
9176           c = 0;
9177           break;
9178         }
9179         if (cone[c] == p) break;
9180       }
9181       if (c >= coneSize) {
9182         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", p));
9183         for (c = 0; c < supportSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[c]));
9184         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9185         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", support[s]));
9186         for (c = 0; c < coneSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[c]));
9187         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9188         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in cone of support point %" PetscInt_FMT, p, support[s]);
9189       }
9190     }
9191   }
9192   if (storagecheck) {
9193     PetscCall(PetscSectionGetStorageSize(coneSection, &csize));
9194     PetscCall(PetscSectionGetStorageSize(supportSection, &ssize));
9195     PetscCheck(csize == ssize, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Total cone size %" PetscInt_FMT " != Total support size %" PetscInt_FMT, csize, ssize);
9196   }
9197   PetscFunctionReturn(PETSC_SUCCESS);
9198 }
9199 
9200 /*
9201   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.
9202 */
9203 static PetscErrorCode DMPlexCellUnsplitVertices_Private(DM dm, PetscInt c, DMPolytopeType ct, PetscInt *unsplit)
9204 {
9205   DMPolytopeType  cct;
9206   PetscInt        ptpoints[4];
9207   const PetscInt *cone, *ccone, *ptcone;
9208   PetscInt        coneSize, cp, cconeSize, ccp, npt = 0, pt;
9209 
9210   PetscFunctionBegin;
9211   *unsplit = 0;
9212   switch (ct) {
9213   case DM_POLYTOPE_POINT_PRISM_TENSOR:
9214     ptpoints[npt++] = c;
9215     break;
9216   case DM_POLYTOPE_SEG_PRISM_TENSOR:
9217     PetscCall(DMPlexGetCone(dm, c, &cone));
9218     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9219     for (cp = 0; cp < coneSize; ++cp) {
9220       PetscCall(DMPlexGetCellType(dm, cone[cp], &cct));
9221       if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) ptpoints[npt++] = cone[cp];
9222     }
9223     break;
9224   case DM_POLYTOPE_TRI_PRISM_TENSOR:
9225   case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9226     PetscCall(DMPlexGetCone(dm, c, &cone));
9227     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9228     for (cp = 0; cp < coneSize; ++cp) {
9229       PetscCall(DMPlexGetCone(dm, cone[cp], &ccone));
9230       PetscCall(DMPlexGetConeSize(dm, cone[cp], &cconeSize));
9231       for (ccp = 0; ccp < cconeSize; ++ccp) {
9232         PetscCall(DMPlexGetCellType(dm, ccone[ccp], &cct));
9233         if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) {
9234           PetscInt p;
9235           for (p = 0; p < npt; ++p)
9236             if (ptpoints[p] == ccone[ccp]) break;
9237           if (p == npt) ptpoints[npt++] = ccone[ccp];
9238         }
9239       }
9240     }
9241     break;
9242   default:
9243     break;
9244   }
9245   for (pt = 0; pt < npt; ++pt) {
9246     PetscCall(DMPlexGetCone(dm, ptpoints[pt], &ptcone));
9247     if (ptcone[0] == ptcone[1]) ++(*unsplit);
9248   }
9249   PetscFunctionReturn(PETSC_SUCCESS);
9250 }
9251 
9252 /*@
9253   DMPlexCheckSkeleton - Check that each cell has the correct number of vertices
9254 
9255   Input Parameters:
9256 + dm         - The `DMPLEX` object
9257 - cellHeight - Normally 0
9258 
9259   Level: developer
9260 
9261   Notes:
9262   This is a useful diagnostic when creating meshes programmatically.
9263   Currently applicable only to homogeneous simplex or tensor meshes.
9264 
9265   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9266 
9267 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9268 @*/
9269 PetscErrorCode DMPlexCheckSkeleton(DM dm, PetscInt cellHeight)
9270 {
9271   DMPlexInterpolatedFlag interp;
9272   DMPolytopeType         ct;
9273   PetscInt               vStart, vEnd, cStart, cEnd, c;
9274 
9275   PetscFunctionBegin;
9276   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9277   PetscCall(DMPlexIsInterpolated(dm, &interp));
9278   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9279   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
9280   for (c = cStart; c < cEnd; ++c) {
9281     PetscInt *closure = NULL;
9282     PetscInt  coneSize, closureSize, cl, Nv = 0;
9283 
9284     PetscCall(DMPlexGetCellType(dm, c, &ct));
9285     if (ct == DM_POLYTOPE_UNKNOWN) continue;
9286     if (interp == DMPLEX_INTERPOLATED_FULL) {
9287       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9288       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));
9289     }
9290     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9291     for (cl = 0; cl < closureSize * 2; cl += 2) {
9292       const PetscInt p = closure[cl];
9293       if ((p >= vStart) && (p < vEnd)) ++Nv;
9294     }
9295     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9296     /* Special Case: Tensor faces with identified vertices */
9297     if (Nv < DMPolytopeTypeGetNumVertices(ct)) {
9298       PetscInt unsplit;
9299 
9300       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9301       if (Nv + unsplit == DMPolytopeTypeGetNumVertices(ct)) continue;
9302     }
9303     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));
9304   }
9305   PetscFunctionReturn(PETSC_SUCCESS);
9306 }
9307 
9308 /*@
9309   DMPlexCheckFaces - Check that the faces of each cell give a vertex order this is consistent with what we expect from the cell type
9310 
9311   Collective
9312 
9313   Input Parameters:
9314 + dm         - The `DMPLEX` object
9315 - cellHeight - Normally 0
9316 
9317   Level: developer
9318 
9319   Notes:
9320   This is a useful diagnostic when creating meshes programmatically.
9321   This routine is only relevant for meshes that are fully interpolated across all ranks.
9322   It will error out if a partially interpolated mesh is given on some rank.
9323   It will do nothing for locally uninterpolated mesh (as there is nothing to check).
9324 
9325   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9326 
9327 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMPlexGetVTKCellHeight()`, `DMSetFromOptions()`
9328 @*/
9329 PetscErrorCode DMPlexCheckFaces(DM dm, PetscInt cellHeight)
9330 {
9331   PetscInt               dim, depth, vStart, vEnd, cStart, cEnd, c, h;
9332   DMPlexInterpolatedFlag interpEnum;
9333 
9334   PetscFunctionBegin;
9335   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9336   PetscCall(DMPlexIsInterpolatedCollective(dm, &interpEnum));
9337   if (interpEnum == DMPLEX_INTERPOLATED_NONE) PetscFunctionReturn(PETSC_SUCCESS);
9338   if (interpEnum != DMPLEX_INTERPOLATED_FULL) {
9339     PetscCall(PetscPrintf(PetscObjectComm((PetscObject)dm), "DMPlexCheckFaces() warning: Mesh is only partially interpolated, this is currently not supported"));
9340     PetscFunctionReturn(PETSC_SUCCESS);
9341   }
9342 
9343   PetscCall(DMGetDimension(dm, &dim));
9344   PetscCall(DMPlexGetDepth(dm, &depth));
9345   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
9346   for (h = cellHeight; h < PetscMin(depth, dim); ++h) {
9347     PetscCall(DMPlexGetHeightStratum(dm, h, &cStart, &cEnd));
9348     for (c = cStart; c < cEnd; ++c) {
9349       const PetscInt       *cone, *ornt, *faceSizes, *faces;
9350       const DMPolytopeType *faceTypes;
9351       DMPolytopeType        ct;
9352       PetscInt              numFaces, coneSize, f;
9353       PetscInt             *closure = NULL, closureSize, cl, numCorners = 0, fOff = 0, unsplit;
9354 
9355       PetscCall(DMPlexGetCellType(dm, c, &ct));
9356       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9357       if (unsplit) continue;
9358       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9359       PetscCall(DMPlexGetCone(dm, c, &cone));
9360       PetscCall(DMPlexGetConeOrientation(dm, c, &ornt));
9361       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9362       for (cl = 0; cl < closureSize * 2; cl += 2) {
9363         const PetscInt p = closure[cl];
9364         if ((p >= vStart) && (p < vEnd)) closure[numCorners++] = p;
9365       }
9366       PetscCall(DMPlexGetRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
9367       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);
9368       for (f = 0; f < numFaces; ++f) {
9369         DMPolytopeType fct;
9370         PetscInt      *fclosure = NULL, fclosureSize, cl, fnumCorners = 0, v;
9371 
9372         PetscCall(DMPlexGetCellType(dm, cone[f], &fct));
9373         PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[f], ornt[f], PETSC_TRUE, &fclosureSize, &fclosure));
9374         for (cl = 0; cl < fclosureSize * 2; cl += 2) {
9375           const PetscInt p = fclosure[cl];
9376           if ((p >= vStart) && (p < vEnd)) fclosure[fnumCorners++] = p;
9377         }
9378         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]);
9379         for (v = 0; v < fnumCorners; ++v) {
9380           if (fclosure[v] != faces[fOff + v]) {
9381             PetscInt v1;
9382 
9383             PetscCall(PetscPrintf(PETSC_COMM_SELF, "face closure:"));
9384             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, fclosure[v1]));
9385             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\ncell face:"));
9386             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, faces[fOff + v1]));
9387             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9388             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]);
9389           }
9390         }
9391         PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[f], PETSC_TRUE, &fclosureSize, &fclosure));
9392         fOff += faceSizes[f];
9393       }
9394       PetscCall(DMPlexRestoreRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
9395       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9396     }
9397   }
9398   PetscFunctionReturn(PETSC_SUCCESS);
9399 }
9400 
9401 /*@
9402   DMPlexCheckGeometry - Check the geometry of mesh cells
9403 
9404   Input Parameter:
9405 . dm - The `DMPLEX` object
9406 
9407   Level: developer
9408 
9409   Notes:
9410   This is a useful diagnostic when creating meshes programmatically.
9411 
9412   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9413 
9414 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9415 @*/
9416 PetscErrorCode DMPlexCheckGeometry(DM dm)
9417 {
9418   Vec       coordinates;
9419   PetscReal detJ, J[9], refVol = 1.0;
9420   PetscReal vol;
9421   PetscInt  dim, depth, dE, d, cStart, cEnd, c;
9422 
9423   PetscFunctionBegin;
9424   PetscCall(DMGetDimension(dm, &dim));
9425   PetscCall(DMGetCoordinateDim(dm, &dE));
9426   if (dim != dE) PetscFunctionReturn(PETSC_SUCCESS);
9427   PetscCall(DMPlexGetDepth(dm, &depth));
9428   for (d = 0; d < dim; ++d) refVol *= 2.0;
9429   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
9430   /* Make sure local coordinates are created, because that step is collective */
9431   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
9432   if (!coordinates) PetscFunctionReturn(PETSC_SUCCESS);
9433   for (c = cStart; c < cEnd; ++c) {
9434     DMPolytopeType ct;
9435     PetscInt       unsplit;
9436     PetscBool      ignoreZeroVol = PETSC_FALSE;
9437 
9438     PetscCall(DMPlexGetCellType(dm, c, &ct));
9439     switch (ct) {
9440     case DM_POLYTOPE_SEG_PRISM_TENSOR:
9441     case DM_POLYTOPE_TRI_PRISM_TENSOR:
9442     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9443       ignoreZeroVol = PETSC_TRUE;
9444       break;
9445     default:
9446       break;
9447     }
9448     switch (ct) {
9449     case DM_POLYTOPE_TRI_PRISM:
9450     case DM_POLYTOPE_TRI_PRISM_TENSOR:
9451     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9452     case DM_POLYTOPE_PYRAMID:
9453       continue;
9454     default:
9455       break;
9456     }
9457     PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9458     if (unsplit) continue;
9459     PetscCall(DMPlexComputeCellGeometryFEM(dm, c, NULL, NULL, J, NULL, &detJ));
9460     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);
9461     PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FEM Volume %g\n", c, (double)(detJ * refVol)));
9462     /* This should work with periodicity since DG coordinates should be used */
9463     if (depth > 1) {
9464       PetscCall(DMPlexComputeCellGeometryFVM(dm, c, &vol, NULL, NULL));
9465       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);
9466       PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FVM Volume %g\n", c, (double)vol));
9467     }
9468   }
9469   PetscFunctionReturn(PETSC_SUCCESS);
9470 }
9471 
9472 /*@
9473   DMPlexCheckPointSF - Check that several necessary conditions are met for the point `PetscSF` of this plex.
9474 
9475   Collective
9476 
9477   Input Parameters:
9478 + dm              - The `DMPLEX` object
9479 . pointSF         - The `PetscSF`, or `NULL` for `PointSF` attached to `DM`
9480 - allowExtraRoots - Flag to allow extra points not present in the `DM`
9481 
9482   Level: developer
9483 
9484   Notes:
9485   This is mainly intended for debugging/testing purposes.
9486 
9487   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9488 
9489   Extra roots can come from periodic cuts, where additional points appear on the boundary
9490 
9491 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMGetPointSF()`, `DMSetFromOptions()`
9492 @*/
9493 PetscErrorCode DMPlexCheckPointSF(DM dm, PetscSF pointSF, PetscBool allowExtraRoots)
9494 {
9495   PetscInt           l, nleaves, nroots, overlap;
9496   const PetscInt    *locals;
9497   const PetscSFNode *remotes;
9498   PetscBool          distributed;
9499   MPI_Comm           comm;
9500   PetscMPIInt        rank;
9501 
9502   PetscFunctionBegin;
9503   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9504   if (pointSF) PetscValidHeaderSpecific(pointSF, PETSCSF_CLASSID, 2);
9505   else pointSF = dm->sf;
9506   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9507   PetscCheck(pointSF, comm, PETSC_ERR_ARG_WRONGSTATE, "DMPlex must have Point SF attached");
9508   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9509   {
9510     PetscMPIInt mpiFlag;
9511 
9512     PetscCallMPI(MPI_Comm_compare(comm, PetscObjectComm((PetscObject)pointSF), &mpiFlag));
9513     PetscCheck(mpiFlag == MPI_CONGRUENT || mpiFlag == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "DM and Point SF have different communicators (flag %d)", mpiFlag);
9514   }
9515   PetscCall(PetscSFGetGraph(pointSF, &nroots, &nleaves, &locals, &remotes));
9516   PetscCall(DMPlexIsDistributed(dm, &distributed));
9517   if (!distributed) {
9518     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);
9519     PetscFunctionReturn(PETSC_SUCCESS);
9520   }
9521   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);
9522   PetscCall(DMPlexGetOverlap(dm, &overlap));
9523 
9524   /* Check SF graph is compatible with DMPlex chart */
9525   {
9526     PetscInt pStart, pEnd, maxLeaf;
9527 
9528     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
9529     PetscCall(PetscSFGetLeafRange(pointSF, NULL, &maxLeaf));
9530     PetscCheck(allowExtraRoots || pEnd - pStart == nroots, PETSC_COMM_SELF, PETSC_ERR_PLIB, "pEnd - pStart = %" PetscInt_FMT " != nroots = %" PetscInt_FMT, pEnd - pStart, nroots);
9531     PetscCheck(maxLeaf < pEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "maxLeaf = %" PetscInt_FMT " >= pEnd = %" PetscInt_FMT, maxLeaf, pEnd);
9532   }
9533 
9534   /* Check Point SF has no local points referenced */
9535   for (l = 0; l < nleaves; l++) {
9536     PetscAssert(remotes[l].rank != (PetscInt)rank, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains local point %" PetscInt_FMT " <- (%" PetscInt_FMT ",%" PetscInt_FMT ")", locals ? locals[l] : l, remotes[l].rank, remotes[l].index);
9537   }
9538 
9539   /* Check there are no cells in interface */
9540   if (!overlap) {
9541     PetscInt cellHeight, cStart, cEnd;
9542 
9543     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9544     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9545     for (l = 0; l < nleaves; ++l) {
9546       const PetscInt point = locals ? locals[l] : l;
9547 
9548       PetscCheck(point < cStart || point >= cEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " which is a cell", point);
9549     }
9550   }
9551 
9552   /* If some point is in interface, then all its cone points must be also in interface (either as leaves or roots) */
9553   {
9554     const PetscInt *rootdegree;
9555 
9556     PetscCall(PetscSFComputeDegreeBegin(pointSF, &rootdegree));
9557     PetscCall(PetscSFComputeDegreeEnd(pointSF, &rootdegree));
9558     for (l = 0; l < nleaves; ++l) {
9559       const PetscInt  point = locals ? locals[l] : l;
9560       const PetscInt *cone;
9561       PetscInt        coneSize, c, idx;
9562 
9563       PetscCall(DMPlexGetConeSize(dm, point, &coneSize));
9564       PetscCall(DMPlexGetCone(dm, point, &cone));
9565       for (c = 0; c < coneSize; ++c) {
9566         if (!rootdegree[cone[c]]) {
9567           if (locals) {
9568             PetscCall(PetscFindInt(cone[c], nleaves, locals, &idx));
9569           } else {
9570             idx = (cone[c] < nleaves) ? cone[c] : -1;
9571           }
9572           PetscCheck(idx >= 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " but not %" PetscInt_FMT " from its cone", point, cone[c]);
9573         }
9574       }
9575     }
9576   }
9577   PetscFunctionReturn(PETSC_SUCCESS);
9578 }
9579 
9580 /*@
9581   DMPlexCheckOrphanVertices - Check that no vertices are disconnected from the mesh, unless the mesh only consists of disconnected vertices.
9582 
9583   Collective
9584 
9585   Input Parameter:
9586 . dm - The `DMPLEX` object
9587 
9588   Level: developer
9589 
9590   Notes:
9591   This is mainly intended for debugging/testing purposes.
9592 
9593   Other cell types which are disconnected would be caught by the symmetry and face checks.
9594 
9595   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9596 
9597 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCheck()`, `DMSetFromOptions()`
9598 @*/
9599 PetscErrorCode DMPlexCheckOrphanVertices(DM dm)
9600 {
9601   PetscInt pStart, pEnd, vStart, vEnd;
9602 
9603   PetscFunctionBegin;
9604   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
9605   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
9606   if (pStart == vStart && pEnd == vEnd) PetscFunctionReturn(PETSC_SUCCESS);
9607   for (PetscInt v = vStart; v < vEnd; ++v) {
9608     PetscInt suppSize;
9609 
9610     PetscCall(DMPlexGetSupportSize(dm, v, &suppSize));
9611     PetscCheck(suppSize, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Vertex %" PetscInt_FMT " is disconnected from the mesh", v);
9612   }
9613   PetscFunctionReturn(PETSC_SUCCESS);
9614 }
9615 
9616 /*@
9617   DMPlexCheck - Perform various checks of `DMPLEX` sanity
9618 
9619   Input Parameter:
9620 . dm - The `DMPLEX` object
9621 
9622   Level: developer
9623 
9624   Notes:
9625   This is a useful diagnostic when creating meshes programmatically.
9626 
9627   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9628 
9629   Currently does not include `DMPlexCheckCellShape()`.
9630 
9631 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9632 @*/
9633 PetscErrorCode DMPlexCheck(DM dm)
9634 {
9635   PetscInt cellHeight;
9636 
9637   PetscFunctionBegin;
9638   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9639   PetscCall(DMPlexCheckSymmetry(dm));
9640   PetscCall(DMPlexCheckSkeleton(dm, cellHeight));
9641   PetscCall(DMPlexCheckFaces(dm, cellHeight));
9642   PetscCall(DMPlexCheckGeometry(dm));
9643   PetscCall(DMPlexCheckPointSF(dm, NULL, PETSC_FALSE));
9644   PetscCall(DMPlexCheckInterfaceCones(dm));
9645   PetscCall(DMPlexCheckOrphanVertices(dm));
9646   PetscFunctionReturn(PETSC_SUCCESS);
9647 }
9648 
9649 typedef struct cell_stats {
9650   PetscReal min, max, sum, squaresum;
9651   PetscInt  count;
9652 } cell_stats_t;
9653 
9654 static void MPIAPI cell_stats_reduce(void *a, void *b, int *len, MPI_Datatype *datatype)
9655 {
9656   PetscInt i, N = *len;
9657 
9658   for (i = 0; i < N; i++) {
9659     cell_stats_t *A = (cell_stats_t *)a;
9660     cell_stats_t *B = (cell_stats_t *)b;
9661 
9662     B->min = PetscMin(A->min, B->min);
9663     B->max = PetscMax(A->max, B->max);
9664     B->sum += A->sum;
9665     B->squaresum += A->squaresum;
9666     B->count += A->count;
9667   }
9668 }
9669 
9670 /*@
9671   DMPlexCheckCellShape - Checks the Jacobian of the mapping from reference to real cells and computes some minimal statistics.
9672 
9673   Collective
9674 
9675   Input Parameters:
9676 + dm        - The `DMPLEX` object
9677 . output    - If true, statistics will be displayed on `stdout`
9678 - condLimit - Display all cells above this condition number, or `PETSC_DETERMINE` for no cell output
9679 
9680   Level: developer
9681 
9682   Notes:
9683   This is mainly intended for debugging/testing purposes.
9684 
9685   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9686 
9687 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexComputeOrthogonalQuality()`
9688 @*/
9689 PetscErrorCode DMPlexCheckCellShape(DM dm, PetscBool output, PetscReal condLimit)
9690 {
9691   DM           dmCoarse;
9692   cell_stats_t stats, globalStats;
9693   MPI_Comm     comm = PetscObjectComm((PetscObject)dm);
9694   PetscReal   *J, *invJ, min = 0, max = 0, mean = 0, stdev = 0;
9695   PetscReal    limit = condLimit > 0 ? condLimit : PETSC_MAX_REAL;
9696   PetscInt     cdim, cStart, cEnd, c, eStart, eEnd, count = 0;
9697   PetscMPIInt  rank, size;
9698 
9699   PetscFunctionBegin;
9700   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9701   stats.min = PETSC_MAX_REAL;
9702   stats.max = PETSC_MIN_REAL;
9703   stats.sum = stats.squaresum = 0.;
9704   stats.count                 = 0;
9705 
9706   PetscCallMPI(MPI_Comm_size(comm, &size));
9707   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9708   PetscCall(DMGetCoordinateDim(dm, &cdim));
9709   PetscCall(PetscMalloc2(PetscSqr(cdim), &J, PetscSqr(cdim), &invJ));
9710   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
9711   PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
9712   for (c = cStart; c < cEnd; c++) {
9713     PetscInt  i;
9714     PetscReal frobJ = 0., frobInvJ = 0., cond2, cond, detJ;
9715 
9716     PetscCall(DMPlexComputeCellGeometryAffineFEM(dm, c, NULL, J, invJ, &detJ));
9717     PetscCheck(detJ >= 0.0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Mesh cell %" PetscInt_FMT " is inverted", c);
9718     for (i = 0; i < PetscSqr(cdim); ++i) {
9719       frobJ += J[i] * J[i];
9720       frobInvJ += invJ[i] * invJ[i];
9721     }
9722     cond2 = frobJ * frobInvJ;
9723     cond  = PetscSqrtReal(cond2);
9724 
9725     stats.min = PetscMin(stats.min, cond);
9726     stats.max = PetscMax(stats.max, cond);
9727     stats.sum += cond;
9728     stats.squaresum += cond2;
9729     stats.count++;
9730     if (output && cond > limit) {
9731       PetscSection coordSection;
9732       Vec          coordsLocal;
9733       PetscScalar *coords = NULL;
9734       PetscInt     Nv, d, clSize, cl, *closure = NULL;
9735 
9736       PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
9737       PetscCall(DMGetCoordinateSection(dm, &coordSection));
9738       PetscCall(DMPlexVecGetClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9739       PetscCall(PetscSynchronizedPrintf(comm, "[%d] Cell %" PetscInt_FMT " cond %g\n", rank, c, (double)cond));
9740       for (i = 0; i < Nv / cdim; ++i) {
9741         PetscCall(PetscSynchronizedPrintf(comm, "  Vertex %" PetscInt_FMT ": (", i));
9742         for (d = 0; d < cdim; ++d) {
9743           if (d > 0) PetscCall(PetscSynchronizedPrintf(comm, ", "));
9744           PetscCall(PetscSynchronizedPrintf(comm, "%g", (double)PetscRealPart(coords[i * cdim + d])));
9745         }
9746         PetscCall(PetscSynchronizedPrintf(comm, ")\n"));
9747       }
9748       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9749       for (cl = 0; cl < clSize * 2; cl += 2) {
9750         const PetscInt edge = closure[cl];
9751 
9752         if ((edge >= eStart) && (edge < eEnd)) {
9753           PetscReal len;
9754 
9755           PetscCall(DMPlexComputeCellGeometryFVM(dm, edge, &len, NULL, NULL));
9756           PetscCall(PetscSynchronizedPrintf(comm, "  Edge %" PetscInt_FMT ": length %g\n", edge, (double)len));
9757         }
9758       }
9759       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9760       PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9761     }
9762   }
9763   if (output) PetscCall(PetscSynchronizedFlush(comm, NULL));
9764 
9765   if (size > 1) {
9766     PetscMPIInt  blockLengths[2] = {4, 1};
9767     MPI_Aint     blockOffsets[2] = {offsetof(cell_stats_t, min), offsetof(cell_stats_t, count)};
9768     MPI_Datatype blockTypes[2]   = {MPIU_REAL, MPIU_INT}, statType;
9769     MPI_Op       statReduce;
9770 
9771     PetscCallMPI(MPI_Type_create_struct(2, blockLengths, blockOffsets, blockTypes, &statType));
9772     PetscCallMPI(MPI_Type_commit(&statType));
9773     PetscCallMPI(MPI_Op_create(cell_stats_reduce, PETSC_TRUE, &statReduce));
9774     PetscCallMPI(MPI_Reduce(&stats, &globalStats, 1, statType, statReduce, 0, comm));
9775     PetscCallMPI(MPI_Op_free(&statReduce));
9776     PetscCallMPI(MPI_Type_free(&statType));
9777   } else {
9778     PetscCall(PetscArraycpy(&globalStats, &stats, 1));
9779   }
9780   if (rank == 0) {
9781     count = globalStats.count;
9782     min   = globalStats.min;
9783     max   = globalStats.max;
9784     mean  = globalStats.sum / globalStats.count;
9785     stdev = globalStats.count > 1 ? PetscSqrtReal(PetscMax((globalStats.squaresum - globalStats.count * mean * mean) / (globalStats.count - 1), 0)) : 0.0;
9786   }
9787 
9788   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));
9789   PetscCall(PetscFree2(J, invJ));
9790 
9791   PetscCall(DMGetCoarseDM(dm, &dmCoarse));
9792   if (dmCoarse) {
9793     PetscBool isplex;
9794 
9795     PetscCall(PetscObjectTypeCompare((PetscObject)dmCoarse, DMPLEX, &isplex));
9796     if (isplex) PetscCall(DMPlexCheckCellShape(dmCoarse, output, condLimit));
9797   }
9798   PetscFunctionReturn(PETSC_SUCCESS);
9799 }
9800 
9801 /*@
9802   DMPlexComputeOrthogonalQuality - Compute cell-wise orthogonal quality mesh statistic. Optionally tags all cells with
9803   orthogonal quality below given tolerance.
9804 
9805   Collective
9806 
9807   Input Parameters:
9808 + dm   - The `DMPLEX` object
9809 . fv   - Optional `PetscFV` object for pre-computed cell/face centroid information
9810 - atol - [0, 1] Absolute tolerance for tagging cells.
9811 
9812   Output Parameters:
9813 + OrthQual      - `Vec` containing orthogonal quality per cell
9814 - OrthQualLabel - `DMLabel` tagging cells below atol with `DM_ADAPT_REFINE`
9815 
9816   Options Database Keys:
9817 + -dm_plex_orthogonal_quality_label_view - view OrthQualLabel if label is requested. Currently only `PETSCVIEWERASCII` is supported.
9818 - -dm_plex_orthogonal_quality_vec_view   - view OrthQual vector.
9819 
9820   Level: intermediate
9821 
9822   Notes:
9823   Orthogonal quality is given by the following formula\:
9824 
9825   $ \min \left[ \frac{A_i \cdot f_i}{\|A_i\| \|f_i\|} , \frac{A_i \cdot c_i}{\|A_i\| \|c_i\|} \right]$
9826 
9827   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
9828   is the vector from the current cells centroid to the centroid of its i'th neighbor (which shares a face with the
9829   current cell). This computes the vector similarity between each cell face and its corresponding neighbor centroid by
9830   calculating the cosine of the angle between these vectors.
9831 
9832   Orthogonal quality ranges from 1 (best) to 0 (worst).
9833 
9834   This routine is mainly useful for FVM, however is not restricted to only FVM. The `PetscFV` object is optionally used to check for
9835   pre-computed FVM cell data, but if it is not passed in then this data will be computed.
9836 
9837   Cells are tagged if they have an orthogonal quality less than or equal to the absolute tolerance.
9838 
9839 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCheckCellShape()`, `DMCreateLabel()`, `PetscFV`, `DMLabel`, `Vec`
9840 @*/
9841 PetscErrorCode DMPlexComputeOrthogonalQuality(DM dm, PetscFV fv, PetscReal atol, Vec *OrthQual, DMLabel *OrthQualLabel)
9842 {
9843   PetscInt               nc, cellHeight, cStart, cEnd, cell, cellIter = 0;
9844   PetscInt              *idx;
9845   PetscScalar           *oqVals;
9846   const PetscScalar     *cellGeomArr, *faceGeomArr;
9847   PetscReal             *ci, *fi, *Ai;
9848   MPI_Comm               comm;
9849   Vec                    cellgeom, facegeom;
9850   DM                     dmFace, dmCell;
9851   IS                     glob;
9852   ISLocalToGlobalMapping ltog;
9853   PetscViewer            vwr;
9854 
9855   PetscFunctionBegin;
9856   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9857   if (fv) PetscValidHeaderSpecific(fv, PETSCFV_CLASSID, 2);
9858   PetscAssertPointer(OrthQual, 4);
9859   PetscCheck(atol >= 0.0 && atol <= 1.0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Absolute tolerance %g not in [0,1]", (double)atol);
9860   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9861   PetscCall(DMGetDimension(dm, &nc));
9862   PetscCheck(nc >= 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must have dimension >= 2 (current %" PetscInt_FMT ")", nc);
9863   {
9864     DMPlexInterpolatedFlag interpFlag;
9865 
9866     PetscCall(DMPlexIsInterpolated(dm, &interpFlag));
9867     if (interpFlag != DMPLEX_INTERPOLATED_FULL) {
9868       PetscMPIInt rank;
9869 
9870       PetscCallMPI(MPI_Comm_rank(comm, &rank));
9871       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must be fully interpolated, DM on rank %d is not fully interpolated", rank);
9872     }
9873   }
9874   if (OrthQualLabel) {
9875     PetscAssertPointer(OrthQualLabel, 5);
9876     PetscCall(DMCreateLabel(dm, "Orthogonal_Quality"));
9877     PetscCall(DMGetLabel(dm, "Orthogonal_Quality", OrthQualLabel));
9878   } else {
9879     *OrthQualLabel = NULL;
9880   }
9881   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9882   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9883   PetscCall(DMPlexCreateCellNumbering(dm, PETSC_TRUE, &glob));
9884   PetscCall(ISLocalToGlobalMappingCreateIS(glob, &ltog));
9885   PetscCall(ISLocalToGlobalMappingSetType(ltog, ISLOCALTOGLOBALMAPPINGHASH));
9886   PetscCall(VecCreate(comm, OrthQual));
9887   PetscCall(VecSetType(*OrthQual, VECSTANDARD));
9888   PetscCall(VecSetSizes(*OrthQual, cEnd - cStart, PETSC_DETERMINE));
9889   PetscCall(VecSetLocalToGlobalMapping(*OrthQual, ltog));
9890   PetscCall(VecSetUp(*OrthQual));
9891   PetscCall(ISDestroy(&glob));
9892   PetscCall(ISLocalToGlobalMappingDestroy(&ltog));
9893   PetscCall(DMPlexGetDataFVM(dm, fv, &cellgeom, &facegeom, NULL));
9894   PetscCall(VecGetArrayRead(cellgeom, &cellGeomArr));
9895   PetscCall(VecGetArrayRead(facegeom, &faceGeomArr));
9896   PetscCall(VecGetDM(cellgeom, &dmCell));
9897   PetscCall(VecGetDM(facegeom, &dmFace));
9898   PetscCall(PetscMalloc5(cEnd - cStart, &idx, cEnd - cStart, &oqVals, nc, &ci, nc, &fi, nc, &Ai));
9899   for (cell = cStart; cell < cEnd; cellIter++, cell++) {
9900     PetscInt         cellneigh, cellneighiter = 0, adjSize = PETSC_DETERMINE;
9901     PetscInt         cellarr[2], *adj = NULL;
9902     PetscScalar     *cArr, *fArr;
9903     PetscReal        minvalc = 1.0, minvalf = 1.0;
9904     PetscFVCellGeom *cg;
9905 
9906     idx[cellIter] = cell - cStart;
9907     cellarr[0]    = cell;
9908     /* Make indexing into cellGeom easier */
9909     PetscCall(DMPlexPointLocalRead(dmCell, cell, cellGeomArr, &cg));
9910     PetscCall(DMPlexGetAdjacency_Internal(dm, cell, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &adjSize, &adj));
9911     /* Technically 1 too big, but easier than fiddling with empty adjacency array */
9912     PetscCall(PetscCalloc2(adjSize, &cArr, adjSize, &fArr));
9913     for (cellneigh = 0; cellneigh < adjSize; cellneighiter++, cellneigh++) {
9914       PetscInt         i;
9915       const PetscInt   neigh  = adj[cellneigh];
9916       PetscReal        normci = 0, normfi = 0, normai = 0;
9917       PetscFVCellGeom *cgneigh;
9918       PetscFVFaceGeom *fg;
9919 
9920       /* Don't count ourselves in the neighbor list */
9921       if (neigh == cell) continue;
9922       PetscCall(DMPlexPointLocalRead(dmCell, neigh, cellGeomArr, &cgneigh));
9923       cellarr[1] = neigh;
9924       {
9925         PetscInt        numcovpts;
9926         const PetscInt *covpts;
9927 
9928         PetscCall(DMPlexGetMeet(dm, 2, cellarr, &numcovpts, &covpts));
9929         PetscCall(DMPlexPointLocalRead(dmFace, covpts[0], faceGeomArr, &fg));
9930         PetscCall(DMPlexRestoreMeet(dm, 2, cellarr, &numcovpts, &covpts));
9931       }
9932 
9933       /* Compute c_i, f_i and their norms */
9934       for (i = 0; i < nc; i++) {
9935         ci[i] = cgneigh->centroid[i] - cg->centroid[i];
9936         fi[i] = fg->centroid[i] - cg->centroid[i];
9937         Ai[i] = fg->normal[i];
9938         normci += PetscPowReal(ci[i], 2);
9939         normfi += PetscPowReal(fi[i], 2);
9940         normai += PetscPowReal(Ai[i], 2);
9941       }
9942       normci = PetscSqrtReal(normci);
9943       normfi = PetscSqrtReal(normfi);
9944       normai = PetscSqrtReal(normai);
9945 
9946       /* Normalize and compute for each face-cell-normal pair */
9947       for (i = 0; i < nc; i++) {
9948         ci[i] = ci[i] / normci;
9949         fi[i] = fi[i] / normfi;
9950         Ai[i] = Ai[i] / normai;
9951         /* PetscAbs because I don't know if normals are guaranteed to point out */
9952         cArr[cellneighiter] += PetscAbs(Ai[i] * ci[i]);
9953         fArr[cellneighiter] += PetscAbs(Ai[i] * fi[i]);
9954       }
9955       if (PetscRealPart(cArr[cellneighiter]) < minvalc) minvalc = PetscRealPart(cArr[cellneighiter]);
9956       if (PetscRealPart(fArr[cellneighiter]) < minvalf) minvalf = PetscRealPart(fArr[cellneighiter]);
9957     }
9958     PetscCall(PetscFree(adj));
9959     PetscCall(PetscFree2(cArr, fArr));
9960     /* Defer to cell if they're equal */
9961     oqVals[cellIter] = PetscMin(minvalf, minvalc);
9962     if (OrthQualLabel) {
9963       if (PetscRealPart(oqVals[cellIter]) <= atol) PetscCall(DMLabelSetValue(*OrthQualLabel, cell, DM_ADAPT_REFINE));
9964     }
9965   }
9966   PetscCall(VecSetValuesLocal(*OrthQual, cEnd - cStart, idx, oqVals, INSERT_VALUES));
9967   PetscCall(VecAssemblyBegin(*OrthQual));
9968   PetscCall(VecAssemblyEnd(*OrthQual));
9969   PetscCall(VecRestoreArrayRead(cellgeom, &cellGeomArr));
9970   PetscCall(VecRestoreArrayRead(facegeom, &faceGeomArr));
9971   PetscCall(PetscOptionsCreateViewer(comm, NULL, NULL, "-dm_plex_orthogonal_quality_label_view", &vwr, NULL, NULL));
9972   if (OrthQualLabel) {
9973     if (vwr) PetscCall(DMLabelView(*OrthQualLabel, vwr));
9974   }
9975   PetscCall(PetscFree5(idx, oqVals, ci, fi, Ai));
9976   PetscCall(PetscViewerDestroy(&vwr));
9977   PetscCall(VecViewFromOptions(*OrthQual, NULL, "-dm_plex_orthogonal_quality_vec_view"));
9978   PetscFunctionReturn(PETSC_SUCCESS);
9979 }
9980 
9981 /* this is here instead of DMGetOutputDM because output DM still has constraints in the local indices that affect
9982  * interpolator construction */
9983 static PetscErrorCode DMGetFullDM(DM dm, DM *odm)
9984 {
9985   PetscSection section, newSection, gsection;
9986   PetscSF      sf;
9987   PetscBool    hasConstraints, ghasConstraints;
9988 
9989   PetscFunctionBegin;
9990   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9991   PetscAssertPointer(odm, 2);
9992   PetscCall(DMGetLocalSection(dm, &section));
9993   PetscCall(PetscSectionHasConstraints(section, &hasConstraints));
9994   PetscCall(MPIU_Allreduce(&hasConstraints, &ghasConstraints, 1, MPIU_BOOL, MPI_LOR, PetscObjectComm((PetscObject)dm)));
9995   if (!ghasConstraints) {
9996     PetscCall(PetscObjectReference((PetscObject)dm));
9997     *odm = dm;
9998     PetscFunctionReturn(PETSC_SUCCESS);
9999   }
10000   PetscCall(DMClone(dm, odm));
10001   PetscCall(DMCopyFields(dm, *odm));
10002   PetscCall(DMGetLocalSection(*odm, &newSection));
10003   PetscCall(DMGetPointSF(*odm, &sf));
10004   PetscCall(PetscSectionCreateGlobalSection(newSection, sf, PETSC_TRUE, PETSC_TRUE, PETSC_FALSE, &gsection));
10005   PetscCall(DMSetGlobalSection(*odm, gsection));
10006   PetscCall(PetscSectionDestroy(&gsection));
10007   PetscFunctionReturn(PETSC_SUCCESS);
10008 }
10009 
10010 static PetscErrorCode DMCreateAffineInterpolationCorrection_Plex(DM dmc, DM dmf, Vec *shift)
10011 {
10012   DM        dmco, dmfo;
10013   Mat       interpo;
10014   Vec       rscale;
10015   Vec       cglobalo, clocal;
10016   Vec       fglobal, fglobalo, flocal;
10017   PetscBool regular;
10018 
10019   PetscFunctionBegin;
10020   PetscCall(DMGetFullDM(dmc, &dmco));
10021   PetscCall(DMGetFullDM(dmf, &dmfo));
10022   PetscCall(DMSetCoarseDM(dmfo, dmco));
10023   PetscCall(DMPlexGetRegularRefinement(dmf, &regular));
10024   PetscCall(DMPlexSetRegularRefinement(dmfo, regular));
10025   PetscCall(DMCreateInterpolation(dmco, dmfo, &interpo, &rscale));
10026   PetscCall(DMCreateGlobalVector(dmco, &cglobalo));
10027   PetscCall(DMCreateLocalVector(dmc, &clocal));
10028   PetscCall(VecSet(cglobalo, 0.));
10029   PetscCall(VecSet(clocal, 0.));
10030   PetscCall(DMCreateGlobalVector(dmf, &fglobal));
10031   PetscCall(DMCreateGlobalVector(dmfo, &fglobalo));
10032   PetscCall(DMCreateLocalVector(dmf, &flocal));
10033   PetscCall(VecSet(fglobal, 0.));
10034   PetscCall(VecSet(fglobalo, 0.));
10035   PetscCall(VecSet(flocal, 0.));
10036   PetscCall(DMPlexInsertBoundaryValues(dmc, PETSC_TRUE, clocal, 0., NULL, NULL, NULL));
10037   PetscCall(DMLocalToGlobalBegin(dmco, clocal, INSERT_VALUES, cglobalo));
10038   PetscCall(DMLocalToGlobalEnd(dmco, clocal, INSERT_VALUES, cglobalo));
10039   PetscCall(MatMult(interpo, cglobalo, fglobalo));
10040   PetscCall(DMGlobalToLocalBegin(dmfo, fglobalo, INSERT_VALUES, flocal));
10041   PetscCall(DMGlobalToLocalEnd(dmfo, fglobalo, INSERT_VALUES, flocal));
10042   PetscCall(DMLocalToGlobalBegin(dmf, flocal, INSERT_VALUES, fglobal));
10043   PetscCall(DMLocalToGlobalEnd(dmf, flocal, INSERT_VALUES, fglobal));
10044   *shift = fglobal;
10045   PetscCall(VecDestroy(&flocal));
10046   PetscCall(VecDestroy(&fglobalo));
10047   PetscCall(VecDestroy(&clocal));
10048   PetscCall(VecDestroy(&cglobalo));
10049   PetscCall(VecDestroy(&rscale));
10050   PetscCall(MatDestroy(&interpo));
10051   PetscCall(DMDestroy(&dmfo));
10052   PetscCall(DMDestroy(&dmco));
10053   PetscFunctionReturn(PETSC_SUCCESS);
10054 }
10055 
10056 PETSC_INTERN PetscErrorCode DMInterpolateSolution_Plex(DM coarse, DM fine, Mat interp, Vec coarseSol, Vec fineSol)
10057 {
10058   PetscObject shifto;
10059   Vec         shift;
10060 
10061   PetscFunctionBegin;
10062   if (!interp) {
10063     Vec rscale;
10064 
10065     PetscCall(DMCreateInterpolation(coarse, fine, &interp, &rscale));
10066     PetscCall(VecDestroy(&rscale));
10067   } else {
10068     PetscCall(PetscObjectReference((PetscObject)interp));
10069   }
10070   PetscCall(PetscObjectQuery((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", &shifto));
10071   if (!shifto) {
10072     PetscCall(DMCreateAffineInterpolationCorrection_Plex(coarse, fine, &shift));
10073     PetscCall(PetscObjectCompose((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", (PetscObject)shift));
10074     shifto = (PetscObject)shift;
10075     PetscCall(VecDestroy(&shift));
10076   }
10077   shift = (Vec)shifto;
10078   PetscCall(MatInterpolate(interp, coarseSol, fineSol));
10079   PetscCall(VecAXPY(fineSol, 1.0, shift));
10080   PetscCall(MatDestroy(&interp));
10081   PetscFunctionReturn(PETSC_SUCCESS);
10082 }
10083 
10084 /* Pointwise interpolation
10085      Just code FEM for now
10086      u^f = I u^c
10087      sum_k u^f_k phi^f_k = I sum_j u^c_j phi^c_j
10088      u^f_i = sum_j psi^f_i I phi^c_j u^c_j
10089      I_{ij} = psi^f_i phi^c_j
10090 */
10091 PetscErrorCode DMCreateInterpolation_Plex(DM dmCoarse, DM dmFine, Mat *interpolation, Vec *scaling)
10092 {
10093   PetscSection gsc, gsf;
10094   PetscInt     m, n;
10095   void        *ctx;
10096   DM           cdm;
10097   PetscBool    regular, ismatis, isRefined = dmCoarse->data == dmFine->data ? PETSC_FALSE : PETSC_TRUE;
10098 
10099   PetscFunctionBegin;
10100   PetscCall(DMGetGlobalSection(dmFine, &gsf));
10101   PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
10102   PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
10103   PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
10104 
10105   PetscCall(PetscStrcmp(dmCoarse->mattype, MATIS, &ismatis));
10106   PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), interpolation));
10107   PetscCall(MatSetSizes(*interpolation, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
10108   PetscCall(MatSetType(*interpolation, ismatis ? MATAIJ : dmCoarse->mattype));
10109   PetscCall(DMGetApplicationContext(dmFine, &ctx));
10110 
10111   PetscCall(DMGetCoarseDM(dmFine, &cdm));
10112   PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
10113   if (!isRefined || (regular && cdm == dmCoarse)) PetscCall(DMPlexComputeInterpolatorNested(dmCoarse, dmFine, isRefined, *interpolation, ctx));
10114   else PetscCall(DMPlexComputeInterpolatorGeneral(dmCoarse, dmFine, *interpolation, ctx));
10115   PetscCall(MatViewFromOptions(*interpolation, NULL, "-interp_mat_view"));
10116   if (scaling) {
10117     /* Use naive scaling */
10118     PetscCall(DMCreateInterpolationScale(dmCoarse, dmFine, *interpolation, scaling));
10119   }
10120   PetscFunctionReturn(PETSC_SUCCESS);
10121 }
10122 
10123 PetscErrorCode DMCreateInjection_Plex(DM dmCoarse, DM dmFine, Mat *mat)
10124 {
10125   VecScatter ctx;
10126 
10127   PetscFunctionBegin;
10128   PetscCall(DMPlexComputeInjectorFEM(dmCoarse, dmFine, &ctx, NULL));
10129   PetscCall(MatCreateScatter(PetscObjectComm((PetscObject)ctx), ctx, mat));
10130   PetscCall(VecScatterDestroy(&ctx));
10131   PetscFunctionReturn(PETSC_SUCCESS);
10132 }
10133 
10134 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[])
10135 {
10136   const PetscInt f  = (PetscInt)PetscRealPart(constants[numConstants]);
10137   const PetscInt Nc = uOff[f + 1] - uOff[f];
10138   for (PetscInt c = 0; c < Nc; ++c) g0[c * Nc + c] = 1.0;
10139 }
10140 
10141 PetscErrorCode DMCreateMassMatrixLumped_Plex(DM dm, Vec *lmass, Vec *mass)
10142 {
10143   DM           dmc;
10144   PetscDS      ds;
10145   Vec          ones, locmass;
10146   IS           cellIS;
10147   PetscFormKey key;
10148   PetscInt     depth;
10149 
10150   PetscFunctionBegin;
10151   PetscCall(DMClone(dm, &dmc));
10152   PetscCall(DMCopyDisc(dm, dmc));
10153   PetscCall(DMGetDS(dmc, &ds));
10154   for (PetscInt f = 0; f < dmc->Nf; ++f) PetscCall(PetscDSSetJacobian(ds, f, f, g0_identity_private, NULL, NULL, NULL));
10155   if (mass) PetscCall(DMCreateGlobalVector(dm, mass));
10156   if (lmass) PetscCall(DMCreateLocalVector(dm, &locmass));
10157   else PetscCall(DMGetLocalVector(dm, &locmass));
10158   PetscCall(DMGetLocalVector(dm, &ones));
10159   PetscCall(DMPlexGetDepth(dm, &depth));
10160   PetscCall(DMGetStratumIS(dm, "depth", depth, &cellIS));
10161   PetscCall(VecSet(locmass, 0.0));
10162   PetscCall(VecSet(ones, 1.0));
10163   key.label = NULL;
10164   key.value = 0;
10165   key.field = 0;
10166   key.part  = 0;
10167   PetscCall(DMPlexComputeJacobian_Action_Internal(dmc, key, cellIS, 0.0, 0.0, ones, NULL, ones, locmass, NULL));
10168   PetscCall(ISDestroy(&cellIS));
10169   if (mass) {
10170     PetscCall(DMLocalToGlobalBegin(dm, locmass, ADD_VALUES, *mass));
10171     PetscCall(DMLocalToGlobalEnd(dm, locmass, ADD_VALUES, *mass));
10172   }
10173   PetscCall(DMRestoreLocalVector(dm, &ones));
10174   if (lmass) *lmass = locmass;
10175   else PetscCall(DMRestoreLocalVector(dm, &locmass));
10176   PetscCall(DMDestroy(&dmc));
10177   PetscFunctionReturn(PETSC_SUCCESS);
10178 }
10179 
10180 PetscErrorCode DMCreateMassMatrix_Plex(DM dmCoarse, DM dmFine, Mat *mass)
10181 {
10182   PetscSection gsc, gsf;
10183   PetscInt     m, n;
10184   void        *ctx;
10185   DM           cdm;
10186   PetscBool    regular;
10187 
10188   PetscFunctionBegin;
10189   if (dmFine == dmCoarse) {
10190     DM            dmc;
10191     PetscDS       ds;
10192     PetscWeakForm wf;
10193     Vec           u;
10194     IS            cellIS;
10195     PetscFormKey  key;
10196     PetscInt      depth;
10197 
10198     PetscCall(DMClone(dmFine, &dmc));
10199     PetscCall(DMCopyDisc(dmFine, dmc));
10200     PetscCall(DMGetDS(dmc, &ds));
10201     PetscCall(PetscDSGetWeakForm(ds, &wf));
10202     PetscCall(PetscWeakFormClear(wf));
10203     for (PetscInt f = 0; f < dmc->Nf; ++f) PetscCall(PetscDSSetJacobian(ds, f, f, g0_identity_private, NULL, NULL, NULL));
10204     PetscCall(DMCreateMatrix(dmc, mass));
10205     PetscCall(DMGetLocalVector(dmc, &u));
10206     PetscCall(DMPlexGetDepth(dmc, &depth));
10207     PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
10208     PetscCall(MatZeroEntries(*mass));
10209     key.label = NULL;
10210     key.value = 0;
10211     key.field = 0;
10212     key.part  = 0;
10213     PetscCall(DMPlexComputeJacobian_Internal(dmc, key, cellIS, 0.0, 0.0, u, NULL, *mass, *mass, NULL));
10214     PetscCall(ISDestroy(&cellIS));
10215     PetscCall(DMRestoreLocalVector(dmc, &u));
10216     PetscCall(DMDestroy(&dmc));
10217   } else {
10218     PetscCall(DMGetGlobalSection(dmFine, &gsf));
10219     PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
10220     PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
10221     PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
10222 
10223     PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), mass));
10224     PetscCall(MatSetSizes(*mass, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
10225     PetscCall(MatSetType(*mass, dmCoarse->mattype));
10226     PetscCall(DMGetApplicationContext(dmFine, &ctx));
10227 
10228     PetscCall(DMGetCoarseDM(dmFine, &cdm));
10229     PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
10230     if (regular && cdm == dmCoarse) PetscCall(DMPlexComputeMassMatrixNested(dmCoarse, dmFine, *mass, ctx));
10231     else PetscCall(DMPlexComputeMassMatrixGeneral(dmCoarse, dmFine, *mass, ctx));
10232   }
10233   PetscCall(MatViewFromOptions(*mass, NULL, "-mass_mat_view"));
10234   PetscFunctionReturn(PETSC_SUCCESS);
10235 }
10236 
10237 /*@
10238   DMPlexGetRegularRefinement - Get the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
10239 
10240   Input Parameter:
10241 . dm - The `DMPLEX` object
10242 
10243   Output Parameter:
10244 . regular - The flag
10245 
10246   Level: intermediate
10247 
10248 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetRegularRefinement()`
10249 @*/
10250 PetscErrorCode DMPlexGetRegularRefinement(DM dm, PetscBool *regular)
10251 {
10252   PetscFunctionBegin;
10253   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10254   PetscAssertPointer(regular, 2);
10255   *regular = ((DM_Plex *)dm->data)->regularRefinement;
10256   PetscFunctionReturn(PETSC_SUCCESS);
10257 }
10258 
10259 /*@
10260   DMPlexSetRegularRefinement - Set the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
10261 
10262   Input Parameters:
10263 + dm      - The `DMPLEX` object
10264 - regular - The flag
10265 
10266   Level: intermediate
10267 
10268 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetRegularRefinement()`
10269 @*/
10270 PetscErrorCode DMPlexSetRegularRefinement(DM dm, PetscBool regular)
10271 {
10272   PetscFunctionBegin;
10273   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10274   ((DM_Plex *)dm->data)->regularRefinement = regular;
10275   PetscFunctionReturn(PETSC_SUCCESS);
10276 }
10277 
10278 /*@
10279   DMPlexGetAnchors - Get the layout of the anchor (point-to-point) constraints.  Typically, the user will not have to
10280   call DMPlexGetAnchors() directly: if there are anchors, then `DMPlexGetAnchors()` is called during `DMGetDefaultConstraints()`.
10281 
10282   Not Collective
10283 
10284   Input Parameter:
10285 . dm - The `DMPLEX` object
10286 
10287   Output Parameters:
10288 + anchorSection - If not `NULL`, set to the section describing which points anchor the constrained points.
10289 - anchorIS      - If not `NULL`, set to the list of anchors indexed by `anchorSection`
10290 
10291   Level: intermediate
10292 
10293 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`, `IS`, `PetscSection`
10294 @*/
10295 PetscErrorCode DMPlexGetAnchors(DM dm, PetscSection *anchorSection, IS *anchorIS)
10296 {
10297   DM_Plex *plex = (DM_Plex *)dm->data;
10298 
10299   PetscFunctionBegin;
10300   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10301   if (!plex->anchorSection && !plex->anchorIS && plex->createanchors) PetscCall((*plex->createanchors)(dm));
10302   if (anchorSection) *anchorSection = plex->anchorSection;
10303   if (anchorIS) *anchorIS = plex->anchorIS;
10304   PetscFunctionReturn(PETSC_SUCCESS);
10305 }
10306 
10307 /*@
10308   DMPlexSetAnchors - Set the layout of the local anchor (point-to-point) constraints.
10309 
10310   Collective
10311 
10312   Input Parameters:
10313 + dm            - The `DMPLEX` object
10314 . anchorSection - The section that describes the mapping from constrained points to the anchor points listed in anchorIS.
10315                   Must have a local communicator (`PETSC_COMM_SELF` or derivative).
10316 - anchorIS      - The list of all anchor points.  Must have a local communicator (`PETSC_COMM_SELF` or derivative).
10317 
10318   Level: intermediate
10319 
10320   Notes:
10321   Unlike boundary conditions, when a point's degrees of freedom in a section are constrained to
10322   an outside value, the anchor constraints set a point's degrees of freedom to be a linear
10323   combination of other points' degrees of freedom.
10324 
10325   After specifying the layout of constraints with `DMPlexSetAnchors()`, one specifies the constraints by calling
10326   `DMGetDefaultConstraints()` and filling in the entries in the constraint matrix.
10327 
10328   The reference counts of `anchorSection` and `anchorIS` are incremented.
10329 
10330 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`
10331 @*/
10332 PetscErrorCode DMPlexSetAnchors(DM dm, PetscSection anchorSection, IS anchorIS)
10333 {
10334   DM_Plex    *plex = (DM_Plex *)dm->data;
10335   PetscMPIInt result;
10336 
10337   PetscFunctionBegin;
10338   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10339   if (anchorSection) {
10340     PetscValidHeaderSpecific(anchorSection, PETSC_SECTION_CLASSID, 2);
10341     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorSection), &result));
10342     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor section must have local communicator");
10343   }
10344   if (anchorIS) {
10345     PetscValidHeaderSpecific(anchorIS, IS_CLASSID, 3);
10346     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorIS), &result));
10347     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor IS must have local communicator");
10348   }
10349 
10350   PetscCall(PetscObjectReference((PetscObject)anchorSection));
10351   PetscCall(PetscSectionDestroy(&plex->anchorSection));
10352   plex->anchorSection = anchorSection;
10353 
10354   PetscCall(PetscObjectReference((PetscObject)anchorIS));
10355   PetscCall(ISDestroy(&plex->anchorIS));
10356   plex->anchorIS = anchorIS;
10357 
10358   if (PetscUnlikelyDebug(anchorIS && anchorSection)) {
10359     PetscInt        size, a, pStart, pEnd;
10360     const PetscInt *anchors;
10361 
10362     PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
10363     PetscCall(ISGetLocalSize(anchorIS, &size));
10364     PetscCall(ISGetIndices(anchorIS, &anchors));
10365     for (a = 0; a < size; a++) {
10366       PetscInt p;
10367 
10368       p = anchors[a];
10369       if (p >= pStart && p < pEnd) {
10370         PetscInt dof;
10371 
10372         PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
10373         if (dof) {
10374           PetscCall(ISRestoreIndices(anchorIS, &anchors));
10375           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Point %" PetscInt_FMT " cannot be constrained and an anchor", p);
10376         }
10377       }
10378     }
10379     PetscCall(ISRestoreIndices(anchorIS, &anchors));
10380   }
10381   /* reset the generic constraints */
10382   PetscCall(DMSetDefaultConstraints(dm, NULL, NULL, NULL));
10383   PetscFunctionReturn(PETSC_SUCCESS);
10384 }
10385 
10386 static PetscErrorCode DMPlexCreateConstraintSection_Anchors(DM dm, PetscSection section, PetscSection *cSec)
10387 {
10388   PetscSection anchorSection;
10389   PetscInt     pStart, pEnd, sStart, sEnd, p, dof, numFields, f;
10390 
10391   PetscFunctionBegin;
10392   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10393   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
10394   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, cSec));
10395   PetscCall(PetscSectionGetNumFields(section, &numFields));
10396   if (numFields) {
10397     PetscInt f;
10398     PetscCall(PetscSectionSetNumFields(*cSec, numFields));
10399 
10400     for (f = 0; f < numFields; f++) {
10401       PetscInt numComp;
10402 
10403       PetscCall(PetscSectionGetFieldComponents(section, f, &numComp));
10404       PetscCall(PetscSectionSetFieldComponents(*cSec, f, numComp));
10405     }
10406   }
10407   PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
10408   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
10409   pStart = PetscMax(pStart, sStart);
10410   pEnd   = PetscMin(pEnd, sEnd);
10411   pEnd   = PetscMax(pStart, pEnd);
10412   PetscCall(PetscSectionSetChart(*cSec, pStart, pEnd));
10413   for (p = pStart; p < pEnd; p++) {
10414     PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
10415     if (dof) {
10416       PetscCall(PetscSectionGetDof(section, p, &dof));
10417       PetscCall(PetscSectionSetDof(*cSec, p, dof));
10418       for (f = 0; f < numFields; f++) {
10419         PetscCall(PetscSectionGetFieldDof(section, p, f, &dof));
10420         PetscCall(PetscSectionSetFieldDof(*cSec, p, f, dof));
10421       }
10422     }
10423   }
10424   PetscCall(PetscSectionSetUp(*cSec));
10425   PetscCall(PetscObjectSetName((PetscObject)*cSec, "Constraint Section"));
10426   PetscFunctionReturn(PETSC_SUCCESS);
10427 }
10428 
10429 static PetscErrorCode DMPlexCreateConstraintMatrix_Anchors(DM dm, PetscSection section, PetscSection cSec, Mat *cMat)
10430 {
10431   PetscSection    aSec;
10432   PetscInt        pStart, pEnd, p, sStart, sEnd, dof, aDof, aOff, off, nnz, annz, m, n, q, a, offset, *i, *j;
10433   const PetscInt *anchors;
10434   PetscInt        numFields, f;
10435   IS              aIS;
10436   MatType         mtype;
10437   PetscBool       iscuda, iskokkos;
10438 
10439   PetscFunctionBegin;
10440   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10441   PetscCall(PetscSectionGetStorageSize(cSec, &m));
10442   PetscCall(PetscSectionGetStorageSize(section, &n));
10443   PetscCall(MatCreate(PETSC_COMM_SELF, cMat));
10444   PetscCall(MatSetSizes(*cMat, m, n, m, n));
10445   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJCUSPARSE, &iscuda));
10446   if (!iscuda) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJCUSPARSE, &iscuda));
10447   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJKOKKOS, &iskokkos));
10448   if (!iskokkos) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJKOKKOS, &iskokkos));
10449   if (iscuda) mtype = MATSEQAIJCUSPARSE;
10450   else if (iskokkos) mtype = MATSEQAIJKOKKOS;
10451   else mtype = MATSEQAIJ;
10452   PetscCall(MatSetType(*cMat, mtype));
10453   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
10454   PetscCall(ISGetIndices(aIS, &anchors));
10455   /* cSec will be a subset of aSec and section */
10456   PetscCall(PetscSectionGetChart(cSec, &pStart, &pEnd));
10457   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
10458   PetscCall(PetscMalloc1(m + 1, &i));
10459   i[0] = 0;
10460   PetscCall(PetscSectionGetNumFields(section, &numFields));
10461   for (p = pStart; p < pEnd; p++) {
10462     PetscInt rDof, rOff, r;
10463 
10464     PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10465     if (!rDof) continue;
10466     PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10467     if (numFields) {
10468       for (f = 0; f < numFields; f++) {
10469         annz = 0;
10470         for (r = 0; r < rDof; r++) {
10471           a = anchors[rOff + r];
10472           if (a < sStart || a >= sEnd) continue;
10473           PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
10474           annz += aDof;
10475         }
10476         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
10477         PetscCall(PetscSectionGetFieldOffset(cSec, p, f, &off));
10478         for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
10479       }
10480     } else {
10481       annz = 0;
10482       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10483       for (q = 0; q < dof; q++) {
10484         a = anchors[rOff + q];
10485         if (a < sStart || a >= sEnd) continue;
10486         PetscCall(PetscSectionGetDof(section, a, &aDof));
10487         annz += aDof;
10488       }
10489       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10490       PetscCall(PetscSectionGetOffset(cSec, p, &off));
10491       for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
10492     }
10493   }
10494   nnz = i[m];
10495   PetscCall(PetscMalloc1(nnz, &j));
10496   offset = 0;
10497   for (p = pStart; p < pEnd; p++) {
10498     if (numFields) {
10499       for (f = 0; f < numFields; f++) {
10500         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
10501         for (q = 0; q < dof; q++) {
10502           PetscInt rDof, rOff, r;
10503           PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10504           PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10505           for (r = 0; r < rDof; r++) {
10506             PetscInt s;
10507 
10508             a = anchors[rOff + r];
10509             if (a < sStart || a >= sEnd) continue;
10510             PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
10511             PetscCall(PetscSectionGetFieldOffset(section, a, f, &aOff));
10512             for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
10513           }
10514         }
10515       }
10516     } else {
10517       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10518       for (q = 0; q < dof; q++) {
10519         PetscInt rDof, rOff, r;
10520         PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10521         PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10522         for (r = 0; r < rDof; r++) {
10523           PetscInt s;
10524 
10525           a = anchors[rOff + r];
10526           if (a < sStart || a >= sEnd) continue;
10527           PetscCall(PetscSectionGetDof(section, a, &aDof));
10528           PetscCall(PetscSectionGetOffset(section, a, &aOff));
10529           for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
10530         }
10531       }
10532     }
10533   }
10534   PetscCall(MatSeqAIJSetPreallocationCSR(*cMat, i, j, NULL));
10535   PetscCall(PetscFree(i));
10536   PetscCall(PetscFree(j));
10537   PetscCall(ISRestoreIndices(aIS, &anchors));
10538   PetscFunctionReturn(PETSC_SUCCESS);
10539 }
10540 
10541 PetscErrorCode DMCreateDefaultConstraints_Plex(DM dm)
10542 {
10543   DM_Plex     *plex = (DM_Plex *)dm->data;
10544   PetscSection anchorSection, section, cSec;
10545   Mat          cMat;
10546 
10547   PetscFunctionBegin;
10548   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10549   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
10550   if (anchorSection) {
10551     PetscInt Nf;
10552 
10553     PetscCall(DMGetLocalSection(dm, &section));
10554     PetscCall(DMPlexCreateConstraintSection_Anchors(dm, section, &cSec));
10555     PetscCall(DMPlexCreateConstraintMatrix_Anchors(dm, section, cSec, &cMat));
10556     PetscCall(DMGetNumFields(dm, &Nf));
10557     if (Nf && plex->computeanchormatrix) PetscCall((*plex->computeanchormatrix)(dm, section, cSec, cMat));
10558     PetscCall(DMSetDefaultConstraints(dm, cSec, cMat, NULL));
10559     PetscCall(PetscSectionDestroy(&cSec));
10560     PetscCall(MatDestroy(&cMat));
10561   }
10562   PetscFunctionReturn(PETSC_SUCCESS);
10563 }
10564 
10565 PetscErrorCode DMCreateSubDomainDM_Plex(DM dm, DMLabel label, PetscInt value, IS *is, DM *subdm)
10566 {
10567   IS           subis;
10568   PetscSection section, subsection;
10569 
10570   PetscFunctionBegin;
10571   PetscCall(DMGetLocalSection(dm, &section));
10572   PetscCheck(section, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set default section for DM before splitting subdomain");
10573   PetscCheck(subdm, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set output subDM for splitting subdomain");
10574   /* Create subdomain */
10575   PetscCall(DMPlexFilter(dm, label, value, PETSC_FALSE, PETSC_FALSE, NULL, subdm));
10576   /* Create submodel */
10577   PetscCall(DMPlexGetSubpointIS(*subdm, &subis));
10578   PetscCall(PetscSectionCreateSubmeshSection(section, subis, &subsection));
10579   PetscCall(DMSetLocalSection(*subdm, subsection));
10580   PetscCall(PetscSectionDestroy(&subsection));
10581   PetscCall(DMCopyDisc(dm, *subdm));
10582   /* Create map from submodel to global model */
10583   if (is) {
10584     PetscSection    sectionGlobal, subsectionGlobal;
10585     IS              spIS;
10586     const PetscInt *spmap;
10587     PetscInt       *subIndices;
10588     PetscInt        subSize = 0, subOff = 0, pStart, pEnd, p;
10589     PetscInt        Nf, f, bs = -1, bsLocal[2], bsMinMax[2];
10590 
10591     PetscCall(DMPlexGetSubpointIS(*subdm, &spIS));
10592     PetscCall(ISGetIndices(spIS, &spmap));
10593     PetscCall(PetscSectionGetNumFields(section, &Nf));
10594     PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
10595     PetscCall(DMGetGlobalSection(*subdm, &subsectionGlobal));
10596     PetscCall(PetscSectionGetChart(subsection, &pStart, &pEnd));
10597     for (p = pStart; p < pEnd; ++p) {
10598       PetscInt gdof, pSubSize = 0;
10599 
10600       PetscCall(PetscSectionGetDof(sectionGlobal, p, &gdof));
10601       if (gdof > 0) {
10602         for (f = 0; f < Nf; ++f) {
10603           PetscInt fdof, fcdof;
10604 
10605           PetscCall(PetscSectionGetFieldDof(subsection, p, f, &fdof));
10606           PetscCall(PetscSectionGetFieldConstraintDof(subsection, p, f, &fcdof));
10607           pSubSize += fdof - fcdof;
10608         }
10609         subSize += pSubSize;
10610         if (pSubSize) {
10611           if (bs < 0) {
10612             bs = pSubSize;
10613           } else if (bs != pSubSize) {
10614             /* Layout does not admit a pointwise block size */
10615             bs = 1;
10616           }
10617         }
10618       }
10619     }
10620     /* Must have same blocksize on all procs (some might have no points) */
10621     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs;
10622     bsLocal[1] = bs;
10623     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
10624     if (bsMinMax[0] != bsMinMax[1]) {
10625       bs = 1;
10626     } else {
10627       bs = bsMinMax[0];
10628     }
10629     PetscCall(PetscMalloc1(subSize, &subIndices));
10630     for (p = pStart; p < pEnd; ++p) {
10631       PetscInt gdof, goff;
10632 
10633       PetscCall(PetscSectionGetDof(subsectionGlobal, p, &gdof));
10634       if (gdof > 0) {
10635         const PetscInt point = spmap[p];
10636 
10637         PetscCall(PetscSectionGetOffset(sectionGlobal, point, &goff));
10638         for (f = 0; f < Nf; ++f) {
10639           PetscInt fdof, fcdof, fc, f2, poff = 0;
10640 
10641           /* Can get rid of this loop by storing field information in the global section */
10642           for (f2 = 0; f2 < f; ++f2) {
10643             PetscCall(PetscSectionGetFieldDof(section, p, f2, &fdof));
10644             PetscCall(PetscSectionGetFieldConstraintDof(section, p, f2, &fcdof));
10645             poff += fdof - fcdof;
10646           }
10647           PetscCall(PetscSectionGetFieldDof(section, p, f, &fdof));
10648           PetscCall(PetscSectionGetFieldConstraintDof(section, p, f, &fcdof));
10649           for (fc = 0; fc < fdof - fcdof; ++fc, ++subOff) subIndices[subOff] = goff + poff + fc;
10650         }
10651       }
10652     }
10653     PetscCall(ISRestoreIndices(spIS, &spmap));
10654     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), subSize, subIndices, PETSC_OWN_POINTER, is));
10655     if (bs > 1) {
10656       /* We need to check that the block size does not come from non-contiguous fields */
10657       PetscInt i, j, set = 1;
10658       for (i = 0; i < subSize; i += bs) {
10659         for (j = 0; j < bs; ++j) {
10660           if (subIndices[i + j] != subIndices[i] + j) {
10661             set = 0;
10662             break;
10663           }
10664         }
10665       }
10666       if (set) PetscCall(ISSetBlockSize(*is, bs));
10667     }
10668     /* Attach nullspace */
10669     for (f = 0; f < Nf; ++f) {
10670       (*subdm)->nullspaceConstructors[f] = dm->nullspaceConstructors[f];
10671       if ((*subdm)->nullspaceConstructors[f]) break;
10672     }
10673     if (f < Nf) {
10674       MatNullSpace nullSpace;
10675       PetscCall((*(*subdm)->nullspaceConstructors[f])(*subdm, f, f, &nullSpace));
10676 
10677       PetscCall(PetscObjectCompose((PetscObject)*is, "nullspace", (PetscObject)nullSpace));
10678       PetscCall(MatNullSpaceDestroy(&nullSpace));
10679     }
10680   }
10681   PetscFunctionReturn(PETSC_SUCCESS);
10682 }
10683 
10684 /*@
10685   DMPlexMonitorThroughput - Report the cell throughput of FE integration
10686 
10687   Input Parameters:
10688 + dm    - The `DM`
10689 - dummy - unused argument
10690 
10691   Options Database Key:
10692 . -dm_plex_monitor_throughput - Activate the monitor
10693 
10694   Level: developer
10695 
10696 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexCreate()`
10697 @*/
10698 PetscErrorCode DMPlexMonitorThroughput(DM dm, void *dummy)
10699 {
10700   PetscLogHandler default_handler;
10701 
10702   PetscFunctionBegin;
10703   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10704   PetscCall(PetscLogGetDefaultHandler(&default_handler));
10705   if (default_handler) {
10706     PetscLogEvent      event;
10707     PetscEventPerfInfo eventInfo;
10708     PetscReal          cellRate, flopRate;
10709     PetscInt           cStart, cEnd, Nf, N;
10710     const char        *name;
10711 
10712     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
10713     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
10714     PetscCall(DMGetNumFields(dm, &Nf));
10715     PetscCall(PetscLogEventGetId("DMPlexResidualFE", &event));
10716     PetscCall(PetscLogEventGetPerfInfo(PETSC_DEFAULT, event, &eventInfo));
10717     N        = (cEnd - cStart) * Nf * eventInfo.count;
10718     flopRate = eventInfo.flops / eventInfo.time;
10719     cellRate = N / eventInfo.time;
10720     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)));
10721   } else {
10722     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.");
10723   }
10724   PetscFunctionReturn(PETSC_SUCCESS);
10725 }
10726