xref: /petsc/src/dm/impls/plex/plex.c (revision 81f9dd79c015d5e171e921c88d7abd12dcb61442) !
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         PetscScalar       *coords = NULL, *a = NULL;
430         const PetscScalar *coords_arr;
431         PetscBool          isDG;
432         PetscInt           numCoords, color[4] = {-1, -1, -1, -1};
433 
434         PetscCall(DMPlexPointLocalRead(fdm, c, array, &a));
435         if (a) {
436           color[0] = PetscDrawRealToColor(PetscRealPart(a[comp]), vbound[0], vbound[1]);
437           color[1] = color[2] = color[3] = color[0];
438         } else {
439           PetscScalar *vals = NULL;
440           PetscInt     numVals, va;
441 
442           PetscCall(DMPlexVecGetClosure(fdm, NULL, fv, c, &numVals, &vals));
443           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);
444           switch (numVals / Nc) {
445           case 3: /* P1 Triangle */
446           case 4: /* P1 Quadrangle */
447             for (va = 0; va < numVals / Nc; ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp]), vbound[0], vbound[1]);
448             break;
449           case 6: /* P2 Triangle */
450           case 8: /* P2 Quadrangle */
451             for (va = 0; va < numVals / (Nc * 2); ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp + numVals / (Nc * 2)]), vbound[0], vbound[1]);
452             break;
453           default:
454             SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of values for cell closure %" PetscInt_FMT " cannot be handled", numVals / Nc);
455           }
456           PetscCall(DMPlexVecRestoreClosure(fdm, NULL, fv, c, &numVals, &vals));
457         }
458         PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
459         switch (numCoords) {
460         case 6:
461         case 12: /* Localized triangle */
462           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]));
463           break;
464         case 8:
465         case 16: /* Localized quadrilateral */
466           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]));
467           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]));
468           break;
469         default:
470           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells with %" PetscInt_FMT " coordinates", numCoords);
471         }
472         PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
473       }
474       PetscCall(VecRestoreArrayRead(fv, &array));
475       PetscCall(PetscDrawFlush(draw));
476       PetscCall(PetscDrawPause(draw));
477       PetscCall(PetscDrawSave(draw));
478     }
479     if (Nf > 1) {
480       PetscCall(VecRestoreSubVector(v, fis, &fv));
481       PetscCall(ISDestroy(&fis));
482       PetscCall(DMDestroy(&fdm));
483     }
484   }
485   PetscFunctionReturn(PETSC_SUCCESS);
486 }
487 
488 static PetscErrorCode VecView_Plex_Local_Draw(Vec v, PetscViewer viewer)
489 {
490   DM        dm;
491   PetscDraw draw;
492   PetscInt  dim;
493   PetscBool isnull;
494 
495   PetscFunctionBegin;
496   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
497   PetscCall(PetscDrawIsNull(draw, &isnull));
498   if (isnull) PetscFunctionReturn(PETSC_SUCCESS);
499 
500   PetscCall(VecGetDM(v, &dm));
501   PetscCall(DMGetCoordinateDim(dm, &dim));
502   switch (dim) {
503   case 1:
504     PetscCall(VecView_Plex_Local_Draw_1D(v, viewer));
505     break;
506   case 2:
507     PetscCall(VecView_Plex_Local_Draw_2D(v, viewer));
508     break;
509   default:
510     SETERRQ(PetscObjectComm((PetscObject)v), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT ". Try PETSCVIEWERGLVIS", dim);
511   }
512   PetscFunctionReturn(PETSC_SUCCESS);
513 }
514 
515 static PetscErrorCode VecView_Plex_Local_VTK(Vec v, PetscViewer viewer)
516 {
517   DM                      dm;
518   Vec                     locv;
519   const char             *name;
520   PetscSection            section;
521   PetscInt                pStart, pEnd;
522   PetscInt                numFields;
523   PetscViewerVTKFieldType ft;
524 
525   PetscFunctionBegin;
526   PetscCall(VecGetDM(v, &dm));
527   PetscCall(DMCreateLocalVector(dm, &locv)); /* VTK viewer requires exclusive ownership of the vector */
528   PetscCall(PetscObjectGetName((PetscObject)v, &name));
529   PetscCall(PetscObjectSetName((PetscObject)locv, name));
530   PetscCall(VecCopy(v, locv));
531   PetscCall(DMGetLocalSection(dm, &section));
532   PetscCall(PetscSectionGetNumFields(section, &numFields));
533   if (!numFields) {
534     PetscCall(DMPlexGetFieldType_Internal(dm, section, PETSC_DETERMINE, &pStart, &pEnd, &ft));
535     PetscCall(PetscViewerVTKAddField(viewer, (PetscObject)dm, DMPlexVTKWriteAll, PETSC_DEFAULT, ft, PETSC_TRUE, (PetscObject)locv));
536   } else {
537     PetscInt f;
538 
539     for (f = 0; f < numFields; f++) {
540       PetscCall(DMPlexGetFieldType_Internal(dm, section, f, &pStart, &pEnd, &ft));
541       if (ft == PETSC_VTK_INVALID) continue;
542       PetscCall(PetscObjectReference((PetscObject)locv));
543       PetscCall(PetscViewerVTKAddField(viewer, (PetscObject)dm, DMPlexVTKWriteAll, f, ft, PETSC_TRUE, (PetscObject)locv));
544     }
545     PetscCall(VecDestroy(&locv));
546   }
547   PetscFunctionReturn(PETSC_SUCCESS);
548 }
549 
550 PetscErrorCode VecView_Plex_Local(Vec v, PetscViewer viewer)
551 {
552   DM        dm;
553   PetscBool isvtk, ishdf5, isdraw, isglvis, iscgns;
554 
555   PetscFunctionBegin;
556   PetscCall(VecGetDM(v, &dm));
557   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
558   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
559   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
560   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
561   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
562   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
563   if (isvtk || ishdf5 || isdraw || isglvis || iscgns) {
564     PetscInt    i, numFields;
565     PetscObject fe;
566     PetscBool   fem  = PETSC_FALSE;
567     Vec         locv = v;
568     const char *name;
569     PetscInt    step;
570     PetscReal   time;
571 
572     PetscCall(DMGetNumFields(dm, &numFields));
573     for (i = 0; i < numFields; i++) {
574       PetscCall(DMGetField(dm, i, NULL, &fe));
575       if (fe->classid == PETSCFE_CLASSID) {
576         fem = PETSC_TRUE;
577         break;
578       }
579     }
580     if (fem) {
581       PetscObject isZero;
582 
583       PetscCall(DMGetLocalVector(dm, &locv));
584       PetscCall(PetscObjectGetName((PetscObject)v, &name));
585       PetscCall(PetscObjectSetName((PetscObject)locv, name));
586       PetscCall(PetscObjectQuery((PetscObject)v, "__Vec_bc_zero__", &isZero));
587       PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", isZero));
588       PetscCall(VecCopy(v, locv));
589       PetscCall(DMGetOutputSequenceNumber(dm, NULL, &time));
590       PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_TRUE, locv, time, NULL, NULL, NULL));
591     }
592     if (isvtk) {
593       PetscCall(VecView_Plex_Local_VTK(locv, viewer));
594     } else if (ishdf5) {
595 #if defined(PETSC_HAVE_HDF5)
596       PetscCall(VecView_Plex_Local_HDF5_Internal(locv, viewer));
597 #else
598       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
599 #endif
600     } else if (isdraw) {
601       PetscCall(VecView_Plex_Local_Draw(locv, viewer));
602     } else if (isglvis) {
603       PetscCall(DMGetOutputSequenceNumber(dm, &step, NULL));
604       PetscCall(PetscViewerGLVisSetSnapId(viewer, step));
605       PetscCall(VecView_GLVis(locv, viewer));
606     } else if (iscgns) {
607 #if defined(PETSC_HAVE_CGNS)
608       PetscCall(VecView_Plex_Local_CGNS(locv, viewer));
609 #else
610       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "CGNS not supported in this build.\nPlease reconfigure using --download-cgns");
611 #endif
612     }
613     if (fem) {
614       PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", NULL));
615       PetscCall(DMRestoreLocalVector(dm, &locv));
616     }
617   } else {
618     PetscBool isseq;
619 
620     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
621     if (isseq) PetscCall(VecView_Seq(v, viewer));
622     else PetscCall(VecView_MPI(v, viewer));
623   }
624   PetscFunctionReturn(PETSC_SUCCESS);
625 }
626 
627 PetscErrorCode VecView_Plex(Vec v, PetscViewer viewer)
628 {
629   DM        dm;
630   PetscBool isvtk, ishdf5, isdraw, isglvis, isexodusii, iscgns;
631 
632   PetscFunctionBegin;
633   PetscCall(VecGetDM(v, &dm));
634   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
635   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
636   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
637   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
638   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
639   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
640   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodusii));
641   if (isvtk || isdraw || isglvis || iscgns) {
642     Vec         locv;
643     PetscObject isZero;
644     const char *name;
645 
646     PetscCall(DMGetLocalVector(dm, &locv));
647     PetscCall(PetscObjectGetName((PetscObject)v, &name));
648     PetscCall(PetscObjectSetName((PetscObject)locv, name));
649     PetscCall(DMGlobalToLocalBegin(dm, v, INSERT_VALUES, locv));
650     PetscCall(DMGlobalToLocalEnd(dm, v, INSERT_VALUES, locv));
651     PetscCall(PetscObjectQuery((PetscObject)v, "__Vec_bc_zero__", &isZero));
652     PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", isZero));
653     PetscCall(VecView_Plex_Local(locv, viewer));
654     PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", NULL));
655     PetscCall(DMRestoreLocalVector(dm, &locv));
656     /* Call flush for proper logging of VecView timings */
657     if (isvtk) PetscCall(PetscViewerFlush(viewer));
658   } else if (ishdf5) {
659 #if defined(PETSC_HAVE_HDF5)
660     PetscCall(VecView_Plex_HDF5_Internal(v, viewer));
661 #else
662     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
663 #endif
664   } else if (isexodusii) {
665 #if defined(PETSC_HAVE_EXODUSII)
666     PetscCall(VecView_PlexExodusII_Internal(v, viewer));
667 #else
668     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
669 #endif
670   } else {
671     PetscBool isseq;
672 
673     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
674     if (isseq) PetscCall(VecView_Seq(v, viewer));
675     else PetscCall(VecView_MPI(v, viewer));
676   }
677   PetscFunctionReturn(PETSC_SUCCESS);
678 }
679 
680 PetscErrorCode VecView_Plex_Native(Vec originalv, PetscViewer viewer)
681 {
682   DM                dm;
683   MPI_Comm          comm;
684   PetscViewerFormat format;
685   Vec               v;
686   PetscBool         isvtk, ishdf5;
687 
688   PetscFunctionBegin;
689   PetscCall(VecGetDM(originalv, &dm));
690   PetscCall(PetscObjectGetComm((PetscObject)originalv, &comm));
691   PetscCheck(dm, comm, PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
692   PetscCall(PetscViewerGetFormat(viewer, &format));
693   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
694   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
695   if (format == PETSC_VIEWER_NATIVE) {
696     /* Natural ordering is the common case for DMDA, NATIVE means plain vector, for PLEX is the opposite */
697     /* this need a better fix */
698     if (dm->useNatural) {
699       if (dm->sfNatural) {
700         const char *vecname;
701         PetscInt    n, nroots;
702 
703         PetscCall(VecGetLocalSize(originalv, &n));
704         PetscCall(PetscSFGetGraph(dm->sfNatural, &nroots, NULL, NULL, NULL));
705         if (n == nroots) {
706           PetscCall(DMPlexCreateNaturalVector(dm, &v));
707           PetscCall(DMPlexGlobalToNaturalBegin(dm, originalv, v));
708           PetscCall(DMPlexGlobalToNaturalEnd(dm, originalv, v));
709           PetscCall(PetscObjectGetName((PetscObject)originalv, &vecname));
710           PetscCall(PetscObjectSetName((PetscObject)v, vecname));
711         } else SETERRQ(comm, PETSC_ERR_ARG_WRONG, "DM global to natural SF only handles global vectors");
712       } else SETERRQ(comm, PETSC_ERR_ARG_WRONGSTATE, "DM global to natural SF was not created");
713     } else v = originalv;
714   } else v = originalv;
715 
716   if (ishdf5) {
717 #if defined(PETSC_HAVE_HDF5)
718     PetscCall(VecView_Plex_HDF5_Native_Internal(v, viewer));
719 #else
720     SETERRQ(comm, PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
721 #endif
722   } else if (isvtk) {
723     SETERRQ(comm, PETSC_ERR_SUP, "VTK format does not support viewing in natural order. Please switch to HDF5.");
724   } else {
725     PetscBool isseq;
726 
727     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
728     if (isseq) PetscCall(VecView_Seq(v, viewer));
729     else PetscCall(VecView_MPI(v, viewer));
730   }
731   if (v != originalv) PetscCall(VecDestroy(&v));
732   PetscFunctionReturn(PETSC_SUCCESS);
733 }
734 
735 PetscErrorCode VecLoad_Plex_Local(Vec v, PetscViewer viewer)
736 {
737   DM        dm;
738   PetscBool ishdf5;
739 
740   PetscFunctionBegin;
741   PetscCall(VecGetDM(v, &dm));
742   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
743   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
744   if (ishdf5) {
745     DM          dmBC;
746     Vec         gv;
747     const char *name;
748 
749     PetscCall(DMGetOutputDM(dm, &dmBC));
750     PetscCall(DMGetGlobalVector(dmBC, &gv));
751     PetscCall(PetscObjectGetName((PetscObject)v, &name));
752     PetscCall(PetscObjectSetName((PetscObject)gv, name));
753     PetscCall(VecLoad_Default(gv, viewer));
754     PetscCall(DMGlobalToLocalBegin(dmBC, gv, INSERT_VALUES, v));
755     PetscCall(DMGlobalToLocalEnd(dmBC, gv, INSERT_VALUES, v));
756     PetscCall(DMRestoreGlobalVector(dmBC, &gv));
757   } else PetscCall(VecLoad_Default(v, viewer));
758   PetscFunctionReturn(PETSC_SUCCESS);
759 }
760 
761 PetscErrorCode VecLoad_Plex(Vec v, PetscViewer viewer)
762 {
763   DM        dm;
764   PetscBool ishdf5, isexodusii;
765 
766   PetscFunctionBegin;
767   PetscCall(VecGetDM(v, &dm));
768   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
769   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
770   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodusii));
771   if (ishdf5) {
772 #if defined(PETSC_HAVE_HDF5)
773     PetscCall(VecLoad_Plex_HDF5_Internal(v, viewer));
774 #else
775     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
776 #endif
777   } else if (isexodusii) {
778 #if defined(PETSC_HAVE_EXODUSII)
779     PetscCall(VecLoad_PlexExodusII_Internal(v, viewer));
780 #else
781     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
782 #endif
783   } else PetscCall(VecLoad_Default(v, viewer));
784   PetscFunctionReturn(PETSC_SUCCESS);
785 }
786 
787 PetscErrorCode VecLoad_Plex_Native(Vec originalv, PetscViewer viewer)
788 {
789   DM                dm;
790   PetscViewerFormat format;
791   PetscBool         ishdf5;
792 
793   PetscFunctionBegin;
794   PetscCall(VecGetDM(originalv, &dm));
795   PetscCheck(dm, PetscObjectComm((PetscObject)originalv), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
796   PetscCall(PetscViewerGetFormat(viewer, &format));
797   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
798   if (format == PETSC_VIEWER_NATIVE) {
799     if (dm->useNatural) {
800       if (dm->sfNatural) {
801         if (ishdf5) {
802 #if defined(PETSC_HAVE_HDF5)
803           Vec         v;
804           const char *vecname;
805 
806           PetscCall(DMPlexCreateNaturalVector(dm, &v));
807           PetscCall(PetscObjectGetName((PetscObject)originalv, &vecname));
808           PetscCall(PetscObjectSetName((PetscObject)v, vecname));
809           PetscCall(VecLoad_Plex_HDF5_Native_Internal(v, viewer));
810           PetscCall(DMPlexNaturalToGlobalBegin(dm, v, originalv));
811           PetscCall(DMPlexNaturalToGlobalEnd(dm, v, originalv));
812           PetscCall(VecDestroy(&v));
813 #else
814           SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
815 #endif
816         } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Reading in natural order is not supported for anything but HDF5.");
817       }
818     } else PetscCall(VecLoad_Default(originalv, viewer));
819   }
820   PetscFunctionReturn(PETSC_SUCCESS);
821 }
822 
823 PETSC_UNUSED static PetscErrorCode DMPlexView_Ascii_Geometry(DM dm, PetscViewer viewer)
824 {
825   PetscSection       coordSection;
826   Vec                coordinates;
827   DMLabel            depthLabel, celltypeLabel;
828   const char        *name[4];
829   const PetscScalar *a;
830   PetscInt           dim, pStart, pEnd, cStart, cEnd, c;
831 
832   PetscFunctionBegin;
833   PetscCall(DMGetDimension(dm, &dim));
834   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
835   PetscCall(DMGetCoordinateSection(dm, &coordSection));
836   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
837   PetscCall(DMPlexGetCellTypeLabel(dm, &celltypeLabel));
838   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
839   PetscCall(PetscSectionGetChart(coordSection, &pStart, &pEnd));
840   PetscCall(VecGetArrayRead(coordinates, &a));
841   name[0]       = "vertex";
842   name[1]       = "edge";
843   name[dim - 1] = "face";
844   name[dim]     = "cell";
845   for (c = cStart; c < cEnd; ++c) {
846     PetscInt *closure = NULL;
847     PetscInt  closureSize, cl, ct;
848 
849     PetscCall(DMLabelGetValue(celltypeLabel, c, &ct));
850     PetscCall(PetscViewerASCIIPrintf(viewer, "Geometry for cell %" PetscInt_FMT " polytope type %s:\n", c, DMPolytopeTypes[ct]));
851     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
852     PetscCall(PetscViewerASCIIPushTab(viewer));
853     for (cl = 0; cl < closureSize * 2; cl += 2) {
854       PetscInt point = closure[cl], depth, dof, off, d, p;
855 
856       if ((point < pStart) || (point >= pEnd)) continue;
857       PetscCall(PetscSectionGetDof(coordSection, point, &dof));
858       if (!dof) continue;
859       PetscCall(DMLabelGetValue(depthLabel, point, &depth));
860       PetscCall(PetscSectionGetOffset(coordSection, point, &off));
861       PetscCall(PetscViewerASCIIPrintf(viewer, "%s %" PetscInt_FMT " coords:", name[depth], point));
862       for (p = 0; p < dof / dim; ++p) {
863         PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
864         for (d = 0; d < dim; ++d) {
865           if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
866           PetscCall(PetscViewerASCIIPrintf(viewer, "%g", (double)PetscRealPart(a[off + p * dim + d])));
867         }
868         PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
869       }
870       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
871     }
872     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
873     PetscCall(PetscViewerASCIIPopTab(viewer));
874   }
875   PetscCall(VecRestoreArrayRead(coordinates, &a));
876   PetscFunctionReturn(PETSC_SUCCESS);
877 }
878 
879 typedef enum {
880   CS_CARTESIAN,
881   CS_POLAR,
882   CS_CYLINDRICAL,
883   CS_SPHERICAL
884 } CoordSystem;
885 const char *CoordSystems[] = {"cartesian", "polar", "cylindrical", "spherical", "CoordSystem", "CS_", NULL};
886 
887 static PetscErrorCode DMPlexView_Ascii_Coordinates(PetscViewer viewer, CoordSystem cs, PetscInt dim, const PetscScalar x[])
888 {
889   PetscInt i;
890 
891   PetscFunctionBegin;
892   if (dim > 3) {
893     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double)PetscRealPart(x[i])));
894   } else {
895     PetscReal coords[3], trcoords[3] = {0., 0., 0.};
896 
897     for (i = 0; i < dim; ++i) coords[i] = PetscRealPart(x[i]);
898     switch (cs) {
899     case CS_CARTESIAN:
900       for (i = 0; i < dim; ++i) trcoords[i] = coords[i];
901       break;
902     case CS_POLAR:
903       PetscCheck(dim == 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Polar coordinates are for 2 dimension, not %" PetscInt_FMT, dim);
904       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
905       trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
906       break;
907     case CS_CYLINDRICAL:
908       PetscCheck(dim == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cylindrical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
909       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
910       trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
911       trcoords[2] = coords[2];
912       break;
913     case CS_SPHERICAL:
914       PetscCheck(dim == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Spherical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
915       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]) + PetscSqr(coords[2]));
916       trcoords[1] = PetscAtan2Real(PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1])), coords[2]);
917       trcoords[2] = PetscAtan2Real(coords[1], coords[0]);
918       break;
919     }
920     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double)trcoords[i]));
921   }
922   PetscFunctionReturn(PETSC_SUCCESS);
923 }
924 
925 static PetscErrorCode DMPlexView_Ascii(DM dm, PetscViewer viewer)
926 {
927   DM_Plex          *mesh = (DM_Plex *)dm->data;
928   DM                cdm, cdmCell;
929   PetscSection      coordSection, coordSectionCell;
930   Vec               coordinates, coordinatesCell;
931   PetscViewerFormat format;
932 
933   PetscFunctionBegin;
934   PetscCall(PetscViewerGetFormat(viewer, &format));
935   if (format == PETSC_VIEWER_ASCII_INFO_DETAIL) {
936     const char *name;
937     PetscInt    dim, cellHeight, maxConeSize, maxSupportSize;
938     PetscInt    pStart, pEnd, p, numLabels, l;
939     PetscMPIInt rank, size;
940 
941     PetscCall(DMGetCoordinateDM(dm, &cdm));
942     PetscCall(DMGetCoordinateSection(dm, &coordSection));
943     PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
944     PetscCall(DMGetCellCoordinateDM(dm, &cdmCell));
945     PetscCall(DMGetCellCoordinateSection(dm, &coordSectionCell));
946     PetscCall(DMGetCellCoordinatesLocal(dm, &coordinatesCell));
947     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
948     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
949     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
950     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
951     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
952     PetscCall(DMGetDimension(dm, &dim));
953     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
954     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
955     else PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
956     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
957     PetscCall(PetscViewerASCIIPrintf(viewer, "Supports:\n"));
958     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
959     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max support size: %" PetscInt_FMT "\n", rank, maxSupportSize));
960     for (p = pStart; p < pEnd; ++p) {
961       PetscInt dof, off, s;
962 
963       PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
964       PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
965       for (s = off; s < off + dof; ++s) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d]: %" PetscInt_FMT " ----> %" PetscInt_FMT "\n", rank, p, mesh->supports[s]));
966     }
967     PetscCall(PetscViewerFlush(viewer));
968     PetscCall(PetscViewerASCIIPrintf(viewer, "Cones:\n"));
969     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max cone size: %" PetscInt_FMT "\n", rank, maxConeSize));
970     for (p = pStart; p < pEnd; ++p) {
971       PetscInt dof, off, c;
972 
973       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
974       PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
975       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]));
976     }
977     PetscCall(PetscViewerFlush(viewer));
978     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
979     if (coordSection && coordinates) {
980       CoordSystem        cs = CS_CARTESIAN;
981       const PetscScalar *array, *arrayCell = NULL;
982       PetscInt           Nf, Nc, pvStart, pvEnd, pcStart = PETSC_MAX_INT, pcEnd = PETSC_MIN_INT, pStart, pEnd, p;
983       PetscMPIInt        rank;
984       const char        *name;
985 
986       PetscCall(PetscOptionsGetEnum(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_coord_system", CoordSystems, (PetscEnum *)&cs, NULL));
987       PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)viewer), &rank));
988       PetscCall(PetscSectionGetNumFields(coordSection, &Nf));
989       PetscCheck(Nf == 1, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Coordinate section should have 1 field, not %" PetscInt_FMT, Nf);
990       PetscCall(PetscSectionGetFieldComponents(coordSection, 0, &Nc));
991       PetscCall(PetscSectionGetChart(coordSection, &pvStart, &pvEnd));
992       if (coordSectionCell) PetscCall(PetscSectionGetChart(coordSectionCell, &pcStart, &pcEnd));
993       pStart = PetscMin(pvStart, pcStart);
994       pEnd   = PetscMax(pvEnd, pcEnd);
995       PetscCall(PetscObjectGetName((PetscObject)coordinates, &name));
996       PetscCall(PetscViewerASCIIPrintf(viewer, "%s with %" PetscInt_FMT " fields\n", name, Nf));
997       PetscCall(PetscViewerASCIIPrintf(viewer, "  field 0 with %" PetscInt_FMT " components\n", Nc));
998       if (cs != CS_CARTESIAN) PetscCall(PetscViewerASCIIPrintf(viewer, "  output coordinate system: %s\n", CoordSystems[cs]));
999 
1000       PetscCall(VecGetArrayRead(coordinates, &array));
1001       if (coordinatesCell) PetscCall(VecGetArrayRead(coordinatesCell, &arrayCell));
1002       PetscCall(PetscViewerASCIIPushSynchronized(viewer));
1003       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "Process %d:\n", rank));
1004       for (p = pStart; p < pEnd; ++p) {
1005         PetscInt dof, off;
1006 
1007         if (p >= pvStart && p < pvEnd) {
1008           PetscCall(PetscSectionGetDof(coordSection, p, &dof));
1009           PetscCall(PetscSectionGetOffset(coordSection, p, &off));
1010           if (dof) {
1011             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dim %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
1012             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &array[off]));
1013             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
1014           }
1015         }
1016         if (cdmCell && p >= pcStart && p < pcEnd) {
1017           PetscCall(PetscSectionGetDof(coordSectionCell, p, &dof));
1018           PetscCall(PetscSectionGetOffset(coordSectionCell, p, &off));
1019           if (dof) {
1020             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dim %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
1021             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &arrayCell[off]));
1022             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
1023           }
1024         }
1025       }
1026       PetscCall(PetscViewerFlush(viewer));
1027       PetscCall(PetscViewerASCIIPopSynchronized(viewer));
1028       PetscCall(VecRestoreArrayRead(coordinates, &array));
1029       if (coordinatesCell) PetscCall(VecRestoreArrayRead(coordinatesCell, &arrayCell));
1030     }
1031     PetscCall(DMGetNumLabels(dm, &numLabels));
1032     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
1033     for (l = 0; l < numLabels; ++l) {
1034       DMLabel     label;
1035       PetscBool   isdepth;
1036       const char *name;
1037 
1038       PetscCall(DMGetLabelName(dm, l, &name));
1039       PetscCall(PetscStrcmp(name, "depth", &isdepth));
1040       if (isdepth) continue;
1041       PetscCall(DMGetLabel(dm, name, &label));
1042       PetscCall(DMLabelView(label, viewer));
1043     }
1044     if (size > 1) {
1045       PetscSF sf;
1046 
1047       PetscCall(DMGetPointSF(dm, &sf));
1048       PetscCall(PetscSFView(sf, viewer));
1049     }
1050     if (mesh->periodic.face_sfs)
1051       for (PetscInt i = 0; i < mesh->periodic.num_face_sfs; i++) PetscCall(PetscSFView(mesh->periodic.face_sfs[i], viewer));
1052     PetscCall(PetscViewerFlush(viewer));
1053   } else if (format == PETSC_VIEWER_ASCII_LATEX) {
1054     const char  *name, *color;
1055     const char  *defcolors[3]  = {"gray", "orange", "green"};
1056     const char  *deflcolors[4] = {"blue", "cyan", "red", "magenta"};
1057     char         lname[PETSC_MAX_PATH_LEN];
1058     PetscReal    scale      = 2.0;
1059     PetscReal    tikzscale  = 1.0;
1060     PetscBool    useNumbers = PETSC_TRUE, drawNumbers[4], drawColors[4], useLabels, useColors, plotEdges, drawHasse = PETSC_FALSE;
1061     double       tcoords[3];
1062     PetscScalar *coords;
1063     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;
1064     PetscMPIInt  rank, size;
1065     char       **names, **colors, **lcolors;
1066     PetscBool    flg, lflg;
1067     PetscBT      wp = NULL;
1068     PetscInt     pEnd, pStart;
1069 
1070     PetscCall(DMGetCoordinateDM(dm, &cdm));
1071     PetscCall(DMGetCoordinateSection(dm, &coordSection));
1072     PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1073     PetscCall(DMGetCellCoordinateDM(dm, &cdmCell));
1074     PetscCall(DMGetCellCoordinateSection(dm, &coordSectionCell));
1075     PetscCall(DMGetCellCoordinatesLocal(dm, &coordinatesCell));
1076     PetscCall(DMGetDimension(dm, &dim));
1077     PetscCall(DMPlexGetDepth(dm, &depth));
1078     PetscCall(DMGetNumLabels(dm, &numLabels));
1079     numLabels  = PetscMax(numLabels, 10);
1080     numColors  = 10;
1081     numLColors = 10;
1082     PetscCall(PetscCalloc3(numLabels, &names, numColors, &colors, numLColors, &lcolors));
1083     PetscCall(PetscOptionsGetReal(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_scale", &scale, NULL));
1084     PetscCall(PetscOptionsGetReal(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_tikzscale", &tikzscale, NULL));
1085     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_numbers", &useNumbers, NULL));
1086     for (d = 0; d < 4; ++d) drawNumbers[d] = useNumbers;
1087     for (d = 0; d < 4; ++d) drawColors[d] = PETSC_TRUE;
1088     n = 4;
1089     PetscCall(PetscOptionsGetBoolArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_numbers_depth", drawNumbers, &n, &flg));
1090     PetscCheck(!flg || n == dim + 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim + 1);
1091     n = 4;
1092     PetscCall(PetscOptionsGetBoolArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_colors_depth", drawColors, &n, &flg));
1093     PetscCheck(!flg || n == dim + 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim + 1);
1094     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_labels", names, &numLabels, &useLabels));
1095     if (!useLabels) numLabels = 0;
1096     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_colors", colors, &numColors, &useColors));
1097     if (!useColors) {
1098       numColors = 3;
1099       for (c = 0; c < numColors; ++c) PetscCall(PetscStrallocpy(defcolors[c], &colors[c]));
1100     }
1101     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_lcolors", lcolors, &numLColors, &useColors));
1102     if (!useColors) {
1103       numLColors = 4;
1104       for (c = 0; c < numLColors; ++c) PetscCall(PetscStrallocpy(deflcolors[c], &lcolors[c]));
1105     }
1106     PetscCall(PetscOptionsGetString(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_label_filter", lname, sizeof(lname), &lflg));
1107     plotEdges = (PetscBool)(depth > 1 && drawNumbers[1] && dim < 3);
1108     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_edges", &plotEdges, &flg));
1109     PetscCheck(!flg || !plotEdges || depth >= dim, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Mesh must be interpolated");
1110     if (depth < dim) plotEdges = PETSC_FALSE;
1111     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_hasse", &drawHasse, NULL));
1112 
1113     /* filter points with labelvalue != labeldefaultvalue */
1114     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
1115     PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1116     PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
1117     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1118     PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd));
1119     if (lflg) {
1120       DMLabel lbl;
1121 
1122       PetscCall(DMGetLabel(dm, lname, &lbl));
1123       if (lbl) {
1124         PetscInt val, defval;
1125 
1126         PetscCall(DMLabelGetDefaultValue(lbl, &defval));
1127         PetscCall(PetscBTCreate(pEnd - pStart, &wp));
1128         for (c = pStart; c < pEnd; c++) {
1129           PetscInt *closure = NULL;
1130           PetscInt  closureSize;
1131 
1132           PetscCall(DMLabelGetValue(lbl, c, &val));
1133           if (val == defval) continue;
1134 
1135           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1136           for (p = 0; p < closureSize * 2; p += 2) PetscCall(PetscBTSet(wp, closure[p] - pStart));
1137           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1138         }
1139       }
1140     }
1141 
1142     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1143     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
1144     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1145     PetscCall(PetscViewerASCIIPrintf(viewer, "\
1146 \\documentclass[tikz]{standalone}\n\n\
1147 \\usepackage{pgflibraryshapes}\n\
1148 \\usetikzlibrary{backgrounds}\n\
1149 \\usetikzlibrary{arrows}\n\
1150 \\begin{document}\n"));
1151     if (size > 1) {
1152       PetscCall(PetscViewerASCIIPrintf(viewer, "%s for process ", name));
1153       for (p = 0; p < size; ++p) {
1154         if (p) PetscCall(PetscViewerASCIIPrintf(viewer, (p == size - 1) ? ", and " : ", "));
1155         PetscCall(PetscViewerASCIIPrintf(viewer, "{\\textcolor{%s}%" PetscInt_FMT "}", colors[p % numColors], p));
1156       }
1157       PetscCall(PetscViewerASCIIPrintf(viewer, ".\n\n\n"));
1158     }
1159     if (drawHasse) {
1160       PetscInt maxStratum = PetscMax(vEnd - vStart, PetscMax(eEnd - eStart, PetscMax(fEnd - fStart, cEnd - cStart)));
1161 
1162       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vStart}{%" PetscInt_FMT "}\n", vStart));
1163       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vEnd}{%" PetscInt_FMT "}\n", vEnd - 1));
1164       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numVertices}{%" PetscInt_FMT "}\n", vEnd - vStart));
1165       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vShift}{%.2f}\n", 3 + (maxStratum - (vEnd - vStart)) / 2.));
1166       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eStart}{%" PetscInt_FMT "}\n", eStart));
1167       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eEnd}{%" PetscInt_FMT "}\n", eEnd - 1));
1168       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eShift}{%.2f}\n", 3 + (maxStratum - (eEnd - eStart)) / 2.));
1169       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numEdges}{%" PetscInt_FMT "}\n", eEnd - eStart));
1170       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\fStart}{%" PetscInt_FMT "}\n", fStart));
1171       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\fEnd}{%" PetscInt_FMT "}\n", fEnd - 1));
1172       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\fShift}{%.2f}\n", 3 + (maxStratum - (fEnd - fStart)) / 2.));
1173       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numFaces}{%" PetscInt_FMT "}\n", fEnd - fStart));
1174       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cStart}{%" PetscInt_FMT "}\n", cStart));
1175       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cEnd}{%" PetscInt_FMT "}\n", cEnd - 1));
1176       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numCells}{%" PetscInt_FMT "}\n", cEnd - cStart));
1177       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cShift}{%.2f}\n", 3 + (maxStratum - (cEnd - cStart)) / 2.));
1178     }
1179     PetscCall(PetscViewerASCIIPrintf(viewer, "\\begin{tikzpicture}[scale = %g,font=\\fontsize{8}{8}\\selectfont]\n", (double)tikzscale));
1180 
1181     /* Plot vertices */
1182     PetscCall(VecGetArray(coordinates, &coords));
1183     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
1184     for (v = vStart; v < vEnd; ++v) {
1185       PetscInt  off, dof, d;
1186       PetscBool isLabeled = PETSC_FALSE;
1187 
1188       if (wp && !PetscBTLookup(wp, v - pStart)) continue;
1189       PetscCall(PetscSectionGetDof(coordSection, v, &dof));
1190       PetscCall(PetscSectionGetOffset(coordSection, v, &off));
1191       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1192       PetscCheck(dof <= 3, PETSC_COMM_SELF, PETSC_ERR_PLIB, "coordSection vertex %" PetscInt_FMT " has dof %" PetscInt_FMT " > 3", v, dof);
1193       for (d = 0; d < dof; ++d) {
1194         tcoords[d] = (double)(scale * PetscRealPart(coords[off + d]));
1195         tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1196       }
1197       /* Rotate coordinates since PGF makes z point out of the page instead of up */
1198       if (dim == 3) {
1199         PetscReal tmp = tcoords[1];
1200         tcoords[1]    = tcoords[2];
1201         tcoords[2]    = -tmp;
1202       }
1203       for (d = 0; d < dof; ++d) {
1204         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1205         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)tcoords[d]));
1206       }
1207       if (drawHasse) color = colors[0 % numColors];
1208       else color = colors[rank % numColors];
1209       for (l = 0; l < numLabels; ++l) {
1210         PetscInt val;
1211         PetscCall(DMGetLabelValue(dm, names[l], v, &val));
1212         if (val >= 0) {
1213           color     = lcolors[l % numLColors];
1214           isLabeled = PETSC_TRUE;
1215           break;
1216         }
1217       }
1218       if (drawNumbers[0]) {
1219         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", v, rank, color, v));
1220       } else if (drawColors[0]) {
1221         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", v, rank, !isLabeled ? 1 : 2, color));
1222       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", v, rank));
1223     }
1224     PetscCall(VecRestoreArray(coordinates, &coords));
1225     PetscCall(PetscViewerFlush(viewer));
1226     /* Plot edges */
1227     if (plotEdges) {
1228       PetscCall(VecGetArray(coordinates, &coords));
1229       PetscCall(PetscViewerASCIIPrintf(viewer, "\\path\n"));
1230       for (e = eStart; e < eEnd; ++e) {
1231         const PetscInt *cone;
1232         PetscInt        coneSize, offA, offB, dof, d;
1233 
1234         if (wp && !PetscBTLookup(wp, e - pStart)) continue;
1235         PetscCall(DMPlexGetConeSize(dm, e, &coneSize));
1236         PetscCheck(coneSize == 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Edge %" PetscInt_FMT " cone should have two vertices, not %" PetscInt_FMT, e, coneSize);
1237         PetscCall(DMPlexGetCone(dm, e, &cone));
1238         PetscCall(PetscSectionGetDof(coordSection, cone[0], &dof));
1239         PetscCall(PetscSectionGetOffset(coordSection, cone[0], &offA));
1240         PetscCall(PetscSectionGetOffset(coordSection, cone[1], &offB));
1241         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "("));
1242         for (d = 0; d < dof; ++d) {
1243           tcoords[d] = (double)(0.5 * scale * PetscRealPart(coords[offA + d] + coords[offB + d]));
1244           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1245         }
1246         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1247         if (dim == 3) {
1248           PetscReal tmp = tcoords[1];
1249           tcoords[1]    = tcoords[2];
1250           tcoords[2]    = -tmp;
1251         }
1252         for (d = 0; d < dof; ++d) {
1253           if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1254           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)tcoords[d]));
1255         }
1256         if (drawHasse) color = colors[1 % numColors];
1257         else color = colors[rank % numColors];
1258         for (l = 0; l < numLabels; ++l) {
1259           PetscInt val;
1260           PetscCall(DMGetLabelValue(dm, names[l], e, &val));
1261           if (val >= 0) {
1262             color = lcolors[l % numLColors];
1263             break;
1264           }
1265         }
1266         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "} --\n", e, rank, color, e));
1267       }
1268       PetscCall(VecRestoreArray(coordinates, &coords));
1269       PetscCall(PetscViewerFlush(viewer));
1270       PetscCall(PetscViewerASCIIPrintf(viewer, "(0,0);\n"));
1271     }
1272     /* Plot cells */
1273     if (dim == 3 || !drawNumbers[1]) {
1274       for (e = eStart; e < eEnd; ++e) {
1275         const PetscInt *cone;
1276 
1277         if (wp && !PetscBTLookup(wp, e - pStart)) continue;
1278         color = colors[rank % numColors];
1279         for (l = 0; l < numLabels; ++l) {
1280           PetscInt val;
1281           PetscCall(DMGetLabelValue(dm, names[l], e, &val));
1282           if (val >= 0) {
1283             color = lcolors[l % numLColors];
1284             break;
1285           }
1286         }
1287         PetscCall(DMPlexGetCone(dm, e, &cone));
1288         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", color, cone[0], rank, cone[1], rank));
1289       }
1290     } else {
1291       DMPolytopeType ct;
1292 
1293       /* Drawing a 2D polygon */
1294       for (c = cStart; c < cEnd; ++c) {
1295         if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1296         PetscCall(DMPlexGetCellType(dm, c, &ct));
1297         if (DMPolytopeTypeIsHybrid(ct)) {
1298           const PetscInt *cone;
1299           PetscInt        coneSize, e;
1300 
1301           PetscCall(DMPlexGetCone(dm, c, &cone));
1302           PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
1303           for (e = 0; e < coneSize; ++e) {
1304             const PetscInt *econe;
1305 
1306             PetscCall(DMPlexGetCone(dm, cone[e], &econe));
1307             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));
1308           }
1309         } else {
1310           PetscInt *closure = NULL;
1311           PetscInt  closureSize, Nv = 0, v;
1312 
1313           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1314           for (p = 0; p < closureSize * 2; p += 2) {
1315             const PetscInt point = closure[p];
1316 
1317             if ((point >= vStart) && (point < vEnd)) closure[Nv++] = point;
1318           }
1319           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] ", colors[rank % numColors]));
1320           for (v = 0; v <= Nv; ++v) {
1321             const PetscInt vertex = closure[v % Nv];
1322 
1323             if (v > 0) {
1324               if (plotEdges) {
1325                 const PetscInt *edge;
1326                 PetscInt        endpoints[2], ne;
1327 
1328                 endpoints[0] = closure[v - 1];
1329                 endpoints[1] = vertex;
1330                 PetscCall(DMPlexGetJoin(dm, 2, endpoints, &ne, &edge));
1331                 PetscCheck(ne == 1, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Could not find edge for vertices %" PetscInt_FMT ", %" PetscInt_FMT, endpoints[0], endpoints[1]);
1332                 PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- (%" PetscInt_FMT "_%d) -- ", edge[0], rank));
1333                 PetscCall(DMPlexRestoreJoin(dm, 2, endpoints, &ne, &edge));
1334               } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- "));
1335             }
1336             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "(%" PetscInt_FMT "_%d)", vertex, rank));
1337           }
1338           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ";\n"));
1339           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1340         }
1341       }
1342     }
1343     for (c = cStart; c < cEnd; ++c) {
1344       double             ccoords[3] = {0.0, 0.0, 0.0};
1345       PetscBool          isLabeled  = PETSC_FALSE;
1346       PetscScalar       *cellCoords = NULL;
1347       const PetscScalar *array;
1348       PetscInt           numCoords, cdim, d;
1349       PetscBool          isDG;
1350 
1351       if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1352       PetscCall(DMGetCoordinateDim(dm, &cdim));
1353       PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1354       PetscCheck(!(numCoords % cdim), PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "coordinate dim %" PetscInt_FMT " does not divide numCoords %" PetscInt_FMT, cdim, numCoords);
1355       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1356       for (p = 0; p < numCoords / cdim; ++p) {
1357         for (d = 0; d < cdim; ++d) {
1358           tcoords[d] = (double)(scale * PetscRealPart(cellCoords[p * cdim + d]));
1359           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1360         }
1361         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1362         if (cdim == 3) {
1363           PetscReal tmp = tcoords[1];
1364           tcoords[1]    = tcoords[2];
1365           tcoords[2]    = -tmp;
1366         }
1367         for (d = 0; d < dim; ++d) ccoords[d] += tcoords[d];
1368       }
1369       for (d = 0; d < cdim; ++d) ccoords[d] /= (numCoords / cdim);
1370       PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1371       for (d = 0; d < cdim; ++d) {
1372         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1373         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)ccoords[d]));
1374       }
1375       if (drawHasse) color = colors[depth % numColors];
1376       else color = colors[rank % numColors];
1377       for (l = 0; l < numLabels; ++l) {
1378         PetscInt val;
1379         PetscCall(DMGetLabelValue(dm, names[l], c, &val));
1380         if (val >= 0) {
1381           color     = lcolors[l % numLColors];
1382           isLabeled = PETSC_TRUE;
1383           break;
1384         }
1385       }
1386       if (drawNumbers[dim]) {
1387         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", c, rank, color, c));
1388       } else if (drawColors[dim]) {
1389         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", c, rank, !isLabeled ? 1 : 2, color));
1390       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", c, rank));
1391     }
1392     if (drawHasse) {
1393       int height = 0;
1394 
1395       color = colors[depth % numColors];
1396       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Cells\n"));
1397       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\c in {\\cStart,...,\\cEnd}\n"));
1398       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1399       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\c_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\cShift+\\c-\\cStart,%d) {\\c};\n", rank, color, height++));
1400       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1401 
1402       if (depth > 2) {
1403         color = colors[1 % numColors];
1404         PetscCall(PetscViewerASCIIPrintf(viewer, "%% Faces\n"));
1405         PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\f in {\\fStart,...,\\fEnd}\n"));
1406         PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1407         PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\f_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\fShift+\\f-\\fStart,%d) {\\f};\n", rank, color, height++));
1408         PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1409       }
1410 
1411       color = colors[1 % numColors];
1412       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Edges\n"));
1413       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\e in {\\eStart,...,\\eEnd}\n"));
1414       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1415       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\e_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\eShift+\\e-\\eStart,%d) {\\e};\n", rank, color, height++));
1416       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1417 
1418       color = colors[0 % numColors];
1419       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Vertices\n"));
1420       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\v in {\\vStart,...,\\vEnd}\n"));
1421       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1422       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\v_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\vShift+\\v-\\vStart,%d) {\\v};\n", rank, color, height++));
1423       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1424 
1425       for (p = pStart; p < pEnd; ++p) {
1426         const PetscInt *cone;
1427         PetscInt        coneSize, cp;
1428 
1429         PetscCall(DMPlexGetCone(dm, p, &cone));
1430         PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
1431         for (cp = 0; cp < coneSize; ++cp) PetscCall(PetscViewerASCIIPrintf(viewer, "\\draw[->, shorten >=1pt] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", cone[cp], rank, p, rank));
1432       }
1433     }
1434     PetscCall(PetscViewerFlush(viewer));
1435     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
1436     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{tikzpicture}\n"));
1437     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{document}\n"));
1438     for (l = 0; l < numLabels; ++l) PetscCall(PetscFree(names[l]));
1439     for (c = 0; c < numColors; ++c) PetscCall(PetscFree(colors[c]));
1440     for (c = 0; c < numLColors; ++c) PetscCall(PetscFree(lcolors[c]));
1441     PetscCall(PetscFree3(names, colors, lcolors));
1442     PetscCall(PetscBTDestroy(&wp));
1443   } else if (format == PETSC_VIEWER_LOAD_BALANCE) {
1444     Vec                    cown, acown;
1445     VecScatter             sct;
1446     ISLocalToGlobalMapping g2l;
1447     IS                     gid, acis;
1448     MPI_Comm               comm, ncomm = MPI_COMM_NULL;
1449     MPI_Group              ggroup, ngroup;
1450     PetscScalar           *array, nid;
1451     const PetscInt        *idxs;
1452     PetscInt              *idxs2, *start, *adjacency, *work;
1453     PetscInt64             lm[3], gm[3];
1454     PetscInt               i, c, cStart, cEnd, cum, numVertices, ect, ectn, cellHeight;
1455     PetscMPIInt            d1, d2, rank;
1456 
1457     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1458     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1459 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1460     PetscCallMPI(MPI_Comm_split_type(comm, MPI_COMM_TYPE_SHARED, rank, MPI_INFO_NULL, &ncomm));
1461 #endif
1462     if (ncomm != MPI_COMM_NULL) {
1463       PetscCallMPI(MPI_Comm_group(comm, &ggroup));
1464       PetscCallMPI(MPI_Comm_group(ncomm, &ngroup));
1465       d1 = 0;
1466       PetscCallMPI(MPI_Group_translate_ranks(ngroup, 1, &d1, ggroup, &d2));
1467       nid = d2;
1468       PetscCallMPI(MPI_Group_free(&ggroup));
1469       PetscCallMPI(MPI_Group_free(&ngroup));
1470       PetscCallMPI(MPI_Comm_free(&ncomm));
1471     } else nid = 0.0;
1472 
1473     /* Get connectivity */
1474     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1475     PetscCall(DMPlexCreatePartitionerGraph(dm, cellHeight, &numVertices, &start, &adjacency, &gid));
1476 
1477     /* filter overlapped local cells */
1478     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
1479     PetscCall(ISGetIndices(gid, &idxs));
1480     PetscCall(ISGetLocalSize(gid, &cum));
1481     PetscCall(PetscMalloc1(cum, &idxs2));
1482     for (c = cStart, cum = 0; c < cEnd; c++) {
1483       if (idxs[c - cStart] < 0) continue;
1484       idxs2[cum++] = idxs[c - cStart];
1485     }
1486     PetscCall(ISRestoreIndices(gid, &idxs));
1487     PetscCheck(numVertices == cum, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Unexpected %" PetscInt_FMT " != %" PetscInt_FMT, numVertices, cum);
1488     PetscCall(ISDestroy(&gid));
1489     PetscCall(ISCreateGeneral(comm, numVertices, idxs2, PETSC_OWN_POINTER, &gid));
1490 
1491     /* support for node-aware cell locality */
1492     PetscCall(ISCreateGeneral(comm, start[numVertices], adjacency, PETSC_USE_POINTER, &acis));
1493     PetscCall(VecCreateSeq(PETSC_COMM_SELF, start[numVertices], &acown));
1494     PetscCall(VecCreateMPI(comm, numVertices, PETSC_DECIDE, &cown));
1495     PetscCall(VecGetArray(cown, &array));
1496     for (c = 0; c < numVertices; c++) array[c] = nid;
1497     PetscCall(VecRestoreArray(cown, &array));
1498     PetscCall(VecScatterCreate(cown, acis, acown, NULL, &sct));
1499     PetscCall(VecScatterBegin(sct, cown, acown, INSERT_VALUES, SCATTER_FORWARD));
1500     PetscCall(VecScatterEnd(sct, cown, acown, INSERT_VALUES, SCATTER_FORWARD));
1501     PetscCall(ISDestroy(&acis));
1502     PetscCall(VecScatterDestroy(&sct));
1503     PetscCall(VecDestroy(&cown));
1504 
1505     /* compute edgeCut */
1506     for (c = 0, cum = 0; c < numVertices; c++) cum = PetscMax(cum, start[c + 1] - start[c]);
1507     PetscCall(PetscMalloc1(cum, &work));
1508     PetscCall(ISLocalToGlobalMappingCreateIS(gid, &g2l));
1509     PetscCall(ISLocalToGlobalMappingSetType(g2l, ISLOCALTOGLOBALMAPPINGHASH));
1510     PetscCall(ISDestroy(&gid));
1511     PetscCall(VecGetArray(acown, &array));
1512     for (c = 0, ect = 0, ectn = 0; c < numVertices; c++) {
1513       PetscInt totl;
1514 
1515       totl = start[c + 1] - start[c];
1516       PetscCall(ISGlobalToLocalMappingApply(g2l, IS_GTOLM_MASK, totl, adjacency + start[c], NULL, work));
1517       for (i = 0; i < totl; i++) {
1518         if (work[i] < 0) {
1519           ect += 1;
1520           ectn += (array[i + start[c]] != nid) ? 0 : 1;
1521         }
1522       }
1523     }
1524     PetscCall(PetscFree(work));
1525     PetscCall(VecRestoreArray(acown, &array));
1526     lm[0] = numVertices > 0 ? numVertices : PETSC_MAX_INT;
1527     lm[1] = -numVertices;
1528     PetscCall(MPIU_Allreduce(lm, gm, 2, MPIU_INT64, MPI_MIN, comm));
1529     PetscCall(PetscViewerASCIIPrintf(viewer, "  Cell balance: %.2f (max %" PetscInt_FMT ", min %" PetscInt_FMT, -((double)gm[1]) / ((double)gm[0]), -(PetscInt)gm[1], (PetscInt)gm[0]));
1530     lm[0] = ect;                     /* edgeCut */
1531     lm[1] = ectn;                    /* node-aware edgeCut */
1532     lm[2] = numVertices > 0 ? 0 : 1; /* empty processes */
1533     PetscCall(MPIU_Allreduce(lm, gm, 3, MPIU_INT64, MPI_SUM, comm));
1534     PetscCall(PetscViewerASCIIPrintf(viewer, ", empty %" PetscInt_FMT ")\n", (PetscInt)gm[2]));
1535 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1536     PetscCall(PetscViewerASCIIPrintf(viewer, "  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n", (PetscInt)(gm[0] / 2), gm[0] ? ((double)gm[1]) / ((double)gm[0]) : 1.));
1537 #else
1538     PetscCall(PetscViewerASCIIPrintf(viewer, "  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n", (PetscInt)(gm[0] / 2), 0.0));
1539 #endif
1540     PetscCall(ISLocalToGlobalMappingDestroy(&g2l));
1541     PetscCall(PetscFree(start));
1542     PetscCall(PetscFree(adjacency));
1543     PetscCall(VecDestroy(&acown));
1544   } else {
1545     const char    *name;
1546     PetscInt      *sizes, *hybsizes, *ghostsizes;
1547     PetscInt       locDepth, depth, cellHeight, dim, d;
1548     PetscInt       pStart, pEnd, p, gcStart, gcEnd, gcNum;
1549     PetscInt       numLabels, l, maxSize = 17;
1550     DMPolytopeType ct0 = DM_POLYTOPE_UNKNOWN;
1551     MPI_Comm       comm;
1552     PetscMPIInt    size, rank;
1553 
1554     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1555     PetscCallMPI(MPI_Comm_size(comm, &size));
1556     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1557     PetscCall(DMGetDimension(dm, &dim));
1558     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1559     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1560     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
1561     else PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
1562     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
1563     PetscCall(DMPlexGetDepth(dm, &locDepth));
1564     PetscCall(MPIU_Allreduce(&locDepth, &depth, 1, MPIU_INT, MPI_MAX, comm));
1565     PetscCall(DMPlexGetCellTypeStratum(dm, DM_POLYTOPE_FV_GHOST, &gcStart, &gcEnd));
1566     gcNum = gcEnd - gcStart;
1567     if (size < maxSize) PetscCall(PetscCalloc3(size, &sizes, size, &hybsizes, size, &ghostsizes));
1568     else PetscCall(PetscCalloc3(3, &sizes, 3, &hybsizes, 3, &ghostsizes));
1569     for (d = 0; d <= depth; d++) {
1570       PetscInt Nc[2] = {0, 0}, ict;
1571 
1572       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
1573       if (pStart < pEnd) PetscCall(DMPlexGetCellType(dm, pStart, &ct0));
1574       ict = ct0;
1575       PetscCallMPI(MPI_Bcast(&ict, 1, MPIU_INT, 0, comm));
1576       ct0 = (DMPolytopeType)ict;
1577       for (p = pStart; p < pEnd; ++p) {
1578         DMPolytopeType ct;
1579 
1580         PetscCall(DMPlexGetCellType(dm, p, &ct));
1581         if (ct == ct0) ++Nc[0];
1582         else ++Nc[1];
1583       }
1584       if (size < maxSize) {
1585         PetscCallMPI(MPI_Gather(&Nc[0], 1, MPIU_INT, sizes, 1, MPIU_INT, 0, comm));
1586         PetscCallMPI(MPI_Gather(&Nc[1], 1, MPIU_INT, hybsizes, 1, MPIU_INT, 0, comm));
1587         if (d == depth) PetscCallMPI(MPI_Gather(&gcNum, 1, MPIU_INT, ghostsizes, 1, MPIU_INT, 0, comm));
1588         PetscCall(PetscViewerASCIIPrintf(viewer, "  Number of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1589         for (p = 0; p < size; ++p) {
1590           if (rank == 0) {
1591             PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT, sizes[p] + hybsizes[p]));
1592             if (hybsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT ")", hybsizes[p]));
1593             if (ghostsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "]", ghostsizes[p]));
1594           }
1595         }
1596       } else {
1597         PetscInt locMinMax[2];
1598 
1599         locMinMax[0] = Nc[0] + Nc[1];
1600         locMinMax[1] = Nc[0] + Nc[1];
1601         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, sizes));
1602         locMinMax[0] = Nc[1];
1603         locMinMax[1] = Nc[1];
1604         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, hybsizes));
1605         if (d == depth) {
1606           locMinMax[0] = gcNum;
1607           locMinMax[1] = gcNum;
1608           PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, ghostsizes));
1609         }
1610         PetscCall(PetscViewerASCIIPrintf(viewer, "  Min/Max of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1611         PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT "/%" PetscInt_FMT, sizes[0], sizes[1]));
1612         if (hybsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT "/%" PetscInt_FMT ")", hybsizes[0], hybsizes[1]));
1613         if (ghostsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "/%" PetscInt_FMT "]", ghostsizes[0], ghostsizes[1]));
1614       }
1615       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
1616     }
1617     PetscCall(PetscFree3(sizes, hybsizes, ghostsizes));
1618     {
1619       const PetscReal *maxCell;
1620       const PetscReal *L;
1621       PetscBool        localized;
1622 
1623       PetscCall(DMGetPeriodicity(dm, &maxCell, NULL, &L));
1624       PetscCall(DMGetCoordinatesLocalized(dm, &localized));
1625       if (L || localized) {
1626         PetscCall(PetscViewerASCIIPrintf(viewer, "Periodic mesh"));
1627         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1628         if (L) {
1629           PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
1630           for (d = 0; d < dim; ++d) {
1631             if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1632             PetscCall(PetscViewerASCIIPrintf(viewer, "%s", L[d] > 0.0 ? "PERIODIC" : "NONE"));
1633           }
1634           PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
1635         }
1636         PetscCall(PetscViewerASCIIPrintf(viewer, " coordinates %s\n", localized ? "localized" : "not localized"));
1637         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1638       }
1639     }
1640     PetscCall(DMGetNumLabels(dm, &numLabels));
1641     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
1642     for (l = 0; l < numLabels; ++l) {
1643       DMLabel     label;
1644       const char *name;
1645       PetscInt   *values;
1646       PetscInt    numValues, v;
1647 
1648       PetscCall(DMGetLabelName(dm, l, &name));
1649       PetscCall(DMGetLabel(dm, name, &label));
1650       PetscCall(DMLabelGetNumValues(label, &numValues));
1651       PetscCall(PetscViewerASCIIPrintf(viewer, "  %s: %" PetscInt_FMT " strata with value/size (", name, numValues));
1652 
1653       { // Extract array of DMLabel values so it can be sorted
1654         IS              is_values;
1655         const PetscInt *is_values_local = NULL;
1656 
1657         PetscCall(DMLabelGetValueIS(label, &is_values));
1658         PetscCall(ISGetIndices(is_values, &is_values_local));
1659         PetscCall(PetscMalloc1(numValues, &values));
1660         PetscCall(PetscArraycpy(values, is_values_local, numValues));
1661         PetscCall(PetscSortInt(numValues, values));
1662         PetscCall(ISRestoreIndices(is_values, &is_values_local));
1663         PetscCall(ISDestroy(&is_values));
1664       }
1665       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1666       for (v = 0; v < numValues; ++v) {
1667         PetscInt size;
1668 
1669         PetscCall(DMLabelGetStratumSize(label, values[v], &size));
1670         if (v > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1671         PetscCall(PetscViewerASCIIPrintf(viewer, "%" PetscInt_FMT " (%" PetscInt_FMT ")", values[v], size));
1672       }
1673       PetscCall(PetscViewerASCIIPrintf(viewer, ")\n"));
1674       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1675       PetscCall(PetscFree(values));
1676     }
1677     {
1678       char    **labelNames;
1679       PetscInt  Nl = numLabels;
1680       PetscBool flg;
1681 
1682       PetscCall(PetscMalloc1(Nl, &labelNames));
1683       PetscCall(PetscOptionsGetStringArray(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_plex_view_labels", labelNames, &Nl, &flg));
1684       for (l = 0; l < Nl; ++l) {
1685         DMLabel label;
1686 
1687         PetscCall(DMHasLabel(dm, labelNames[l], &flg));
1688         if (flg) {
1689           PetscCall(DMGetLabel(dm, labelNames[l], &label));
1690           PetscCall(DMLabelView(label, viewer));
1691         }
1692         PetscCall(PetscFree(labelNames[l]));
1693       }
1694       PetscCall(PetscFree(labelNames));
1695     }
1696     /* If no fields are specified, people do not want to see adjacency */
1697     if (dm->Nf) {
1698       PetscInt f;
1699 
1700       for (f = 0; f < dm->Nf; ++f) {
1701         const char *name;
1702 
1703         PetscCall(PetscObjectGetName(dm->fields[f].disc, &name));
1704         if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Field %s:\n", name));
1705         PetscCall(PetscViewerASCIIPushTab(viewer));
1706         if (dm->fields[f].label) PetscCall(DMLabelView(dm->fields[f].label, viewer));
1707         if (dm->fields[f].adjacency[0]) {
1708           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM++\n"));
1709           else PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM\n"));
1710         } else {
1711           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FEM\n"));
1712           else PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FUNKY\n"));
1713         }
1714         PetscCall(PetscViewerASCIIPopTab(viewer));
1715       }
1716     }
1717     PetscCall(DMGetCoarseDM(dm, &cdm));
1718     if (cdm) {
1719       PetscCall(PetscViewerASCIIPushTab(viewer));
1720       PetscCall(PetscViewerASCIIPrintf(viewer, "Defined by transform from:\n"));
1721       PetscCall(DMPlexView_Ascii(cdm, viewer));
1722       PetscCall(PetscViewerASCIIPopTab(viewer));
1723     }
1724   }
1725   PetscFunctionReturn(PETSC_SUCCESS);
1726 }
1727 
1728 static PetscErrorCode DMPlexDrawCell(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[])
1729 {
1730   DMPolytopeType ct;
1731   PetscMPIInt    rank;
1732   PetscInt       cdim;
1733 
1734   PetscFunctionBegin;
1735   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1736   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1737   PetscCall(DMGetCoordinateDim(dm, &cdim));
1738   switch (ct) {
1739   case DM_POLYTOPE_SEGMENT:
1740   case DM_POLYTOPE_POINT_PRISM_TENSOR:
1741     switch (cdim) {
1742     case 1: {
1743       const PetscReal y  = 0.5;  /* TODO Put it in the middle of the viewport */
1744       const PetscReal dy = 0.05; /* TODO Make it a fraction of the total length */
1745 
1746       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y, PetscRealPart(coords[1]), y, PETSC_DRAW_BLACK));
1747       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y + dy, PetscRealPart(coords[0]), y - dy, PETSC_DRAW_BLACK));
1748       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[1]), y + dy, PetscRealPart(coords[1]), y - dy, PETSC_DRAW_BLACK));
1749     } break;
1750     case 2: {
1751       const PetscReal dx = (PetscRealPart(coords[3]) - PetscRealPart(coords[1]));
1752       const PetscReal dy = (PetscRealPart(coords[2]) - PetscRealPart(coords[0]));
1753       const PetscReal l  = 0.1 / PetscSqrtReal(dx * dx + dy * dy);
1754 
1755       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1756       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));
1757       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));
1758     } break;
1759     default:
1760       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of dimension %" PetscInt_FMT, cdim);
1761     }
1762     break;
1763   case DM_POLYTOPE_TRIANGLE:
1764     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));
1765     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1766     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1767     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1768     break;
1769   case DM_POLYTOPE_QUADRILATERAL:
1770     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));
1771     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));
1772     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1773     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1774     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_BLACK));
1775     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1776     break;
1777   case DM_POLYTOPE_SEG_PRISM_TENSOR:
1778     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));
1779     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));
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[6]), PetscRealPart(coords[7]), PETSC_DRAW_BLACK));
1782     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1783     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1784     break;
1785   case DM_POLYTOPE_FV_GHOST:
1786     break;
1787   default:
1788     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1789   }
1790   PetscFunctionReturn(PETSC_SUCCESS);
1791 }
1792 
1793 static PetscErrorCode DrawPolygon_Private(DM dm, PetscDraw draw, PetscInt cell, PetscInt Nv, const PetscReal refVertices[], const PetscScalar coords[], PetscInt edgeDiv, PetscReal refCoords[], PetscReal edgeCoords[])
1794 {
1795   PetscReal   centroid[2] = {0., 0.};
1796   PetscMPIInt rank;
1797   PetscInt    fillColor;
1798 
1799   PetscFunctionBegin;
1800   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1801   fillColor = PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2;
1802   for (PetscInt v = 0; v < Nv; ++v) {
1803     centroid[0] += PetscRealPart(coords[v * 2 + 0]) / Nv;
1804     centroid[1] += PetscRealPart(coords[v * 2 + 1]) / Nv;
1805   }
1806   for (PetscInt e = 0; e < Nv; ++e) {
1807     refCoords[0] = refVertices[e * 2 + 0];
1808     refCoords[1] = refVertices[e * 2 + 1];
1809     for (PetscInt d = 1; d <= edgeDiv; ++d) {
1810       refCoords[d * 2 + 0] = refCoords[0] + (refVertices[(e + 1) % Nv * 2 + 0] - refCoords[0]) * d / edgeDiv;
1811       refCoords[d * 2 + 1] = refCoords[1] + (refVertices[(e + 1) % Nv * 2 + 1] - refCoords[1]) * d / edgeDiv;
1812     }
1813     PetscCall(DMPlexReferenceToCoordinates(dm, cell, edgeDiv + 1, refCoords, edgeCoords));
1814     for (PetscInt d = 0; d < edgeDiv; ++d) {
1815       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));
1816       PetscCall(PetscDrawLine(draw, edgeCoords[d * 2 + 0], edgeCoords[d * 2 + 1], edgeCoords[(d + 1) * 2 + 0], edgeCoords[(d + 1) * 2 + 1], PETSC_DRAW_BLACK));
1817     }
1818   }
1819   PetscFunctionReturn(PETSC_SUCCESS);
1820 }
1821 
1822 static PetscErrorCode DMPlexDrawCellHighOrder(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[], PetscInt edgeDiv, PetscReal refCoords[], PetscReal edgeCoords[])
1823 {
1824   DMPolytopeType ct;
1825 
1826   PetscFunctionBegin;
1827   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1828   switch (ct) {
1829   case DM_POLYTOPE_TRIANGLE: {
1830     PetscReal refVertices[6] = {-1., -1., 1., -1., -1., 1.};
1831 
1832     PetscCall(DrawPolygon_Private(dm, draw, cell, 3, refVertices, coords, edgeDiv, refCoords, edgeCoords));
1833   } break;
1834   case DM_POLYTOPE_QUADRILATERAL: {
1835     PetscReal refVertices[8] = {-1., -1., 1., -1., 1., 1., -1., 1.};
1836 
1837     PetscCall(DrawPolygon_Private(dm, draw, cell, 4, refVertices, coords, edgeDiv, refCoords, edgeCoords));
1838   } break;
1839   default:
1840     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1841   }
1842   PetscFunctionReturn(PETSC_SUCCESS);
1843 }
1844 
1845 static PetscErrorCode DMPlexView_Draw(DM dm, PetscViewer viewer)
1846 {
1847   PetscDraw    draw;
1848   DM           cdm;
1849   PetscSection coordSection;
1850   Vec          coordinates;
1851   PetscReal    xyl[3], xyr[3];
1852   PetscReal   *refCoords, *edgeCoords;
1853   PetscBool    isnull, drawAffine;
1854   PetscInt     dim, vStart, vEnd, cStart, cEnd, c, cDegree, edgeDiv;
1855 
1856   PetscFunctionBegin;
1857   PetscCall(DMGetCoordinateDim(dm, &dim));
1858   PetscCheck(dim <= 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT, dim);
1859   PetscCall(DMGetCoordinateDegree_Internal(dm, &cDegree));
1860   drawAffine = cDegree > 1 ? PETSC_FALSE : PETSC_TRUE;
1861   edgeDiv    = cDegree + 1;
1862   PetscCall(PetscOptionsGetBool(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_view_draw_affine", &drawAffine, NULL));
1863   if (!drawAffine) PetscCall(PetscMalloc2((edgeDiv + 1) * dim, &refCoords, (edgeDiv + 1) * dim, &edgeCoords));
1864   PetscCall(DMGetCoordinateDM(dm, &cdm));
1865   PetscCall(DMGetLocalSection(cdm, &coordSection));
1866   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1867   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1868   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1869 
1870   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
1871   PetscCall(PetscDrawIsNull(draw, &isnull));
1872   if (isnull) PetscFunctionReturn(PETSC_SUCCESS);
1873   PetscCall(PetscDrawSetTitle(draw, "Mesh"));
1874 
1875   PetscCall(DMGetBoundingBox(dm, xyl, xyr));
1876   PetscCall(PetscDrawSetCoordinates(draw, xyl[0], xyl[1], xyr[0], xyr[1]));
1877   PetscCall(PetscDrawClear(draw));
1878 
1879   for (c = cStart; c < cEnd; ++c) {
1880     PetscScalar       *coords = NULL;
1881     const PetscScalar *coords_arr;
1882     PetscInt           numCoords;
1883     PetscBool          isDG;
1884 
1885     PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
1886     if (drawAffine) PetscCall(DMPlexDrawCell(dm, draw, c, coords));
1887     else PetscCall(DMPlexDrawCellHighOrder(dm, draw, c, coords, edgeDiv, refCoords, edgeCoords));
1888     PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
1889   }
1890   if (!drawAffine) PetscCall(PetscFree2(refCoords, edgeCoords));
1891   PetscCall(PetscDrawFlush(draw));
1892   PetscCall(PetscDrawPause(draw));
1893   PetscCall(PetscDrawSave(draw));
1894   PetscFunctionReturn(PETSC_SUCCESS);
1895 }
1896 
1897 static PetscErrorCode DMPlexCreateHighOrderSurrogate_Internal(DM dm, DM *hdm)
1898 {
1899   DM           odm = dm, rdm = dm, cdm;
1900   PetscFE      fe;
1901   PetscSpace   sp;
1902   PetscClassId id;
1903   PetscInt     degree;
1904   PetscBool    hoView = PETSC_TRUE;
1905 
1906   PetscFunctionBegin;
1907   PetscObjectOptionsBegin((PetscObject)dm);
1908   PetscCall(PetscOptionsBool("-dm_plex_high_order_view", "Subsample to view meshes with high order coordinates", "DMPlexCreateHighOrderSurrogate_Internal", hoView, &hoView, NULL));
1909   PetscOptionsEnd();
1910   PetscCall(PetscObjectReference((PetscObject)dm));
1911   *hdm = dm;
1912   if (!hoView) PetscFunctionReturn(PETSC_SUCCESS);
1913   PetscCall(DMGetCoordinateDM(dm, &cdm));
1914   PetscCall(DMGetField(cdm, 0, NULL, (PetscObject *)&fe));
1915   PetscCall(PetscObjectGetClassId((PetscObject)fe, &id));
1916   if (id != PETSCFE_CLASSID) PetscFunctionReturn(PETSC_SUCCESS);
1917   PetscCall(PetscFEGetBasisSpace(fe, &sp));
1918   PetscCall(PetscSpaceGetDegree(sp, &degree, NULL));
1919   for (PetscInt r = 0, rd = PetscCeilReal(((PetscReal)degree) / 2.); r < (PetscInt)PetscCeilReal(PetscLog2Real(degree)); ++r, rd = PetscCeilReal(((PetscReal)rd) / 2.)) {
1920     DM  cdm, rcdm;
1921     Mat In;
1922     Vec cl, rcl;
1923 
1924     PetscCall(DMRefine(odm, PetscObjectComm((PetscObject)odm), &rdm));
1925     PetscCall(DMPlexCreateCoordinateSpace(rdm, rd, PETSC_FALSE, NULL));
1926     PetscCall(PetscObjectSetName((PetscObject)rdm, "Refined Mesh with Linear Coordinates"));
1927     PetscCall(DMGetCoordinateDM(odm, &cdm));
1928     PetscCall(DMGetCoordinateDM(rdm, &rcdm));
1929     PetscCall(DMGetCoordinatesLocal(odm, &cl));
1930     PetscCall(DMGetCoordinatesLocal(rdm, &rcl));
1931     PetscCall(DMSetCoarseDM(rcdm, cdm));
1932     PetscCall(DMCreateInterpolation(cdm, rcdm, &In, NULL));
1933     PetscCall(MatMult(In, cl, rcl));
1934     PetscCall(MatDestroy(&In));
1935     PetscCall(DMSetCoordinatesLocal(rdm, rcl));
1936     PetscCall(DMDestroy(&odm));
1937     odm = rdm;
1938   }
1939   *hdm = rdm;
1940   PetscFunctionReturn(PETSC_SUCCESS);
1941 }
1942 
1943 #if defined(PETSC_HAVE_EXODUSII)
1944   #include <exodusII.h>
1945   #include <petscviewerexodusii.h>
1946 #endif
1947 
1948 PetscErrorCode DMView_Plex(DM dm, PetscViewer viewer)
1949 {
1950   PetscBool iascii, ishdf5, isvtk, isdraw, flg, isglvis, isexodus, iscgns;
1951   char      name[PETSC_MAX_PATH_LEN];
1952 
1953   PetscFunctionBegin;
1954   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1955   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1956   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &iascii));
1957   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
1958   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1959   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
1960   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
1961   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodus));
1962   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
1963   if (iascii) {
1964     PetscViewerFormat format;
1965     PetscCall(PetscViewerGetFormat(viewer, &format));
1966     if (format == PETSC_VIEWER_ASCII_GLVIS) PetscCall(DMPlexView_GLVis(dm, viewer));
1967     else PetscCall(DMPlexView_Ascii(dm, viewer));
1968   } else if (ishdf5) {
1969 #if defined(PETSC_HAVE_HDF5)
1970     PetscCall(DMPlexView_HDF5_Internal(dm, viewer));
1971 #else
1972     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1973 #endif
1974   } else if (isvtk) {
1975     PetscCall(DMPlexVTKWriteAll((PetscObject)dm, viewer));
1976   } else if (isdraw) {
1977     DM hdm;
1978 
1979     PetscCall(DMPlexCreateHighOrderSurrogate_Internal(dm, &hdm));
1980     PetscCall(DMPlexView_Draw(hdm, viewer));
1981     PetscCall(DMDestroy(&hdm));
1982   } else if (isglvis) {
1983     PetscCall(DMPlexView_GLVis(dm, viewer));
1984 #if defined(PETSC_HAVE_EXODUSII)
1985   } else if (isexodus) {
1986     /*
1987       exodusII requires that all sets be part of exactly one cell set.
1988       If the dm does not have a "Cell Sets" label defined, we create one
1989       with ID 1, containing all cells.
1990       Note that if the Cell Sets label is defined but does not cover all cells,
1991       we may still have a problem. This should probably be checked here or in the viewer;
1992     */
1993     PetscInt numCS;
1994     PetscCall(DMGetLabelSize(dm, "Cell Sets", &numCS));
1995     if (!numCS) {
1996       PetscInt cStart, cEnd, c;
1997       PetscCall(DMCreateLabel(dm, "Cell Sets"));
1998       PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1999       for (c = cStart; c < cEnd; ++c) PetscCall(DMSetLabelValue(dm, "Cell Sets", c, 1));
2000     }
2001     PetscCall(DMView_PlexExodusII(dm, viewer));
2002 #endif
2003 #if defined(PETSC_HAVE_CGNS)
2004   } else if (iscgns) {
2005     PetscCall(DMView_PlexCGNS(dm, viewer));
2006 #endif
2007   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex writing", ((PetscObject)viewer)->type_name);
2008   /* Optionally view the partition */
2009   PetscCall(PetscOptionsHasName(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_partition_view", &flg));
2010   if (flg) {
2011     Vec ranks;
2012     PetscCall(DMPlexCreateRankField(dm, &ranks));
2013     PetscCall(VecView(ranks, viewer));
2014     PetscCall(VecDestroy(&ranks));
2015   }
2016   /* Optionally view a label */
2017   PetscCall(PetscOptionsGetString(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_label_view", name, sizeof(name), &flg));
2018   if (flg) {
2019     DMLabel label;
2020     Vec     val;
2021 
2022     PetscCall(DMGetLabel(dm, name, &label));
2023     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Label %s provided to -dm_label_view does not exist in this DM", name);
2024     PetscCall(DMPlexCreateLabelField(dm, label, &val));
2025     PetscCall(VecView(val, viewer));
2026     PetscCall(VecDestroy(&val));
2027   }
2028   PetscFunctionReturn(PETSC_SUCCESS);
2029 }
2030 
2031 /*@
2032   DMPlexTopologyView - Saves a `DMPLEX` topology into a file
2033 
2034   Collective
2035 
2036   Input Parameters:
2037 + dm     - The `DM` whose topology is to be saved
2038 - viewer - The `PetscViewer` to save it in
2039 
2040   Level: advanced
2041 
2042 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexTopologyLoad()`, `PetscViewer`
2043 @*/
2044 PetscErrorCode DMPlexTopologyView(DM dm, PetscViewer viewer)
2045 {
2046   PetscBool ishdf5;
2047 
2048   PetscFunctionBegin;
2049   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2050   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2051   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2052   PetscCall(PetscLogEventBegin(DMPLEX_TopologyView, viewer, 0, 0, 0));
2053   if (ishdf5) {
2054 #if defined(PETSC_HAVE_HDF5)
2055     PetscViewerFormat format;
2056     PetscCall(PetscViewerGetFormat(viewer, &format));
2057     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2058       IS globalPointNumbering;
2059 
2060       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
2061       PetscCall(DMPlexTopologyView_HDF5_Internal(dm, globalPointNumbering, viewer));
2062       PetscCall(ISDestroy(&globalPointNumbering));
2063     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
2064 #else
2065     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2066 #endif
2067   }
2068   PetscCall(PetscLogEventEnd(DMPLEX_TopologyView, viewer, 0, 0, 0));
2069   PetscFunctionReturn(PETSC_SUCCESS);
2070 }
2071 
2072 /*@
2073   DMPlexCoordinatesView - Saves `DMPLEX` coordinates into a file
2074 
2075   Collective
2076 
2077   Input Parameters:
2078 + dm     - The `DM` whose coordinates are to be saved
2079 - viewer - The `PetscViewer` for saving
2080 
2081   Level: advanced
2082 
2083 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexLabelsView()`, `DMPlexCoordinatesLoad()`, `PetscViewer`
2084 @*/
2085 PetscErrorCode DMPlexCoordinatesView(DM dm, PetscViewer viewer)
2086 {
2087   PetscBool ishdf5;
2088 
2089   PetscFunctionBegin;
2090   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2091   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2092   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2093   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
2094   if (ishdf5) {
2095 #if defined(PETSC_HAVE_HDF5)
2096     PetscViewerFormat format;
2097     PetscCall(PetscViewerGetFormat(viewer, &format));
2098     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2099       PetscCall(DMPlexCoordinatesView_HDF5_Internal(dm, viewer));
2100     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
2101 #else
2102     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2103 #endif
2104   }
2105   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
2106   PetscFunctionReturn(PETSC_SUCCESS);
2107 }
2108 
2109 /*@
2110   DMPlexLabelsView - Saves `DMPLEX` labels into a file
2111 
2112   Collective
2113 
2114   Input Parameters:
2115 + dm     - The `DM` whose labels are to be saved
2116 - viewer - The `PetscViewer` for saving
2117 
2118   Level: advanced
2119 
2120 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsLoad()`, `PetscViewer`
2121 @*/
2122 PetscErrorCode DMPlexLabelsView(DM dm, PetscViewer viewer)
2123 {
2124   PetscBool ishdf5;
2125 
2126   PetscFunctionBegin;
2127   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2128   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2129   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2130   PetscCall(PetscLogEventBegin(DMPLEX_LabelsView, viewer, 0, 0, 0));
2131   if (ishdf5) {
2132 #if defined(PETSC_HAVE_HDF5)
2133     IS                globalPointNumbering;
2134     PetscViewerFormat format;
2135 
2136     PetscCall(PetscViewerGetFormat(viewer, &format));
2137     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2138       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
2139       PetscCall(DMPlexLabelsView_HDF5_Internal(dm, globalPointNumbering, viewer));
2140       PetscCall(ISDestroy(&globalPointNumbering));
2141     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2142 #else
2143     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2144 #endif
2145   }
2146   PetscCall(PetscLogEventEnd(DMPLEX_LabelsView, viewer, 0, 0, 0));
2147   PetscFunctionReturn(PETSC_SUCCESS);
2148 }
2149 
2150 /*@
2151   DMPlexSectionView - Saves a section associated with a `DMPLEX`
2152 
2153   Collective
2154 
2155   Input Parameters:
2156 + dm        - The `DM` that contains the topology on which the section to be saved is defined
2157 . viewer    - The `PetscViewer` for saving
2158 - sectiondm - The `DM` that contains the section to be saved, can be `NULL`
2159 
2160   Level: advanced
2161 
2162   Notes:
2163   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.
2164 
2165   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.
2166 
2167 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`, `PetscSectionView()`, `DMPlexSectionLoad()`, `PetscViewer`
2168 @*/
2169 PetscErrorCode DMPlexSectionView(DM dm, PetscViewer viewer, DM sectiondm)
2170 {
2171   PetscBool ishdf5;
2172 
2173   PetscFunctionBegin;
2174   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2175   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2176   if (!sectiondm) sectiondm = dm;
2177   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2178   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2179   PetscCall(PetscLogEventBegin(DMPLEX_SectionView, viewer, 0, 0, 0));
2180   if (ishdf5) {
2181 #if defined(PETSC_HAVE_HDF5)
2182     PetscCall(DMPlexSectionView_HDF5_Internal(dm, viewer, sectiondm));
2183 #else
2184     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2185 #endif
2186   }
2187   PetscCall(PetscLogEventEnd(DMPLEX_SectionView, viewer, 0, 0, 0));
2188   PetscFunctionReturn(PETSC_SUCCESS);
2189 }
2190 
2191 /*@
2192   DMPlexGlobalVectorView - Saves a global vector
2193 
2194   Collective
2195 
2196   Input Parameters:
2197 + dm        - The `DM` that represents the topology
2198 . viewer    - The `PetscViewer` to save data with
2199 . sectiondm - The `DM` that contains the global section on which vec is defined, can be `NULL`
2200 - vec       - The global vector to be saved
2201 
2202   Level: advanced
2203 
2204   Notes:
2205   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.
2206 
2207   Calling sequence:
2208 .vb
2209        DMCreate(PETSC_COMM_WORLD, &dm);
2210        DMSetType(dm, DMPLEX);
2211        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2212        DMClone(dm, &sectiondm);
2213        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2214        PetscSectionCreate(PETSC_COMM_WORLD, &section);
2215        DMPlexGetChart(sectiondm, &pStart, &pEnd);
2216        PetscSectionSetChart(section, pStart, pEnd);
2217        PetscSectionSetUp(section);
2218        DMSetLocalSection(sectiondm, section);
2219        PetscSectionDestroy(&section);
2220        DMGetGlobalVector(sectiondm, &vec);
2221        PetscObjectSetName((PetscObject)vec, "vec_name");
2222        DMPlexTopologyView(dm, viewer);
2223        DMPlexSectionView(dm, viewer, sectiondm);
2224        DMPlexGlobalVectorView(dm, viewer, sectiondm, vec);
2225        DMRestoreGlobalVector(sectiondm, &vec);
2226        DMDestroy(&sectiondm);
2227        DMDestroy(&dm);
2228 .ve
2229 
2230 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexLocalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2231 @*/
2232 PetscErrorCode DMPlexGlobalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2233 {
2234   PetscBool ishdf5;
2235 
2236   PetscFunctionBegin;
2237   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2238   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2239   if (!sectiondm) sectiondm = dm;
2240   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2241   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2242   /* Check consistency */
2243   {
2244     PetscSection section;
2245     PetscBool    includesConstraints;
2246     PetscInt     m, m1;
2247 
2248     PetscCall(VecGetLocalSize(vec, &m1));
2249     PetscCall(DMGetGlobalSection(sectiondm, &section));
2250     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2251     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2252     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2253     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2254   }
2255   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2256   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorView, viewer, 0, 0, 0));
2257   if (ishdf5) {
2258 #if defined(PETSC_HAVE_HDF5)
2259     PetscCall(DMPlexGlobalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2260 #else
2261     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2262 #endif
2263   }
2264   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorView, viewer, 0, 0, 0));
2265   PetscFunctionReturn(PETSC_SUCCESS);
2266 }
2267 
2268 /*@
2269   DMPlexLocalVectorView - Saves a local vector
2270 
2271   Collective
2272 
2273   Input Parameters:
2274 + dm        - The `DM` that represents the topology
2275 . viewer    - The `PetscViewer` to save data with
2276 . sectiondm - The `DM` that contains the local section on which `vec` is defined, can be `NULL`
2277 - vec       - The local vector to be saved
2278 
2279   Level: advanced
2280 
2281   Note:
2282   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.
2283 
2284   Calling sequence:
2285 .vb
2286        DMCreate(PETSC_COMM_WORLD, &dm);
2287        DMSetType(dm, DMPLEX);
2288        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2289        DMClone(dm, &sectiondm);
2290        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2291        PetscSectionCreate(PETSC_COMM_WORLD, &section);
2292        DMPlexGetChart(sectiondm, &pStart, &pEnd);
2293        PetscSectionSetChart(section, pStart, pEnd);
2294        PetscSectionSetUp(section);
2295        DMSetLocalSection(sectiondm, section);
2296        DMGetLocalVector(sectiondm, &vec);
2297        PetscObjectSetName((PetscObject)vec, "vec_name");
2298        DMPlexTopologyView(dm, viewer);
2299        DMPlexSectionView(dm, viewer, sectiondm);
2300        DMPlexLocalVectorView(dm, viewer, sectiondm, vec);
2301        DMRestoreLocalVector(sectiondm, &vec);
2302        DMDestroy(&sectiondm);
2303        DMDestroy(&dm);
2304 .ve
2305 
2306 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexGlobalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2307 @*/
2308 PetscErrorCode DMPlexLocalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2309 {
2310   PetscBool ishdf5;
2311 
2312   PetscFunctionBegin;
2313   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2314   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2315   if (!sectiondm) sectiondm = dm;
2316   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2317   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2318   /* Check consistency */
2319   {
2320     PetscSection section;
2321     PetscBool    includesConstraints;
2322     PetscInt     m, m1;
2323 
2324     PetscCall(VecGetLocalSize(vec, &m1));
2325     PetscCall(DMGetLocalSection(sectiondm, &section));
2326     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2327     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2328     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2329     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2330   }
2331   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2332   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorView, viewer, 0, 0, 0));
2333   if (ishdf5) {
2334 #if defined(PETSC_HAVE_HDF5)
2335     PetscCall(DMPlexLocalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2336 #else
2337     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2338 #endif
2339   }
2340   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorView, viewer, 0, 0, 0));
2341   PetscFunctionReturn(PETSC_SUCCESS);
2342 }
2343 
2344 PetscErrorCode DMLoad_Plex(DM dm, PetscViewer viewer)
2345 {
2346   PetscBool ishdf5;
2347 
2348   PetscFunctionBegin;
2349   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2350   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2351   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2352   if (ishdf5) {
2353 #if defined(PETSC_HAVE_HDF5)
2354     PetscViewerFormat format;
2355     PetscCall(PetscViewerGetFormat(viewer, &format));
2356     if (format == PETSC_VIEWER_HDF5_XDMF || format == PETSC_VIEWER_HDF5_VIZ) {
2357       PetscCall(DMPlexLoad_HDF5_Xdmf_Internal(dm, viewer));
2358     } else if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2359       PetscCall(DMPlexLoad_HDF5_Internal(dm, viewer));
2360     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2361     PetscFunctionReturn(PETSC_SUCCESS);
2362 #else
2363     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2364 #endif
2365   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex loading", ((PetscObject)viewer)->type_name);
2366 }
2367 
2368 /*@
2369   DMPlexTopologyLoad - Loads a topology into a `DMPLEX`
2370 
2371   Collective
2372 
2373   Input Parameters:
2374 + dm     - The `DM` into which the topology is loaded
2375 - viewer - The `PetscViewer` for the saved topology
2376 
2377   Output Parameter:
2378 . 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;
2379   `NULL` if unneeded
2380 
2381   Level: advanced
2382 
2383 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2384           `PetscViewer`, `PetscSF`
2385 @*/
2386 PetscErrorCode DMPlexTopologyLoad(DM dm, PetscViewer viewer, PetscSF *globalToLocalPointSF)
2387 {
2388   PetscBool ishdf5;
2389 
2390   PetscFunctionBegin;
2391   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2392   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2393   if (globalToLocalPointSF) PetscAssertPointer(globalToLocalPointSF, 3);
2394   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2395   PetscCall(PetscLogEventBegin(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2396   if (ishdf5) {
2397 #if defined(PETSC_HAVE_HDF5)
2398     PetscViewerFormat format;
2399     PetscCall(PetscViewerGetFormat(viewer, &format));
2400     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2401       PetscCall(DMPlexTopologyLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2402     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2403 #else
2404     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2405 #endif
2406   }
2407   PetscCall(PetscLogEventEnd(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2408   PetscFunctionReturn(PETSC_SUCCESS);
2409 }
2410 
2411 /*@
2412   DMPlexCoordinatesLoad - Loads coordinates into a `DMPLEX`
2413 
2414   Collective
2415 
2416   Input Parameters:
2417 + dm                   - The `DM` into which the coordinates are loaded
2418 . viewer               - The `PetscViewer` for the saved coordinates
2419 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad()` when loading dm from viewer
2420 
2421   Level: advanced
2422 
2423 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2424           `PetscSF`, `PetscViewer`
2425 @*/
2426 PetscErrorCode DMPlexCoordinatesLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2427 {
2428   PetscBool ishdf5;
2429 
2430   PetscFunctionBegin;
2431   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2432   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2433   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2434   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2435   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesLoad, viewer, 0, 0, 0));
2436   if (ishdf5) {
2437 #if defined(PETSC_HAVE_HDF5)
2438     PetscViewerFormat format;
2439     PetscCall(PetscViewerGetFormat(viewer, &format));
2440     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2441       PetscCall(DMPlexCoordinatesLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2442     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2443 #else
2444     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2445 #endif
2446   }
2447   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesLoad, viewer, 0, 0, 0));
2448   PetscFunctionReturn(PETSC_SUCCESS);
2449 }
2450 
2451 /*@
2452   DMPlexLabelsLoad - Loads labels into a `DMPLEX`
2453 
2454   Collective
2455 
2456   Input Parameters:
2457 + dm                   - The `DM` into which the labels are loaded
2458 . viewer               - The `PetscViewer` for the saved labels
2459 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad()` when loading `dm` from viewer
2460 
2461   Level: advanced
2462 
2463   Note:
2464   The `PetscSF` argument must not be `NULL` if the `DM` is distributed, otherwise an error occurs.
2465 
2466 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2467           `PetscSF`, `PetscViewer`
2468 @*/
2469 PetscErrorCode DMPlexLabelsLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2470 {
2471   PetscBool ishdf5;
2472 
2473   PetscFunctionBegin;
2474   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2475   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2476   if (globalToLocalPointSF) PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2477   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2478   PetscCall(PetscLogEventBegin(DMPLEX_LabelsLoad, viewer, 0, 0, 0));
2479   if (ishdf5) {
2480 #if defined(PETSC_HAVE_HDF5)
2481     PetscViewerFormat format;
2482 
2483     PetscCall(PetscViewerGetFormat(viewer, &format));
2484     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2485       PetscCall(DMPlexLabelsLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2486     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2487 #else
2488     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2489 #endif
2490   }
2491   PetscCall(PetscLogEventEnd(DMPLEX_LabelsLoad, viewer, 0, 0, 0));
2492   PetscFunctionReturn(PETSC_SUCCESS);
2493 }
2494 
2495 /*@
2496   DMPlexSectionLoad - Loads section into a `DMPLEX`
2497 
2498   Collective
2499 
2500   Input Parameters:
2501 + dm                   - The `DM` that represents the topology
2502 . viewer               - The `PetscViewer` that represents the on-disk section (sectionA)
2503 . sectiondm            - The `DM` into which the on-disk section (sectionA) is migrated, can be `NULL`
2504 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad(`) when loading dm from viewer
2505 
2506   Output Parameters:
2507 + 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)
2508 - 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)
2509 
2510   Level: advanced
2511 
2512   Notes:
2513   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.
2514 
2515   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.
2516 
2517   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.
2518 
2519   Example using 2 processes:
2520 .vb
2521   NX (number of points on dm): 4
2522   sectionA                   : the on-disk section
2523   vecA                       : a vector associated with sectionA
2524   sectionB                   : sectiondm's local section constructed in this function
2525   vecB (local)               : a vector associated with sectiondm's local section
2526   vecB (global)              : a vector associated with sectiondm's global section
2527 
2528                                      rank 0    rank 1
2529   vecA (global)                  : [.0 .4 .1 | .2 .3]        <- to be loaded in DMPlexGlobalVectorLoad() or DMPlexLocalVectorLoad()
2530   sectionA->atlasOff             :       0 2 | 1             <- loaded in PetscSectionLoad()
2531   sectionA->atlasDof             :       1 3 | 1             <- loaded in PetscSectionLoad()
2532   sectionA's global point numbers:       0 2 | 3             <- loaded in DMPlexSectionLoad()
2533   [0, NX)                        :       0 1 | 2 3           <- conceptual partition used in globalToLocalPointSF
2534   sectionB's global point numbers:     0 1 3 | 3 2           <- associated with [0, NX) by globalToLocalPointSF
2535   sectionB->atlasDof             :     1 0 1 | 1 3
2536   sectionB->atlasOff (no perm)   :     0 1 1 | 0 1
2537   vecB (local)                   :   [.0 .4] | [.4 .1 .2 .3] <- to be constructed by calling DMPlexLocalVectorLoad() with localDofSF
2538   vecB (global)                  :    [.0 .4 | .1 .2 .3]     <- to be constructed by calling DMPlexGlobalVectorLoad() with globalDofSF
2539 .ve
2540   where "|" represents a partition of loaded data, and global point 3 is assumed to be owned by rank 0.
2541 
2542 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`, `PetscSectionLoad()`, `DMPlexSectionView()`, `PetscSF`, `PetscViewer`
2543 @*/
2544 PetscErrorCode DMPlexSectionLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF globalToLocalPointSF, PetscSF *globalDofSF, PetscSF *localDofSF)
2545 {
2546   PetscBool ishdf5;
2547 
2548   PetscFunctionBegin;
2549   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2550   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2551   if (!sectiondm) sectiondm = dm;
2552   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2553   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 4);
2554   if (globalDofSF) PetscAssertPointer(globalDofSF, 5);
2555   if (localDofSF) PetscAssertPointer(localDofSF, 6);
2556   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2557   PetscCall(PetscLogEventBegin(DMPLEX_SectionLoad, viewer, 0, 0, 0));
2558   if (ishdf5) {
2559 #if defined(PETSC_HAVE_HDF5)
2560     PetscCall(DMPlexSectionLoad_HDF5_Internal(dm, viewer, sectiondm, globalToLocalPointSF, globalDofSF, localDofSF));
2561 #else
2562     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2563 #endif
2564   }
2565   PetscCall(PetscLogEventEnd(DMPLEX_SectionLoad, viewer, 0, 0, 0));
2566   PetscFunctionReturn(PETSC_SUCCESS);
2567 }
2568 
2569 /*@
2570   DMPlexGlobalVectorLoad - Loads on-disk vector data into a global vector
2571 
2572   Collective
2573 
2574   Input Parameters:
2575 + dm        - The `DM` that represents the topology
2576 . viewer    - The `PetscViewer` that represents the on-disk vector data
2577 . sectiondm - The `DM` that contains the global section on which vec is defined, can be `NULL`
2578 . sf        - The `PetscSF` that migrates the on-disk vector data into vec
2579 - vec       - The global vector to set values of
2580 
2581   Level: advanced
2582 
2583   Notes:
2584   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.
2585 
2586   Calling sequence:
2587 .vb
2588        DMCreate(PETSC_COMM_WORLD, &dm);
2589        DMSetType(dm, DMPLEX);
2590        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2591        DMPlexTopologyLoad(dm, viewer, &sfX);
2592        DMClone(dm, &sectiondm);
2593        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2594        DMPlexSectionLoad(dm, viewer, sectiondm, sfX, &gsf, NULL);
2595        DMGetGlobalVector(sectiondm, &vec);
2596        PetscObjectSetName((PetscObject)vec, "vec_name");
2597        DMPlexGlobalVectorLoad(dm, viewer, sectiondm, gsf, vec);
2598        DMRestoreGlobalVector(sectiondm, &vec);
2599        PetscSFDestroy(&gsf);
2600        PetscSFDestroy(&sfX);
2601        DMDestroy(&sectiondm);
2602        DMDestroy(&dm);
2603 .ve
2604 
2605 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexLocalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`,
2606           `PetscSF`, `PetscViewer`
2607 @*/
2608 PetscErrorCode DMPlexGlobalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2609 {
2610   PetscBool ishdf5;
2611 
2612   PetscFunctionBegin;
2613   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2614   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2615   if (!sectiondm) sectiondm = dm;
2616   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2617   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2618   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2619   /* Check consistency */
2620   {
2621     PetscSection section;
2622     PetscBool    includesConstraints;
2623     PetscInt     m, m1;
2624 
2625     PetscCall(VecGetLocalSize(vec, &m1));
2626     PetscCall(DMGetGlobalSection(sectiondm, &section));
2627     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2628     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2629     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2630     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2631   }
2632   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2633   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorLoad, viewer, 0, 0, 0));
2634   if (ishdf5) {
2635 #if defined(PETSC_HAVE_HDF5)
2636     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2637 #else
2638     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2639 #endif
2640   }
2641   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorLoad, viewer, 0, 0, 0));
2642   PetscFunctionReturn(PETSC_SUCCESS);
2643 }
2644 
2645 /*@
2646   DMPlexLocalVectorLoad - Loads on-disk vector data into a local vector
2647 
2648   Collective
2649 
2650   Input Parameters:
2651 + dm        - The `DM` that represents the topology
2652 . viewer    - The `PetscViewer` that represents the on-disk vector data
2653 . sectiondm - The `DM` that contains the local section on which vec is defined, can be `NULL`
2654 . sf        - The `PetscSF` that migrates the on-disk vector data into vec
2655 - vec       - The local vector to set values of
2656 
2657   Level: advanced
2658 
2659   Notes:
2660   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.
2661 
2662   Calling sequence:
2663 .vb
2664        DMCreate(PETSC_COMM_WORLD, &dm);
2665        DMSetType(dm, DMPLEX);
2666        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2667        DMPlexTopologyLoad(dm, viewer, &sfX);
2668        DMClone(dm, &sectiondm);
2669        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2670        DMPlexSectionLoad(dm, viewer, sectiondm, sfX, NULL, &lsf);
2671        DMGetLocalVector(sectiondm, &vec);
2672        PetscObjectSetName((PetscObject)vec, "vec_name");
2673        DMPlexLocalVectorLoad(dm, viewer, sectiondm, lsf, vec);
2674        DMRestoreLocalVector(sectiondm, &vec);
2675        PetscSFDestroy(&lsf);
2676        PetscSFDestroy(&sfX);
2677        DMDestroy(&sectiondm);
2678        DMDestroy(&dm);
2679 .ve
2680 
2681 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`,
2682           `PetscSF`, `PetscViewer`
2683 @*/
2684 PetscErrorCode DMPlexLocalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2685 {
2686   PetscBool ishdf5;
2687 
2688   PetscFunctionBegin;
2689   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2690   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2691   if (!sectiondm) sectiondm = dm;
2692   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2693   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2694   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2695   /* Check consistency */
2696   {
2697     PetscSection section;
2698     PetscBool    includesConstraints;
2699     PetscInt     m, m1;
2700 
2701     PetscCall(VecGetLocalSize(vec, &m1));
2702     PetscCall(DMGetLocalSection(sectiondm, &section));
2703     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2704     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2705     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2706     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2707   }
2708   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2709   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorLoad, viewer, 0, 0, 0));
2710   if (ishdf5) {
2711 #if defined(PETSC_HAVE_HDF5)
2712     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2713 #else
2714     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2715 #endif
2716   }
2717   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorLoad, viewer, 0, 0, 0));
2718   PetscFunctionReturn(PETSC_SUCCESS);
2719 }
2720 
2721 PetscErrorCode DMDestroy_Plex(DM dm)
2722 {
2723   DM_Plex *mesh = (DM_Plex *)dm->data;
2724 
2725   PetscFunctionBegin;
2726   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMSetUpGLVisViewer_C", NULL));
2727   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertBoundaryValues_C", NULL));
2728   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMCreateNeumannOverlap_C", NULL));
2729   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMInterpolateSolution_C", NULL));
2730   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertTimeDerivativeBoundaryValues_C", NULL));
2731   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", NULL));
2732   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeGetDefault_C", NULL));
2733   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeSetDefault_C", NULL));
2734   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "MatComputeNeumannOverlap_C", NULL));
2735   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderGetDefault_C", NULL));
2736   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderSetDefault_C", NULL));
2737   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionGetDefault_C", NULL));
2738   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionSetDefault_C", NULL));
2739   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionGetType_C", NULL));
2740   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionSetType_C", NULL));
2741   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", NULL));
2742   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexSetOverlap_C", NULL));
2743   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetUseCeed_C", NULL));
2744   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexSetUseCeed_C", NULL));
2745   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMGetIsoperiodicPointSF_C", NULL));
2746   if (--mesh->refct > 0) PetscFunctionReturn(PETSC_SUCCESS);
2747   PetscCall(PetscSectionDestroy(&mesh->coneSection));
2748   PetscCall(PetscFree(mesh->cones));
2749   PetscCall(PetscFree(mesh->coneOrientations));
2750   PetscCall(PetscSectionDestroy(&mesh->supportSection));
2751   PetscCall(PetscSectionDestroy(&mesh->subdomainSection));
2752   PetscCall(PetscFree(mesh->supports));
2753   PetscCall(PetscFree(mesh->cellTypes));
2754   PetscCall(DMPlexTransformDestroy(&mesh->tr));
2755   PetscCall(PetscFree(mesh->tetgenOpts));
2756   PetscCall(PetscFree(mesh->triangleOpts));
2757   PetscCall(PetscFree(mesh->transformType));
2758   PetscCall(PetscFree(mesh->distributionName));
2759   PetscCall(PetscPartitionerDestroy(&mesh->partitioner));
2760   PetscCall(DMLabelDestroy(&mesh->subpointMap));
2761   PetscCall(ISDestroy(&mesh->subpointIS));
2762   PetscCall(ISDestroy(&mesh->globalVertexNumbers));
2763   PetscCall(ISDestroy(&mesh->globalCellNumbers));
2764   if (mesh->periodic.face_sfs) {
2765     for (PetscInt i = 0; i < mesh->periodic.num_face_sfs; i++) PetscCall(PetscSFDestroy(&mesh->periodic.face_sfs[i]));
2766     PetscCall(PetscFree(mesh->periodic.face_sfs));
2767   }
2768   PetscCall(PetscSFDestroy(&mesh->periodic.composed_sf));
2769   if (mesh->periodic.periodic_points) {
2770     for (PetscInt i = 0; i < mesh->periodic.num_face_sfs; i++) PetscCall(ISDestroy(&mesh->periodic.periodic_points[i]));
2771     PetscCall(PetscFree(mesh->periodic.periodic_points));
2772   }
2773   if (mesh->periodic.transform) PetscCall(PetscFree(mesh->periodic.transform));
2774   PetscCall(PetscSectionDestroy(&mesh->anchorSection));
2775   PetscCall(ISDestroy(&mesh->anchorIS));
2776   PetscCall(PetscSectionDestroy(&mesh->parentSection));
2777   PetscCall(PetscFree(mesh->parents));
2778   PetscCall(PetscFree(mesh->childIDs));
2779   PetscCall(PetscSectionDestroy(&mesh->childSection));
2780   PetscCall(PetscFree(mesh->children));
2781   PetscCall(DMDestroy(&mesh->referenceTree));
2782   PetscCall(PetscGridHashDestroy(&mesh->lbox));
2783   PetscCall(PetscFree(mesh->neighbors));
2784   if (mesh->metricCtx) PetscCall(PetscFree(mesh->metricCtx));
2785   if (mesh->nonempty_comm != MPI_COMM_NULL && mesh->nonempty_comm != MPI_COMM_SELF) PetscCallMPI(MPI_Comm_free(&mesh->nonempty_comm));
2786   /* This was originally freed in DMDestroy(), but that prevents reference counting of backend objects */
2787   PetscCall(PetscFree(mesh));
2788   PetscFunctionReturn(PETSC_SUCCESS);
2789 }
2790 
2791 PetscErrorCode DMCreateMatrix_Plex(DM dm, Mat *J)
2792 {
2793   PetscSection           sectionGlobal, sectionLocal;
2794   PetscInt               bs = -1, mbs;
2795   PetscInt               localSize, localStart = 0;
2796   PetscBool              isShell, isBlock, isSeqBlock, isMPIBlock, isSymBlock, isSymSeqBlock, isSymMPIBlock, isMatIS;
2797   MatType                mtype;
2798   ISLocalToGlobalMapping ltog;
2799 
2800   PetscFunctionBegin;
2801   PetscCall(MatInitializePackage());
2802   mtype = dm->mattype;
2803   PetscCall(DMGetLocalSection(dm, &sectionLocal));
2804   PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
2805   /* PetscCall(PetscSectionGetStorageSize(sectionGlobal, &localSize)); */
2806   PetscCall(PetscSectionGetConstrainedStorageSize(sectionGlobal, &localSize));
2807   PetscCallMPI(MPI_Exscan(&localSize, &localStart, 1, MPIU_INT, MPI_SUM, PetscObjectComm((PetscObject)dm)));
2808   PetscCall(MatCreate(PetscObjectComm((PetscObject)dm), J));
2809   PetscCall(MatSetSizes(*J, localSize, localSize, PETSC_DETERMINE, PETSC_DETERMINE));
2810   PetscCall(MatSetType(*J, mtype));
2811   PetscCall(MatSetFromOptions(*J));
2812   PetscCall(MatGetBlockSize(*J, &mbs));
2813   if (mbs > 1) bs = mbs;
2814   PetscCall(PetscStrcmp(mtype, MATSHELL, &isShell));
2815   PetscCall(PetscStrcmp(mtype, MATBAIJ, &isBlock));
2816   PetscCall(PetscStrcmp(mtype, MATSEQBAIJ, &isSeqBlock));
2817   PetscCall(PetscStrcmp(mtype, MATMPIBAIJ, &isMPIBlock));
2818   PetscCall(PetscStrcmp(mtype, MATSBAIJ, &isSymBlock));
2819   PetscCall(PetscStrcmp(mtype, MATSEQSBAIJ, &isSymSeqBlock));
2820   PetscCall(PetscStrcmp(mtype, MATMPISBAIJ, &isSymMPIBlock));
2821   PetscCall(PetscStrcmp(mtype, MATIS, &isMatIS));
2822   if (!isShell) {
2823     // There are three states with pblocks, since block starts can have no dofs:
2824     // UNKNOWN) New Block:   An open block has been signalled by pblocks[p] == 1
2825     // TRUE)    Block Start: The first entry in a block has been added
2826     // FALSE)   Block Add:   An additional block entry has been added, since pblocks[p] == 0
2827     PetscBT         blst;
2828     PetscBool3      bstate     = PETSC_BOOL3_UNKNOWN;
2829     PetscBool       fillMatrix = (PetscBool)(!dm->prealloc_only && !isMatIS);
2830     const PetscInt *perm       = NULL;
2831     PetscInt       *dnz, *onz, *dnzu, *onzu, bsLocal[2], bsMinMax[2], *pblocks;
2832     PetscInt        pStart, pEnd, dof, cdof, num_fields;
2833 
2834     PetscCall(DMGetLocalToGlobalMapping(dm, &ltog));
2835     PetscCall(PetscSectionGetBlockStarts(sectionLocal, &blst));
2836     if (sectionLocal->perm) PetscCall(ISGetIndices(sectionLocal->perm, &perm));
2837 
2838     PetscCall(PetscCalloc1(localSize, &pblocks));
2839     PetscCall(PetscSectionGetChart(sectionGlobal, &pStart, &pEnd));
2840     PetscCall(PetscSectionGetNumFields(sectionGlobal, &num_fields));
2841     // We need to process in the permuted order to get block sizes right
2842     for (PetscInt point = pStart; point < pEnd; ++point) {
2843       const PetscInt p = perm ? perm[point] : point;
2844 
2845       switch (dm->blocking_type) {
2846       case DM_BLOCKING_TOPOLOGICAL_POINT: { // One block per topological point
2847         PetscInt bdof, offset;
2848 
2849         PetscCall(PetscSectionGetDof(sectionGlobal, p, &dof));
2850         PetscCall(PetscSectionGetOffset(sectionGlobal, p, &offset));
2851         PetscCall(PetscSectionGetConstraintDof(sectionGlobal, p, &cdof));
2852         if (blst && PetscBTLookup(blst, p)) bstate = PETSC_BOOL3_UNKNOWN;
2853         if (dof > 0) {
2854           // State change
2855           if (bstate == PETSC_BOOL3_UNKNOWN) bstate = PETSC_BOOL3_TRUE;
2856           else if (bstate == PETSC_BOOL3_TRUE && blst && !PetscBTLookup(blst, p)) bstate = PETSC_BOOL3_FALSE;
2857 
2858           for (PetscInt i = 0; i < dof - cdof; ++i) pblocks[offset - localStart + i] = dof - cdof;
2859           // Signal block concatenation
2860           if (bstate == PETSC_BOOL3_FALSE && dof - cdof) pblocks[offset - localStart] = -(dof - cdof);
2861         }
2862         dof  = dof < 0 ? -(dof + 1) : dof;
2863         bdof = cdof && (dof - cdof) ? 1 : dof;
2864         if (dof) {
2865           if (bs < 0) {
2866             bs = bdof;
2867           } else if (bs != bdof) {
2868             bs = 1;
2869           }
2870         }
2871       } break;
2872       case DM_BLOCKING_FIELD_NODE: {
2873         for (PetscInt field = 0; field < num_fields; field++) {
2874           PetscInt num_comp, bdof, offset;
2875           PetscCall(PetscSectionGetFieldComponents(sectionGlobal, field, &num_comp));
2876           PetscCall(PetscSectionGetFieldDof(sectionGlobal, p, field, &dof));
2877           if (dof < 0) continue;
2878           PetscCall(PetscSectionGetFieldOffset(sectionGlobal, p, field, &offset));
2879           PetscCall(PetscSectionGetFieldConstraintDof(sectionGlobal, p, field, &cdof));
2880           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);
2881           PetscInt num_nodes = dof / num_comp;
2882           for (PetscInt i = 0; i < dof - cdof; i++) pblocks[offset - localStart + i] = (dof - cdof) / num_nodes;
2883           // Handle possibly constant block size (unlikely)
2884           bdof = cdof && (dof - cdof) ? 1 : dof;
2885           if (dof) {
2886             if (bs < 0) {
2887               bs = bdof;
2888             } else if (bs != bdof) {
2889               bs = 1;
2890             }
2891           }
2892         }
2893       } break;
2894       }
2895     }
2896     if (sectionLocal->perm) PetscCall(ISRestoreIndices(sectionLocal->perm, &perm));
2897     /* Must have same blocksize on all procs (some might have no points) */
2898     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs;
2899     bsLocal[1] = bs;
2900     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
2901     if (bsMinMax[0] != bsMinMax[1]) bs = 1;
2902     else bs = bsMinMax[0];
2903     bs = PetscMax(1, bs);
2904     PetscCall(MatSetLocalToGlobalMapping(*J, ltog, ltog));
2905     if (dm->prealloc_skip) { // User will likely use MatSetPreallocationCOO(), but still set structural parameters
2906       PetscCall(MatSetBlockSize(*J, bs));
2907       PetscCall(MatSetUp(*J));
2908     } else {
2909       PetscCall(PetscCalloc4(localSize / bs, &dnz, localSize / bs, &onz, localSize / bs, &dnzu, localSize / bs, &onzu));
2910       PetscCall(DMPlexPreallocateOperator(dm, bs, dnz, onz, dnzu, onzu, *J, fillMatrix));
2911       PetscCall(PetscFree4(dnz, onz, dnzu, onzu));
2912     }
2913     if (pblocks) { // Consolidate blocks
2914       PetscInt nblocks = 0;
2915       pblocks[0]       = PetscAbs(pblocks[0]);
2916       for (PetscInt i = 0; i < localSize; i += PetscMax(1, pblocks[i])) {
2917         if (pblocks[i] == 0) continue;
2918         // Negative block size indicates the blocks should be concatenated
2919         if (pblocks[i] < 0) {
2920           pblocks[i] = -pblocks[i];
2921           pblocks[nblocks - 1] += pblocks[i];
2922         } else {
2923           pblocks[nblocks++] = pblocks[i]; // nblocks always <= i
2924         }
2925         for (PetscInt j = 1; j < pblocks[i]; j++)
2926           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);
2927       }
2928       PetscCall(MatSetVariableBlockSizes(*J, nblocks, pblocks));
2929     }
2930     PetscCall(PetscFree(pblocks));
2931   }
2932   PetscCall(MatSetDM(*J, dm));
2933   PetscFunctionReturn(PETSC_SUCCESS);
2934 }
2935 
2936 /*@
2937   DMPlexGetSubdomainSection - Returns the section associated with the subdomain
2938 
2939   Not Collective
2940 
2941   Input Parameter:
2942 . dm - The `DMPLEX`
2943 
2944   Output Parameter:
2945 . subsection - The subdomain section
2946 
2947   Level: developer
2948 
2949 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `PetscSection`
2950 @*/
2951 PetscErrorCode DMPlexGetSubdomainSection(DM dm, PetscSection *subsection)
2952 {
2953   DM_Plex *mesh = (DM_Plex *)dm->data;
2954 
2955   PetscFunctionBegin;
2956   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2957   if (!mesh->subdomainSection) {
2958     PetscSection section;
2959     PetscSF      sf;
2960 
2961     PetscCall(PetscSFCreate(PETSC_COMM_SELF, &sf));
2962     PetscCall(DMGetLocalSection(dm, &section));
2963     PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_TRUE, PETSC_FALSE, PETSC_TRUE, &mesh->subdomainSection));
2964     PetscCall(PetscSFDestroy(&sf));
2965   }
2966   *subsection = mesh->subdomainSection;
2967   PetscFunctionReturn(PETSC_SUCCESS);
2968 }
2969 
2970 /*@
2971   DMPlexGetChart - Return the interval for all mesh points [`pStart`, `pEnd`)
2972 
2973   Not Collective
2974 
2975   Input Parameter:
2976 . dm - The `DMPLEX`
2977 
2978   Output Parameters:
2979 + pStart - The first mesh point
2980 - pEnd   - The upper bound for mesh points
2981 
2982   Level: beginner
2983 
2984 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetChart()`
2985 @*/
2986 PetscErrorCode DMPlexGetChart(DM dm, PetscInt *pStart, PetscInt *pEnd)
2987 {
2988   DM_Plex *mesh = (DM_Plex *)dm->data;
2989 
2990   PetscFunctionBegin;
2991   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2992   if (mesh->tr) PetscCall(DMPlexTransformGetChart(mesh->tr, pStart, pEnd));
2993   else PetscCall(PetscSectionGetChart(mesh->coneSection, pStart, pEnd));
2994   PetscFunctionReturn(PETSC_SUCCESS);
2995 }
2996 
2997 /*@
2998   DMPlexSetChart - Set the interval for all mesh points [`pStart`, `pEnd`)
2999 
3000   Not Collective
3001 
3002   Input Parameters:
3003 + dm     - The `DMPLEX`
3004 . pStart - The first mesh point
3005 - pEnd   - The upper bound for mesh points
3006 
3007   Level: beginner
3008 
3009 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetChart()`
3010 @*/
3011 PetscErrorCode DMPlexSetChart(DM dm, PetscInt pStart, PetscInt pEnd)
3012 {
3013   DM_Plex *mesh = (DM_Plex *)dm->data;
3014 
3015   PetscFunctionBegin;
3016   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3017   PetscCall(PetscSectionSetChart(mesh->coneSection, pStart, pEnd));
3018   PetscCall(PetscSectionSetChart(mesh->supportSection, pStart, pEnd));
3019   PetscCall(PetscFree(mesh->cellTypes));
3020   PetscFunctionReturn(PETSC_SUCCESS);
3021 }
3022 
3023 /*@
3024   DMPlexGetConeSize - Return the number of in-edges for this point in the DAG
3025 
3026   Not Collective
3027 
3028   Input Parameters:
3029 + dm - The `DMPLEX`
3030 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3031 
3032   Output Parameter:
3033 . size - The cone size for point `p`
3034 
3035   Level: beginner
3036 
3037 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
3038 @*/
3039 PetscErrorCode DMPlexGetConeSize(DM dm, PetscInt p, PetscInt *size)
3040 {
3041   DM_Plex *mesh = (DM_Plex *)dm->data;
3042 
3043   PetscFunctionBegin;
3044   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3045   PetscAssertPointer(size, 3);
3046   if (mesh->tr) PetscCall(DMPlexTransformGetConeSize(mesh->tr, p, size));
3047   else PetscCall(PetscSectionGetDof(mesh->coneSection, p, size));
3048   PetscFunctionReturn(PETSC_SUCCESS);
3049 }
3050 
3051 /*@
3052   DMPlexSetConeSize - Set the number of in-edges for this point in the DAG
3053 
3054   Not Collective
3055 
3056   Input Parameters:
3057 + dm   - The `DMPLEX`
3058 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3059 - size - The cone size for point `p`
3060 
3061   Level: beginner
3062 
3063   Note:
3064   This should be called after `DMPlexSetChart()`.
3065 
3066 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetCone()`, `DMPlexCreate()`, `DMPlexGetConeSize()`, `DMPlexSetChart()`
3067 @*/
3068 PetscErrorCode DMPlexSetConeSize(DM dm, PetscInt p, PetscInt size)
3069 {
3070   DM_Plex *mesh = (DM_Plex *)dm->data;
3071 
3072   PetscFunctionBegin;
3073   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3074   PetscCheck(!mesh->tr, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Cannot call DMPlexSetConeSize() on a mesh with a transform defined.");
3075   PetscCall(PetscSectionSetDof(mesh->coneSection, p, size));
3076   PetscFunctionReturn(PETSC_SUCCESS);
3077 }
3078 
3079 /*@C
3080   DMPlexGetCone - Return the points on the in-edges for this point in the DAG
3081 
3082   Not Collective
3083 
3084   Input Parameters:
3085 + dm - The `DMPLEX`
3086 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3087 
3088   Output Parameter:
3089 . cone - An array of points which are on the in-edges for point `p`, the length of `cone` is the result of `DMPlexGetConeSize()`
3090 
3091   Level: beginner
3092 
3093   Fortran Notes:
3094   `cone` must be declared with
3095 .vb
3096   PetscInt, pointer :: cone(:)
3097 .ve
3098 
3099   You must also call `DMPlexRestoreCone()` after you finish using the array.
3100   `DMPlexRestoreCone()` is not needed/available in C.
3101 
3102 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSize()`, `DMPlexSetCone()`, `DMPlexGetConeTuple()`, `DMPlexSetChart()`, `DMPlexRestoreCone()`
3103 @*/
3104 PetscErrorCode DMPlexGetCone(DM dm, PetscInt p, const PetscInt *cone[])
3105 {
3106   DM_Plex *mesh = (DM_Plex *)dm->data;
3107   PetscInt off;
3108 
3109   PetscFunctionBegin;
3110   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3111   PetscAssertPointer(cone, 3);
3112   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3113   *cone = PetscSafePointerPlusOffset(mesh->cones, off);
3114   PetscFunctionReturn(PETSC_SUCCESS);
3115 }
3116 
3117 /*@
3118   DMPlexGetConeTuple - Return the points on the in-edges of several points in the DAG
3119 
3120   Not Collective
3121 
3122   Input Parameters:
3123 + dm - The `DMPLEX`
3124 - p  - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3125 
3126   Output Parameters:
3127 + pConesSection - `PetscSection` describing the layout of `pCones`
3128 - pCones        - An `IS` containing the points which are on the in-edges for the point set `p`
3129 
3130   Level: intermediate
3131 
3132 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeRecursive()`, `DMPlexSetChart()`, `PetscSection`, `IS`
3133 @*/
3134 PetscErrorCode DMPlexGetConeTuple(DM dm, IS p, PetscSection *pConesSection, IS *pCones)
3135 {
3136   PetscSection cs, newcs;
3137   PetscInt    *cones;
3138   PetscInt    *newarr = NULL;
3139   PetscInt     n;
3140 
3141   PetscFunctionBegin;
3142   PetscCall(DMPlexGetCones(dm, &cones));
3143   PetscCall(DMPlexGetConeSection(dm, &cs));
3144   PetscCall(PetscSectionExtractDofsFromArray(cs, MPIU_INT, cones, p, &newcs, pCones ? ((void **)&newarr) : NULL));
3145   if (pConesSection) *pConesSection = newcs;
3146   if (pCones) {
3147     PetscCall(PetscSectionGetStorageSize(newcs, &n));
3148     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)p), n, newarr, PETSC_OWN_POINTER, pCones));
3149   }
3150   PetscFunctionReturn(PETSC_SUCCESS);
3151 }
3152 
3153 /*@
3154   DMPlexGetConeRecursiveVertices - Expand each given point into its cone points and do that recursively until we end up just with vertices.
3155 
3156   Not Collective
3157 
3158   Input Parameters:
3159 + dm     - The `DMPLEX`
3160 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3161 
3162   Output Parameter:
3163 . expandedPoints - An `IS` containing the of vertices recursively expanded from input points
3164 
3165   Level: advanced
3166 
3167   Notes:
3168   Like `DMPlexGetConeRecursive()` but returns only the 0-depth `IS` (i.e. vertices only) and no sections.
3169 
3170   There is no corresponding Restore function, just call `ISDestroy()` on the returned `IS` to deallocate.
3171 
3172 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexRestoreConeRecursive()`,
3173           `DMPlexGetDepth()`, `IS`
3174 @*/
3175 PetscErrorCode DMPlexGetConeRecursiveVertices(DM dm, IS points, IS *expandedPoints)
3176 {
3177   IS      *expandedPointsAll;
3178   PetscInt depth;
3179 
3180   PetscFunctionBegin;
3181   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3182   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
3183   PetscAssertPointer(expandedPoints, 3);
3184   PetscCall(DMPlexGetConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
3185   *expandedPoints = expandedPointsAll[0];
3186   PetscCall(PetscObjectReference((PetscObject)expandedPointsAll[0]));
3187   PetscCall(DMPlexRestoreConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
3188   PetscFunctionReturn(PETSC_SUCCESS);
3189 }
3190 
3191 /*@
3192   DMPlexGetConeRecursive - Expand each given point into its cone points and do that recursively until we end up just with vertices
3193   (DAG points of depth 0, i.e., without cones).
3194 
3195   Not Collective
3196 
3197   Input Parameters:
3198 + dm     - The `DMPLEX`
3199 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3200 
3201   Output Parameters:
3202 + depth          - (optional) Size of the output arrays, equal to `DMPLEX` depth, returned by `DMPlexGetDepth()`
3203 . expandedPoints - (optional) An array of index sets with recursively expanded cones
3204 - sections       - (optional) An array of sections which describe mappings from points to their cone points
3205 
3206   Level: advanced
3207 
3208   Notes:
3209   Like `DMPlexGetConeTuple()` but recursive.
3210 
3211   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.
3212   For example, for d=0 it contains only vertices, for d=1 it can contain vertices and edges, etc.
3213 
3214   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\:
3215   (1) DAG points in `expandedPoints`[d+1] with `depth` d+1 to their cone points in `expandedPoints`[d];
3216   (2) DAG points in `expandedPoints`[d+1] with `depth` in [0,d] to the same points in `expandedPoints`[d].
3217 
3218 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexRestoreConeRecursive()`, `DMPlexGetConeRecursiveVertices()`,
3219           `DMPlexGetDepth()`, `PetscSection`, `IS`
3220 @*/
3221 PetscErrorCode DMPlexGetConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
3222 {
3223   const PetscInt *arr0 = NULL, *cone = NULL;
3224   PetscInt       *arr = NULL, *newarr = NULL;
3225   PetscInt        d, depth_, i, n, newn, cn, co, start, end;
3226   IS             *expandedPoints_;
3227   PetscSection   *sections_;
3228 
3229   PetscFunctionBegin;
3230   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3231   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
3232   if (depth) PetscAssertPointer(depth, 3);
3233   if (expandedPoints) PetscAssertPointer(expandedPoints, 4);
3234   if (sections) PetscAssertPointer(sections, 5);
3235   PetscCall(ISGetLocalSize(points, &n));
3236   PetscCall(ISGetIndices(points, &arr0));
3237   PetscCall(DMPlexGetDepth(dm, &depth_));
3238   PetscCall(PetscCalloc1(depth_, &expandedPoints_));
3239   PetscCall(PetscCalloc1(depth_, &sections_));
3240   arr = (PetscInt *)arr0; /* this is ok because first generation of arr is not modified */
3241   for (d = depth_ - 1; d >= 0; d--) {
3242     PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &sections_[d]));
3243     PetscCall(PetscSectionSetChart(sections_[d], 0, n));
3244     for (i = 0; i < n; i++) {
3245       PetscCall(DMPlexGetDepthStratum(dm, d + 1, &start, &end));
3246       if (arr[i] >= start && arr[i] < end) {
3247         PetscCall(DMPlexGetConeSize(dm, arr[i], &cn));
3248         PetscCall(PetscSectionSetDof(sections_[d], i, cn));
3249       } else {
3250         PetscCall(PetscSectionSetDof(sections_[d], i, 1));
3251       }
3252     }
3253     PetscCall(PetscSectionSetUp(sections_[d]));
3254     PetscCall(PetscSectionGetStorageSize(sections_[d], &newn));
3255     PetscCall(PetscMalloc1(newn, &newarr));
3256     for (i = 0; i < n; i++) {
3257       PetscCall(PetscSectionGetDof(sections_[d], i, &cn));
3258       PetscCall(PetscSectionGetOffset(sections_[d], i, &co));
3259       if (cn > 1) {
3260         PetscCall(DMPlexGetCone(dm, arr[i], &cone));
3261         PetscCall(PetscMemcpy(&newarr[co], cone, cn * sizeof(PetscInt)));
3262       } else {
3263         newarr[co] = arr[i];
3264       }
3265     }
3266     PetscCall(ISCreateGeneral(PETSC_COMM_SELF, newn, newarr, PETSC_OWN_POINTER, &expandedPoints_[d]));
3267     arr = newarr;
3268     n   = newn;
3269   }
3270   PetscCall(ISRestoreIndices(points, &arr0));
3271   *depth = depth_;
3272   if (expandedPoints) *expandedPoints = expandedPoints_;
3273   else {
3274     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&expandedPoints_[d]));
3275     PetscCall(PetscFree(expandedPoints_));
3276   }
3277   if (sections) *sections = sections_;
3278   else {
3279     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&sections_[d]));
3280     PetscCall(PetscFree(sections_));
3281   }
3282   PetscFunctionReturn(PETSC_SUCCESS);
3283 }
3284 
3285 /*@
3286   DMPlexRestoreConeRecursive - Deallocates arrays created by `DMPlexGetConeRecursive()`
3287 
3288   Not Collective
3289 
3290   Input Parameters:
3291 + dm     - The `DMPLEX`
3292 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3293 
3294   Output Parameters:
3295 + depth          - (optional) Size of the output arrays, equal to `DMPLEX` depth, returned by `DMPlexGetDepth()`
3296 . expandedPoints - (optional) An array of recursively expanded cones
3297 - sections       - (optional) An array of sections which describe mappings from points to their cone points
3298 
3299   Level: advanced
3300 
3301   Note:
3302   See `DMPlexGetConeRecursive()`
3303 
3304 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexGetConeRecursiveVertices()`,
3305           `DMPlexGetDepth()`, `IS`, `PetscSection`
3306 @*/
3307 PetscErrorCode DMPlexRestoreConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
3308 {
3309   PetscInt d, depth_;
3310 
3311   PetscFunctionBegin;
3312   PetscCall(DMPlexGetDepth(dm, &depth_));
3313   PetscCheck(!depth || *depth == depth_, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "depth changed since last call to DMPlexGetConeRecursive");
3314   if (depth) *depth = 0;
3315   if (expandedPoints) {
3316     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&((*expandedPoints)[d])));
3317     PetscCall(PetscFree(*expandedPoints));
3318   }
3319   if (sections) {
3320     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&((*sections)[d])));
3321     PetscCall(PetscFree(*sections));
3322   }
3323   PetscFunctionReturn(PETSC_SUCCESS);
3324 }
3325 
3326 /*@
3327   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
3328 
3329   Not Collective
3330 
3331   Input Parameters:
3332 + dm   - The `DMPLEX`
3333 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3334 - cone - An array of points which are on the in-edges for point `p`, its length must have been previously provided with `DMPlexSetConeSize()`
3335 
3336   Level: beginner
3337 
3338   Note:
3339   This should be called after all calls to `DMPlexSetConeSize()` and `DMSetUp()`.
3340 
3341 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`, `DMPlexSetSupport()`, `DMPlexSetSupportSize()`
3342 @*/
3343 PetscErrorCode DMPlexSetCone(DM dm, PetscInt p, const PetscInt cone[])
3344 {
3345   DM_Plex *mesh = (DM_Plex *)dm->data;
3346   PetscInt dof, off, c;
3347 
3348   PetscFunctionBegin;
3349   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3350   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3351   if (dof) PetscAssertPointer(cone, 3);
3352   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3353   if (PetscDefined(USE_DEBUG)) {
3354     PetscInt pStart, pEnd;
3355     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3356     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);
3357     for (c = 0; c < dof; ++c) {
3358       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);
3359       mesh->cones[off + c] = cone[c];
3360     }
3361   } else {
3362     for (c = 0; c < dof; ++c) mesh->cones[off + c] = cone[c];
3363   }
3364   PetscFunctionReturn(PETSC_SUCCESS);
3365 }
3366 
3367 /*@C
3368   DMPlexGetConeOrientation - Return the orientations on the in-edges for this point in the DAG
3369 
3370   Not Collective
3371 
3372   Input Parameters:
3373 + dm - The `DMPLEX`
3374 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3375 
3376   Output Parameter:
3377 . coneOrientation - An array of orientations which are on the in-edges for point `p`. An orientation is an
3378                     integer giving the prescription for cone traversal. Its length is given by the result of `DMPlexSetConeSize()`
3379 
3380   Level: beginner
3381 
3382   Note:
3383   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3384   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3385   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3386   with the identity.
3387 
3388   Fortran Notes:
3389   You must call `DMPlexRestoreConeOrientation()` after you finish using the returned array.
3390   `DMPlexRestoreConeOrientation()` is not needed/available in C.
3391 
3392 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetConeSize()`, `DMPolytopeTypeComposeOrientation()`, `DMPolytopeTypeComposeOrientationInv()`,
3393           `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetCone()`, `DMPlexSetChart()`
3394 @*/
3395 PetscErrorCode DMPlexGetConeOrientation(DM dm, PetscInt p, const PetscInt *coneOrientation[])
3396 {
3397   DM_Plex *mesh = (DM_Plex *)dm->data;
3398   PetscInt off;
3399 
3400   PetscFunctionBegin;
3401   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3402   if (PetscDefined(USE_DEBUG)) {
3403     PetscInt dof;
3404     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3405     if (dof) PetscAssertPointer(coneOrientation, 3);
3406   }
3407   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3408 
3409   *coneOrientation = &mesh->coneOrientations[off];
3410   PetscFunctionReturn(PETSC_SUCCESS);
3411 }
3412 
3413 /*@
3414   DMPlexSetConeOrientation - Set the orientations on the in-edges for this point in the DAG
3415 
3416   Not Collective
3417 
3418   Input Parameters:
3419 + dm              - The `DMPLEX`
3420 . p               - The point, which must lie in the chart set with `DMPlexSetChart()`
3421 - coneOrientation - An array of orientations. Its length is given by the result of `DMPlexSetConeSize()`
3422 
3423   Level: beginner
3424 
3425   Notes:
3426   This should be called after all calls to `DMPlexSetConeSize()` and `DMSetUp()`.
3427 
3428   The meaning of coneOrientation is detailed in `DMPlexGetConeOrientation()`.
3429 
3430 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetConeOrientation()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3431 @*/
3432 PetscErrorCode DMPlexSetConeOrientation(DM dm, PetscInt p, const PetscInt coneOrientation[])
3433 {
3434   DM_Plex *mesh = (DM_Plex *)dm->data;
3435   PetscInt pStart, pEnd;
3436   PetscInt dof, off, c;
3437 
3438   PetscFunctionBegin;
3439   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3440   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3441   if (dof) PetscAssertPointer(coneOrientation, 3);
3442   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3443   if (PetscDefined(USE_DEBUG)) {
3444     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3445     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);
3446     for (c = 0; c < dof; ++c) {
3447       PetscInt cdof, o = coneOrientation[c];
3448 
3449       PetscCall(PetscSectionGetDof(mesh->coneSection, mesh->cones[off + c], &cdof));
3450       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);
3451       mesh->coneOrientations[off + c] = o;
3452     }
3453   } else {
3454     for (c = 0; c < dof; ++c) mesh->coneOrientations[off + c] = coneOrientation[c];
3455   }
3456   PetscFunctionReturn(PETSC_SUCCESS);
3457 }
3458 
3459 /*@
3460   DMPlexInsertCone - Insert a point into the in-edges for the point p in the DAG
3461 
3462   Not Collective
3463 
3464   Input Parameters:
3465 + dm        - The `DMPLEX`
3466 . p         - The point, which must lie in the chart set with `DMPlexSetChart()`
3467 . conePos   - The local index in the cone where the point should be put
3468 - conePoint - The mesh point to insert
3469 
3470   Level: beginner
3471 
3472 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3473 @*/
3474 PetscErrorCode DMPlexInsertCone(DM dm, PetscInt p, PetscInt conePos, PetscInt conePoint)
3475 {
3476   DM_Plex *mesh = (DM_Plex *)dm->data;
3477   PetscInt pStart, pEnd;
3478   PetscInt dof, off;
3479 
3480   PetscFunctionBegin;
3481   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3482   if (PetscDefined(USE_DEBUG)) {
3483     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3484     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);
3485     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);
3486     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3487     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);
3488   }
3489   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3490   mesh->cones[off + conePos] = conePoint;
3491   PetscFunctionReturn(PETSC_SUCCESS);
3492 }
3493 
3494 /*@
3495   DMPlexInsertConeOrientation - Insert a point orientation for the in-edge for the point p in the DAG
3496 
3497   Not Collective
3498 
3499   Input Parameters:
3500 + dm              - The `DMPLEX`
3501 . p               - The point, which must lie in the chart set with `DMPlexSetChart()`
3502 . conePos         - The local index in the cone where the point should be put
3503 - coneOrientation - The point orientation to insert
3504 
3505   Level: beginner
3506 
3507   Note:
3508   The meaning of coneOrientation values is detailed in `DMPlexGetConeOrientation()`.
3509 
3510 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3511 @*/
3512 PetscErrorCode DMPlexInsertConeOrientation(DM dm, PetscInt p, PetscInt conePos, PetscInt coneOrientation)
3513 {
3514   DM_Plex *mesh = (DM_Plex *)dm->data;
3515   PetscInt pStart, pEnd;
3516   PetscInt dof, off;
3517 
3518   PetscFunctionBegin;
3519   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3520   if (PetscDefined(USE_DEBUG)) {
3521     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3522     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);
3523     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3524     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);
3525   }
3526   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3527   mesh->coneOrientations[off + conePos] = coneOrientation;
3528   PetscFunctionReturn(PETSC_SUCCESS);
3529 }
3530 
3531 /*@C
3532   DMPlexGetOrientedCone - Return the points and orientations on the in-edges for this point in the DAG
3533 
3534   Not collective
3535 
3536   Input Parameters:
3537 + dm - The DMPlex
3538 - p  - The point, which must lie in the chart set with DMPlexSetChart()
3539 
3540   Output Parameters:
3541 + cone - An array of points which are on the in-edges for point `p`
3542 - ornt - An array of orientations which are on the in-edges for point `p`. An orientation is an
3543          integer giving the prescription for cone traversal.
3544 
3545   Level: beginner
3546 
3547   Notes:
3548   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3549   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3550   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3551   with the identity.
3552 
3553   You must also call `DMPlexRestoreOrientedCone()` after you finish using the returned array.
3554 
3555   Fortran Notes:
3556   `cone` and `ornt` must be declared with
3557 .vb
3558   PetscInt, pointer :: cone(:)
3559   PetscInt, pointer :: ornt(:)
3560 .ve
3561 
3562 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreOrientedCone()`, `DMPlexGetConeSize()`, `DMPlexGetCone()`, `DMPlexGetChart()`
3563 @*/
3564 PetscErrorCode DMPlexGetOrientedCone(DM dm, PetscInt p, const PetscInt *cone[], const PetscInt *ornt[])
3565 {
3566   DM_Plex *mesh = (DM_Plex *)dm->data;
3567 
3568   PetscFunctionBegin;
3569   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3570   if (mesh->tr) {
3571     PetscCall(DMPlexTransformGetCone(mesh->tr, p, cone, ornt));
3572   } else {
3573     PetscInt off;
3574     if (PetscDefined(USE_DEBUG)) {
3575       PetscInt dof;
3576       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3577       if (dof) {
3578         if (cone) PetscAssertPointer(cone, 3);
3579         if (ornt) PetscAssertPointer(ornt, 4);
3580       }
3581     }
3582     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3583     if (cone) *cone = PetscSafePointerPlusOffset(mesh->cones, off);
3584     if (ornt) *ornt = PetscSafePointerPlusOffset(mesh->coneOrientations, off);
3585   }
3586   PetscFunctionReturn(PETSC_SUCCESS);
3587 }
3588 
3589 /*@C
3590   DMPlexRestoreOrientedCone - Restore the points and orientations on the in-edges for this point in the DAG obtained with `DMPlexGetOrientedCone()`
3591 
3592   Not Collective
3593 
3594   Input Parameters:
3595 + dm   - The DMPlex
3596 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3597 . cone - An array of points which are on the in-edges for point p
3598 - ornt - An array of orientations which are on the in-edges for point `p`. An orientation is an
3599          integer giving the prescription for cone traversal.
3600 
3601   Level: beginner
3602 
3603 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetOrientedCone()`, `DMPlexGetConeSize()`, `DMPlexGetCone()`, `DMPlexGetChart()`
3604 @*/
3605 PetscErrorCode DMPlexRestoreOrientedCone(DM dm, PetscInt p, const PetscInt *cone[], const PetscInt *ornt[])
3606 {
3607   DM_Plex *mesh = (DM_Plex *)dm->data;
3608 
3609   PetscFunctionBegin;
3610   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3611   if (mesh->tr) PetscCall(DMPlexTransformRestoreCone(mesh->tr, p, cone, ornt));
3612   PetscFunctionReturn(PETSC_SUCCESS);
3613 }
3614 
3615 /*@
3616   DMPlexGetSupportSize - Return the number of out-edges for this point in the DAG
3617 
3618   Not Collective
3619 
3620   Input Parameters:
3621 + dm - The `DMPLEX`
3622 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3623 
3624   Output Parameter:
3625 . size - The support size for point `p`
3626 
3627   Level: beginner
3628 
3629 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`, `DMPlexGetConeSize()`
3630 @*/
3631 PetscErrorCode DMPlexGetSupportSize(DM dm, PetscInt p, PetscInt *size)
3632 {
3633   DM_Plex *mesh = (DM_Plex *)dm->data;
3634 
3635   PetscFunctionBegin;
3636   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3637   PetscAssertPointer(size, 3);
3638   PetscCall(PetscSectionGetDof(mesh->supportSection, p, size));
3639   PetscFunctionReturn(PETSC_SUCCESS);
3640 }
3641 
3642 /*@
3643   DMPlexSetSupportSize - Set the number of out-edges for this point in the DAG
3644 
3645   Not Collective
3646 
3647   Input Parameters:
3648 + dm   - The `DMPLEX`
3649 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3650 - size - The support size for point `p`
3651 
3652   Level: beginner
3653 
3654   Note:
3655   This should be called after `DMPlexSetChart()`.
3656 
3657 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetSupportSize()`, `DMPlexSetChart()`
3658 @*/
3659 PetscErrorCode DMPlexSetSupportSize(DM dm, PetscInt p, PetscInt size)
3660 {
3661   DM_Plex *mesh = (DM_Plex *)dm->data;
3662 
3663   PetscFunctionBegin;
3664   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3665   PetscCall(PetscSectionSetDof(mesh->supportSection, p, size));
3666   PetscFunctionReturn(PETSC_SUCCESS);
3667 }
3668 
3669 /*@C
3670   DMPlexGetSupport - Return the points on the out-edges for this point in the DAG
3671 
3672   Not Collective
3673 
3674   Input Parameters:
3675 + dm - The `DMPLEX`
3676 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3677 
3678   Output Parameter:
3679 . support - An array of points which are on the out-edges for point `p`, its length is that obtained from `DMPlexGetSupportSize()`
3680 
3681   Level: beginner
3682 
3683   Fortran Notes:
3684   `support` must be declared with
3685 .vb
3686   PetscInt, pointer :: support(:)
3687 .ve
3688 
3689   You must also call `DMPlexRestoreSupport()` after you finish using the returned array.
3690   `DMPlexRestoreSupport()` is not needed/available in C.
3691 
3692 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSupportSize()`, `DMPlexSetSupport()`, `DMPlexGetCone()`, `DMPlexSetChart()`
3693 @*/
3694 PetscErrorCode DMPlexGetSupport(DM dm, PetscInt p, const PetscInt *support[])
3695 {
3696   DM_Plex *mesh = (DM_Plex *)dm->data;
3697   PetscInt off;
3698 
3699   PetscFunctionBegin;
3700   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3701   PetscAssertPointer(support, 3);
3702   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3703   *support = PetscSafePointerPlusOffset(mesh->supports, off);
3704   PetscFunctionReturn(PETSC_SUCCESS);
3705 }
3706 
3707 /*@
3708   DMPlexSetSupport - Set the points on the out-edges for this point in the DAG, that is the list of points that this point covers
3709 
3710   Not Collective
3711 
3712   Input Parameters:
3713 + dm      - The `DMPLEX`
3714 . p       - The point, which must lie in the chart set with `DMPlexSetChart()`
3715 - support - An array of points which are on the out-edges for point `p`, its length is that obtained from `DMPlexGetSupportSize()`
3716 
3717   Level: beginner
3718 
3719   Note:
3720   This should be called after all calls to `DMPlexSetSupportSize()` and `DMSetUp()`.
3721 
3722 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetCone()`, `DMPlexSetConeSize()`, `DMPlexCreate()`, `DMPlexGetSupport()`, `DMPlexSetChart()`, `DMPlexSetSupportSize()`, `DMSetUp()`
3723 @*/
3724 PetscErrorCode DMPlexSetSupport(DM dm, PetscInt p, const PetscInt support[])
3725 {
3726   DM_Plex *mesh = (DM_Plex *)dm->data;
3727   PetscInt pStart, pEnd;
3728   PetscInt dof, off, c;
3729 
3730   PetscFunctionBegin;
3731   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3732   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3733   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3734   if (dof) PetscAssertPointer(support, 3);
3735   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3736   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);
3737   for (c = 0; c < dof; ++c) {
3738     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);
3739     mesh->supports[off + c] = support[c];
3740   }
3741   PetscFunctionReturn(PETSC_SUCCESS);
3742 }
3743 
3744 /*@
3745   DMPlexInsertSupport - Insert a point into the out-edges for the point p in the DAG
3746 
3747   Not Collective
3748 
3749   Input Parameters:
3750 + dm           - The `DMPLEX`
3751 . p            - The point, which must lie in the chart set with `DMPlexSetChart()`
3752 . supportPos   - The local index in the cone where the point should be put
3753 - supportPoint - The mesh point to insert
3754 
3755   Level: beginner
3756 
3757 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3758 @*/
3759 PetscErrorCode DMPlexInsertSupport(DM dm, PetscInt p, PetscInt supportPos, PetscInt supportPoint)
3760 {
3761   DM_Plex *mesh = (DM_Plex *)dm->data;
3762   PetscInt pStart, pEnd;
3763   PetscInt dof, off;
3764 
3765   PetscFunctionBegin;
3766   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3767   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3768   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3769   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3770   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);
3771   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);
3772   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);
3773   mesh->supports[off + supportPos] = supportPoint;
3774   PetscFunctionReturn(PETSC_SUCCESS);
3775 }
3776 
3777 /* Converts an orientation o in the current numbering to the previous scheme used in Plex */
3778 PetscInt DMPolytopeConvertNewOrientation_Internal(DMPolytopeType ct, PetscInt o)
3779 {
3780   switch (ct) {
3781   case DM_POLYTOPE_SEGMENT:
3782     if (o == -1) return -2;
3783     break;
3784   case DM_POLYTOPE_TRIANGLE:
3785     if (o == -3) return -1;
3786     if (o == -2) return -3;
3787     if (o == -1) return -2;
3788     break;
3789   case DM_POLYTOPE_QUADRILATERAL:
3790     if (o == -4) return -2;
3791     if (o == -3) return -1;
3792     if (o == -2) return -4;
3793     if (o == -1) return -3;
3794     break;
3795   default:
3796     return o;
3797   }
3798   return o;
3799 }
3800 
3801 /* Converts an orientation o in the previous scheme used in Plex to the current numbering */
3802 PetscInt DMPolytopeConvertOldOrientation_Internal(DMPolytopeType ct, PetscInt o)
3803 {
3804   switch (ct) {
3805   case DM_POLYTOPE_SEGMENT:
3806     if ((o == -2) || (o == 1)) return -1;
3807     if (o == -1) return 0;
3808     break;
3809   case DM_POLYTOPE_TRIANGLE:
3810     if (o == -3) return -2;
3811     if (o == -2) return -1;
3812     if (o == -1) return -3;
3813     break;
3814   case DM_POLYTOPE_QUADRILATERAL:
3815     if (o == -4) return -2;
3816     if (o == -3) return -1;
3817     if (o == -2) return -4;
3818     if (o == -1) return -3;
3819     break;
3820   default:
3821     return o;
3822   }
3823   return o;
3824 }
3825 
3826 /* Takes in a mesh whose orientations are in the previous scheme and converts them all to the current numbering */
3827 PetscErrorCode DMPlexConvertOldOrientations_Internal(DM dm)
3828 {
3829   PetscInt pStart, pEnd, p;
3830 
3831   PetscFunctionBegin;
3832   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3833   for (p = pStart; p < pEnd; ++p) {
3834     const PetscInt *cone, *ornt;
3835     PetscInt        coneSize, c;
3836 
3837     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
3838     PetscCall(DMPlexGetCone(dm, p, &cone));
3839     PetscCall(DMPlexGetConeOrientation(dm, p, &ornt));
3840     for (c = 0; c < coneSize; ++c) {
3841       DMPolytopeType ct;
3842       const PetscInt o = ornt[c];
3843 
3844       PetscCall(DMPlexGetCellType(dm, cone[c], &ct));
3845       switch (ct) {
3846       case DM_POLYTOPE_SEGMENT:
3847         if ((o == -2) || (o == 1)) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3848         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, 0));
3849         break;
3850       case DM_POLYTOPE_TRIANGLE:
3851         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3852         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3853         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3854         break;
3855       case DM_POLYTOPE_QUADRILATERAL:
3856         if (o == -4) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3857         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3858         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -4));
3859         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3860         break;
3861       default:
3862         break;
3863       }
3864     }
3865   }
3866   PetscFunctionReturn(PETSC_SUCCESS);
3867 }
3868 
3869 static inline PetscErrorCode DMPlexGetTransitiveClosure_Hot_Private(DM dm, PetscInt p, PetscBool useCone, PetscInt *size, const PetscInt *arr[], const PetscInt *ornt[])
3870 {
3871   DM_Plex *mesh = (DM_Plex *)dm->data;
3872 
3873   PetscFunctionBeginHot;
3874   if (PetscDefined(USE_DEBUG) || mesh->tr) {
3875     if (useCone) {
3876       PetscCall(DMPlexGetConeSize(dm, p, size));
3877       PetscCall(DMPlexGetOrientedCone(dm, p, arr, ornt));
3878     } else {
3879       PetscCall(DMPlexGetSupportSize(dm, p, size));
3880       PetscCall(DMPlexGetSupport(dm, p, arr));
3881     }
3882   } else {
3883     if (useCone) {
3884       const PetscSection s   = mesh->coneSection;
3885       const PetscInt     ps  = p - s->pStart;
3886       const PetscInt     off = s->atlasOff[ps];
3887 
3888       *size = s->atlasDof[ps];
3889       *arr  = mesh->cones + off;
3890       *ornt = mesh->coneOrientations + off;
3891     } else {
3892       const PetscSection s   = mesh->supportSection;
3893       const PetscInt     ps  = p - s->pStart;
3894       const PetscInt     off = s->atlasOff[ps];
3895 
3896       *size = s->atlasDof[ps];
3897       *arr  = mesh->supports + off;
3898     }
3899   }
3900   PetscFunctionReturn(PETSC_SUCCESS);
3901 }
3902 
3903 static inline PetscErrorCode DMPlexRestoreTransitiveClosure_Hot_Private(DM dm, PetscInt p, PetscBool useCone, PetscInt *size, const PetscInt *arr[], const PetscInt *ornt[])
3904 {
3905   DM_Plex *mesh = (DM_Plex *)dm->data;
3906 
3907   PetscFunctionBeginHot;
3908   if (PetscDefined(USE_DEBUG) || mesh->tr) {
3909     if (useCone) PetscCall(DMPlexRestoreOrientedCone(dm, p, arr, ornt));
3910   }
3911   PetscFunctionReturn(PETSC_SUCCESS);
3912 }
3913 
3914 static PetscErrorCode DMPlexGetTransitiveClosure_Depth1_Private(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3915 {
3916   DMPolytopeType  ct = DM_POLYTOPE_UNKNOWN;
3917   PetscInt       *closure;
3918   const PetscInt *tmp = NULL, *tmpO = NULL;
3919   PetscInt        off = 0, tmpSize, t;
3920 
3921   PetscFunctionBeginHot;
3922   if (ornt) {
3923     PetscCall(DMPlexGetCellType(dm, p, &ct));
3924     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;
3925   }
3926   if (*points) {
3927     closure = *points;
3928   } else {
3929     PetscInt maxConeSize, maxSupportSize;
3930     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3931     PetscCall(DMGetWorkArray(dm, 2 * (PetscMax(maxConeSize, maxSupportSize) + 1), MPIU_INT, &closure));
3932   }
3933   PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, p, useCone, &tmpSize, &tmp, &tmpO));
3934   if (ct == DM_POLYTOPE_UNKNOWN) {
3935     closure[off++] = p;
3936     closure[off++] = 0;
3937     for (t = 0; t < tmpSize; ++t) {
3938       closure[off++] = tmp[t];
3939       closure[off++] = tmpO ? tmpO[t] : 0;
3940     }
3941   } else {
3942     const PetscInt *arr = DMPolytopeTypeGetArrangement(ct, ornt);
3943 
3944     /* We assume that cells with a valid type have faces with a valid type */
3945     closure[off++] = p;
3946     closure[off++] = ornt;
3947     for (t = 0; t < tmpSize; ++t) {
3948       DMPolytopeType ft;
3949 
3950       PetscCall(DMPlexGetCellType(dm, tmp[t], &ft));
3951       closure[off++] = tmp[arr[t]];
3952       closure[off++] = tmpO ? DMPolytopeTypeComposeOrientation(ft, ornt, tmpO[t]) : 0;
3953     }
3954   }
3955   PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, p, useCone, &tmpSize, &tmp, &tmpO));
3956   if (numPoints) *numPoints = tmpSize + 1;
3957   if (points) *points = closure;
3958   PetscFunctionReturn(PETSC_SUCCESS);
3959 }
3960 
3961 /* We need a special tensor version because we want to allow duplicate points in the endcaps for hybrid cells */
3962 static PetscErrorCode DMPlexTransitiveClosure_Tensor_Internal(DM dm, PetscInt point, DMPolytopeType ct, PetscInt o, PetscBool useCone, PetscInt *numPoints, PetscInt **points)
3963 {
3964   const PetscInt *arr = DMPolytopeTypeGetArrangement(ct, o);
3965   const PetscInt *cone, *ornt;
3966   PetscInt       *pts, *closure = NULL;
3967   DMPolytopeType  ft;
3968   PetscInt        maxConeSize, maxSupportSize, coneSeries, supportSeries, maxSize;
3969   PetscInt        dim, coneSize, c, d, clSize, cl;
3970 
3971   PetscFunctionBeginHot;
3972   PetscCall(DMGetDimension(dm, &dim));
3973   PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, point, PETSC_TRUE, &coneSize, &cone, &ornt));
3974   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3975   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, dim + 1) - 1) / (maxConeSize - 1)) : dim + 1;
3976   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, dim + 1) - 1) / (maxSupportSize - 1)) : dim + 1;
3977   maxSize       = PetscMax(coneSeries, supportSeries);
3978   if (*points) {
3979     pts = *points;
3980   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &pts));
3981   c        = 0;
3982   pts[c++] = point;
3983   pts[c++] = o;
3984   PetscCall(DMPlexGetCellType(dm, cone[arr[0 * 2 + 0]], &ft));
3985   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[0 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[0 * 2 + 1], ornt[0]), useCone, &clSize, &closure));
3986   for (cl = 0; cl < clSize * 2; cl += 2) {
3987     pts[c++] = closure[cl];
3988     pts[c++] = closure[cl + 1];
3989   }
3990   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[1 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[1 * 2 + 1], ornt[1]), useCone, &clSize, &closure));
3991   for (cl = 0; cl < clSize * 2; cl += 2) {
3992     pts[c++] = closure[cl];
3993     pts[c++] = closure[cl + 1];
3994   }
3995   PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[0], useCone, &clSize, &closure));
3996   for (d = 2; d < coneSize; ++d) {
3997     PetscCall(DMPlexGetCellType(dm, cone[arr[d * 2 + 0]], &ft));
3998     pts[c++] = cone[arr[d * 2 + 0]];
3999     pts[c++] = DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]);
4000   }
4001   PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, point, PETSC_TRUE, &coneSize, &cone, &ornt));
4002   if (dim >= 3) {
4003     for (d = 2; d < coneSize; ++d) {
4004       const PetscInt  fpoint = cone[arr[d * 2 + 0]];
4005       const PetscInt *fcone, *fornt;
4006       PetscInt        fconeSize, fc, i;
4007 
4008       PetscCall(DMPlexGetCellType(dm, fpoint, &ft));
4009       const PetscInt *farr = DMPolytopeTypeGetArrangement(ft, DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]));
4010       PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, fpoint, PETSC_TRUE, &fconeSize, &fcone, &fornt));
4011       for (fc = 0; fc < fconeSize; ++fc) {
4012         const PetscInt cp = fcone[farr[fc * 2 + 0]];
4013         const PetscInt co = farr[fc * 2 + 1];
4014 
4015         for (i = 0; i < c; i += 2)
4016           if (pts[i] == cp) break;
4017         if (i == c) {
4018           PetscCall(DMPlexGetCellType(dm, cp, &ft));
4019           pts[c++] = cp;
4020           pts[c++] = DMPolytopeTypeComposeOrientation(ft, co, fornt[farr[fc * 2 + 0]]);
4021         }
4022       }
4023       PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, fpoint, PETSC_TRUE, &fconeSize, &fcone, &fornt));
4024     }
4025   }
4026   *numPoints = c / 2;
4027   *points    = pts;
4028   PetscFunctionReturn(PETSC_SUCCESS);
4029 }
4030 
4031 PetscErrorCode DMPlexGetTransitiveClosure_Internal(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
4032 {
4033   DMPolytopeType ct;
4034   PetscInt      *closure, *fifo;
4035   PetscInt       closureSize = 0, fifoStart = 0, fifoSize = 0;
4036   PetscInt       maxConeSize, maxSupportSize, coneSeries, supportSeries;
4037   PetscInt       depth, maxSize;
4038 
4039   PetscFunctionBeginHot;
4040   PetscCall(DMPlexGetDepth(dm, &depth));
4041   if (depth == 1) {
4042     PetscCall(DMPlexGetTransitiveClosure_Depth1_Private(dm, p, ornt, useCone, numPoints, points));
4043     PetscFunctionReturn(PETSC_SUCCESS);
4044   }
4045   PetscCall(DMPlexGetCellType(dm, p, &ct));
4046   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;
4047   if (DMPolytopeTypeIsHybrid(ct) && ct != DM_POLYTOPE_POINT_PRISM_TENSOR) {
4048     PetscCall(DMPlexTransitiveClosure_Tensor_Internal(dm, p, ct, ornt, useCone, numPoints, points));
4049     PetscFunctionReturn(PETSC_SUCCESS);
4050   }
4051   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
4052   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, depth + 1) - 1) / (maxConeSize - 1)) : depth + 1;
4053   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, depth + 1) - 1) / (maxSupportSize - 1)) : depth + 1;
4054   maxSize       = PetscMax(coneSeries, supportSeries);
4055   PetscCall(DMGetWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
4056   if (*points) {
4057     closure = *points;
4058   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &closure));
4059   closure[closureSize++] = p;
4060   closure[closureSize++] = ornt;
4061   fifo[fifoSize++]       = p;
4062   fifo[fifoSize++]       = ornt;
4063   fifo[fifoSize++]       = ct;
4064   /* Should kick out early when depth is reached, rather than checking all vertices for empty cones */
4065   while (fifoSize - fifoStart) {
4066     const PetscInt       q    = fifo[fifoStart++];
4067     const PetscInt       o    = fifo[fifoStart++];
4068     const DMPolytopeType qt   = (DMPolytopeType)fifo[fifoStart++];
4069     const PetscInt      *qarr = DMPolytopeTypeGetArrangement(qt, o);
4070     const PetscInt      *tmp, *tmpO = NULL;
4071     PetscInt             tmpSize, t;
4072 
4073     if (PetscDefined(USE_DEBUG)) {
4074       PetscInt nO = DMPolytopeTypeGetNumArrangements(qt) / 2;
4075       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);
4076     }
4077     PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, q, useCone, &tmpSize, &tmp, &tmpO));
4078     for (t = 0; t < tmpSize; ++t) {
4079       const PetscInt ip = useCone && qarr ? qarr[t * 2] : t;
4080       const PetscInt io = useCone && qarr ? qarr[t * 2 + 1] : 0;
4081       const PetscInt cp = tmp[ip];
4082       PetscCall(DMPlexGetCellType(dm, cp, &ct));
4083       const PetscInt co = tmpO ? DMPolytopeTypeComposeOrientation(ct, io, tmpO[ip]) : 0;
4084       PetscInt       c;
4085 
4086       /* Check for duplicate */
4087       for (c = 0; c < closureSize; c += 2) {
4088         if (closure[c] == cp) break;
4089       }
4090       if (c == closureSize) {
4091         closure[closureSize++] = cp;
4092         closure[closureSize++] = co;
4093         fifo[fifoSize++]       = cp;
4094         fifo[fifoSize++]       = co;
4095         fifo[fifoSize++]       = ct;
4096       }
4097     }
4098     PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, q, useCone, &tmpSize, &tmp, &tmpO));
4099   }
4100   PetscCall(DMRestoreWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
4101   if (numPoints) *numPoints = closureSize / 2;
4102   if (points) *points = closure;
4103   PetscFunctionReturn(PETSC_SUCCESS);
4104 }
4105 
4106 /*@C
4107   DMPlexGetTransitiveClosure - Return the points on the transitive closure of the in-edges or out-edges for this point in the DAG
4108 
4109   Not Collective
4110 
4111   Input Parameters:
4112 + dm      - The `DMPLEX`
4113 . p       - The mesh point
4114 - useCone - `PETSC_TRUE` for the closure, otherwise return the star
4115 
4116   Input/Output Parameter:
4117 . points - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...];
4118            if *points is `NULL` on input, internal storage will be returned, use `DMPlexRestoreTransitiveClosure()`,
4119            otherwise the provided array is used to hold the values
4120 
4121   Output Parameter:
4122 . numPoints - The number of points in the closure, so `points` is of size 2*`numPoints`
4123 
4124   Level: beginner
4125 
4126   Note:
4127   If using internal storage (points is `NULL` on input), each call overwrites the last output.
4128 
4129   Fortran Notes:
4130   `points` must be declared with
4131 .vb
4132   PetscInt, pointer :: points(:)
4133 .ve
4134   and is always allocated by the function.
4135 
4136   The `numPoints` argument is not present in the Fortran binding.
4137 
4138 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
4139 @*/
4140 PetscErrorCode DMPlexGetTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
4141 {
4142   PetscFunctionBeginHot;
4143   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4144   if (numPoints) PetscAssertPointer(numPoints, 4);
4145   if (points) PetscAssertPointer(points, 5);
4146   if (PetscDefined(USE_DEBUG)) {
4147     PetscInt pStart, pEnd;
4148     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4149     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);
4150   }
4151   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, p, 0, useCone, numPoints, points));
4152   PetscFunctionReturn(PETSC_SUCCESS);
4153 }
4154 
4155 /*@C
4156   DMPlexRestoreTransitiveClosure - Restore the array of points on the transitive closure of the in-edges or out-edges for this point in the DAG
4157 
4158   Not Collective
4159 
4160   Input Parameters:
4161 + dm        - The `DMPLEX`
4162 . p         - The mesh point
4163 . useCone   - `PETSC_TRUE` for the closure, otherwise return the star
4164 . numPoints - The number of points in the closure, so points[] is of size 2*`numPoints`
4165 - points    - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...]
4166 
4167   Level: beginner
4168 
4169   Note:
4170   If not using internal storage (points is not `NULL` on input), this call is unnecessary
4171 
4172 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
4173 @*/
4174 PetscErrorCode DMPlexRestoreTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
4175 {
4176   PetscFunctionBeginHot;
4177   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4178   if (numPoints) *numPoints = 0;
4179   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, points));
4180   PetscFunctionReturn(PETSC_SUCCESS);
4181 }
4182 
4183 /*@
4184   DMPlexGetMaxSizes - Return the maximum number of in-edges (cone) and out-edges (support) for any point in the DAG
4185 
4186   Not Collective
4187 
4188   Input Parameter:
4189 . dm - The `DMPLEX`
4190 
4191   Output Parameters:
4192 + maxConeSize    - The maximum number of in-edges
4193 - maxSupportSize - The maximum number of out-edges
4194 
4195   Level: beginner
4196 
4197 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
4198 @*/
4199 PetscErrorCode DMPlexGetMaxSizes(DM dm, PetscInt *maxConeSize, PetscInt *maxSupportSize)
4200 {
4201   DM_Plex *mesh = (DM_Plex *)dm->data;
4202 
4203   PetscFunctionBegin;
4204   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4205   if (maxConeSize) PetscCall(PetscSectionGetMaxDof(mesh->coneSection, maxConeSize));
4206   if (maxSupportSize) PetscCall(PetscSectionGetMaxDof(mesh->supportSection, maxSupportSize));
4207   PetscFunctionReturn(PETSC_SUCCESS);
4208 }
4209 
4210 PetscErrorCode DMSetUp_Plex(DM dm)
4211 {
4212   DM_Plex *mesh = (DM_Plex *)dm->data;
4213   PetscInt size, maxSupportSize;
4214 
4215   PetscFunctionBegin;
4216   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4217   PetscCall(PetscSectionSetUp(mesh->coneSection));
4218   PetscCall(PetscSectionGetStorageSize(mesh->coneSection, &size));
4219   PetscCall(PetscMalloc1(size, &mesh->cones));
4220   PetscCall(PetscCalloc1(size, &mesh->coneOrientations));
4221   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
4222   if (maxSupportSize) {
4223     PetscCall(PetscSectionSetUp(mesh->supportSection));
4224     PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &size));
4225     PetscCall(PetscMalloc1(size, &mesh->supports));
4226   }
4227   PetscFunctionReturn(PETSC_SUCCESS);
4228 }
4229 
4230 PetscErrorCode DMCreateSubDM_Plex(DM dm, PetscInt numFields, const PetscInt fields[], IS *is, DM *subdm)
4231 {
4232   PetscFunctionBegin;
4233   if (subdm) PetscCall(DMClone(dm, subdm));
4234   PetscCall(DMCreateSectionSubDM(dm, numFields, fields, NULL, NULL, is, subdm));
4235   if (subdm) (*subdm)->useNatural = dm->useNatural;
4236   if (dm->useNatural && dm->sfMigration) {
4237     PetscSF sfNatural;
4238 
4239     (*subdm)->sfMigration = dm->sfMigration;
4240     PetscCall(PetscObjectReference((PetscObject)dm->sfMigration));
4241     PetscCall(DMPlexCreateGlobalToNaturalSF(*subdm, NULL, (*subdm)->sfMigration, &sfNatural));
4242     (*subdm)->sfNatural = sfNatural;
4243   }
4244   PetscFunctionReturn(PETSC_SUCCESS);
4245 }
4246 
4247 PetscErrorCode DMCreateSuperDM_Plex(DM dms[], PetscInt len, IS **is, DM *superdm)
4248 {
4249   PetscInt i = 0;
4250 
4251   PetscFunctionBegin;
4252   PetscCall(DMClone(dms[0], superdm));
4253   PetscCall(DMCreateSectionSuperDM(dms, len, is, superdm));
4254   (*superdm)->useNatural = PETSC_FALSE;
4255   for (i = 0; i < len; i++) {
4256     if (dms[i]->useNatural && dms[i]->sfMigration) {
4257       PetscSF sfNatural;
4258 
4259       (*superdm)->sfMigration = dms[i]->sfMigration;
4260       PetscCall(PetscObjectReference((PetscObject)dms[i]->sfMigration));
4261       (*superdm)->useNatural = PETSC_TRUE;
4262       PetscCall(DMPlexCreateGlobalToNaturalSF(*superdm, NULL, (*superdm)->sfMigration, &sfNatural));
4263       (*superdm)->sfNatural = sfNatural;
4264       break;
4265     }
4266   }
4267   PetscFunctionReturn(PETSC_SUCCESS);
4268 }
4269 
4270 /*@
4271   DMPlexSymmetrize - Create support (out-edge) information from cone (in-edge) information
4272 
4273   Not Collective
4274 
4275   Input Parameter:
4276 . dm - The `DMPLEX`
4277 
4278   Level: beginner
4279 
4280   Note:
4281   This should be called after all calls to `DMPlexSetCone()`
4282 
4283 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMPlexSetCone()`
4284 @*/
4285 PetscErrorCode DMPlexSymmetrize(DM dm)
4286 {
4287   DM_Plex  *mesh = (DM_Plex *)dm->data;
4288   PetscInt *offsets;
4289   PetscInt  supportSize;
4290   PetscInt  pStart, pEnd, p;
4291 
4292   PetscFunctionBegin;
4293   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4294   PetscCheck(!mesh->supports, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONGSTATE, "Supports were already setup in this DMPlex");
4295   PetscCall(PetscLogEventBegin(DMPLEX_Symmetrize, dm, 0, 0, 0));
4296   /* Calculate support sizes */
4297   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4298   for (p = pStart; p < pEnd; ++p) {
4299     PetscInt dof, off, c;
4300 
4301     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
4302     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
4303     for (c = off; c < off + dof; ++c) PetscCall(PetscSectionAddDof(mesh->supportSection, mesh->cones[c], 1));
4304   }
4305   PetscCall(PetscSectionSetUp(mesh->supportSection));
4306   /* Calculate supports */
4307   PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &supportSize));
4308   PetscCall(PetscMalloc1(supportSize, &mesh->supports));
4309   PetscCall(PetscCalloc1(pEnd - pStart, &offsets));
4310   for (p = pStart; p < pEnd; ++p) {
4311     PetscInt dof, off, c;
4312 
4313     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
4314     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
4315     for (c = off; c < off + dof; ++c) {
4316       const PetscInt q = mesh->cones[c];
4317       PetscInt       offS;
4318 
4319       PetscCall(PetscSectionGetOffset(mesh->supportSection, q, &offS));
4320 
4321       mesh->supports[offS + offsets[q]] = p;
4322       ++offsets[q];
4323     }
4324   }
4325   PetscCall(PetscFree(offsets));
4326   PetscCall(PetscLogEventEnd(DMPLEX_Symmetrize, dm, 0, 0, 0));
4327   PetscFunctionReturn(PETSC_SUCCESS);
4328 }
4329 
4330 static PetscErrorCode DMPlexCreateDepthStratum(DM dm, DMLabel label, PetscInt depth, PetscInt pStart, PetscInt pEnd)
4331 {
4332   IS stratumIS;
4333 
4334   PetscFunctionBegin;
4335   if (pStart >= pEnd) PetscFunctionReturn(PETSC_SUCCESS);
4336   if (PetscDefined(USE_DEBUG)) {
4337     PetscInt  qStart, qEnd, numLevels, level;
4338     PetscBool overlap = PETSC_FALSE;
4339     PetscCall(DMLabelGetNumValues(label, &numLevels));
4340     for (level = 0; level < numLevels; level++) {
4341       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4342       if ((pStart >= qStart && pStart < qEnd) || (pEnd > qStart && pEnd <= qEnd)) {
4343         overlap = PETSC_TRUE;
4344         break;
4345       }
4346     }
4347     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);
4348   }
4349   PetscCall(ISCreateStride(PETSC_COMM_SELF, pEnd - pStart, pStart, 1, &stratumIS));
4350   PetscCall(DMLabelSetStratumIS(label, depth, stratumIS));
4351   PetscCall(ISDestroy(&stratumIS));
4352   PetscFunctionReturn(PETSC_SUCCESS);
4353 }
4354 
4355 static PetscErrorCode DMPlexStratify_CellType_Private(DM dm, DMLabel label)
4356 {
4357   PetscInt *pMin, *pMax;
4358   PetscInt  pStart, pEnd;
4359   PetscInt  dmin = PETSC_MAX_INT, dmax = PETSC_MIN_INT;
4360 
4361   PetscFunctionBegin;
4362   {
4363     DMLabel label2;
4364 
4365     PetscCall(DMPlexGetCellTypeLabel(dm, &label2));
4366     PetscCall(PetscObjectViewFromOptions((PetscObject)label2, NULL, "-ct_view"));
4367   }
4368   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4369   for (PetscInt p = pStart; p < pEnd; ++p) {
4370     DMPolytopeType ct;
4371 
4372     PetscCall(DMPlexGetCellType(dm, p, &ct));
4373     dmin = PetscMin(DMPolytopeTypeGetDim(ct), dmin);
4374     dmax = PetscMax(DMPolytopeTypeGetDim(ct), dmax);
4375   }
4376   PetscCall(PetscMalloc2(dmax + 1, &pMin, dmax + 1, &pMax));
4377   for (PetscInt d = dmin; d <= dmax; ++d) {
4378     pMin[d] = PETSC_MAX_INT;
4379     pMax[d] = PETSC_MIN_INT;
4380   }
4381   for (PetscInt p = pStart; p < pEnd; ++p) {
4382     DMPolytopeType ct;
4383     PetscInt       d;
4384 
4385     PetscCall(DMPlexGetCellType(dm, p, &ct));
4386     d       = DMPolytopeTypeGetDim(ct);
4387     pMin[d] = PetscMin(p, pMin[d]);
4388     pMax[d] = PetscMax(p, pMax[d]);
4389   }
4390   for (PetscInt d = dmin; d <= dmax; ++d) {
4391     if (pMin[d] > pMax[d]) continue;
4392     PetscCall(DMPlexCreateDepthStratum(dm, label, d, pMin[d], pMax[d] + 1));
4393   }
4394   PetscCall(PetscFree2(pMin, pMax));
4395   PetscFunctionReturn(PETSC_SUCCESS);
4396 }
4397 
4398 static PetscErrorCode DMPlexStratify_Topological_Private(DM dm, DMLabel label)
4399 {
4400   PetscInt pStart, pEnd;
4401   PetscInt numRoots = 0, numLeaves = 0;
4402 
4403   PetscFunctionBegin;
4404   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4405   {
4406     /* Initialize roots and count leaves */
4407     PetscInt sMin = PETSC_MAX_INT;
4408     PetscInt sMax = PETSC_MIN_INT;
4409     PetscInt coneSize, supportSize;
4410 
4411     for (PetscInt p = pStart; p < pEnd; ++p) {
4412       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4413       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
4414       if (!coneSize && supportSize) {
4415         sMin = PetscMin(p, sMin);
4416         sMax = PetscMax(p, sMax);
4417         ++numRoots;
4418       } else if (!supportSize && coneSize) {
4419         ++numLeaves;
4420       } else if (!supportSize && !coneSize) {
4421         /* Isolated points */
4422         sMin = PetscMin(p, sMin);
4423         sMax = PetscMax(p, sMax);
4424       }
4425     }
4426     PetscCall(DMPlexCreateDepthStratum(dm, label, 0, sMin, sMax + 1));
4427   }
4428 
4429   if (numRoots + numLeaves == (pEnd - pStart)) {
4430     PetscInt sMin = PETSC_MAX_INT;
4431     PetscInt sMax = PETSC_MIN_INT;
4432     PetscInt coneSize, supportSize;
4433 
4434     for (PetscInt p = pStart; p < pEnd; ++p) {
4435       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4436       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
4437       if (!supportSize && coneSize) {
4438         sMin = PetscMin(p, sMin);
4439         sMax = PetscMax(p, sMax);
4440       }
4441     }
4442     PetscCall(DMPlexCreateDepthStratum(dm, label, 1, sMin, sMax + 1));
4443   } else {
4444     PetscInt level = 0;
4445     PetscInt qStart, qEnd;
4446 
4447     PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4448     while (qEnd > qStart) {
4449       PetscInt sMin = PETSC_MAX_INT;
4450       PetscInt sMax = PETSC_MIN_INT;
4451 
4452       for (PetscInt q = qStart; q < qEnd; ++q) {
4453         const PetscInt *support;
4454         PetscInt        supportSize;
4455 
4456         PetscCall(DMPlexGetSupportSize(dm, q, &supportSize));
4457         PetscCall(DMPlexGetSupport(dm, q, &support));
4458         for (PetscInt s = 0; s < supportSize; ++s) {
4459           sMin = PetscMin(support[s], sMin);
4460           sMax = PetscMax(support[s], sMax);
4461         }
4462       }
4463       PetscCall(DMLabelGetNumValues(label, &level));
4464       PetscCall(DMPlexCreateDepthStratum(dm, label, level, sMin, sMax + 1));
4465       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4466     }
4467   }
4468   PetscFunctionReturn(PETSC_SUCCESS);
4469 }
4470 
4471 /*@
4472   DMPlexStratify - Computes the strata for all points in the `DMPLEX`
4473 
4474   Collective
4475 
4476   Input Parameter:
4477 . dm - The `DMPLEX`
4478 
4479   Level: beginner
4480 
4481   Notes:
4482   The strata group all points of the same grade, and this function calculates the strata. This
4483   grade can be seen as the height (or depth) of the point in the DAG.
4484 
4485   The DAG for most topologies is a graded poset (https://en.wikipedia.org/wiki/Graded_poset), and
4486   can be illustrated by a Hasse Diagram (https://en.wikipedia.org/wiki/Hasse_diagram).
4487   Concretely, `DMPlexStratify()` creates a new label named "depth" containing the depth in the DAG of each point. For cell-vertex
4488   meshes, vertices are depth 0 and cells are depth 1. For fully interpolated meshes, depth 0 for vertices, 1 for edges, and so on
4489   until cells have depth equal to the dimension of the mesh. The depth label can be accessed through `DMPlexGetDepthLabel()` or `DMPlexGetDepthStratum()`, or
4490   manually via `DMGetLabel()`.  The height is defined implicitly by height = maxDimension - depth, and can be accessed
4491   via `DMPlexGetHeightStratum()`.  For example, cells have height 0 and faces have height 1.
4492 
4493   The depth of a point is calculated by executing a breadth-first search (BFS) on the DAG. This could produce surprising results
4494   if run on a partially interpolated mesh, meaning one that had some edges and faces, but not others. For example, suppose that
4495   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
4496   to interpolate only that one (e0), so that
4497 .vb
4498   cone(c0) = {e0, v2}
4499   cone(e0) = {v0, v1}
4500 .ve
4501   If `DMPlexStratify()` is run on this mesh, it will give depths
4502 .vb
4503    depth 0 = {v0, v1, v2}
4504    depth 1 = {e0, c0}
4505 .ve
4506   where the triangle has been given depth 1, instead of 2, because it is reachable from vertex v2.
4507 
4508   `DMPlexStratify()` should be called after all calls to `DMPlexSymmetrize()`
4509 
4510 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexComputeCellTypes()`
4511 @*/
4512 PetscErrorCode DMPlexStratify(DM dm)
4513 {
4514   DM_Plex  *mesh = (DM_Plex *)dm->data;
4515   DMLabel   label;
4516   PetscBool flg = PETSC_FALSE;
4517 
4518   PetscFunctionBegin;
4519   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4520   PetscCall(PetscLogEventBegin(DMPLEX_Stratify, dm, 0, 0, 0));
4521 
4522   // Create depth label
4523   PetscCall(DMRemoveLabel(dm, "depth", NULL));
4524   PetscCall(DMCreateLabel(dm, "depth"));
4525   PetscCall(DMPlexGetDepthLabel(dm, &label));
4526 
4527   PetscCall(PetscOptionsGetBool(NULL, dm->hdr.prefix, "-dm_plex_stratify_celltype", &flg, NULL));
4528   if (flg) PetscCall(DMPlexStratify_CellType_Private(dm, label));
4529   else PetscCall(DMPlexStratify_Topological_Private(dm, label));
4530 
4531   { /* just in case there is an empty process */
4532     PetscInt numValues, maxValues = 0, v;
4533 
4534     PetscCall(DMLabelGetNumValues(label, &numValues));
4535     PetscCall(MPIU_Allreduce(&numValues, &maxValues, 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
4536     for (v = numValues; v < maxValues; v++) PetscCall(DMLabelAddStratum(label, v));
4537   }
4538   PetscCall(PetscObjectStateGet((PetscObject)label, &mesh->depthState));
4539   PetscCall(PetscLogEventEnd(DMPLEX_Stratify, dm, 0, 0, 0));
4540   PetscFunctionReturn(PETSC_SUCCESS);
4541 }
4542 
4543 PetscErrorCode DMPlexComputeCellType_Internal(DM dm, PetscInt p, PetscInt pdepth, DMPolytopeType *pt)
4544 {
4545   DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4546   PetscInt       dim, depth, pheight, coneSize;
4547 
4548   PetscFunctionBeginHot;
4549   PetscCall(DMGetDimension(dm, &dim));
4550   PetscCall(DMPlexGetDepth(dm, &depth));
4551   PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4552   pheight = depth - pdepth;
4553   if (depth <= 1) {
4554     switch (pdepth) {
4555     case 0:
4556       ct = DM_POLYTOPE_POINT;
4557       break;
4558     case 1:
4559       switch (coneSize) {
4560       case 2:
4561         ct = DM_POLYTOPE_SEGMENT;
4562         break;
4563       case 3:
4564         ct = DM_POLYTOPE_TRIANGLE;
4565         break;
4566       case 4:
4567         switch (dim) {
4568         case 2:
4569           ct = DM_POLYTOPE_QUADRILATERAL;
4570           break;
4571         case 3:
4572           ct = DM_POLYTOPE_TETRAHEDRON;
4573           break;
4574         default:
4575           break;
4576         }
4577         break;
4578       case 5:
4579         ct = DM_POLYTOPE_PYRAMID;
4580         break;
4581       case 6:
4582         ct = DM_POLYTOPE_TRI_PRISM_TENSOR;
4583         break;
4584       case 8:
4585         ct = DM_POLYTOPE_HEXAHEDRON;
4586         break;
4587       default:
4588         break;
4589       }
4590     }
4591   } else {
4592     if (pdepth == 0) {
4593       ct = DM_POLYTOPE_POINT;
4594     } else if (pheight == 0) {
4595       switch (dim) {
4596       case 1:
4597         switch (coneSize) {
4598         case 2:
4599           ct = DM_POLYTOPE_SEGMENT;
4600           break;
4601         default:
4602           break;
4603         }
4604         break;
4605       case 2:
4606         switch (coneSize) {
4607         case 3:
4608           ct = DM_POLYTOPE_TRIANGLE;
4609           break;
4610         case 4:
4611           ct = DM_POLYTOPE_QUADRILATERAL;
4612           break;
4613         default:
4614           break;
4615         }
4616         break;
4617       case 3:
4618         switch (coneSize) {
4619         case 4:
4620           ct = DM_POLYTOPE_TETRAHEDRON;
4621           break;
4622         case 5: {
4623           const PetscInt *cone;
4624           PetscInt        faceConeSize;
4625 
4626           PetscCall(DMPlexGetCone(dm, p, &cone));
4627           PetscCall(DMPlexGetConeSize(dm, cone[0], &faceConeSize));
4628           switch (faceConeSize) {
4629           case 3:
4630             ct = DM_POLYTOPE_TRI_PRISM_TENSOR;
4631             break;
4632           case 4:
4633             ct = DM_POLYTOPE_PYRAMID;
4634             break;
4635           }
4636         } break;
4637         case 6:
4638           ct = DM_POLYTOPE_HEXAHEDRON;
4639           break;
4640         default:
4641           break;
4642         }
4643         break;
4644       default:
4645         break;
4646       }
4647     } else if (pheight > 0) {
4648       switch (coneSize) {
4649       case 2:
4650         ct = DM_POLYTOPE_SEGMENT;
4651         break;
4652       case 3:
4653         ct = DM_POLYTOPE_TRIANGLE;
4654         break;
4655       case 4:
4656         ct = DM_POLYTOPE_QUADRILATERAL;
4657         break;
4658       default:
4659         break;
4660       }
4661     }
4662   }
4663   *pt = ct;
4664   PetscFunctionReturn(PETSC_SUCCESS);
4665 }
4666 
4667 /*@
4668   DMPlexComputeCellTypes - Infer the polytope type of every cell using its dimension and cone size.
4669 
4670   Collective
4671 
4672   Input Parameter:
4673 . dm - The `DMPLEX`
4674 
4675   Level: developer
4676 
4677   Note:
4678   This function is normally called automatically when a cell type is requested. It creates an
4679   internal `DMLabel` named "celltype" which can be directly accessed using `DMGetLabel()`. A user may disable
4680   automatic creation by creating the label manually, using `DMCreateLabel`(dm, "celltype").
4681 
4682   `DMPlexComputeCellTypes()` should be called after all calls to `DMPlexSymmetrize()` and `DMPlexStratify()`
4683 
4684 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexStratify()`, `DMGetLabel()`, `DMCreateLabel()`
4685 @*/
4686 PetscErrorCode DMPlexComputeCellTypes(DM dm)
4687 {
4688   DM_Plex *mesh;
4689   DMLabel  ctLabel;
4690   PetscInt pStart, pEnd, p;
4691 
4692   PetscFunctionBegin;
4693   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4694   mesh = (DM_Plex *)dm->data;
4695   PetscCall(DMCreateLabel(dm, "celltype"));
4696   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
4697   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4698   PetscCall(PetscFree(mesh->cellTypes));
4699   PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
4700   for (p = pStart; p < pEnd; ++p) {
4701     DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4702     PetscInt       pdepth;
4703 
4704     PetscCall(DMPlexGetPointDepth(dm, p, &pdepth));
4705     PetscCall(DMPlexComputeCellType_Internal(dm, p, pdepth, &ct));
4706     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]);
4707     PetscCall(DMLabelSetValue(ctLabel, p, ct));
4708     mesh->cellTypes[p - pStart].value_as_uint8 = ct;
4709   }
4710   PetscCall(PetscObjectStateGet((PetscObject)ctLabel, &mesh->celltypeState));
4711   PetscCall(PetscObjectViewFromOptions((PetscObject)ctLabel, NULL, "-dm_plex_celltypes_view"));
4712   PetscFunctionReturn(PETSC_SUCCESS);
4713 }
4714 
4715 /*@C
4716   DMPlexGetJoin - Get an array for the join of the set of points
4717 
4718   Not Collective
4719 
4720   Input Parameters:
4721 + dm        - The `DMPLEX` object
4722 . numPoints - The number of input points for the join
4723 - points    - The input points
4724 
4725   Output Parameters:
4726 + numCoveredPoints - The number of points in the join
4727 - coveredPoints    - The points in the join
4728 
4729   Level: intermediate
4730 
4731   Note:
4732   Currently, this is restricted to a single level join
4733 
4734   Fortran Notes:
4735   `converedPoints` must be declared with
4736 .vb
4737   PetscInt, pointer :: coveredPints(:)
4738 .ve
4739 
4740   The `numCoveredPoints` argument is not present in the Fortran binding.
4741 
4742 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4743 @*/
4744 PetscErrorCode DMPlexGetJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt *coveredPoints[])
4745 {
4746   DM_Plex  *mesh = (DM_Plex *)dm->data;
4747   PetscInt *join[2];
4748   PetscInt  joinSize, i = 0;
4749   PetscInt  dof, off, p, c, m;
4750   PetscInt  maxSupportSize;
4751 
4752   PetscFunctionBegin;
4753   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4754   PetscAssertPointer(points, 3);
4755   PetscAssertPointer(numCoveredPoints, 4);
4756   PetscAssertPointer(coveredPoints, 5);
4757   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
4758   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[0]));
4759   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[1]));
4760   /* Copy in support of first point */
4761   PetscCall(PetscSectionGetDof(mesh->supportSection, points[0], &dof));
4762   PetscCall(PetscSectionGetOffset(mesh->supportSection, points[0], &off));
4763   for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = mesh->supports[off + joinSize];
4764   /* Check each successive support */
4765   for (p = 1; p < numPoints; ++p) {
4766     PetscInt newJoinSize = 0;
4767 
4768     PetscCall(PetscSectionGetDof(mesh->supportSection, points[p], &dof));
4769     PetscCall(PetscSectionGetOffset(mesh->supportSection, points[p], &off));
4770     for (c = 0; c < dof; ++c) {
4771       const PetscInt point = mesh->supports[off + c];
4772 
4773       for (m = 0; m < joinSize; ++m) {
4774         if (point == join[i][m]) {
4775           join[1 - i][newJoinSize++] = point;
4776           break;
4777         }
4778       }
4779     }
4780     joinSize = newJoinSize;
4781     i        = 1 - i;
4782   }
4783   *numCoveredPoints = joinSize;
4784   *coveredPoints    = join[i];
4785   PetscCall(DMRestoreWorkArray(dm, maxSupportSize, MPIU_INT, &join[1 - i]));
4786   PetscFunctionReturn(PETSC_SUCCESS);
4787 }
4788 
4789 /*@C
4790   DMPlexRestoreJoin - Restore an array for the join of the set of points obtained with `DMPlexGetJoin()`
4791 
4792   Not Collective
4793 
4794   Input Parameters:
4795 + dm        - The `DMPLEX` object
4796 . numPoints - The number of input points for the join
4797 - points    - The input points
4798 
4799   Output Parameters:
4800 + numCoveredPoints - The number of points in the join
4801 - coveredPoints    - The points in the join
4802 
4803   Level: intermediate
4804 
4805   Fortran Notes:
4806   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4807 
4808 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetJoin()`, `DMPlexGetFullJoin()`, `DMPlexGetMeet()`
4809 @*/
4810 PetscErrorCode DMPlexRestoreJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt *coveredPoints[])
4811 {
4812   PetscFunctionBegin;
4813   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4814   if (points) PetscAssertPointer(points, 3);
4815   if (numCoveredPoints) PetscAssertPointer(numCoveredPoints, 4);
4816   PetscAssertPointer(coveredPoints, 5);
4817   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
4818   if (numCoveredPoints) *numCoveredPoints = 0;
4819   PetscFunctionReturn(PETSC_SUCCESS);
4820 }
4821 
4822 /*@C
4823   DMPlexGetFullJoin - Get an array for the join of the set of points
4824 
4825   Not Collective
4826 
4827   Input Parameters:
4828 + dm        - The `DMPLEX` object
4829 . numPoints - The number of input points for the join
4830 - points    - The input points, its length is `numPoints`
4831 
4832   Output Parameters:
4833 + numCoveredPoints - The number of points in the join
4834 - coveredPoints    - The points in the join, its length is `numCoveredPoints`
4835 
4836   Level: intermediate
4837 
4838   Fortran Notes:
4839   `points` and `converedPoints` must be declared with
4840 .vb
4841   PetscInt, pointer :: points(:)
4842   PetscInt, pointer :: coveredPints(:)
4843 .ve
4844 
4845   The `numCoveredPoints` argument is not present in the Fortran binding.
4846 
4847 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetJoin()`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4848 @*/
4849 PetscErrorCode DMPlexGetFullJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt *coveredPoints[])
4850 {
4851   PetscInt *offsets, **closures;
4852   PetscInt *join[2];
4853   PetscInt  depth = 0, maxSize, joinSize = 0, i = 0;
4854   PetscInt  p, d, c, m, ms;
4855 
4856   PetscFunctionBegin;
4857   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4858   PetscAssertPointer(points, 3);
4859   PetscAssertPointer(numCoveredPoints, 4);
4860   PetscAssertPointer(coveredPoints, 5);
4861 
4862   PetscCall(DMPlexGetDepth(dm, &depth));
4863   PetscCall(PetscCalloc1(numPoints, &closures));
4864   PetscCall(DMGetWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
4865   PetscCall(DMPlexGetMaxSizes(dm, NULL, &ms));
4866   maxSize = (ms > 1) ? ((PetscPowInt(ms, depth + 1) - 1) / (ms - 1)) : depth + 1;
4867   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[0]));
4868   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[1]));
4869 
4870   for (p = 0; p < numPoints; ++p) {
4871     PetscInt closureSize;
4872 
4873     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_FALSE, &closureSize, &closures[p]));
4874 
4875     offsets[p * (depth + 2) + 0] = 0;
4876     for (d = 0; d < depth + 1; ++d) {
4877       PetscInt pStart, pEnd, i;
4878 
4879       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
4880       for (i = offsets[p * (depth + 2) + d]; i < closureSize; ++i) {
4881         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
4882           offsets[p * (depth + 2) + d + 1] = i;
4883           break;
4884         }
4885       }
4886       if (i == closureSize) offsets[p * (depth + 2) + d + 1] = i;
4887     }
4888     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);
4889   }
4890   for (d = 0; d < depth + 1; ++d) {
4891     PetscInt dof;
4892 
4893     /* Copy in support of first point */
4894     dof = offsets[d + 1] - offsets[d];
4895     for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = closures[0][(offsets[d] + joinSize) * 2];
4896     /* Check each successive cone */
4897     for (p = 1; p < numPoints && joinSize; ++p) {
4898       PetscInt newJoinSize = 0;
4899 
4900       dof = offsets[p * (depth + 2) + d + 1] - offsets[p * (depth + 2) + d];
4901       for (c = 0; c < dof; ++c) {
4902         const PetscInt point = closures[p][(offsets[p * (depth + 2) + d] + c) * 2];
4903 
4904         for (m = 0; m < joinSize; ++m) {
4905           if (point == join[i][m]) {
4906             join[1 - i][newJoinSize++] = point;
4907             break;
4908           }
4909         }
4910       }
4911       joinSize = newJoinSize;
4912       i        = 1 - i;
4913     }
4914     if (joinSize) break;
4915   }
4916   *numCoveredPoints = joinSize;
4917   *coveredPoints    = join[i];
4918   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_FALSE, NULL, &closures[p]));
4919   PetscCall(PetscFree(closures));
4920   PetscCall(DMRestoreWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
4921   PetscCall(DMRestoreWorkArray(dm, ms, MPIU_INT, &join[1 - i]));
4922   PetscFunctionReturn(PETSC_SUCCESS);
4923 }
4924 
4925 /*@C
4926   DMPlexGetMeet - Get an array for the meet of the set of points
4927 
4928   Not Collective
4929 
4930   Input Parameters:
4931 + dm        - The `DMPLEX` object
4932 . numPoints - The number of input points for the meet
4933 - points    - The input points, of length `numPoints`
4934 
4935   Output Parameters:
4936 + numCoveringPoints - The number of points in the meet
4937 - coveringPoints    - The points in the meet, of length `numCoveringPoints`
4938 
4939   Level: intermediate
4940 
4941   Note:
4942   Currently, this is restricted to a single level meet
4943 
4944   Fortran Notes:
4945   `coveringPoints` must be declared with
4946 .vb
4947   PetscInt, pointer :: coveringPoints(:)
4948 .ve
4949 
4950   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4951 
4952 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
4953 @*/
4954 PetscErrorCode DMPlexGetMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveringPoints, const PetscInt *coveringPoints[])
4955 {
4956   DM_Plex  *mesh = (DM_Plex *)dm->data;
4957   PetscInt *meet[2];
4958   PetscInt  meetSize, i = 0;
4959   PetscInt  dof, off, p, c, m;
4960   PetscInt  maxConeSize;
4961 
4962   PetscFunctionBegin;
4963   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4964   PetscAssertPointer(points, 3);
4965   PetscAssertPointer(numCoveringPoints, 4);
4966   PetscAssertPointer(coveringPoints, 5);
4967   PetscCall(PetscSectionGetMaxDof(mesh->coneSection, &maxConeSize));
4968   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[0]));
4969   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[1]));
4970   /* Copy in cone of first point */
4971   PetscCall(PetscSectionGetDof(mesh->coneSection, points[0], &dof));
4972   PetscCall(PetscSectionGetOffset(mesh->coneSection, points[0], &off));
4973   for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = mesh->cones[off + meetSize];
4974   /* Check each successive cone */
4975   for (p = 1; p < numPoints; ++p) {
4976     PetscInt newMeetSize = 0;
4977 
4978     PetscCall(PetscSectionGetDof(mesh->coneSection, points[p], &dof));
4979     PetscCall(PetscSectionGetOffset(mesh->coneSection, points[p], &off));
4980     for (c = 0; c < dof; ++c) {
4981       const PetscInt point = mesh->cones[off + c];
4982 
4983       for (m = 0; m < meetSize; ++m) {
4984         if (point == meet[i][m]) {
4985           meet[1 - i][newMeetSize++] = point;
4986           break;
4987         }
4988       }
4989     }
4990     meetSize = newMeetSize;
4991     i        = 1 - i;
4992   }
4993   *numCoveringPoints = meetSize;
4994   *coveringPoints    = meet[i];
4995   PetscCall(DMRestoreWorkArray(dm, maxConeSize, MPIU_INT, &meet[1 - i]));
4996   PetscFunctionReturn(PETSC_SUCCESS);
4997 }
4998 
4999 /*@C
5000   DMPlexRestoreMeet - Restore an array for the meet of the set of points obtained with `DMPlexGetMeet()`
5001 
5002   Not Collective
5003 
5004   Input Parameters:
5005 + dm        - The `DMPLEX` object
5006 . numPoints - The number of input points for the meet
5007 - points    - The input points
5008 
5009   Output Parameters:
5010 + numCoveredPoints - The number of points in the meet
5011 - coveredPoints    - The points in the meet
5012 
5013   Level: intermediate
5014 
5015   Fortran Notes:
5016   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
5017 
5018 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetMeet()`, `DMPlexGetFullMeet()`, `DMPlexGetJoin()`
5019 @*/
5020 PetscErrorCode DMPlexRestoreMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt *coveredPoints[])
5021 {
5022   PetscFunctionBegin;
5023   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5024   if (points) PetscAssertPointer(points, 3);
5025   if (numCoveredPoints) PetscAssertPointer(numCoveredPoints, 4);
5026   PetscAssertPointer(coveredPoints, 5);
5027   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
5028   if (numCoveredPoints) *numCoveredPoints = 0;
5029   PetscFunctionReturn(PETSC_SUCCESS);
5030 }
5031 
5032 /*@C
5033   DMPlexGetFullMeet - Get an array for the meet of the set of points
5034 
5035   Not Collective
5036 
5037   Input Parameters:
5038 + dm        - The `DMPLEX` object
5039 . numPoints - The number of input points for the meet
5040 - points    - The input points, of length  `numPoints`
5041 
5042   Output Parameters:
5043 + numCoveredPoints - The number of points in the meet
5044 - coveredPoints    - The points in the meet, of length  `numCoveredPoints`
5045 
5046   Level: intermediate
5047 
5048   Fortran Notes:
5049   `points` and `coveredPoints` must be declared with
5050 .vb
5051   PetscInt, pointer :: points(:)
5052   PetscInt, pointer :: coveredPoints(:)
5053 .ve
5054 
5055   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
5056 
5057 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetMeet()`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
5058 @*/
5059 PetscErrorCode DMPlexGetFullMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt *coveredPoints[])
5060 {
5061   PetscInt *offsets, **closures;
5062   PetscInt *meet[2];
5063   PetscInt  height = 0, maxSize, meetSize = 0, i = 0;
5064   PetscInt  p, h, c, m, mc;
5065 
5066   PetscFunctionBegin;
5067   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5068   PetscAssertPointer(points, 3);
5069   PetscAssertPointer(numCoveredPoints, 4);
5070   PetscAssertPointer(coveredPoints, 5);
5071 
5072   PetscCall(DMPlexGetDepth(dm, &height));
5073   PetscCall(PetscMalloc1(numPoints, &closures));
5074   PetscCall(DMGetWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
5075   PetscCall(DMPlexGetMaxSizes(dm, &mc, NULL));
5076   maxSize = (mc > 1) ? ((PetscPowInt(mc, height + 1) - 1) / (mc - 1)) : height + 1;
5077   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[0]));
5078   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[1]));
5079 
5080   for (p = 0; p < numPoints; ++p) {
5081     PetscInt closureSize;
5082 
5083     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_TRUE, &closureSize, &closures[p]));
5084 
5085     offsets[p * (height + 2) + 0] = 0;
5086     for (h = 0; h < height + 1; ++h) {
5087       PetscInt pStart, pEnd, i;
5088 
5089       PetscCall(DMPlexGetHeightStratum(dm, h, &pStart, &pEnd));
5090       for (i = offsets[p * (height + 2) + h]; i < closureSize; ++i) {
5091         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
5092           offsets[p * (height + 2) + h + 1] = i;
5093           break;
5094         }
5095       }
5096       if (i == closureSize) offsets[p * (height + 2) + h + 1] = i;
5097     }
5098     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);
5099   }
5100   for (h = 0; h < height + 1; ++h) {
5101     PetscInt dof;
5102 
5103     /* Copy in cone of first point */
5104     dof = offsets[h + 1] - offsets[h];
5105     for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = closures[0][(offsets[h] + meetSize) * 2];
5106     /* Check each successive cone */
5107     for (p = 1; p < numPoints && meetSize; ++p) {
5108       PetscInt newMeetSize = 0;
5109 
5110       dof = offsets[p * (height + 2) + h + 1] - offsets[p * (height + 2) + h];
5111       for (c = 0; c < dof; ++c) {
5112         const PetscInt point = closures[p][(offsets[p * (height + 2) + h] + c) * 2];
5113 
5114         for (m = 0; m < meetSize; ++m) {
5115           if (point == meet[i][m]) {
5116             meet[1 - i][newMeetSize++] = point;
5117             break;
5118           }
5119         }
5120       }
5121       meetSize = newMeetSize;
5122       i        = 1 - i;
5123     }
5124     if (meetSize) break;
5125   }
5126   *numCoveredPoints = meetSize;
5127   *coveredPoints    = meet[i];
5128   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_TRUE, NULL, &closures[p]));
5129   PetscCall(PetscFree(closures));
5130   PetscCall(DMRestoreWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
5131   PetscCall(DMRestoreWorkArray(dm, mc, MPIU_INT, &meet[1 - i]));
5132   PetscFunctionReturn(PETSC_SUCCESS);
5133 }
5134 
5135 /*@
5136   DMPlexEqual - Determine if two `DM` have the same topology
5137 
5138   Not Collective
5139 
5140   Input Parameters:
5141 + dmA - A `DMPLEX` object
5142 - dmB - A `DMPLEX` object
5143 
5144   Output Parameter:
5145 . equal - `PETSC_TRUE` if the topologies are identical
5146 
5147   Level: intermediate
5148 
5149   Note:
5150   We are not solving graph isomorphism, so we do not permute.
5151 
5152 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCone()`
5153 @*/
5154 PetscErrorCode DMPlexEqual(DM dmA, DM dmB, PetscBool *equal)
5155 {
5156   PetscInt depth, depthB, pStart, pEnd, pStartB, pEndB, p;
5157 
5158   PetscFunctionBegin;
5159   PetscValidHeaderSpecific(dmA, DM_CLASSID, 1);
5160   PetscValidHeaderSpecific(dmB, DM_CLASSID, 2);
5161   PetscAssertPointer(equal, 3);
5162 
5163   *equal = PETSC_FALSE;
5164   PetscCall(DMPlexGetDepth(dmA, &depth));
5165   PetscCall(DMPlexGetDepth(dmB, &depthB));
5166   if (depth != depthB) PetscFunctionReturn(PETSC_SUCCESS);
5167   PetscCall(DMPlexGetChart(dmA, &pStart, &pEnd));
5168   PetscCall(DMPlexGetChart(dmB, &pStartB, &pEndB));
5169   if ((pStart != pStartB) || (pEnd != pEndB)) PetscFunctionReturn(PETSC_SUCCESS);
5170   for (p = pStart; p < pEnd; ++p) {
5171     const PetscInt *cone, *coneB, *ornt, *orntB, *support, *supportB;
5172     PetscInt        coneSize, coneSizeB, c, supportSize, supportSizeB, s;
5173 
5174     PetscCall(DMPlexGetConeSize(dmA, p, &coneSize));
5175     PetscCall(DMPlexGetCone(dmA, p, &cone));
5176     PetscCall(DMPlexGetConeOrientation(dmA, p, &ornt));
5177     PetscCall(DMPlexGetConeSize(dmB, p, &coneSizeB));
5178     PetscCall(DMPlexGetCone(dmB, p, &coneB));
5179     PetscCall(DMPlexGetConeOrientation(dmB, p, &orntB));
5180     if (coneSize != coneSizeB) PetscFunctionReturn(PETSC_SUCCESS);
5181     for (c = 0; c < coneSize; ++c) {
5182       if (cone[c] != coneB[c]) PetscFunctionReturn(PETSC_SUCCESS);
5183       if (ornt[c] != orntB[c]) PetscFunctionReturn(PETSC_SUCCESS);
5184     }
5185     PetscCall(DMPlexGetSupportSize(dmA, p, &supportSize));
5186     PetscCall(DMPlexGetSupport(dmA, p, &support));
5187     PetscCall(DMPlexGetSupportSize(dmB, p, &supportSizeB));
5188     PetscCall(DMPlexGetSupport(dmB, p, &supportB));
5189     if (supportSize != supportSizeB) PetscFunctionReturn(PETSC_SUCCESS);
5190     for (s = 0; s < supportSize; ++s) {
5191       if (support[s] != supportB[s]) PetscFunctionReturn(PETSC_SUCCESS);
5192     }
5193   }
5194   *equal = PETSC_TRUE;
5195   PetscFunctionReturn(PETSC_SUCCESS);
5196 }
5197 
5198 /*@
5199   DMPlexGetNumFaceVertices - Returns the number of vertices on a face
5200 
5201   Not Collective
5202 
5203   Input Parameters:
5204 + dm         - The `DMPLEX`
5205 . cellDim    - The cell dimension
5206 - numCorners - The number of vertices on a cell
5207 
5208   Output Parameter:
5209 . numFaceVertices - The number of vertices on a face
5210 
5211   Level: developer
5212 
5213   Note:
5214   Of course this can only work for a restricted set of symmetric shapes
5215 
5216 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCone()`
5217 @*/
5218 PetscErrorCode DMPlexGetNumFaceVertices(DM dm, PetscInt cellDim, PetscInt numCorners, PetscInt *numFaceVertices)
5219 {
5220   MPI_Comm comm;
5221 
5222   PetscFunctionBegin;
5223   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
5224   PetscAssertPointer(numFaceVertices, 4);
5225   switch (cellDim) {
5226   case 0:
5227     *numFaceVertices = 0;
5228     break;
5229   case 1:
5230     *numFaceVertices = 1;
5231     break;
5232   case 2:
5233     switch (numCorners) {
5234     case 3:                 /* triangle */
5235       *numFaceVertices = 2; /* Edge has 2 vertices */
5236       break;
5237     case 4:                 /* quadrilateral */
5238       *numFaceVertices = 2; /* Edge has 2 vertices */
5239       break;
5240     case 6:                 /* quadratic triangle, tri and quad cohesive Lagrange cells */
5241       *numFaceVertices = 3; /* Edge has 3 vertices */
5242       break;
5243     case 9:                 /* quadratic quadrilateral, quadratic quad cohesive Lagrange cells */
5244       *numFaceVertices = 3; /* Edge has 3 vertices */
5245       break;
5246     default:
5247       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
5248     }
5249     break;
5250   case 3:
5251     switch (numCorners) {
5252     case 4:                 /* tetradehdron */
5253       *numFaceVertices = 3; /* Face has 3 vertices */
5254       break;
5255     case 6:                 /* tet cohesive cells */
5256       *numFaceVertices = 4; /* Face has 4 vertices */
5257       break;
5258     case 8:                 /* hexahedron */
5259       *numFaceVertices = 4; /* Face has 4 vertices */
5260       break;
5261     case 9:                 /* tet cohesive Lagrange cells */
5262       *numFaceVertices = 6; /* Face has 6 vertices */
5263       break;
5264     case 10:                /* quadratic tetrahedron */
5265       *numFaceVertices = 6; /* Face has 6 vertices */
5266       break;
5267     case 12:                /* hex cohesive Lagrange cells */
5268       *numFaceVertices = 6; /* Face has 6 vertices */
5269       break;
5270     case 18:                /* quadratic tet cohesive Lagrange cells */
5271       *numFaceVertices = 6; /* Face has 6 vertices */
5272       break;
5273     case 27:                /* quadratic hexahedron, quadratic hex cohesive Lagrange cells */
5274       *numFaceVertices = 9; /* Face has 9 vertices */
5275       break;
5276     default:
5277       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
5278     }
5279     break;
5280   default:
5281     SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid cell dimension %" PetscInt_FMT, cellDim);
5282   }
5283   PetscFunctionReturn(PETSC_SUCCESS);
5284 }
5285 
5286 /*@
5287   DMPlexGetDepthLabel - Get the `DMLabel` recording the depth of each point
5288 
5289   Not Collective
5290 
5291   Input Parameter:
5292 . dm - The `DMPLEX` object
5293 
5294   Output Parameter:
5295 . depthLabel - The `DMLabel` recording point depth
5296 
5297   Level: developer
5298 
5299 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepth()`, `DMPlexGetHeightStratum()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`,
5300 @*/
5301 PetscErrorCode DMPlexGetDepthLabel(DM dm, DMLabel *depthLabel)
5302 {
5303   PetscFunctionBegin;
5304   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5305   PetscAssertPointer(depthLabel, 2);
5306   *depthLabel = dm->depthLabel;
5307   PetscFunctionReturn(PETSC_SUCCESS);
5308 }
5309 
5310 /*@
5311   DMPlexGetDepth - Get the depth of the DAG representing this mesh
5312 
5313   Not Collective
5314 
5315   Input Parameter:
5316 . dm - The `DMPLEX` object
5317 
5318   Output Parameter:
5319 . depth - The number of strata (breadth first levels) in the DAG
5320 
5321   Level: developer
5322 
5323   Notes:
5324   This returns maximum of point depths over all points, i.e. maximum value of the label returned by `DMPlexGetDepthLabel()`.
5325 
5326   The point depth is described more in detail in `DMPlexGetDepthStratum()`.
5327 
5328   An empty mesh gives -1.
5329 
5330 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepthLabel()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`
5331 @*/
5332 PetscErrorCode DMPlexGetDepth(DM dm, PetscInt *depth)
5333 {
5334   DM_Plex *mesh = (DM_Plex *)dm->data;
5335   DMLabel  label;
5336   PetscInt d = -1;
5337 
5338   PetscFunctionBegin;
5339   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5340   PetscAssertPointer(depth, 2);
5341   if (mesh->tr) {
5342     PetscCall(DMPlexTransformGetDepth(mesh->tr, depth));
5343   } else {
5344     PetscCall(DMPlexGetDepthLabel(dm, &label));
5345     // Allow missing depths
5346     if (label) PetscCall(DMLabelGetValueBounds(label, NULL, &d));
5347     *depth = d;
5348   }
5349   PetscFunctionReturn(PETSC_SUCCESS);
5350 }
5351 
5352 /*@
5353   DMPlexGetDepthStratum - Get the bounds [`start`, `end`) for all points at a certain depth.
5354 
5355   Not Collective
5356 
5357   Input Parameters:
5358 + dm    - The `DMPLEX` object
5359 - depth - The requested depth
5360 
5361   Output Parameters:
5362 + start - The first point at this `depth`
5363 - end   - One beyond the last point at this `depth`
5364 
5365   Level: developer
5366 
5367   Notes:
5368   Depth indexing is related to topological dimension.  Depth stratum 0 contains the lowest topological dimension points,
5369   often "vertices".  If the mesh is "interpolated" (see `DMPlexInterpolate()`), then depth stratum 1 contains the next
5370   higher dimension, e.g., "edges".
5371 
5372 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetHeightStratum()`, `DMPlexGetCellTypeStratum()`, `DMPlexGetDepth()`, `DMPlexGetDepthLabel()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`, `DMPlexInterpolate()`
5373 @*/
5374 PetscErrorCode DMPlexGetDepthStratum(DM dm, PetscInt depth, PetscInt *start, PetscInt *end)
5375 {
5376   DM_Plex *mesh = (DM_Plex *)dm->data;
5377   DMLabel  label;
5378   PetscInt pStart, pEnd;
5379 
5380   PetscFunctionBegin;
5381   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5382   if (start) {
5383     PetscAssertPointer(start, 3);
5384     *start = 0;
5385   }
5386   if (end) {
5387     PetscAssertPointer(end, 4);
5388     *end = 0;
5389   }
5390   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
5391   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
5392   if (depth < 0) {
5393     if (start) *start = pStart;
5394     if (end) *end = pEnd;
5395     PetscFunctionReturn(PETSC_SUCCESS);
5396   }
5397   if (mesh->tr) {
5398     PetscCall(DMPlexTransformGetDepthStratum(mesh->tr, depth, start, end));
5399   } else {
5400     PetscCall(DMPlexGetDepthLabel(dm, &label));
5401     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named depth was found");
5402     PetscCall(DMLabelGetStratumBounds(label, depth, start, end));
5403   }
5404   PetscFunctionReturn(PETSC_SUCCESS);
5405 }
5406 
5407 /*@
5408   DMPlexGetHeightStratum - Get the bounds [`start`, `end`) for all points at a certain height.
5409 
5410   Not Collective
5411 
5412   Input Parameters:
5413 + dm     - The `DMPLEX` object
5414 - height - The requested height
5415 
5416   Output Parameters:
5417 + start - The first point at this `height`
5418 - end   - One beyond the last point at this `height`
5419 
5420   Level: developer
5421 
5422   Notes:
5423   Height indexing is related to topological codimension.  Height stratum 0 contains the highest topological dimension
5424   points, often called "cells" or "elements".  If the mesh is "interpolated" (see `DMPlexInterpolate()`), then height
5425   stratum 1 contains the boundary of these "cells", often called "faces" or "facets".
5426 
5427 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepthStratum()`, `DMPlexGetCellTypeStratum()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
5428 @*/
5429 PetscErrorCode DMPlexGetHeightStratum(DM dm, PetscInt height, PetscInt *start, PetscInt *end)
5430 {
5431   DMLabel  label;
5432   PetscInt depth, pStart, pEnd;
5433 
5434   PetscFunctionBegin;
5435   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5436   if (start) {
5437     PetscAssertPointer(start, 3);
5438     *start = 0;
5439   }
5440   if (end) {
5441     PetscAssertPointer(end, 4);
5442     *end = 0;
5443   }
5444   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
5445   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
5446   if (height < 0) {
5447     if (start) *start = pStart;
5448     if (end) *end = pEnd;
5449     PetscFunctionReturn(PETSC_SUCCESS);
5450   }
5451   PetscCall(DMPlexGetDepthLabel(dm, &label));
5452   if (label) PetscCall(DMLabelGetNumValues(label, &depth));
5453   else PetscCall(DMGetDimension(dm, &depth));
5454   PetscCheck(depth >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Depth not yet computed");
5455   PetscCall(DMPlexGetDepthStratum(dm, depth - 1 - height, start, end));
5456   PetscFunctionReturn(PETSC_SUCCESS);
5457 }
5458 
5459 /*@
5460   DMPlexGetPointDepth - Get the `depth` of a given point
5461 
5462   Not Collective
5463 
5464   Input Parameters:
5465 + dm    - The `DMPLEX` object
5466 - point - The point
5467 
5468   Output Parameter:
5469 . depth - The depth of the `point`
5470 
5471   Level: intermediate
5472 
5473 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
5474 @*/
5475 PetscErrorCode DMPlexGetPointDepth(DM dm, PetscInt point, PetscInt *depth)
5476 {
5477   PetscFunctionBegin;
5478   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5479   PetscAssertPointer(depth, 3);
5480   PetscCall(DMLabelGetValue(dm->depthLabel, point, depth));
5481   PetscFunctionReturn(PETSC_SUCCESS);
5482 }
5483 
5484 /*@
5485   DMPlexGetPointHeight - Get the `height` of a given point
5486 
5487   Not Collective
5488 
5489   Input Parameters:
5490 + dm    - The `DMPLEX` object
5491 - point - The point
5492 
5493   Output Parameter:
5494 . height - The height of the `point`
5495 
5496   Level: intermediate
5497 
5498 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointDepth()`
5499 @*/
5500 PetscErrorCode DMPlexGetPointHeight(DM dm, PetscInt point, PetscInt *height)
5501 {
5502   PetscInt n, pDepth;
5503 
5504   PetscFunctionBegin;
5505   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5506   PetscAssertPointer(height, 3);
5507   PetscCall(DMLabelGetNumValues(dm->depthLabel, &n));
5508   PetscCall(DMLabelGetValue(dm->depthLabel, point, &pDepth));
5509   *height = n - 1 - pDepth; /* DAG depth is n-1 */
5510   PetscFunctionReturn(PETSC_SUCCESS);
5511 }
5512 
5513 /*@
5514   DMPlexGetCellTypeLabel - Get the `DMLabel` recording the polytope type of each cell
5515 
5516   Not Collective
5517 
5518   Input Parameter:
5519 . dm - The `DMPLEX` object
5520 
5521   Output Parameter:
5522 . celltypeLabel - The `DMLabel` recording cell polytope type
5523 
5524   Level: developer
5525 
5526   Note:
5527   This function will trigger automatica computation of cell types. This can be disabled by calling
5528   `DMCreateLabel`(dm, "celltype") beforehand.
5529 
5530 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMCreateLabel()`
5531 @*/
5532 PetscErrorCode DMPlexGetCellTypeLabel(DM dm, DMLabel *celltypeLabel)
5533 {
5534   PetscFunctionBegin;
5535   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5536   PetscAssertPointer(celltypeLabel, 2);
5537   if (!dm->celltypeLabel) PetscCall(DMPlexComputeCellTypes(dm));
5538   *celltypeLabel = dm->celltypeLabel;
5539   PetscFunctionReturn(PETSC_SUCCESS);
5540 }
5541 
5542 /*@
5543   DMPlexGetCellType - Get the polytope type of a given cell
5544 
5545   Not Collective
5546 
5547   Input Parameters:
5548 + dm   - The `DMPLEX` object
5549 - cell - The cell
5550 
5551   Output Parameter:
5552 . celltype - The polytope type of the cell
5553 
5554   Level: intermediate
5555 
5556 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPolytopeType`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`
5557 @*/
5558 PetscErrorCode DMPlexGetCellType(DM dm, PetscInt cell, DMPolytopeType *celltype)
5559 {
5560   DM_Plex *mesh = (DM_Plex *)dm->data;
5561   DMLabel  label;
5562   PetscInt ct;
5563 
5564   PetscFunctionBegin;
5565   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5566   PetscAssertPointer(celltype, 3);
5567   if (mesh->tr) {
5568     PetscCall(DMPlexTransformGetCellType(mesh->tr, cell, celltype));
5569   } else {
5570     PetscInt pStart, pEnd;
5571 
5572     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, NULL));
5573     if (!mesh->cellTypes) { /* XXX remove? optimize? */
5574       PetscCall(PetscSectionGetChart(mesh->coneSection, NULL, &pEnd));
5575       PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
5576       PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5577       for (PetscInt p = pStart; p < pEnd; p++) {
5578         PetscCall(DMLabelGetValue(label, p, &ct));
5579         mesh->cellTypes[p - pStart].value_as_uint8 = (DMPolytopeType)ct;
5580       }
5581     }
5582     *celltype = (DMPolytopeType)mesh->cellTypes[cell - pStart].value_as_uint8;
5583     if (PetscDefined(USE_DEBUG)) {
5584       PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5585       PetscCall(DMLabelGetValue(label, cell, &ct));
5586       PetscCheck(ct >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Cell %" PetscInt_FMT " has not been assigned a cell type", cell);
5587       PetscCheck(ct == (PetscInt)*celltype, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid cellType for %" PetscInt_FMT ": %d != %" PetscInt_FMT, cell, (int)*celltype, ct);
5588     }
5589   }
5590   PetscFunctionReturn(PETSC_SUCCESS);
5591 }
5592 
5593 /*@
5594   DMPlexSetCellType - Set the polytope type of a given cell
5595 
5596   Not Collective
5597 
5598   Input Parameters:
5599 + dm       - The `DMPLEX` object
5600 . cell     - The cell
5601 - celltype - The polytope type of the cell
5602 
5603   Level: advanced
5604 
5605   Note:
5606   By default, cell types will be automatically computed using `DMPlexComputeCellTypes()` before this function
5607   is executed. This function will override the computed type. However, if automatic classification will not succeed
5608   and a user wants to manually specify all types, the classification must be disabled by calling
5609   DMCreateLabel(dm, "celltype") before getting or setting any cell types.
5610 
5611 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexComputeCellTypes()`, `DMCreateLabel()`
5612 @*/
5613 PetscErrorCode DMPlexSetCellType(DM dm, PetscInt cell, DMPolytopeType celltype)
5614 {
5615   DM_Plex *mesh = (DM_Plex *)dm->data;
5616   DMLabel  label;
5617   PetscInt pStart, pEnd;
5618 
5619   PetscFunctionBegin;
5620   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5621   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
5622   PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5623   PetscCall(DMLabelSetValue(label, cell, celltype));
5624   if (!mesh->cellTypes) PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
5625   mesh->cellTypes[cell - pStart].value_as_uint8 = celltype;
5626   PetscFunctionReturn(PETSC_SUCCESS);
5627 }
5628 
5629 PetscErrorCode DMCreateCoordinateDM_Plex(DM dm, DM *cdm)
5630 {
5631   PetscSection section;
5632   PetscInt     maxHeight;
5633   const char  *prefix;
5634 
5635   PetscFunctionBegin;
5636   PetscCall(DMClone(dm, cdm));
5637   PetscCall(PetscObjectGetOptionsPrefix((PetscObject)dm, &prefix));
5638   PetscCall(PetscObjectSetOptionsPrefix((PetscObject)*cdm, prefix));
5639   PetscCall(PetscObjectAppendOptionsPrefix((PetscObject)*cdm, "cdm_"));
5640   PetscCall(DMPlexGetMaxProjectionHeight(dm, &maxHeight));
5641   PetscCall(DMPlexSetMaxProjectionHeight(*cdm, maxHeight));
5642   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
5643   PetscCall(DMSetLocalSection(*cdm, section));
5644   PetscCall(PetscSectionDestroy(&section));
5645 
5646   PetscCall(DMSetNumFields(*cdm, 1));
5647   PetscCall(DMCreateDS(*cdm));
5648   (*cdm)->cloneOpts = PETSC_TRUE;
5649   if (dm->setfromoptionscalled) PetscCall(DMSetFromOptions(*cdm));
5650   PetscFunctionReturn(PETSC_SUCCESS);
5651 }
5652 
5653 PetscErrorCode DMCreateCoordinateField_Plex(DM dm, DMField *field)
5654 {
5655   Vec coordsLocal, cellCoordsLocal;
5656   DM  coordsDM, cellCoordsDM;
5657 
5658   PetscFunctionBegin;
5659   *field = NULL;
5660   PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
5661   PetscCall(DMGetCoordinateDM(dm, &coordsDM));
5662   PetscCall(DMGetCellCoordinatesLocal(dm, &cellCoordsLocal));
5663   PetscCall(DMGetCellCoordinateDM(dm, &cellCoordsDM));
5664   if (coordsLocal && coordsDM) {
5665     if (cellCoordsLocal && cellCoordsDM) PetscCall(DMFieldCreateDSWithDG(coordsDM, cellCoordsDM, 0, coordsLocal, cellCoordsLocal, field));
5666     else PetscCall(DMFieldCreateDS(coordsDM, 0, coordsLocal, field));
5667   }
5668   PetscFunctionReturn(PETSC_SUCCESS);
5669 }
5670 
5671 /*@
5672   DMPlexGetConeSection - Return a section which describes the layout of cone data
5673 
5674   Not Collective
5675 
5676   Input Parameter:
5677 . dm - The `DMPLEX` object
5678 
5679   Output Parameter:
5680 . section - The `PetscSection` object
5681 
5682   Level: developer
5683 
5684 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSupportSection()`, `DMPlexGetCones()`, `DMPlexGetConeOrientations()`, `PetscSection`
5685 @*/
5686 PetscErrorCode DMPlexGetConeSection(DM dm, PetscSection *section)
5687 {
5688   DM_Plex *mesh = (DM_Plex *)dm->data;
5689 
5690   PetscFunctionBegin;
5691   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5692   if (section) *section = mesh->coneSection;
5693   PetscFunctionReturn(PETSC_SUCCESS);
5694 }
5695 
5696 /*@
5697   DMPlexGetSupportSection - Return a section which describes the layout of support data
5698 
5699   Not Collective
5700 
5701   Input Parameter:
5702 . dm - The `DMPLEX` object
5703 
5704   Output Parameter:
5705 . section - The `PetscSection` object
5706 
5707   Level: developer
5708 
5709 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `PetscSection`
5710 @*/
5711 PetscErrorCode DMPlexGetSupportSection(DM dm, PetscSection *section)
5712 {
5713   DM_Plex *mesh = (DM_Plex *)dm->data;
5714 
5715   PetscFunctionBegin;
5716   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5717   if (section) *section = mesh->supportSection;
5718   PetscFunctionReturn(PETSC_SUCCESS);
5719 }
5720 
5721 /*@C
5722   DMPlexGetCones - Return cone data
5723 
5724   Not Collective
5725 
5726   Input Parameter:
5727 . dm - The `DMPLEX` object
5728 
5729   Output Parameter:
5730 . cones - The cone for each point
5731 
5732   Level: developer
5733 
5734 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`
5735 @*/
5736 PetscErrorCode DMPlexGetCones(DM dm, PetscInt *cones[])
5737 {
5738   DM_Plex *mesh = (DM_Plex *)dm->data;
5739 
5740   PetscFunctionBegin;
5741   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5742   if (cones) *cones = mesh->cones;
5743   PetscFunctionReturn(PETSC_SUCCESS);
5744 }
5745 
5746 /*@C
5747   DMPlexGetConeOrientations - Return cone orientation data
5748 
5749   Not Collective
5750 
5751   Input Parameter:
5752 . dm - The `DMPLEX` object
5753 
5754   Output Parameter:
5755 . coneOrientations - The array of cone orientations for all points
5756 
5757   Level: developer
5758 
5759   Notes:
5760   The `PetscSection` returned by `DMPlexGetConeSection()` partitions coneOrientations into cone orientations of particular points
5761   as returned by `DMPlexGetConeOrientation()`.
5762 
5763   The meaning of coneOrientations values is detailed in `DMPlexGetConeOrientation()`.
5764 
5765 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `DMPlexGetConeOrientation()`, `PetscSection`
5766 @*/
5767 PetscErrorCode DMPlexGetConeOrientations(DM dm, PetscInt *coneOrientations[])
5768 {
5769   DM_Plex *mesh = (DM_Plex *)dm->data;
5770 
5771   PetscFunctionBegin;
5772   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5773   if (coneOrientations) *coneOrientations = mesh->coneOrientations;
5774   PetscFunctionReturn(PETSC_SUCCESS);
5775 }
5776 
5777 /******************************** FEM Support **********************************/
5778 
5779 PetscErrorCode DMPlexGetAllCells_Internal(DM plex, IS *cellIS)
5780 {
5781   PetscInt depth;
5782 
5783   PetscFunctionBegin;
5784   PetscCall(DMPlexGetDepth(plex, &depth));
5785   PetscCall(DMGetStratumIS(plex, "dim", depth, cellIS));
5786   if (!*cellIS) PetscCall(DMGetStratumIS(plex, "depth", depth, cellIS));
5787   PetscFunctionReturn(PETSC_SUCCESS);
5788 }
5789 
5790 PetscErrorCode DMPlexGetAllFaces_Internal(DM plex, IS *faceIS)
5791 {
5792   PetscInt depth;
5793 
5794   PetscFunctionBegin;
5795   PetscCall(DMPlexGetDepth(plex, &depth));
5796   PetscCall(DMGetStratumIS(plex, "dim", depth - 1, faceIS));
5797   if (!*faceIS) PetscCall(DMGetStratumIS(plex, "depth", depth - 1, faceIS));
5798   PetscFunctionReturn(PETSC_SUCCESS);
5799 }
5800 
5801 /*
5802  Returns number of components and tensor degree for the field.  For interpolated meshes, line should be a point
5803  representing a line in the section.
5804 */
5805 static PetscErrorCode PetscSectionFieldGetTensorDegree_Private(DM dm, PetscSection section, PetscInt field, PetscInt line, PetscInt *Nc, PetscInt *k, PetscBool *continuous, PetscBool *tensor)
5806 {
5807   PetscObject  obj;
5808   PetscClassId id;
5809   PetscFE      fe = NULL;
5810 
5811   PetscFunctionBeginHot;
5812   PetscCall(PetscSectionGetFieldComponents(section, field, Nc));
5813   PetscCall(DMGetField(dm, field, NULL, &obj));
5814   PetscCall(PetscObjectGetClassId(obj, &id));
5815   if (id == PETSCFE_CLASSID) fe = (PetscFE)obj;
5816 
5817   if (!fe) {
5818     /* Assume the full interpolated mesh is in the chart; lines in particular */
5819     /* An order k SEM disc has k-1 dofs on an edge */
5820     PetscCall(PetscSectionGetFieldDof(section, line, field, k));
5821     *k = *k / *Nc + 1;
5822   } else {
5823     PetscInt       dual_space_size, dim;
5824     PetscDualSpace dsp;
5825 
5826     PetscCall(DMGetDimension(dm, &dim));
5827     PetscCall(PetscFEGetDualSpace(fe, &dsp));
5828     PetscCall(PetscDualSpaceGetDimension(dsp, &dual_space_size));
5829     *k = (PetscInt)PetscCeilReal(PetscPowReal(dual_space_size / *Nc, 1.0 / dim)) - 1;
5830     PetscCall(PetscDualSpaceLagrangeGetContinuity(dsp, continuous));
5831     PetscCall(PetscDualSpaceLagrangeGetTensor(dsp, tensor));
5832   }
5833   PetscFunctionReturn(PETSC_SUCCESS);
5834 }
5835 
5836 static PetscErrorCode GetFieldSize_Private(PetscInt dim, PetscInt k, PetscBool tensor, PetscInt *dof)
5837 {
5838   PetscFunctionBeginHot;
5839   if (tensor) {
5840     *dof = PetscPowInt(k + 1, dim);
5841   } else {
5842     switch (dim) {
5843     case 1:
5844       *dof = k + 1;
5845       break;
5846     case 2:
5847       *dof = ((k + 1) * (k + 2)) / 2;
5848       break;
5849     case 3:
5850       *dof = ((k + 1) * (k + 2) * (k + 3)) / 6;
5851       break;
5852     default:
5853       *dof = 0;
5854     }
5855   }
5856   PetscFunctionReturn(PETSC_SUCCESS);
5857 }
5858 
5859 /*@
5860   DMPlexSetClosurePermutationTensor - Create a permutation from the default (BFS) point ordering in the closure, to a
5861   lexicographic ordering over the tensor product cell (i.e., line, quad, hex, etc.), and set this permutation in the
5862   section provided (or the section of the `DM`).
5863 
5864   Input Parameters:
5865 + dm      - The `DM`
5866 . point   - Either a cell (highest dim point) or an edge (dim 1 point), or `PETSC_DETERMINE`
5867 - section - The `PetscSection` to reorder, or `NULL` for the default section
5868 
5869   Example:
5870   A typical interpolated single-quad mesh might order points as
5871 .vb
5872   [c0, v1, v2, v3, v4, e5, e6, e7, e8]
5873 
5874   v4 -- e6 -- v3
5875   |           |
5876   e7    c0    e8
5877   |           |
5878   v1 -- e5 -- v2
5879 .ve
5880 
5881   (There is no significance to the ordering described here.)  The default section for a Q3 quad might typically assign
5882   dofs in the order of points, e.g.,
5883 .vb
5884     c0 -> [0,1,2,3]
5885     v1 -> [4]
5886     ...
5887     e5 -> [8, 9]
5888 .ve
5889 
5890   which corresponds to the dofs
5891 .vb
5892     6   10  11  7
5893     13  2   3   15
5894     12  0   1   14
5895     4   8   9   5
5896 .ve
5897 
5898   The closure in BFS ordering works through height strata (cells, edges, vertices) to produce the ordering
5899 .vb
5900   0 1 2 3 8 9 14 15 11 10 13 12 4 5 7 6
5901 .ve
5902 
5903   After calling DMPlexSetClosurePermutationTensor(), the closure will be ordered lexicographically,
5904 .vb
5905    4 8 9 5 12 0 1 14 13 2 3 15 6 10 11 7
5906 .ve
5907 
5908   Level: developer
5909 
5910   Notes:
5911   The point is used to determine the number of dofs/field on an edge. For SEM, this is related to the polynomial
5912   degree of the basis.
5913 
5914   This is required to run with libCEED.
5915 
5916 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMGetLocalSection()`, `PetscSectionSetClosurePermutation()`, `DMSetGlobalSection()`
5917 @*/
5918 PetscErrorCode DMPlexSetClosurePermutationTensor(DM dm, PetscInt point, PetscSection section)
5919 {
5920   DMLabel   label;
5921   PetscInt  dim, depth = -1, eStart = -1, Nf;
5922   PetscBool continuous = PETSC_TRUE, tensor = PETSC_TRUE;
5923 
5924   PetscFunctionBegin;
5925   PetscCall(DMGetDimension(dm, &dim));
5926   if (dim < 1) PetscFunctionReturn(PETSC_SUCCESS);
5927   if (point < 0) {
5928     PetscInt sStart, sEnd;
5929 
5930     PetscCall(DMPlexGetDepthStratum(dm, 1, &sStart, &sEnd));
5931     point = sEnd - sStart ? sStart : point;
5932   }
5933   PetscCall(DMPlexGetDepthLabel(dm, &label));
5934   if (point >= 0) PetscCall(DMLabelGetValue(label, point, &depth));
5935   if (!section) PetscCall(DMGetLocalSection(dm, &section));
5936   if (depth == 1) {
5937     eStart = point;
5938   } else if (depth == dim) {
5939     const PetscInt *cone;
5940 
5941     PetscCall(DMPlexGetCone(dm, point, &cone));
5942     if (dim == 2) eStart = cone[0];
5943     else if (dim == 3) {
5944       const PetscInt *cone2;
5945       PetscCall(DMPlexGetCone(dm, cone[0], &cone2));
5946       eStart = cone2[0];
5947     } 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);
5948   } 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);
5949 
5950   PetscCall(PetscSectionGetNumFields(section, &Nf));
5951   for (PetscInt d = 1; d <= dim; d++) {
5952     PetscInt  k, f, Nc, c, i, j, size = 0, offset = 0, foffset = 0;
5953     PetscInt *perm;
5954 
5955     for (f = 0; f < Nf; ++f) {
5956       PetscInt dof;
5957 
5958       PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
5959       PetscCheck(dim == 1 || tensor || !continuous, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Continuous field %" PetscInt_FMT " must have a tensor product discretization", f);
5960       if (!continuous && d < dim) continue;
5961       PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
5962       size += dof * Nc;
5963     }
5964     PetscCall(PetscMalloc1(size, &perm));
5965     for (f = 0; f < Nf; ++f) {
5966       switch (d) {
5967       case 1:
5968         PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
5969         if (!continuous && d < dim) continue;
5970         /*
5971          Original ordering is [ edge of length k-1; vtx0; vtx1 ]
5972          We want              [ vtx0; edge of length k-1; vtx1 ]
5973          */
5974         if (continuous) {
5975           for (c = 0; c < Nc; c++, offset++) perm[offset] = (k - 1) * Nc + c + foffset;
5976           for (i = 0; i < k - 1; i++)
5977             for (c = 0; c < Nc; c++, offset++) perm[offset] = i * Nc + c + foffset;
5978           for (c = 0; c < Nc; c++, offset++) perm[offset] = k * Nc + c + foffset;
5979           foffset = offset;
5980         } else {
5981           PetscInt dof;
5982 
5983           PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
5984           for (i = 0; i < dof * Nc; ++i, ++offset) perm[offset] = i + foffset;
5985           foffset = offset;
5986         }
5987         break;
5988       case 2:
5989         /* The original quad closure is oriented clockwise, {f, e_b, e_r, e_t, e_l, v_lb, v_rb, v_tr, v_tl} */
5990         PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
5991         if (!continuous && d < dim) continue;
5992         /* The SEM order is
5993 
5994          v_lb, {e_b}, v_rb,
5995          e^{(k-1)-i}_l, {f^{i*(k-1)}}, e^i_r,
5996          v_lt, reverse {e_t}, v_rt
5997          */
5998         if (continuous) {
5999           const PetscInt of   = 0;
6000           const PetscInt oeb  = of + PetscSqr(k - 1);
6001           const PetscInt oer  = oeb + (k - 1);
6002           const PetscInt oet  = oer + (k - 1);
6003           const PetscInt oel  = oet + (k - 1);
6004           const PetscInt ovlb = oel + (k - 1);
6005           const PetscInt ovrb = ovlb + 1;
6006           const PetscInt ovrt = ovrb + 1;
6007           const PetscInt ovlt = ovrt + 1;
6008           PetscInt       o;
6009 
6010           /* bottom */
6011           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlb * Nc + c + foffset;
6012           for (o = oeb; o < oer; ++o)
6013             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6014           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrb * Nc + c + foffset;
6015           /* middle */
6016           for (i = 0; i < k - 1; ++i) {
6017             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oel + (k - 2) - i) * Nc + c + foffset;
6018             for (o = of + (k - 1) * i; o < of + (k - 1) * (i + 1); ++o)
6019               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6020             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oer + i) * Nc + c + foffset;
6021           }
6022           /* top */
6023           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlt * Nc + c + foffset;
6024           for (o = oel - 1; o >= oet; --o)
6025             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6026           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrt * Nc + c + foffset;
6027           foffset = offset;
6028         } else {
6029           PetscInt dof;
6030 
6031           PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
6032           for (i = 0; i < dof * Nc; ++i, ++offset) perm[offset] = i + foffset;
6033           foffset = offset;
6034         }
6035         break;
6036       case 3:
6037         /* The original hex closure is
6038 
6039          {c,
6040          f_b, f_t, f_f, f_b, f_r, f_l,
6041          e_bl, e_bb, e_br, e_bf,  e_tf, e_tr, e_tb, e_tl,  e_rf, e_lf, e_lb, e_rb,
6042          v_blf, v_blb, v_brb, v_brf, v_tlf, v_trf, v_trb, v_tlb}
6043          */
6044         PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
6045         if (!continuous && d < dim) continue;
6046         /* The SEM order is
6047          Bottom Slice
6048          v_blf, {e^{(k-1)-n}_bf}, v_brf,
6049          e^{i}_bl, f^{n*(k-1)+(k-1)-i}_b, e^{(k-1)-i}_br,
6050          v_blb, {e_bb}, v_brb,
6051 
6052          Middle Slice (j)
6053          {e^{(k-1)-j}_lf}, {f^{j*(k-1)+n}_f}, e^j_rf,
6054          f^{i*(k-1)+j}_l, {c^{(j*(k-1) + i)*(k-1)+n}_t}, f^{j*(k-1)+i}_r,
6055          e^j_lb, {f^{j*(k-1)+(k-1)-n}_b}, e^{(k-1)-j}_rb,
6056 
6057          Top Slice
6058          v_tlf, {e_tf}, v_trf,
6059          e^{(k-1)-i}_tl, {f^{i*(k-1)}_t}, e^{i}_tr,
6060          v_tlb, {e^{(k-1)-n}_tb}, v_trb,
6061          */
6062         if (continuous) {
6063           const PetscInt oc    = 0;
6064           const PetscInt ofb   = oc + PetscSqr(k - 1) * (k - 1);
6065           const PetscInt oft   = ofb + PetscSqr(k - 1);
6066           const PetscInt off   = oft + PetscSqr(k - 1);
6067           const PetscInt ofk   = off + PetscSqr(k - 1);
6068           const PetscInt ofr   = ofk + PetscSqr(k - 1);
6069           const PetscInt ofl   = ofr + PetscSqr(k - 1);
6070           const PetscInt oebl  = ofl + PetscSqr(k - 1);
6071           const PetscInt oebb  = oebl + (k - 1);
6072           const PetscInt oebr  = oebb + (k - 1);
6073           const PetscInt oebf  = oebr + (k - 1);
6074           const PetscInt oetf  = oebf + (k - 1);
6075           const PetscInt oetr  = oetf + (k - 1);
6076           const PetscInt oetb  = oetr + (k - 1);
6077           const PetscInt oetl  = oetb + (k - 1);
6078           const PetscInt oerf  = oetl + (k - 1);
6079           const PetscInt oelf  = oerf + (k - 1);
6080           const PetscInt oelb  = oelf + (k - 1);
6081           const PetscInt oerb  = oelb + (k - 1);
6082           const PetscInt ovblf = oerb + (k - 1);
6083           const PetscInt ovblb = ovblf + 1;
6084           const PetscInt ovbrb = ovblb + 1;
6085           const PetscInt ovbrf = ovbrb + 1;
6086           const PetscInt ovtlf = ovbrf + 1;
6087           const PetscInt ovtrf = ovtlf + 1;
6088           const PetscInt ovtrb = ovtrf + 1;
6089           const PetscInt ovtlb = ovtrb + 1;
6090           PetscInt       o, n;
6091 
6092           /* Bottom Slice */
6093           /*   bottom */
6094           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblf * Nc + c + foffset;
6095           for (o = oetf - 1; o >= oebf; --o)
6096             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6097           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrf * Nc + c + foffset;
6098           /*   middle */
6099           for (i = 0; i < k - 1; ++i) {
6100             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebl + i) * Nc + c + foffset;
6101             for (n = 0; n < k - 1; ++n) {
6102               o = ofb + n * (k - 1) + i;
6103               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6104             }
6105             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebr + (k - 2) - i) * Nc + c + foffset;
6106           }
6107           /*   top */
6108           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblb * Nc + c + foffset;
6109           for (o = oebb; o < oebr; ++o)
6110             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6111           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrb * Nc + c + foffset;
6112 
6113           /* Middle Slice */
6114           for (j = 0; j < k - 1; ++j) {
6115             /*   bottom */
6116             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelf + (k - 2) - j) * Nc + c + foffset;
6117             for (o = off + j * (k - 1); o < off + (j + 1) * (k - 1); ++o)
6118               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6119             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerf + j) * Nc + c + foffset;
6120             /*   middle */
6121             for (i = 0; i < k - 1; ++i) {
6122               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofl + i * (k - 1) + j) * Nc + c + foffset;
6123               for (n = 0; n < k - 1; ++n)
6124                 for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oc + (j * (k - 1) + i) * (k - 1) + n) * Nc + c + foffset;
6125               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofr + j * (k - 1) + i) * Nc + c + foffset;
6126             }
6127             /*   top */
6128             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelb + j) * Nc + c + foffset;
6129             for (o = ofk + j * (k - 1) + (k - 2); o >= ofk + j * (k - 1); --o)
6130               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6131             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerb + (k - 2) - j) * Nc + c + foffset;
6132           }
6133 
6134           /* Top Slice */
6135           /*   bottom */
6136           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlf * Nc + c + foffset;
6137           for (o = oetf; o < oetr; ++o)
6138             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6139           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrf * Nc + c + foffset;
6140           /*   middle */
6141           for (i = 0; i < k - 1; ++i) {
6142             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetl + (k - 2) - i) * Nc + c + foffset;
6143             for (n = 0; n < k - 1; ++n)
6144               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oft + i * (k - 1) + n) * Nc + c + foffset;
6145             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetr + i) * Nc + c + foffset;
6146           }
6147           /*   top */
6148           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlb * Nc + c + foffset;
6149           for (o = oetl - 1; o >= oetb; --o)
6150             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6151           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrb * Nc + c + foffset;
6152 
6153           foffset = offset;
6154         } else {
6155           PetscInt dof;
6156 
6157           PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
6158           for (i = 0; i < dof * Nc; ++i, ++offset) perm[offset] = i + foffset;
6159           foffset = offset;
6160         }
6161         break;
6162       default:
6163         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "No spectral ordering for dimension %" PetscInt_FMT, d);
6164       }
6165     }
6166     PetscCheck(offset == size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Number of permutation entries %" PetscInt_FMT " != %" PetscInt_FMT, offset, size);
6167     /* Check permutation */
6168     {
6169       PetscInt *check;
6170 
6171       PetscCall(PetscMalloc1(size, &check));
6172       for (i = 0; i < size; ++i) {
6173         check[i] = -1;
6174         PetscCheck(perm[i] >= 0 && perm[i] < size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid permutation index p[%" PetscInt_FMT "] = %" PetscInt_FMT, i, perm[i]);
6175       }
6176       for (i = 0; i < size; ++i) check[perm[i]] = i;
6177       for (i = 0; i < size; ++i) PetscCheck(check[i] >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Missing permutation index %" PetscInt_FMT, i);
6178       PetscCall(PetscFree(check));
6179     }
6180     PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size, PETSC_OWN_POINTER, perm));
6181     if (d == dim) { // Add permutation for localized (in case this is a coordinate DM)
6182       PetscInt *loc_perm;
6183       PetscCall(PetscMalloc1(size * 2, &loc_perm));
6184       for (PetscInt i = 0; i < size; i++) {
6185         loc_perm[i]        = perm[i];
6186         loc_perm[size + i] = size + perm[i];
6187       }
6188       PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size * 2, PETSC_OWN_POINTER, loc_perm));
6189     }
6190   }
6191   PetscFunctionReturn(PETSC_SUCCESS);
6192 }
6193 
6194 PetscErrorCode DMPlexGetPointDualSpaceFEM(DM dm, PetscInt point, PetscInt field, PetscDualSpace *dspace)
6195 {
6196   PetscDS  prob;
6197   PetscInt depth, Nf, h;
6198   DMLabel  label;
6199 
6200   PetscFunctionBeginHot;
6201   PetscCall(DMGetDS(dm, &prob));
6202   Nf      = prob->Nf;
6203   label   = dm->depthLabel;
6204   *dspace = NULL;
6205   if (field < Nf) {
6206     PetscObject disc = prob->disc[field];
6207 
6208     if (disc->classid == PETSCFE_CLASSID) {
6209       PetscDualSpace dsp;
6210 
6211       PetscCall(PetscFEGetDualSpace((PetscFE)disc, &dsp));
6212       PetscCall(DMLabelGetNumValues(label, &depth));
6213       PetscCall(DMLabelGetValue(label, point, &h));
6214       h = depth - 1 - h;
6215       if (h) {
6216         PetscCall(PetscDualSpaceGetHeightSubspace(dsp, h, dspace));
6217       } else {
6218         *dspace = dsp;
6219       }
6220     }
6221   }
6222   PetscFunctionReturn(PETSC_SUCCESS);
6223 }
6224 
6225 static inline PetscErrorCode DMPlexVecGetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6226 {
6227   PetscScalar       *array;
6228   const PetscScalar *vArray;
6229   const PetscInt    *cone, *coneO;
6230   PetscInt           pStart, pEnd, p, numPoints, size = 0, offset = 0;
6231 
6232   PetscFunctionBeginHot;
6233   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6234   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
6235   PetscCall(DMPlexGetCone(dm, point, &cone));
6236   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
6237   if (!values || !*values) {
6238     if ((point >= pStart) && (point < pEnd)) {
6239       PetscInt dof;
6240 
6241       PetscCall(PetscSectionGetDof(section, point, &dof));
6242       size += dof;
6243     }
6244     for (p = 0; p < numPoints; ++p) {
6245       const PetscInt cp = cone[p];
6246       PetscInt       dof;
6247 
6248       if ((cp < pStart) || (cp >= pEnd)) continue;
6249       PetscCall(PetscSectionGetDof(section, cp, &dof));
6250       size += dof;
6251     }
6252     if (!values) {
6253       if (csize) *csize = size;
6254       PetscFunctionReturn(PETSC_SUCCESS);
6255     }
6256     PetscCall(DMGetWorkArray(dm, size, MPIU_SCALAR, &array));
6257   } else {
6258     array = *values;
6259   }
6260   size = 0;
6261   PetscCall(VecGetArrayRead(v, &vArray));
6262   if ((point >= pStart) && (point < pEnd)) {
6263     PetscInt           dof, off, d;
6264     const PetscScalar *varr;
6265 
6266     PetscCall(PetscSectionGetDof(section, point, &dof));
6267     PetscCall(PetscSectionGetOffset(section, point, &off));
6268     varr = PetscSafePointerPlusOffset(vArray, off);
6269     for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
6270     size += dof;
6271   }
6272   for (p = 0; p < numPoints; ++p) {
6273     const PetscInt     cp = cone[p];
6274     PetscInt           o  = coneO[p];
6275     PetscInt           dof, off, d;
6276     const PetscScalar *varr;
6277 
6278     if ((cp < pStart) || (cp >= pEnd)) continue;
6279     PetscCall(PetscSectionGetDof(section, cp, &dof));
6280     PetscCall(PetscSectionGetOffset(section, cp, &off));
6281     varr = PetscSafePointerPlusOffset(vArray, off);
6282     if (o >= 0) {
6283       for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
6284     } else {
6285       for (d = dof - 1; d >= 0; --d, ++offset) array[offset] = varr[d];
6286     }
6287     size += dof;
6288   }
6289   PetscCall(VecRestoreArrayRead(v, &vArray));
6290   if (!*values) {
6291     if (csize) *csize = size;
6292     *values = array;
6293   } else {
6294     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
6295     *csize = size;
6296   }
6297   PetscFunctionReturn(PETSC_SUCCESS);
6298 }
6299 
6300 /* Compress out points not in the section */
6301 static inline PetscErrorCode CompressPoints_Private(PetscSection section, PetscInt *numPoints, PetscInt points[])
6302 {
6303   const PetscInt np = *numPoints;
6304   PetscInt       pStart, pEnd, p, q;
6305 
6306   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6307   for (p = 0, q = 0; p < np; ++p) {
6308     const PetscInt r = points[p * 2];
6309     if ((r >= pStart) && (r < pEnd)) {
6310       points[q * 2]     = r;
6311       points[q * 2 + 1] = points[p * 2 + 1];
6312       ++q;
6313     }
6314   }
6315   *numPoints = q;
6316   return PETSC_SUCCESS;
6317 }
6318 
6319 /* Compressed closure does not apply closure permutation */
6320 PetscErrorCode DMPlexGetCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt ornt, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
6321 {
6322   const PetscInt *cla = NULL;
6323   PetscInt        np, *pts = NULL;
6324 
6325   PetscFunctionBeginHot;
6326   PetscCall(PetscSectionGetClosureIndex(section, (PetscObject)dm, clSec, clPoints));
6327   if (!ornt && *clPoints) {
6328     PetscInt dof, off;
6329 
6330     PetscCall(PetscSectionGetDof(*clSec, point, &dof));
6331     PetscCall(PetscSectionGetOffset(*clSec, point, &off));
6332     PetscCall(ISGetIndices(*clPoints, &cla));
6333     np  = dof / 2;
6334     pts = PetscSafePointerPlusOffset((PetscInt *)cla, off);
6335   } else {
6336     PetscCall(DMPlexGetTransitiveClosure_Internal(dm, point, ornt, PETSC_TRUE, &np, &pts));
6337     PetscCall(CompressPoints_Private(section, &np, pts));
6338   }
6339   *numPoints = np;
6340   *points    = pts;
6341   *clp       = cla;
6342   PetscFunctionReturn(PETSC_SUCCESS);
6343 }
6344 
6345 PetscErrorCode DMPlexRestoreCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
6346 {
6347   PetscFunctionBeginHot;
6348   if (!*clPoints) {
6349     PetscCall(DMPlexRestoreTransitiveClosure(dm, point, PETSC_TRUE, numPoints, points));
6350   } else {
6351     PetscCall(ISRestoreIndices(*clPoints, clp));
6352   }
6353   *numPoints = 0;
6354   *points    = NULL;
6355   *clSec     = NULL;
6356   *clPoints  = NULL;
6357   *clp       = NULL;
6358   PetscFunctionReturn(PETSC_SUCCESS);
6359 }
6360 
6361 static inline PetscErrorCode DMPlexVecGetClosure_Static(DM dm, PetscSection section, PetscInt numPoints, const PetscInt points[], const PetscInt clperm[], const PetscScalar vArray[], PetscInt *size, PetscScalar array[])
6362 {
6363   PetscInt            offset = 0, p;
6364   const PetscInt    **perms  = NULL;
6365   const PetscScalar **flips  = NULL;
6366 
6367   PetscFunctionBeginHot;
6368   *size = 0;
6369   PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
6370   for (p = 0; p < numPoints; p++) {
6371     const PetscInt     point = points[2 * p];
6372     const PetscInt    *perm  = perms ? perms[p] : NULL;
6373     const PetscScalar *flip  = flips ? flips[p] : NULL;
6374     PetscInt           dof, off, d;
6375     const PetscScalar *varr;
6376 
6377     PetscCall(PetscSectionGetDof(section, point, &dof));
6378     PetscCall(PetscSectionGetOffset(section, point, &off));
6379     varr = PetscSafePointerPlusOffset(vArray, off);
6380     if (clperm) {
6381       if (perm) {
6382         for (d = 0; d < dof; d++) array[clperm[offset + perm[d]]] = varr[d];
6383       } else {
6384         for (d = 0; d < dof; d++) array[clperm[offset + d]] = varr[d];
6385       }
6386       if (flip) {
6387         for (d = 0; d < dof; d++) array[clperm[offset + d]] *= flip[d];
6388       }
6389     } else {
6390       if (perm) {
6391         for (d = 0; d < dof; d++) array[offset + perm[d]] = varr[d];
6392       } else {
6393         for (d = 0; d < dof; d++) array[offset + d] = varr[d];
6394       }
6395       if (flip) {
6396         for (d = 0; d < dof; d++) array[offset + d] *= flip[d];
6397       }
6398     }
6399     offset += dof;
6400   }
6401   PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
6402   *size = offset;
6403   PetscFunctionReturn(PETSC_SUCCESS);
6404 }
6405 
6406 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[])
6407 {
6408   PetscInt offset = 0, f;
6409 
6410   PetscFunctionBeginHot;
6411   *size = 0;
6412   for (f = 0; f < numFields; ++f) {
6413     PetscInt            p;
6414     const PetscInt    **perms = NULL;
6415     const PetscScalar **flips = NULL;
6416 
6417     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6418     for (p = 0; p < numPoints; p++) {
6419       const PetscInt     point = points[2 * p];
6420       PetscInt           fdof, foff, b;
6421       const PetscScalar *varr;
6422       const PetscInt    *perm = perms ? perms[p] : NULL;
6423       const PetscScalar *flip = flips ? flips[p] : NULL;
6424 
6425       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6426       PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6427       varr = &vArray[foff];
6428       if (clperm) {
6429         if (perm) {
6430           for (b = 0; b < fdof; b++) array[clperm[offset + perm[b]]] = varr[b];
6431         } else {
6432           for (b = 0; b < fdof; b++) array[clperm[offset + b]] = varr[b];
6433         }
6434         if (flip) {
6435           for (b = 0; b < fdof; b++) array[clperm[offset + b]] *= flip[b];
6436         }
6437       } else {
6438         if (perm) {
6439           for (b = 0; b < fdof; b++) array[offset + perm[b]] = varr[b];
6440         } else {
6441           for (b = 0; b < fdof; b++) array[offset + b] = varr[b];
6442         }
6443         if (flip) {
6444           for (b = 0; b < fdof; b++) array[offset + b] *= flip[b];
6445         }
6446       }
6447       offset += fdof;
6448     }
6449     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6450   }
6451   *size = offset;
6452   PetscFunctionReturn(PETSC_SUCCESS);
6453 }
6454 
6455 PetscErrorCode DMPlexVecGetOrientedClosure_Internal(DM dm, PetscSection section, PetscBool useClPerm, Vec v, PetscInt point, PetscInt ornt, PetscInt *csize, PetscScalar *values[])
6456 {
6457   PetscSection    clSection;
6458   IS              clPoints;
6459   PetscInt       *points = NULL;
6460   const PetscInt *clp, *perm = NULL;
6461   PetscInt        depth, numFields, numPoints, asize;
6462 
6463   PetscFunctionBeginHot;
6464   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6465   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6466   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6467   PetscValidHeaderSpecific(v, VEC_CLASSID, 4);
6468   PetscCall(DMPlexGetDepth(dm, &depth));
6469   PetscCall(PetscSectionGetNumFields(section, &numFields));
6470   if (depth == 1 && numFields < 2) {
6471     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6472     PetscFunctionReturn(PETSC_SUCCESS);
6473   }
6474   /* Get points */
6475   PetscCall(DMPlexGetCompressedClosure(dm, section, point, ornt, &numPoints, &points, &clSection, &clPoints, &clp));
6476   /* Get sizes */
6477   asize = 0;
6478   for (PetscInt p = 0; p < numPoints * 2; p += 2) {
6479     PetscInt dof;
6480     PetscCall(PetscSectionGetDof(section, points[p], &dof));
6481     asize += dof;
6482   }
6483   if (values) {
6484     const PetscScalar *vArray;
6485     PetscInt           size;
6486 
6487     if (*values) {
6488       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);
6489     } else PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, values));
6490     if (useClPerm) PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, asize, &perm));
6491     PetscCall(VecGetArrayRead(v, &vArray));
6492     /* Get values */
6493     if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, numPoints, points, numFields, perm, vArray, &size, *values));
6494     else PetscCall(DMPlexVecGetClosure_Static(dm, section, numPoints, points, perm, vArray, &size, *values));
6495     PetscCheck(asize == size, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Section size %" PetscInt_FMT " does not match Vec closure size %" PetscInt_FMT, asize, size);
6496     /* Cleanup array */
6497     PetscCall(VecRestoreArrayRead(v, &vArray));
6498   }
6499   if (csize) *csize = asize;
6500   /* Cleanup points */
6501   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6502   PetscFunctionReturn(PETSC_SUCCESS);
6503 }
6504 
6505 /*@C
6506   DMPlexVecGetClosure - Get an array of the values on the closure of 'point'
6507 
6508   Not collective
6509 
6510   Input Parameters:
6511 + dm      - The `DM`
6512 . section - The section describing the layout in `v`, or `NULL` to use the default section
6513 . v       - The local vector
6514 - point   - The point in the `DM`
6515 
6516   Input/Output Parameters:
6517 + csize  - The size of the input values array, or `NULL`; on output the number of values in the closure
6518 - values - An array to use for the values, or *values = `NULL` to have it allocated automatically;
6519            if the user provided `NULL`, it is a borrowed array and should not be freed, use  `DMPlexVecRestoreClosure()` to return it
6520 
6521   Level: intermediate
6522 
6523   Notes:
6524   `DMPlexVecGetClosure()`/`DMPlexVecRestoreClosure()` only allocates the values array if it set to `NULL` in the
6525   calling function. This is because `DMPlexVecGetClosure()` is typically called in the inner loop of a `Vec` or `Mat`
6526   assembly function, and a user may already have allocated storage for this operation.
6527 
6528   A typical use could be
6529 .vb
6530    values = NULL;
6531    PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
6532    for (cl = 0; cl < clSize; ++cl) {
6533      <Compute on closure>
6534    }
6535    PetscCall(DMPlexVecRestoreClosure(dm, NULL, v, p, &clSize, &values));
6536 .ve
6537   or
6538 .vb
6539    PetscMalloc1(clMaxSize, &values);
6540    for (p = pStart; p < pEnd; ++p) {
6541      clSize = clMaxSize;
6542      PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
6543      for (cl = 0; cl < clSize; ++cl) {
6544        <Compute on closure>
6545      }
6546    }
6547    PetscFree(values);
6548 .ve
6549 
6550   Fortran Notes:
6551   The `csize` argument is not present in the Fortran binding.
6552 
6553   `values` must be declared with
6554 .vb
6555   PetscScalar,dimension(:),pointer   :: values
6556 .ve
6557   and it will be allocated internally by PETSc to hold the values returned
6558 
6559 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecRestoreClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6560 @*/
6561 PetscErrorCode DMPlexVecGetClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6562 {
6563   PetscFunctionBeginHot;
6564   PetscCall(DMPlexVecGetOrientedClosure_Internal(dm, section, PETSC_TRUE, v, point, 0, csize, values));
6565   PetscFunctionReturn(PETSC_SUCCESS);
6566 }
6567 
6568 PetscErrorCode DMPlexVecGetClosureAtDepth_Internal(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt depth, PetscInt *csize, PetscScalar *values[])
6569 {
6570   DMLabel            depthLabel;
6571   PetscSection       clSection;
6572   IS                 clPoints;
6573   PetscScalar       *array;
6574   const PetscScalar *vArray;
6575   PetscInt          *points = NULL;
6576   const PetscInt    *clp, *perm = NULL;
6577   PetscInt           mdepth, numFields, numPoints, Np = 0, p, clsize, size;
6578 
6579   PetscFunctionBeginHot;
6580   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6581   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6582   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6583   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6584   PetscCall(DMPlexGetDepth(dm, &mdepth));
6585   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
6586   PetscCall(PetscSectionGetNumFields(section, &numFields));
6587   if (mdepth == 1 && numFields < 2) {
6588     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6589     PetscFunctionReturn(PETSC_SUCCESS);
6590   }
6591   /* Get points */
6592   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
6593   for (clsize = 0, p = 0; p < Np; p++) {
6594     PetscInt dof;
6595     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
6596     clsize += dof;
6597   }
6598   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &perm));
6599   /* Filter points */
6600   for (p = 0; p < numPoints * 2; p += 2) {
6601     PetscInt dep;
6602 
6603     PetscCall(DMLabelGetValue(depthLabel, points[p], &dep));
6604     if (dep != depth) continue;
6605     points[Np * 2 + 0] = points[p];
6606     points[Np * 2 + 1] = points[p + 1];
6607     ++Np;
6608   }
6609   /* Get array */
6610   if (!values || !*values) {
6611     PetscInt asize = 0, dof;
6612 
6613     for (p = 0; p < Np * 2; p += 2) {
6614       PetscCall(PetscSectionGetDof(section, points[p], &dof));
6615       asize += dof;
6616     }
6617     if (!values) {
6618       PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6619       if (csize) *csize = asize;
6620       PetscFunctionReturn(PETSC_SUCCESS);
6621     }
6622     PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, &array));
6623   } else {
6624     array = *values;
6625   }
6626   PetscCall(VecGetArrayRead(v, &vArray));
6627   /* Get values */
6628   if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, Np, points, numFields, perm, vArray, &size, array));
6629   else PetscCall(DMPlexVecGetClosure_Static(dm, section, Np, points, perm, vArray, &size, array));
6630   /* Cleanup points */
6631   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6632   /* Cleanup array */
6633   PetscCall(VecRestoreArrayRead(v, &vArray));
6634   if (!*values) {
6635     if (csize) *csize = size;
6636     *values = array;
6637   } else {
6638     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
6639     *csize = size;
6640   }
6641   PetscFunctionReturn(PETSC_SUCCESS);
6642 }
6643 
6644 /*@C
6645   DMPlexVecRestoreClosure - Restore the array of the values on the closure of 'point' obtained with `DMPlexVecGetClosure()`
6646 
6647   Not collective
6648 
6649   Input Parameters:
6650 + dm      - The `DM`
6651 . section - The section describing the layout in `v`, or `NULL` to use the default section
6652 . v       - The local vector
6653 . point   - The point in the `DM`
6654 . csize   - The number of values in the closure, or `NULL`
6655 - values  - The array of values
6656 
6657   Level: intermediate
6658 
6659   Note:
6660   The array values are discarded and not copied back into `v`. In order to copy values back to `v`, use `DMPlexVecSetClosure()`
6661 
6662   Fortran Note:
6663   The `csize` argument is not present in the Fortran binding since it is internal to the array.
6664 
6665 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6666 @*/
6667 PetscErrorCode DMPlexVecRestoreClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6668 {
6669   PetscInt size = 0;
6670 
6671   PetscFunctionBegin;
6672   /* Should work without recalculating size */
6673   PetscCall(DMRestoreWorkArray(dm, size, MPIU_SCALAR, (void *)values));
6674   *values = NULL;
6675   PetscFunctionReturn(PETSC_SUCCESS);
6676 }
6677 
6678 static inline void add(PetscScalar *x, PetscScalar y)
6679 {
6680   *x += y;
6681 }
6682 static inline void insert(PetscScalar *x, PetscScalar y)
6683 {
6684   *x = y;
6685 }
6686 
6687 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[])
6688 {
6689   PetscInt        cdof;  /* The number of constraints on this point */
6690   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6691   PetscScalar    *a;
6692   PetscInt        off, cind = 0, k;
6693 
6694   PetscFunctionBegin;
6695   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6696   PetscCall(PetscSectionGetOffset(section, point, &off));
6697   a = &array[off];
6698   if (!cdof || setBC) {
6699     if (clperm) {
6700       if (perm) {
6701         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6702       } else {
6703         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6704       }
6705     } else {
6706       if (perm) {
6707         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6708       } else {
6709         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6710       }
6711     }
6712   } else {
6713     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6714     if (clperm) {
6715       if (perm) {
6716         for (k = 0; k < dof; ++k) {
6717           if ((cind < cdof) && (k == cdofs[cind])) {
6718             ++cind;
6719             continue;
6720           }
6721           fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6722         }
6723       } else {
6724         for (k = 0; k < dof; ++k) {
6725           if ((cind < cdof) && (k == cdofs[cind])) {
6726             ++cind;
6727             continue;
6728           }
6729           fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6730         }
6731       }
6732     } else {
6733       if (perm) {
6734         for (k = 0; k < dof; ++k) {
6735           if ((cind < cdof) && (k == cdofs[cind])) {
6736             ++cind;
6737             continue;
6738           }
6739           fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6740         }
6741       } else {
6742         for (k = 0; k < dof; ++k) {
6743           if ((cind < cdof) && (k == cdofs[cind])) {
6744             ++cind;
6745             continue;
6746           }
6747           fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6748         }
6749       }
6750     }
6751   }
6752   PetscFunctionReturn(PETSC_SUCCESS);
6753 }
6754 
6755 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[])
6756 {
6757   PetscInt        cdof;  /* The number of constraints on this point */
6758   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6759   PetscScalar    *a;
6760   PetscInt        off, cind = 0, k;
6761 
6762   PetscFunctionBegin;
6763   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6764   PetscCall(PetscSectionGetOffset(section, point, &off));
6765   a = &array[off];
6766   if (cdof) {
6767     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6768     if (clperm) {
6769       if (perm) {
6770         for (k = 0; k < dof; ++k) {
6771           if ((cind < cdof) && (k == cdofs[cind])) {
6772             fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6773             cind++;
6774           }
6775         }
6776       } else {
6777         for (k = 0; k < dof; ++k) {
6778           if ((cind < cdof) && (k == cdofs[cind])) {
6779             fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6780             cind++;
6781           }
6782         }
6783       }
6784     } else {
6785       if (perm) {
6786         for (k = 0; k < dof; ++k) {
6787           if ((cind < cdof) && (k == cdofs[cind])) {
6788             fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6789             cind++;
6790           }
6791         }
6792       } else {
6793         for (k = 0; k < dof; ++k) {
6794           if ((cind < cdof) && (k == cdofs[cind])) {
6795             fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6796             cind++;
6797           }
6798         }
6799       }
6800     }
6801   }
6802   PetscFunctionReturn(PETSC_SUCCESS);
6803 }
6804 
6805 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[])
6806 {
6807   PetscScalar    *a;
6808   PetscInt        fdof, foff, fcdof, foffset = *offset;
6809   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6810   PetscInt        cind = 0, b;
6811 
6812   PetscFunctionBegin;
6813   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6814   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6815   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6816   a = &array[foff];
6817   if (!fcdof || setBC) {
6818     if (clperm) {
6819       if (perm) {
6820         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6821       } else {
6822         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6823       }
6824     } else {
6825       if (perm) {
6826         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6827       } else {
6828         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6829       }
6830     }
6831   } else {
6832     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6833     if (clperm) {
6834       if (perm) {
6835         for (b = 0; b < fdof; b++) {
6836           if ((cind < fcdof) && (b == fcdofs[cind])) {
6837             ++cind;
6838             continue;
6839           }
6840           fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6841         }
6842       } else {
6843         for (b = 0; b < fdof; b++) {
6844           if ((cind < fcdof) && (b == fcdofs[cind])) {
6845             ++cind;
6846             continue;
6847           }
6848           fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6849         }
6850       }
6851     } else {
6852       if (perm) {
6853         for (b = 0; b < fdof; b++) {
6854           if ((cind < fcdof) && (b == fcdofs[cind])) {
6855             ++cind;
6856             continue;
6857           }
6858           fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6859         }
6860       } else {
6861         for (b = 0; b < fdof; b++) {
6862           if ((cind < fcdof) && (b == fcdofs[cind])) {
6863             ++cind;
6864             continue;
6865           }
6866           fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6867         }
6868       }
6869     }
6870   }
6871   *offset += fdof;
6872   PetscFunctionReturn(PETSC_SUCCESS);
6873 }
6874 
6875 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[])
6876 {
6877   PetscScalar    *a;
6878   PetscInt        fdof, foff, fcdof, foffset = *offset;
6879   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6880   PetscInt        Nc, cind = 0, ncind = 0, b;
6881   PetscBool       ncSet, fcSet;
6882 
6883   PetscFunctionBegin;
6884   PetscCall(PetscSectionGetFieldComponents(section, f, &Nc));
6885   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6886   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6887   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6888   a = &array[foff];
6889   if (fcdof) {
6890     /* We just override fcdof and fcdofs with Ncc and comps */
6891     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6892     if (clperm) {
6893       if (perm) {
6894         if (comps) {
6895           for (b = 0; b < fdof; b++) {
6896             ncSet = fcSet = PETSC_FALSE;
6897             if (b % Nc == comps[ncind]) {
6898               ncind = (ncind + 1) % Ncc;
6899               ncSet = PETSC_TRUE;
6900             }
6901             if ((cind < fcdof) && (b == fcdofs[cind])) {
6902               ++cind;
6903               fcSet = PETSC_TRUE;
6904             }
6905             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6906           }
6907         } else {
6908           for (b = 0; b < fdof; b++) {
6909             if ((cind < fcdof) && (b == fcdofs[cind])) {
6910               fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6911               ++cind;
6912             }
6913           }
6914         }
6915       } else {
6916         if (comps) {
6917           for (b = 0; b < fdof; b++) {
6918             ncSet = fcSet = PETSC_FALSE;
6919             if (b % Nc == comps[ncind]) {
6920               ncind = (ncind + 1) % Ncc;
6921               ncSet = PETSC_TRUE;
6922             }
6923             if ((cind < fcdof) && (b == fcdofs[cind])) {
6924               ++cind;
6925               fcSet = PETSC_TRUE;
6926             }
6927             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6928           }
6929         } else {
6930           for (b = 0; b < fdof; b++) {
6931             if ((cind < fcdof) && (b == fcdofs[cind])) {
6932               fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6933               ++cind;
6934             }
6935           }
6936         }
6937       }
6938     } else {
6939       if (perm) {
6940         if (comps) {
6941           for (b = 0; b < fdof; b++) {
6942             ncSet = fcSet = PETSC_FALSE;
6943             if (b % Nc == comps[ncind]) {
6944               ncind = (ncind + 1) % Ncc;
6945               ncSet = PETSC_TRUE;
6946             }
6947             if ((cind < fcdof) && (b == fcdofs[cind])) {
6948               ++cind;
6949               fcSet = PETSC_TRUE;
6950             }
6951             if (ncSet && fcSet) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6952           }
6953         } else {
6954           for (b = 0; b < fdof; b++) {
6955             if ((cind < fcdof) && (b == fcdofs[cind])) {
6956               fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6957               ++cind;
6958             }
6959           }
6960         }
6961       } else {
6962         if (comps) {
6963           for (b = 0; b < fdof; b++) {
6964             ncSet = fcSet = PETSC_FALSE;
6965             if (b % Nc == comps[ncind]) {
6966               ncind = (ncind + 1) % Ncc;
6967               ncSet = PETSC_TRUE;
6968             }
6969             if ((cind < fcdof) && (b == fcdofs[cind])) {
6970               ++cind;
6971               fcSet = PETSC_TRUE;
6972             }
6973             if (ncSet && fcSet) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6974           }
6975         } else {
6976           for (b = 0; b < fdof; b++) {
6977             if ((cind < fcdof) && (b == fcdofs[cind])) {
6978               fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6979               ++cind;
6980             }
6981           }
6982         }
6983       }
6984     }
6985   }
6986   *offset += fdof;
6987   PetscFunctionReturn(PETSC_SUCCESS);
6988 }
6989 
6990 static inline PetscErrorCode DMPlexVecSetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6991 {
6992   PetscScalar    *array;
6993   const PetscInt *cone, *coneO;
6994   PetscInt        pStart, pEnd, p, numPoints, off, dof;
6995 
6996   PetscFunctionBeginHot;
6997   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6998   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
6999   PetscCall(DMPlexGetCone(dm, point, &cone));
7000   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
7001   PetscCall(VecGetArray(v, &array));
7002   for (p = 0, off = 0; p <= numPoints; ++p, off += dof) {
7003     const PetscInt cp = !p ? point : cone[p - 1];
7004     const PetscInt o  = !p ? 0 : coneO[p - 1];
7005 
7006     if ((cp < pStart) || (cp >= pEnd)) {
7007       dof = 0;
7008       continue;
7009     }
7010     PetscCall(PetscSectionGetDof(section, cp, &dof));
7011     /* ADD_VALUES */
7012     {
7013       const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
7014       PetscScalar    *a;
7015       PetscInt        cdof, coff, cind = 0, k;
7016 
7017       PetscCall(PetscSectionGetConstraintDof(section, cp, &cdof));
7018       PetscCall(PetscSectionGetOffset(section, cp, &coff));
7019       a = &array[coff];
7020       if (!cdof) {
7021         if (o >= 0) {
7022           for (k = 0; k < dof; ++k) a[k] += values[off + k];
7023         } else {
7024           for (k = 0; k < dof; ++k) a[k] += values[off + dof - k - 1];
7025         }
7026       } else {
7027         PetscCall(PetscSectionGetConstraintIndices(section, cp, &cdofs));
7028         if (o >= 0) {
7029           for (k = 0; k < dof; ++k) {
7030             if ((cind < cdof) && (k == cdofs[cind])) {
7031               ++cind;
7032               continue;
7033             }
7034             a[k] += values[off + k];
7035           }
7036         } else {
7037           for (k = 0; k < dof; ++k) {
7038             if ((cind < cdof) && (k == cdofs[cind])) {
7039               ++cind;
7040               continue;
7041             }
7042             a[k] += values[off + dof - k - 1];
7043           }
7044         }
7045       }
7046     }
7047   }
7048   PetscCall(VecRestoreArray(v, &array));
7049   PetscFunctionReturn(PETSC_SUCCESS);
7050 }
7051 
7052 /*@C
7053   DMPlexVecSetClosure - Set an array of the values on the closure of `point`
7054 
7055   Not collective
7056 
7057   Input Parameters:
7058 + dm      - The `DM`
7059 . section - The section describing the layout in `v`, or `NULL` to use the default section
7060 . v       - The local vector
7061 . point   - The point in the `DM`
7062 . values  - The array of values
7063 - mode    - The insert mode. One of `INSERT_ALL_VALUES`, `ADD_ALL_VALUES`, `INSERT_VALUES`, `ADD_VALUES`, `INSERT_BC_VALUES`, and `ADD_BC_VALUES`,
7064             where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions.
7065 
7066   Level: intermediate
7067 
7068   Note:
7069   Usually the input arrays were obtained with `DMPlexVecGetClosure()`
7070 
7071   Fortran Note:
7072   `values` must be declared with
7073 .vb
7074   PetscScalar,dimension(:),pointer   :: values
7075 .ve
7076 
7077 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`
7078 @*/
7079 PetscErrorCode DMPlexVecSetClosure(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
7080 {
7081   PetscSection    clSection;
7082   IS              clPoints;
7083   PetscScalar    *array;
7084   PetscInt       *points = NULL;
7085   const PetscInt *clp, *clperm = NULL;
7086   PetscInt        depth, numFields, numPoints, p, clsize;
7087 
7088   PetscFunctionBeginHot;
7089   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7090   if (!section) PetscCall(DMGetLocalSection(dm, &section));
7091   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7092   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
7093   PetscCall(DMPlexGetDepth(dm, &depth));
7094   PetscCall(PetscSectionGetNumFields(section, &numFields));
7095   if (depth == 1 && numFields < 2 && mode == ADD_VALUES) {
7096     PetscCall(DMPlexVecSetClosure_Depth1_Static(dm, section, v, point, values, mode));
7097     PetscFunctionReturn(PETSC_SUCCESS);
7098   }
7099   /* Get points */
7100   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
7101   for (clsize = 0, p = 0; p < numPoints; p++) {
7102     PetscInt dof;
7103     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
7104     clsize += dof;
7105   }
7106   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
7107   /* Get array */
7108   PetscCall(VecGetArray(v, &array));
7109   /* Get values */
7110   if (numFields > 0) {
7111     PetscInt offset = 0, f;
7112     for (f = 0; f < numFields; ++f) {
7113       const PetscInt    **perms = NULL;
7114       const PetscScalar **flips = NULL;
7115 
7116       PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7117       switch (mode) {
7118       case INSERT_VALUES:
7119         for (p = 0; p < numPoints; p++) {
7120           const PetscInt     point = points[2 * p];
7121           const PetscInt    *perm  = perms ? perms[p] : NULL;
7122           const PetscScalar *flip  = flips ? flips[p] : NULL;
7123           PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, clperm, values, &offset, array));
7124         }
7125         break;
7126       case INSERT_ALL_VALUES:
7127         for (p = 0; p < numPoints; p++) {
7128           const PetscInt     point = points[2 * p];
7129           const PetscInt    *perm  = perms ? perms[p] : NULL;
7130           const PetscScalar *flip  = flips ? flips[p] : NULL;
7131           PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, clperm, values, &offset, array));
7132         }
7133         break;
7134       case INSERT_BC_VALUES:
7135         for (p = 0; p < numPoints; p++) {
7136           const PetscInt     point = points[2 * p];
7137           const PetscInt    *perm  = perms ? perms[p] : NULL;
7138           const PetscScalar *flip  = flips ? flips[p] : NULL;
7139           PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, insert, clperm, values, &offset, array));
7140         }
7141         break;
7142       case ADD_VALUES:
7143         for (p = 0; p < numPoints; p++) {
7144           const PetscInt     point = points[2 * p];
7145           const PetscInt    *perm  = perms ? perms[p] : NULL;
7146           const PetscScalar *flip  = flips ? flips[p] : NULL;
7147           PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, clperm, values, &offset, array));
7148         }
7149         break;
7150       case ADD_ALL_VALUES:
7151         for (p = 0; p < numPoints; p++) {
7152           const PetscInt     point = points[2 * p];
7153           const PetscInt    *perm  = perms ? perms[p] : NULL;
7154           const PetscScalar *flip  = flips ? flips[p] : NULL;
7155           PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, clperm, values, &offset, array));
7156         }
7157         break;
7158       case ADD_BC_VALUES:
7159         for (p = 0; p < numPoints; p++) {
7160           const PetscInt     point = points[2 * p];
7161           const PetscInt    *perm  = perms ? perms[p] : NULL;
7162           const PetscScalar *flip  = flips ? flips[p] : NULL;
7163           PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, add, clperm, values, &offset, array));
7164         }
7165         break;
7166       default:
7167         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
7168       }
7169       PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7170     }
7171   } else {
7172     PetscInt            dof, off;
7173     const PetscInt    **perms = NULL;
7174     const PetscScalar **flips = NULL;
7175 
7176     PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
7177     switch (mode) {
7178     case INSERT_VALUES:
7179       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7180         const PetscInt     point = points[2 * p];
7181         const PetscInt    *perm  = perms ? perms[p] : NULL;
7182         const PetscScalar *flip  = flips ? flips[p] : NULL;
7183         PetscCall(PetscSectionGetDof(section, point, &dof));
7184         PetscCall(updatePoint_private(section, point, dof, insert, PETSC_FALSE, perm, flip, clperm, values, off, array));
7185       }
7186       break;
7187     case INSERT_ALL_VALUES:
7188       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7189         const PetscInt     point = points[2 * p];
7190         const PetscInt    *perm  = perms ? perms[p] : NULL;
7191         const PetscScalar *flip  = flips ? flips[p] : NULL;
7192         PetscCall(PetscSectionGetDof(section, point, &dof));
7193         PetscCall(updatePoint_private(section, point, dof, insert, PETSC_TRUE, perm, flip, clperm, values, off, array));
7194       }
7195       break;
7196     case INSERT_BC_VALUES:
7197       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7198         const PetscInt     point = points[2 * p];
7199         const PetscInt    *perm  = perms ? perms[p] : NULL;
7200         const PetscScalar *flip  = flips ? flips[p] : NULL;
7201         PetscCall(PetscSectionGetDof(section, point, &dof));
7202         PetscCall(updatePointBC_private(section, point, dof, insert, perm, flip, clperm, values, off, array));
7203       }
7204       break;
7205     case ADD_VALUES:
7206       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7207         const PetscInt     point = points[2 * p];
7208         const PetscInt    *perm  = perms ? perms[p] : NULL;
7209         const PetscScalar *flip  = flips ? flips[p] : NULL;
7210         PetscCall(PetscSectionGetDof(section, point, &dof));
7211         PetscCall(updatePoint_private(section, point, dof, add, PETSC_FALSE, perm, flip, clperm, values, off, array));
7212       }
7213       break;
7214     case ADD_ALL_VALUES:
7215       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7216         const PetscInt     point = points[2 * p];
7217         const PetscInt    *perm  = perms ? perms[p] : NULL;
7218         const PetscScalar *flip  = flips ? flips[p] : NULL;
7219         PetscCall(PetscSectionGetDof(section, point, &dof));
7220         PetscCall(updatePoint_private(section, point, dof, add, PETSC_TRUE, perm, flip, clperm, values, off, array));
7221       }
7222       break;
7223     case ADD_BC_VALUES:
7224       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7225         const PetscInt     point = points[2 * p];
7226         const PetscInt    *perm  = perms ? perms[p] : NULL;
7227         const PetscScalar *flip  = flips ? flips[p] : NULL;
7228         PetscCall(PetscSectionGetDof(section, point, &dof));
7229         PetscCall(updatePointBC_private(section, point, dof, add, perm, flip, clperm, values, off, array));
7230       }
7231       break;
7232     default:
7233       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
7234     }
7235     PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
7236   }
7237   /* Cleanup points */
7238   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
7239   /* Cleanup array */
7240   PetscCall(VecRestoreArray(v, &array));
7241   PetscFunctionReturn(PETSC_SUCCESS);
7242 }
7243 
7244 /* Check whether the given point is in the label. If not, update the offset to skip this point */
7245 static inline PetscErrorCode CheckPoint_Private(DMLabel label, PetscInt labelId, PetscSection section, PetscInt point, PetscInt f, PetscInt *offset, PetscBool *contains)
7246 {
7247   PetscFunctionBegin;
7248   *contains = PETSC_TRUE;
7249   if (label) {
7250     PetscInt fdof;
7251 
7252     PetscCall(DMLabelStratumHasPoint(label, labelId, point, contains));
7253     if (!*contains) {
7254       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7255       *offset += fdof;
7256       PetscFunctionReturn(PETSC_SUCCESS);
7257     }
7258   }
7259   PetscFunctionReturn(PETSC_SUCCESS);
7260 }
7261 
7262 /* Unlike DMPlexVecSetClosure(), this uses plex-native closure permutation, not a user-specified permutation such as DMPlexSetClosurePermutationTensor(). */
7263 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)
7264 {
7265   PetscSection    clSection;
7266   IS              clPoints;
7267   PetscScalar    *array;
7268   PetscInt       *points = NULL;
7269   const PetscInt *clp;
7270   PetscInt        numFields, numPoints, p;
7271   PetscInt        offset = 0, f;
7272 
7273   PetscFunctionBeginHot;
7274   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7275   if (!section) PetscCall(DMGetLocalSection(dm, &section));
7276   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7277   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
7278   PetscCall(PetscSectionGetNumFields(section, &numFields));
7279   /* Get points */
7280   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
7281   /* Get array */
7282   PetscCall(VecGetArray(v, &array));
7283   /* Get values */
7284   for (f = 0; f < numFields; ++f) {
7285     const PetscInt    **perms = NULL;
7286     const PetscScalar **flips = NULL;
7287     PetscBool           contains;
7288 
7289     if (!fieldActive[f]) {
7290       for (p = 0; p < numPoints * 2; p += 2) {
7291         PetscInt fdof;
7292         PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
7293         offset += fdof;
7294       }
7295       continue;
7296     }
7297     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7298     switch (mode) {
7299     case INSERT_VALUES:
7300       for (p = 0; p < numPoints; p++) {
7301         const PetscInt     point = points[2 * p];
7302         const PetscInt    *perm  = perms ? perms[p] : NULL;
7303         const PetscScalar *flip  = flips ? flips[p] : NULL;
7304         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7305         if (!contains) continue;
7306         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, NULL, values, &offset, array));
7307       }
7308       break;
7309     case INSERT_ALL_VALUES:
7310       for (p = 0; p < numPoints; p++) {
7311         const PetscInt     point = points[2 * p];
7312         const PetscInt    *perm  = perms ? perms[p] : NULL;
7313         const PetscScalar *flip  = flips ? flips[p] : NULL;
7314         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7315         if (!contains) continue;
7316         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, NULL, values, &offset, array));
7317       }
7318       break;
7319     case INSERT_BC_VALUES:
7320       for (p = 0; p < numPoints; p++) {
7321         const PetscInt     point = points[2 * p];
7322         const PetscInt    *perm  = perms ? perms[p] : NULL;
7323         const PetscScalar *flip  = flips ? flips[p] : NULL;
7324         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7325         if (!contains) continue;
7326         PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, Ncc, comps, insert, NULL, values, &offset, array));
7327       }
7328       break;
7329     case ADD_VALUES:
7330       for (p = 0; p < numPoints; p++) {
7331         const PetscInt     point = points[2 * p];
7332         const PetscInt    *perm  = perms ? perms[p] : NULL;
7333         const PetscScalar *flip  = flips ? flips[p] : NULL;
7334         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7335         if (!contains) continue;
7336         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, NULL, values, &offset, array));
7337       }
7338       break;
7339     case ADD_ALL_VALUES:
7340       for (p = 0; p < numPoints; p++) {
7341         const PetscInt     point = points[2 * p];
7342         const PetscInt    *perm  = perms ? perms[p] : NULL;
7343         const PetscScalar *flip  = flips ? flips[p] : NULL;
7344         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7345         if (!contains) continue;
7346         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, NULL, values, &offset, array));
7347       }
7348       break;
7349     default:
7350       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
7351     }
7352     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7353   }
7354   /* Cleanup points */
7355   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
7356   /* Cleanup array */
7357   PetscCall(VecRestoreArray(v, &array));
7358   PetscFunctionReturn(PETSC_SUCCESS);
7359 }
7360 
7361 static PetscErrorCode DMPlexPrintMatSetValues(PetscViewer viewer, Mat A, PetscInt point, PetscInt numRIndices, const PetscInt rindices[], PetscInt numCIndices, const PetscInt cindices[], const PetscScalar values[])
7362 {
7363   PetscMPIInt rank;
7364   PetscInt    i, j;
7365 
7366   PetscFunctionBegin;
7367   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7368   PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat for point %" PetscInt_FMT "\n", rank, point));
7369   for (i = 0; i < numRIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat row indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, rindices[i]));
7370   for (i = 0; i < numCIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat col indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, cindices[i]));
7371   numCIndices = numCIndices ? numCIndices : numRIndices;
7372   if (!values) PetscFunctionReturn(PETSC_SUCCESS);
7373   for (i = 0; i < numRIndices; i++) {
7374     PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]", rank));
7375     for (j = 0; j < numCIndices; j++) {
7376 #if defined(PETSC_USE_COMPLEX)
7377       PetscCall(PetscViewerASCIIPrintf(viewer, " (%g,%g)", (double)PetscRealPart(values[i * numCIndices + j]), (double)PetscImaginaryPart(values[i * numCIndices + j])));
7378 #else
7379       PetscCall(PetscViewerASCIIPrintf(viewer, " %g", (double)values[i * numCIndices + j]));
7380 #endif
7381     }
7382     PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
7383   }
7384   PetscFunctionReturn(PETSC_SUCCESS);
7385 }
7386 
7387 /*
7388   DMPlexGetIndicesPoint_Internal - Add the indices for dofs on a point to an index array
7389 
7390   Input Parameters:
7391 + section - The section for this data layout
7392 . islocal - Is the section (and thus indices being requested) local or global?
7393 . point   - The point contributing dofs with these indices
7394 . off     - The global offset of this point
7395 . loff    - The local offset of each field
7396 . setBC   - The flag determining whether to include indices of boundary values
7397 . perm    - A permutation of the dofs on this point, or NULL
7398 - indperm - A permutation of the entire indices array, or NULL
7399 
7400   Output Parameter:
7401 . indices - Indices for dofs on this point
7402 
7403   Level: developer
7404 
7405   Note: The indices could be local or global, depending on the value of 'off'.
7406 */
7407 PetscErrorCode DMPlexGetIndicesPoint_Internal(PetscSection section, PetscBool islocal, PetscInt point, PetscInt off, PetscInt *loff, PetscBool setBC, const PetscInt perm[], const PetscInt indperm[], PetscInt indices[])
7408 {
7409   PetscInt        dof;   /* The number of unknowns on this point */
7410   PetscInt        cdof;  /* The number of constraints on this point */
7411   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
7412   PetscInt        cind = 0, k;
7413 
7414   PetscFunctionBegin;
7415   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
7416   PetscCall(PetscSectionGetDof(section, point, &dof));
7417   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
7418   if (!cdof || setBC) {
7419     for (k = 0; k < dof; ++k) {
7420       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
7421       const PetscInt ind    = indperm ? indperm[preind] : preind;
7422 
7423       indices[ind] = off + k;
7424     }
7425   } else {
7426     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
7427     for (k = 0; k < dof; ++k) {
7428       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
7429       const PetscInt ind    = indperm ? indperm[preind] : preind;
7430 
7431       if ((cind < cdof) && (k == cdofs[cind])) {
7432         /* Insert check for returning constrained indices */
7433         indices[ind] = -(off + k + 1);
7434         ++cind;
7435       } else {
7436         indices[ind] = off + k - (islocal ? 0 : cind);
7437       }
7438     }
7439   }
7440   *loff += dof;
7441   PetscFunctionReturn(PETSC_SUCCESS);
7442 }
7443 
7444 /*
7445  DMPlexGetIndicesPointFields_Internal - gets section indices for a point in its canonical ordering.
7446 
7447  Input Parameters:
7448 + section - a section (global or local)
7449 - islocal - `PETSC_TRUE` if requesting local indices (i.e., section is local); `PETSC_FALSE` for global
7450 . point - point within section
7451 . off - The offset of this point in the (local or global) indexed space - should match islocal and (usually) the section
7452 . foffs - array of length numFields containing the offset in canonical point ordering (the location in indices) of each field
7453 . setBC - identify constrained (boundary condition) points via involution.
7454 . perms - perms[f][permsoff][:] is a permutation of dofs within each field
7455 . permsoff - offset
7456 - indperm - index permutation
7457 
7458  Output Parameter:
7459 . foffs - each entry is incremented by the number of (unconstrained if setBC=FALSE) dofs in that field
7460 . indices - array to hold indices (as defined by section) of each dof associated with point
7461 
7462  Notes:
7463  If section is local and setBC=true, there is no distinction between constrained and unconstrained dofs.
7464  If section is local and setBC=false, the indices for constrained points are the involution -(i+1) of their position
7465  in the local vector.
7466 
7467  If section is global and setBC=false, the indices for constrained points are negative (and their value is not
7468  significant).  It is invalid to call with a global section and setBC=true.
7469 
7470  Developer Note:
7471  The section is only used for field layout, so islocal is technically a statement about the offset (off).  At some point
7472  in the future, global sections may have fields set, in which case we could pass the global section and obtain the
7473  offset could be obtained from the section instead of passing it explicitly as we do now.
7474 
7475  Example:
7476  Suppose a point contains one field with three components, and for which the unconstrained indices are {10, 11, 12}.
7477  When the middle component is constrained, we get the array {10, -12, 12} for (islocal=TRUE, setBC=FALSE).
7478  Note that -12 is the involution of 11, so the user can involute negative indices to recover local indices.
7479  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.
7480 
7481  Level: developer
7482 */
7483 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[])
7484 {
7485   PetscInt numFields, foff, f;
7486 
7487   PetscFunctionBegin;
7488   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
7489   PetscCall(PetscSectionGetNumFields(section, &numFields));
7490   for (f = 0, foff = 0; f < numFields; ++f) {
7491     PetscInt        fdof, cfdof;
7492     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7493     PetscInt        cind = 0, b;
7494     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7495 
7496     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7497     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7498     if (!cfdof || setBC) {
7499       for (b = 0; b < fdof; ++b) {
7500         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7501         const PetscInt ind    = indperm ? indperm[preind] : preind;
7502 
7503         indices[ind] = off + foff + b;
7504       }
7505     } else {
7506       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7507       for (b = 0; b < fdof; ++b) {
7508         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7509         const PetscInt ind    = indperm ? indperm[preind] : preind;
7510 
7511         if ((cind < cfdof) && (b == fcdofs[cind])) {
7512           indices[ind] = -(off + foff + b + 1);
7513           ++cind;
7514         } else {
7515           indices[ind] = off + foff + b - (islocal ? 0 : cind);
7516         }
7517       }
7518     }
7519     foff += (setBC || islocal ? fdof : (fdof - cfdof));
7520     foffs[f] += fdof;
7521   }
7522   PetscFunctionReturn(PETSC_SUCCESS);
7523 }
7524 
7525 /*
7526   This version believes the globalSection offsets for each field, rather than just the point offset
7527 
7528  . foffs - The offset into 'indices' for each field, since it is segregated by field
7529 
7530  Notes:
7531  The semantics of this function relate to that of setBC=FALSE in DMPlexGetIndicesPointFields_Internal.
7532  Since this function uses global indices, setBC=TRUE would be invalid, so no such argument exists.
7533 */
7534 static PetscErrorCode DMPlexGetIndicesPointFieldsSplit_Internal(PetscSection section, PetscSection globalSection, PetscInt point, PetscInt foffs[], const PetscInt ***perms, PetscInt permsoff, const PetscInt indperm[], PetscInt indices[])
7535 {
7536   PetscInt numFields, foff, f;
7537 
7538   PetscFunctionBegin;
7539   PetscCall(PetscSectionGetNumFields(section, &numFields));
7540   for (f = 0; f < numFields; ++f) {
7541     PetscInt        fdof, cfdof;
7542     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7543     PetscInt        cind = 0, b;
7544     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7545 
7546     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7547     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7548     PetscCall(PetscSectionGetFieldOffset(globalSection, point, f, &foff));
7549     if (!cfdof) {
7550       for (b = 0; b < fdof; ++b) {
7551         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7552         const PetscInt ind    = indperm ? indperm[preind] : preind;
7553 
7554         indices[ind] = foff + b;
7555       }
7556     } else {
7557       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7558       for (b = 0; b < fdof; ++b) {
7559         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7560         const PetscInt ind    = indperm ? indperm[preind] : preind;
7561 
7562         if ((cind < cfdof) && (b == fcdofs[cind])) {
7563           indices[ind] = -(foff + b + 1);
7564           ++cind;
7565         } else {
7566           indices[ind] = foff + b - cind;
7567         }
7568       }
7569     }
7570     foffs[f] += fdof;
7571   }
7572   PetscFunctionReturn(PETSC_SUCCESS);
7573 }
7574 
7575 static PetscErrorCode DMPlexAnchorsGetSubMatIndices(PetscInt nPoints, const PetscInt pnts[], PetscSection section, PetscSection cSec, PetscInt tmpIndices[], PetscInt fieldOffsets[], PetscInt indices[], const PetscInt ***perms)
7576 {
7577   PetscInt numFields, sStart, sEnd, cStart, cEnd;
7578 
7579   PetscFunctionBegin;
7580   PetscCall(PetscSectionGetNumFields(section, &numFields));
7581   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
7582   PetscCall(PetscSectionGetChart(cSec, &cStart, &cEnd));
7583   for (PetscInt p = 0; p < nPoints; p++) {
7584     PetscInt     b       = pnts[2 * p];
7585     PetscInt     bSecDof = 0, bOff;
7586     PetscInt     cSecDof = 0;
7587     PetscSection indices_section;
7588 
7589     if (b >= sStart && b < sEnd) PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7590     if (!bSecDof) continue;
7591     if (b >= cStart && b < cEnd) PetscCall(PetscSectionGetDof(cSec, b, &cSecDof));
7592     indices_section = cSecDof > 0 ? cSec : section;
7593     if (numFields) {
7594       PetscInt fStart[32], fEnd[32];
7595 
7596       fStart[0] = 0;
7597       fEnd[0]   = 0;
7598       for (PetscInt f = 0; f < numFields; f++) {
7599         PetscInt fDof = 0;
7600 
7601         PetscCall(PetscSectionGetFieldDof(indices_section, b, f, &fDof));
7602         fStart[f + 1] = fStart[f] + fDof;
7603         fEnd[f + 1]   = fStart[f + 1];
7604       }
7605       PetscCall(PetscSectionGetOffset(indices_section, b, &bOff));
7606       // only apply permutations on one side
7607       PetscCall(DMPlexGetIndicesPointFields_Internal(indices_section, PETSC_TRUE, b, bOff, fEnd, PETSC_TRUE, perms, perms ? p : -1, NULL, tmpIndices));
7608       for (PetscInt f = 0; f < numFields; f++) {
7609         for (PetscInt i = fStart[f]; i < fEnd[f]; i++) { indices[fieldOffsets[f]++] = (cSecDof > 0) ? tmpIndices[i] : -(tmpIndices[i] + 1); }
7610       }
7611     } else {
7612       PetscInt bEnd = 0;
7613 
7614       PetscCall(PetscSectionGetOffset(indices_section, b, &bOff));
7615       PetscCall(DMPlexGetIndicesPoint_Internal(indices_section, PETSC_TRUE, b, bOff, &bEnd, PETSC_TRUE, (perms && perms[0]) ? perms[0][p] : NULL, NULL, tmpIndices));
7616 
7617       for (PetscInt i = 0; i < bEnd; i++) indices[fieldOffsets[0]++] = (cSecDof > 0) ? tmpIndices[i] : -(tmpIndices[i] + 1);
7618     }
7619   }
7620   PetscFunctionReturn(PETSC_SUCCESS);
7621 }
7622 
7623 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[])
7624 {
7625   Mat             cMat;
7626   PetscSection    aSec, cSec;
7627   IS              aIS;
7628   PetscInt        aStart = -1, aEnd = -1;
7629   PetscInt        sStart = -1, sEnd = -1;
7630   PetscInt        cStart = -1, cEnd = -1;
7631   const PetscInt *anchors;
7632   PetscInt        numFields, p;
7633   PetscInt        newNumPoints = 0, newNumIndices = 0;
7634   PetscInt       *newPoints, *indices, *newIndices, *tmpIndices, *tmpNewIndices;
7635   PetscInt        oldOffsets[32];
7636   PetscInt        newOffsets[32];
7637   PetscInt        oldOffsetsCopy[32];
7638   PetscInt        newOffsetsCopy[32];
7639   PetscScalar    *modMat         = NULL;
7640   PetscBool       anyConstrained = PETSC_FALSE;
7641 
7642   PetscFunctionBegin;
7643   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7644   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7645   PetscCall(PetscSectionGetNumFields(section, &numFields));
7646 
7647   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
7648   /* if there are point-to-point constraints */
7649   if (aSec) {
7650     PetscCall(PetscArrayzero(newOffsets, 32));
7651     PetscCall(PetscArrayzero(oldOffsets, 32));
7652     PetscCall(ISGetIndices(aIS, &anchors));
7653     PetscCall(PetscSectionGetChart(aSec, &aStart, &aEnd));
7654     PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
7655     /* figure out how many points are going to be in the new element matrix
7656      * (we allow double counting, because it's all just going to be summed
7657      * into the global matrix anyway) */
7658     for (p = 0; p < 2 * numPoints; p += 2) {
7659       PetscInt b    = points[p];
7660       PetscInt bDof = 0, bSecDof = 0;
7661 
7662       if (b >= sStart && b < sEnd) PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7663       if (!bSecDof) continue;
7664 
7665       for (PetscInt f = 0; f < numFields; f++) {
7666         PetscInt fDof = 0;
7667 
7668         PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7669         oldOffsets[f + 1] += fDof;
7670       }
7671       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7672       if (bDof) {
7673         /* this point is constrained */
7674         /* it is going to be replaced by its anchors */
7675         PetscInt bOff, q;
7676 
7677         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7678         for (q = 0; q < bDof; q++) {
7679           PetscInt a    = anchors[bOff + q];
7680           PetscInt aDof = 0;
7681 
7682           if (a >= sStart && a < sEnd) PetscCall(PetscSectionGetDof(section, a, &aDof));
7683           if (aDof) {
7684             anyConstrained = PETSC_TRUE;
7685             newNumPoints += 1;
7686           }
7687           newNumIndices += aDof;
7688           for (PetscInt f = 0; f < numFields; ++f) {
7689             PetscInt fDof = 0;
7690 
7691             if (a >= sStart && a < sEnd) PetscCall(PetscSectionGetFieldDof(section, a, f, &fDof));
7692             newOffsets[f + 1] += fDof;
7693           }
7694         }
7695       } else {
7696         /* this point is not constrained */
7697         newNumPoints++;
7698         newNumIndices += bSecDof;
7699         for (PetscInt f = 0; f < numFields; ++f) {
7700           PetscInt fDof;
7701 
7702           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7703           newOffsets[f + 1] += fDof;
7704         }
7705       }
7706     }
7707   }
7708   if (!anyConstrained) {
7709     if (outNumPoints) *outNumPoints = 0;
7710     if (outNumIndices) *outNumIndices = 0;
7711     if (outPoints) *outPoints = NULL;
7712     if (outMat) *outMat = NULL;
7713     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7714     PetscFunctionReturn(PETSC_SUCCESS);
7715   }
7716 
7717   if (outNumPoints) *outNumPoints = newNumPoints;
7718   if (outNumIndices) *outNumIndices = newNumIndices;
7719 
7720   for (PetscInt f = 0; f < numFields; ++f) newOffsets[f + 1] += newOffsets[f];
7721   for (PetscInt f = 0; f < numFields; ++f) oldOffsets[f + 1] += oldOffsets[f];
7722 
7723   if (!outPoints && !outMat) {
7724     if (offsets) {
7725       for (PetscInt f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7726     }
7727     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7728     PetscFunctionReturn(PETSC_SUCCESS);
7729   }
7730 
7731   PetscCheck(!numFields || newOffsets[numFields] == newNumIndices, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, newOffsets[numFields], newNumIndices);
7732   PetscCheck(!numFields || oldOffsets[numFields] == numIndices, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, oldOffsets[numFields], numIndices);
7733 
7734   PetscCall(DMGetDefaultConstraints(dm, &cSec, &cMat, NULL));
7735   PetscCall(PetscSectionGetChart(cSec, &cStart, &cEnd));
7736 
7737   /* output arrays */
7738   PetscCall(DMGetWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7739   PetscCall(PetscArrayzero(newPoints, 2 * newNumPoints));
7740 
7741   // get the new Points
7742   for (PetscInt p = 0, newP = 0; p < numPoints; p++) {
7743     PetscInt b    = points[2 * p];
7744     PetscInt bDof = 0, bSecDof = 0, bOff;
7745 
7746     if (b >= sStart && b < sEnd) PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7747     if (!bSecDof) continue;
7748     if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7749     if (bDof) {
7750       PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7751       for (PetscInt q = 0; q < bDof; q++) {
7752         PetscInt a = anchors[bOff + q], aDof = 0;
7753 
7754         if (a >= sStart && a < sEnd) PetscCall(PetscSectionGetDof(section, a, &aDof));
7755         if (aDof) {
7756           newPoints[2 * newP]     = a;
7757           newPoints[2 * newP + 1] = 0; // orientations are accounted for in constructing the matrix, newly added points are in default orientation
7758           newP++;
7759         }
7760       }
7761     } else {
7762       newPoints[2 * newP]     = b;
7763       newPoints[2 * newP + 1] = points[2 * p + 1];
7764       newP++;
7765     }
7766   }
7767 
7768   if (outMat) {
7769     PetscScalar *tmpMat;
7770     PetscCall(PetscArraycpy(oldOffsetsCopy, oldOffsets, 32));
7771     PetscCall(PetscArraycpy(newOffsetsCopy, newOffsets, 32));
7772 
7773     PetscCall(DMGetWorkArray(dm, numIndices, MPIU_INT, &indices));
7774     PetscCall(DMGetWorkArray(dm, numIndices, MPIU_INT, &tmpIndices));
7775     PetscCall(DMGetWorkArray(dm, newNumIndices, MPIU_INT, &newIndices));
7776     PetscCall(DMGetWorkArray(dm, newNumIndices, MPIU_INT, &tmpNewIndices));
7777 
7778     for (PetscInt i = 0; i < numIndices; i++) indices[i] = -1;
7779     for (PetscInt i = 0; i < newNumIndices; i++) newIndices[i] = -1;
7780 
7781     PetscCall(DMPlexAnchorsGetSubMatIndices(numPoints, points, section, cSec, tmpIndices, oldOffsetsCopy, indices, perms));
7782     PetscCall(DMPlexAnchorsGetSubMatIndices(newNumPoints, newPoints, section, section, tmpNewIndices, newOffsetsCopy, newIndices, NULL));
7783 
7784     PetscCall(DMGetWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &modMat));
7785     PetscCall(DMGetWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &tmpMat));
7786     PetscCall(PetscArrayzero(modMat, newNumIndices * numIndices));
7787     // for each field, insert the anchor modification into modMat
7788     for (PetscInt f = 0; f < PetscMax(1, numFields); f++) {
7789       PetscInt fStart    = oldOffsets[f];
7790       PetscInt fNewStart = newOffsets[f];
7791       for (PetscInt p = 0, newP = 0, o = fStart, oNew = fNewStart; p < numPoints; p++) {
7792         PetscInt b    = points[2 * p];
7793         PetscInt bDof = 0, bSecDof = 0, bOff;
7794 
7795         if (b >= sStart && b < sEnd) {
7796           if (numFields) {
7797             PetscCall(PetscSectionGetFieldDof(section, b, f, &bSecDof));
7798           } else {
7799             PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7800           }
7801         }
7802         if (!bSecDof) continue;
7803         if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7804         if (bDof) {
7805           PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7806           for (PetscInt q = 0; q < bDof; q++, newP++) {
7807             PetscInt a = anchors[bOff + q], aDof = 0;
7808 
7809             if (a >= sStart && a < sEnd) {
7810               if (numFields) {
7811                 PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
7812               } else {
7813                 PetscCall(PetscSectionGetDof(section, a, &aDof));
7814               }
7815             }
7816             if (aDof) {
7817               PetscCall(MatGetValues(cMat, bSecDof, &indices[o], aDof, &newIndices[oNew], tmpMat));
7818               for (PetscInt d = 0; d < bSecDof; d++) {
7819                 for (PetscInt e = 0; e < aDof; e++) modMat[(o + d) * newNumIndices + oNew + e] = tmpMat[d * aDof + e];
7820               }
7821             }
7822             oNew += aDof;
7823           }
7824         } else {
7825           // Insert the identity matrix in this block
7826           for (PetscInt d = 0; d < bSecDof; d++) modMat[(o + d) * newNumIndices + oNew + d] = 1;
7827           oNew += bSecDof;
7828           newP++;
7829         }
7830         o += bSecDof;
7831       }
7832     }
7833 
7834     *outMat = modMat;
7835 
7836     PetscCall(DMRestoreWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &tmpMat));
7837     PetscCall(DMRestoreWorkArray(dm, newNumIndices, MPIU_INT, &tmpNewIndices));
7838     PetscCall(DMRestoreWorkArray(dm, newNumIndices, MPIU_INT, &newIndices));
7839     PetscCall(DMRestoreWorkArray(dm, numIndices, MPIU_INT, &tmpIndices));
7840     PetscCall(DMRestoreWorkArray(dm, numIndices, MPIU_INT, &indices));
7841   }
7842   PetscCall(ISRestoreIndices(aIS, &anchors));
7843 
7844   /* output */
7845   if (outPoints) {
7846     *outPoints = newPoints;
7847   } else {
7848     PetscCall(DMRestoreWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7849   }
7850   for (PetscInt f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7851   PetscFunctionReturn(PETSC_SUCCESS);
7852 }
7853 
7854 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)
7855 {
7856   PetscScalar *modMat        = NULL;
7857   PetscInt     newNumIndices = -1;
7858 
7859   PetscFunctionBegin;
7860   /* 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.
7861      modMat is that matrix C */
7862   PetscCall(DMPlexAnchorsGetSubMatModification(dm, section, numPoints, numIndices, points, perms, outNumPoints, &newNumIndices, outPoints, offsets, outValues ? &modMat : NULL));
7863   if (outNumIndices) *outNumIndices = newNumIndices;
7864   if (modMat) {
7865     const PetscScalar *newValues = values;
7866 
7867     if (multiplyRight) {
7868       PetscScalar *newNewValues = NULL;
7869       PetscBLASInt M            = newNumIndices;
7870       PetscBLASInt N            = numRows;
7871       PetscBLASInt K            = numIndices;
7872       PetscScalar  a = 1.0, b = 0.0;
7873 
7874       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);
7875 
7876       PetscCall(DMGetWorkArray(dm, numRows * newNumIndices, MPIU_SCALAR, &newNewValues));
7877       // row-major to column-major conversion, right multiplication becomes left multiplication
7878       PetscCallBLAS("BLASgemm", BLASgemm_("N", "N", &M, &N, &K, &a, modMat, &M, newValues, &K, &b, newNewValues, &M));
7879 
7880       numCols   = newNumIndices;
7881       newValues = newNewValues;
7882     }
7883 
7884     if (multiplyLeft) {
7885       PetscScalar *newNewValues = NULL;
7886       PetscBLASInt M            = numCols;
7887       PetscBLASInt N            = newNumIndices;
7888       PetscBLASInt K            = numIndices;
7889       PetscScalar  a = 1.0, b = 0.0;
7890 
7891       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);
7892 
7893       PetscCall(DMGetWorkArray(dm, newNumIndices * numCols, MPIU_SCALAR, &newNewValues));
7894       // row-major to column-major conversion, left multiplication becomes right multiplication
7895       PetscCallBLAS("BLASgemm", BLASgemm_("N", "T", &M, &N, &K, &a, newValues, &M, modMat, &N, &b, newNewValues, &M));
7896       if (newValues != values) PetscCall(DMRestoreWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &newValues));
7897       newValues = newNewValues;
7898     }
7899     *outValues = (PetscScalar *)newValues;
7900     PetscCall(DMRestoreWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &modMat));
7901   }
7902   PetscFunctionReturn(PETSC_SUCCESS);
7903 }
7904 
7905 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)
7906 {
7907   PetscFunctionBegin;
7908   PetscCall(DMPlexAnchorsModifyMat_Internal(dm, section, numPoints, numIndices, points, perms, numIndices, numIndices, values, outNumPoints, outNumIndices, outPoints, outValues, offsets, PETSC_TRUE, multiplyLeft));
7909   PetscFunctionReturn(PETSC_SUCCESS);
7910 }
7911 
7912 static PetscErrorCode DMPlexGetClosureIndicesSize_Internal(DM dm, PetscSection section, PetscInt point, PetscInt *closureSize)
7913 {
7914   /* Closure ordering */
7915   PetscSection    clSection;
7916   IS              clPoints;
7917   const PetscInt *clp;
7918   PetscInt       *points;
7919   PetscInt        Ncl, Ni = 0;
7920 
7921   PetscFunctionBeginHot;
7922   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &Ncl, &points, &clSection, &clPoints, &clp));
7923   for (PetscInt p = 0; p < Ncl * 2; p += 2) {
7924     PetscInt dof;
7925 
7926     PetscCall(PetscSectionGetDof(section, points[p], &dof));
7927     Ni += dof;
7928   }
7929   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7930   *closureSize = Ni;
7931   PetscFunctionReturn(PETSC_SUCCESS);
7932 }
7933 
7934 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)
7935 {
7936   /* Closure ordering */
7937   PetscSection    clSection;
7938   IS              clPoints;
7939   const PetscInt *clp;
7940   PetscInt       *points;
7941   const PetscInt *clperm = NULL;
7942   /* Dof permutation and sign flips */
7943   const PetscInt    **perms[32] = {NULL};
7944   const PetscScalar **flips[32] = {NULL};
7945   PetscScalar        *valCopy   = NULL;
7946   /* Hanging node constraints */
7947   PetscInt    *pointsC = NULL;
7948   PetscScalar *valuesC = NULL;
7949   PetscInt     NclC, NiC;
7950 
7951   PetscInt *idx;
7952   PetscInt  Nf, Ncl, Ni = 0, offsets[32], p, f;
7953   PetscBool isLocal = (section == idxSection) ? PETSC_TRUE : PETSC_FALSE;
7954   PetscInt  idxStart, idxEnd;
7955   PetscInt  nRows, nCols;
7956 
7957   PetscFunctionBeginHot;
7958   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7959   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7960   PetscValidHeaderSpecific(idxSection, PETSC_SECTION_CLASSID, 3);
7961   PetscAssertPointer(numRows, 6);
7962   PetscAssertPointer(numCols, 7);
7963   if (indices) PetscAssertPointer(indices, 8);
7964   if (outOffsets) PetscAssertPointer(outOffsets, 9);
7965   if (values) PetscAssertPointer(values, 10);
7966   PetscCall(PetscSectionGetNumFields(section, &Nf));
7967   PetscCheck(Nf <= 31, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", Nf);
7968   PetscCall(PetscArrayzero(offsets, 32));
7969   /* 1) Get points in closure */
7970   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &Ncl, &points, &clSection, &clPoints, &clp));
7971   if (useClPerm) {
7972     PetscInt depth, clsize;
7973     PetscCall(DMPlexGetPointDepth(dm, point, &depth));
7974     for (clsize = 0, p = 0; p < Ncl; p++) {
7975       PetscInt dof;
7976       PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
7977       clsize += dof;
7978     }
7979     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
7980   }
7981   /* 2) Get number of indices on these points and field offsets from section */
7982   for (p = 0; p < Ncl * 2; p += 2) {
7983     PetscInt dof, fdof;
7984 
7985     PetscCall(PetscSectionGetDof(section, points[p], &dof));
7986     for (f = 0; f < Nf; ++f) {
7987       PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
7988       offsets[f + 1] += fdof;
7989     }
7990     Ni += dof;
7991   }
7992   if (*numRows == -1) *numRows = Ni;
7993   if (*numCols == -1) *numCols = Ni;
7994   nRows = *numRows;
7995   nCols = *numCols;
7996   for (f = 1; f < Nf; ++f) offsets[f + 1] += offsets[f];
7997   PetscCheck(!Nf || offsets[Nf] == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, offsets[Nf], Ni);
7998   /* 3) Get symmetries and sign flips. Apply sign flips to values if passed in (only works for square values matrix) */
7999   if (multiplyRight) PetscCheck(nCols == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Expected %" PetscInt_FMT " columns, got %" PetscInt_FMT, Ni, nCols);
8000   if (multiplyLeft) PetscCheck(nRows == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Expected %" PetscInt_FMT " rows, got %" PetscInt_FMT, Ni, nRows);
8001   for (f = 0; f < PetscMax(1, Nf); ++f) {
8002     if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
8003     else PetscCall(PetscSectionGetPointSyms(section, Ncl, points, &perms[f], &flips[f]));
8004     /* may need to apply sign changes to the element matrix */
8005     if (values && flips[f]) {
8006       PetscInt foffset = offsets[f];
8007 
8008       for (p = 0; p < Ncl; ++p) {
8009         PetscInt           pnt  = points[2 * p], fdof;
8010         const PetscScalar *flip = flips[f] ? flips[f][p] : NULL;
8011 
8012         if (!Nf) PetscCall(PetscSectionGetDof(section, pnt, &fdof));
8013         else PetscCall(PetscSectionGetFieldDof(section, pnt, f, &fdof));
8014         if (flip) {
8015           PetscInt i, j, k;
8016 
8017           if (!valCopy) {
8018             PetscCall(DMGetWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
8019             for (j = 0; j < Ni * Ni; ++j) valCopy[j] = (*values)[j];
8020             *values = valCopy;
8021           }
8022           for (i = 0; i < fdof; ++i) {
8023             PetscScalar fval = flip[i];
8024 
8025             if (multiplyRight) {
8026               for (k = 0; k < nRows; ++k) { valCopy[Ni * k + (foffset + i)] *= fval; }
8027             }
8028             if (multiplyLeft) {
8029               for (k = 0; k < nCols; ++k) { valCopy[nCols * (foffset + i) + k] *= fval; }
8030             }
8031           }
8032         }
8033         foffset += fdof;
8034       }
8035     }
8036   }
8037   /* 4) Apply hanging node constraints. Get new symmetries and replace all storage with constrained storage */
8038   PetscCall(DMPlexAnchorsModifyMat_Internal(dm, section, Ncl, Ni, points, perms, nRows, nCols, values ? *values : NULL, &NclC, &NiC, &pointsC, values ? &valuesC : NULL, offsets, multiplyRight, multiplyLeft));
8039   if (NclC) {
8040     if (multiplyRight) { *numCols = nCols = NiC; }
8041     if (multiplyLeft) { *numRows = nRows = NiC; }
8042     if (valCopy) PetscCall(DMRestoreWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
8043     for (f = 0; f < PetscMax(1, Nf); ++f) {
8044       if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
8045       else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
8046     }
8047     for (f = 0; f < PetscMax(1, Nf); ++f) {
8048       if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, NclC, pointsC, &perms[f], &flips[f]));
8049       else PetscCall(PetscSectionGetPointSyms(section, NclC, pointsC, &perms[f], &flips[f]));
8050     }
8051     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
8052     Ncl    = NclC;
8053     Ni     = NiC;
8054     points = pointsC;
8055     if (values) *values = valuesC;
8056   }
8057   /* 5) Calculate indices */
8058   PetscCall(DMGetWorkArray(dm, Ni, MPIU_INT, &idx));
8059   PetscCall(PetscSectionGetChart(idxSection, &idxStart, &idxEnd));
8060   if (Nf) {
8061     PetscInt  idxOff;
8062     PetscBool useFieldOffsets;
8063 
8064     if (outOffsets) {
8065       for (f = 0; f <= Nf; f++) outOffsets[f] = offsets[f];
8066     }
8067     PetscCall(PetscSectionGetUseFieldOffsets(idxSection, &useFieldOffsets));
8068     if (useFieldOffsets) {
8069       for (p = 0; p < Ncl; ++p) {
8070         const PetscInt pnt = points[p * 2];
8071 
8072         PetscCall(DMPlexGetIndicesPointFieldsSplit_Internal(section, idxSection, pnt, offsets, perms, p, clperm, idx));
8073       }
8074     } else {
8075       for (p = 0; p < Ncl; ++p) {
8076         const PetscInt pnt = points[p * 2];
8077 
8078         if (pnt < idxStart || pnt >= idxEnd) continue;
8079         PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
8080         /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
8081          * not (at the time of this writing) have fields set. They probably should, in which case we would pass the
8082          * global section. */
8083         PetscCall(DMPlexGetIndicesPointFields_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, offsets, PETSC_FALSE, perms, p, clperm, idx));
8084       }
8085     }
8086   } else {
8087     PetscInt off = 0, idxOff;
8088 
8089     for (p = 0; p < Ncl; ++p) {
8090       const PetscInt  pnt  = points[p * 2];
8091       const PetscInt *perm = perms[0] ? perms[0][p] : NULL;
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 global section. */
8097       PetscCall(DMPlexGetIndicesPoint_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, &off, PETSC_FALSE, perm, clperm, idx));
8098     }
8099   }
8100   /* 6) Cleanup */
8101   for (f = 0; f < PetscMax(1, Nf); ++f) {
8102     if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
8103     else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
8104   }
8105   if (NclC) {
8106     PetscCall(DMRestoreWorkArray(dm, NclC * 2, MPIU_INT, &pointsC));
8107   } else {
8108     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
8109   }
8110 
8111   if (indices) *indices = idx;
8112   PetscFunctionReturn(PETSC_SUCCESS);
8113 }
8114 
8115 /*@C
8116   DMPlexGetClosureIndices - Gets the global dof indices associated with the closure of the given point within the provided sections.
8117 
8118   Not collective
8119 
8120   Input Parameters:
8121 + dm         - The `DM`
8122 . section    - The `PetscSection` describing the points (a local section)
8123 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
8124 . point      - The point defining the closure
8125 - useClPerm  - Use the closure point permutation if available
8126 
8127   Output Parameters:
8128 + numIndices - The number of dof indices in the closure of point with the input sections
8129 . indices    - The dof indices
8130 . outOffsets - Array to write the field offsets into, or `NULL`
8131 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or `NULL`
8132 
8133   Level: advanced
8134 
8135   Notes:
8136   Call `DMPlexRestoreClosureIndices()` to free allocated memory
8137 
8138   If `idxSection` is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
8139   of those indices is not significant.  If `idxSection` is local, the constrained dofs will yield the involution -(idx+1)
8140   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
8141   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when `idxSection` == section, otherwise global
8142   indices (with the above semantics) are implied.
8143 
8144 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`,
8145           `PetscSection`, `DMGetGlobalSection()`
8146 @*/
8147 PetscErrorCode DMPlexGetClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
8148 {
8149   PetscInt numRows = -1, numCols = -1;
8150 
8151   PetscFunctionBeginHot;
8152   PetscCall(DMPlexGetClosureIndices_Internal(dm, section, idxSection, point, useClPerm, &numRows, &numCols, indices, outOffsets, values, PETSC_TRUE, PETSC_TRUE));
8153   PetscCheck(numRows == numCols, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Symmetric matrix transformation produces rectangular dimensions (%" PetscInt_FMT ", %" PetscInt_FMT ")", numRows, numCols);
8154   *numIndices = numRows;
8155   PetscFunctionReturn(PETSC_SUCCESS);
8156 }
8157 
8158 /*@C
8159   DMPlexRestoreClosureIndices - Restores the global dof indices associated with the closure of the given point within the provided sections.
8160 
8161   Not collective
8162 
8163   Input Parameters:
8164 + dm         - The `DM`
8165 . section    - The `PetscSection` describing the points (a local section)
8166 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
8167 . point      - The point defining the closure
8168 - useClPerm  - Use the closure point permutation if available
8169 
8170   Output Parameters:
8171 + numIndices - The number of dof indices in the closure of point with the input sections
8172 . indices    - The dof indices
8173 . outOffsets - Array to write the field offsets into, or `NULL`
8174 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or `NULL`
8175 
8176   Level: advanced
8177 
8178   Notes:
8179   If values were modified, the user is responsible for calling `DMRestoreWorkArray`(dm, 0, `MPIU_SCALAR`, &values).
8180 
8181   If idxSection is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
8182   of those indices is not significant.  If idxSection is local, the constrained dofs will yield the involution -(idx+1)
8183   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
8184   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when idxSection == section, otherwise global
8185   indices (with the above semantics) are implied.
8186 
8187 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`, `DMGetGlobalSection()`
8188 @*/
8189 PetscErrorCode DMPlexRestoreClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
8190 {
8191   PetscFunctionBegin;
8192   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8193   PetscAssertPointer(indices, 7);
8194   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, indices));
8195   PetscFunctionReturn(PETSC_SUCCESS);
8196 }
8197 
8198 PetscErrorCode DMPlexMatSetClosure_Internal(DM dm, PetscSection section, PetscSection globalSection, PetscBool useClPerm, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8199 {
8200   DM_Plex           *mesh = (DM_Plex *)dm->data;
8201   PetscInt          *indices;
8202   PetscInt           numIndices;
8203   const PetscScalar *valuesOrig = values;
8204   PetscErrorCode     ierr;
8205 
8206   PetscFunctionBegin;
8207   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8208   if (!section) PetscCall(DMGetLocalSection(dm, &section));
8209   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
8210   if (!globalSection) PetscCall(DMGetGlobalSection(dm, &globalSection));
8211   PetscValidHeaderSpecific(globalSection, PETSC_SECTION_CLASSID, 3);
8212   PetscValidHeaderSpecific(A, MAT_CLASSID, 5);
8213 
8214   PetscCall(DMPlexGetClosureIndices(dm, section, globalSection, point, useClPerm, &numIndices, &indices, NULL, (PetscScalar **)&values));
8215 
8216   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndices, indices, 0, NULL, values));
8217   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8218   ierr = MatSetValues(A, numIndices, indices, numIndices, indices, values, mode);
8219   if (ierr) {
8220     PetscMPIInt rank;
8221 
8222     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8223     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8224     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndices, indices, 0, NULL, values));
8225     PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
8226     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
8227     SETERRQ(PetscObjectComm((PetscObject)dm), ierr, "Not possible to set matrix values");
8228   }
8229   if (mesh->printFEM > 1) {
8230     PetscInt i;
8231     PetscCall(PetscPrintf(PETSC_COMM_SELF, "  Indices:"));
8232     for (i = 0; i < numIndices; ++i) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, indices[i]));
8233     PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8234   }
8235 
8236   PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
8237   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
8238   PetscFunctionReturn(PETSC_SUCCESS);
8239 }
8240 
8241 /*@C
8242   DMPlexMatSetClosure - Set an array of the values on the closure of 'point'
8243 
8244   Not collective
8245 
8246   Input Parameters:
8247 + dm            - The `DM`
8248 . section       - The section describing the layout in `v`, or `NULL` to use the default section
8249 . globalSection - The section describing the layout in `v`, or `NULL` to use the default global section
8250 . A             - The matrix
8251 . point         - The point in the `DM`
8252 . values        - The array of values
8253 - mode          - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
8254 
8255   Level: intermediate
8256 
8257 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosureGeneral()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
8258 @*/
8259 PetscErrorCode DMPlexMatSetClosure(DM dm, PetscSection section, PetscSection globalSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8260 {
8261   PetscFunctionBegin;
8262   PetscCall(DMPlexMatSetClosure_Internal(dm, section, globalSection, PETSC_TRUE, A, point, values, mode));
8263   PetscFunctionReturn(PETSC_SUCCESS);
8264 }
8265 
8266 /*@C
8267   DMPlexMatSetClosureGeneral - Set an array of the values on the closure of 'point' using a different row and column section
8268 
8269   Not collective
8270 
8271   Input Parameters:
8272 + dmRow            - The `DM` for the row fields
8273 . sectionRow       - The section describing the layout, or `NULL` to use the default section in `dmRow`
8274 . useRowPerm       - The flag to use the closure permutation of the `dmRow` if available
8275 . globalSectionRow - The section describing the layout, or `NULL` to use the default global section in `dmRow`
8276 . dmCol            - The `DM` for the column fields
8277 . sectionCol       - The section describing the layout, or `NULL` to use the default section in `dmCol`
8278 . useColPerm       - The flag to use the closure permutation of the `dmCol` if available
8279 . globalSectionCol - The section describing the layout, or `NULL` to use the default global section in `dmCol`
8280 . A                - The matrix
8281 . point            - The point in the `DM`
8282 . values           - The array of values
8283 - mode             - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
8284 
8285   Level: intermediate
8286 
8287 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosure()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
8288 @*/
8289 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)
8290 {
8291   DM_Plex           *mesh = (DM_Plex *)dmRow->data;
8292   PetscInt          *indicesRow, *indicesCol;
8293   PetscInt           numIndicesRow = -1, numIndicesCol = -1;
8294   const PetscScalar *valuesV0 = values, *valuesV1, *valuesV2;
8295 
8296   PetscErrorCode ierr;
8297 
8298   PetscFunctionBegin;
8299   PetscValidHeaderSpecific(dmRow, DM_CLASSID, 1);
8300   if (!sectionRow) PetscCall(DMGetLocalSection(dmRow, &sectionRow));
8301   PetscValidHeaderSpecific(sectionRow, PETSC_SECTION_CLASSID, 2);
8302   if (!globalSectionRow) PetscCall(DMGetGlobalSection(dmRow, &globalSectionRow));
8303   PetscValidHeaderSpecific(globalSectionRow, PETSC_SECTION_CLASSID, 3);
8304   PetscValidHeaderSpecific(dmCol, DM_CLASSID, 5);
8305   if (!sectionCol) PetscCall(DMGetLocalSection(dmCol, &sectionCol));
8306   PetscValidHeaderSpecific(sectionCol, PETSC_SECTION_CLASSID, 6);
8307   if (!globalSectionCol) PetscCall(DMGetGlobalSection(dmCol, &globalSectionCol));
8308   PetscValidHeaderSpecific(globalSectionCol, PETSC_SECTION_CLASSID, 7);
8309   PetscValidHeaderSpecific(A, MAT_CLASSID, 9);
8310 
8311   PetscCall(DMPlexGetClosureIndicesSize_Internal(dmRow, sectionRow, point, &numIndicesRow));
8312   PetscCall(DMPlexGetClosureIndicesSize_Internal(dmCol, sectionCol, point, &numIndicesCol));
8313   valuesV1 = valuesV0;
8314   PetscCall(DMPlexGetClosureIndices_Internal(dmRow, sectionRow, globalSectionRow, point, useRowPerm, &numIndicesRow, &numIndicesCol, &indicesRow, NULL, (PetscScalar **)&valuesV1, PETSC_FALSE, PETSC_TRUE));
8315   valuesV2 = valuesV1;
8316   PetscCall(DMPlexGetClosureIndices_Internal(dmCol, sectionCol, globalSectionCol, point, useColPerm, &numIndicesRow, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&valuesV2, PETSC_TRUE, PETSC_FALSE));
8317 
8318   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, valuesV2));
8319   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8320   ierr = MatSetValues(A, numIndicesRow, indicesRow, numIndicesCol, indicesCol, valuesV2, mode);
8321   if (ierr) {
8322     PetscMPIInt rank;
8323 
8324     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8325     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8326     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
8327     PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesRow, NULL, (PetscScalar **)&valuesV2));
8328     PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&valuesV1));
8329     if (valuesV2 != valuesV1) PetscCall(DMRestoreWorkArray(dmCol, 0, MPIU_SCALAR, &valuesV2));
8330     if (valuesV1 != valuesV0) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &valuesV1));
8331   }
8332 
8333   PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, useColPerm, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&valuesV2));
8334   PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, useRowPerm, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&valuesV1));
8335   if (valuesV2 != valuesV1) PetscCall(DMRestoreWorkArray(dmCol, 0, MPIU_SCALAR, &valuesV2));
8336   if (valuesV1 != valuesV0) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &valuesV1));
8337   PetscFunctionReturn(PETSC_SUCCESS);
8338 }
8339 
8340 PetscErrorCode DMPlexMatSetClosureRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8341 {
8342   DM_Plex        *mesh    = (DM_Plex *)dmf->data;
8343   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
8344   PetscInt       *cpoints = NULL;
8345   PetscInt       *findices, *cindices;
8346   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
8347   PetscInt        foffsets[32], coffsets[32];
8348   DMPolytopeType  ct;
8349   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
8350   PetscErrorCode  ierr;
8351 
8352   PetscFunctionBegin;
8353   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
8354   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
8355   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
8356   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
8357   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
8358   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
8359   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
8360   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
8361   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
8362   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
8363   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
8364   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
8365   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
8366   PetscCall(PetscArrayzero(foffsets, 32));
8367   PetscCall(PetscArrayzero(coffsets, 32));
8368   /* Column indices */
8369   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8370   maxFPoints = numCPoints;
8371   /* Compress out points not in the section */
8372   /*   TODO: Squeeze out points with 0 dof as well */
8373   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
8374   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
8375     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
8376       cpoints[q * 2]     = cpoints[p];
8377       cpoints[q * 2 + 1] = cpoints[p + 1];
8378       ++q;
8379     }
8380   }
8381   numCPoints = q;
8382   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
8383     PetscInt fdof;
8384 
8385     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
8386     if (!dof) continue;
8387     for (f = 0; f < numFields; ++f) {
8388       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
8389       coffsets[f + 1] += fdof;
8390     }
8391     numCIndices += dof;
8392   }
8393   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
8394   /* Row indices */
8395   PetscCall(DMPlexGetCellType(dmc, point, &ct));
8396   {
8397     DMPlexTransform tr;
8398     DMPolytopeType *rct;
8399     PetscInt       *rsize, *rcone, *rornt, Nt;
8400 
8401     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8402     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8403     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8404     numSubcells = rsize[Nt - 1];
8405     PetscCall(DMPlexTransformDestroy(&tr));
8406   }
8407   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8408   for (r = 0, q = 0; r < numSubcells; ++r) {
8409     /* TODO Map from coarse to fine cells */
8410     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8411     /* Compress out points not in the section */
8412     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8413     for (p = 0; p < numFPoints * 2; p += 2) {
8414       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8415         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8416         if (!dof) continue;
8417         for (s = 0; s < q; ++s)
8418           if (fpoints[p] == ftotpoints[s * 2]) break;
8419         if (s < q) continue;
8420         ftotpoints[q * 2]     = fpoints[p];
8421         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8422         ++q;
8423       }
8424     }
8425     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8426   }
8427   numFPoints = q;
8428   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8429     PetscInt fdof;
8430 
8431     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8432     if (!dof) continue;
8433     for (f = 0; f < numFields; ++f) {
8434       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8435       foffsets[f + 1] += fdof;
8436     }
8437     numFIndices += dof;
8438   }
8439   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8440 
8441   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8442   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8443   PetscCall(DMGetWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8444   PetscCall(DMGetWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8445   if (numFields) {
8446     const PetscInt **permsF[32] = {NULL};
8447     const PetscInt **permsC[32] = {NULL};
8448 
8449     for (f = 0; f < numFields; f++) {
8450       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8451       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8452     }
8453     for (p = 0; p < numFPoints; p++) {
8454       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8455       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8456     }
8457     for (p = 0; p < numCPoints; p++) {
8458       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8459       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8460     }
8461     for (f = 0; f < numFields; f++) {
8462       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8463       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8464     }
8465   } else {
8466     const PetscInt **permsF = NULL;
8467     const PetscInt **permsC = NULL;
8468 
8469     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8470     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8471     for (p = 0, off = 0; p < numFPoints; p++) {
8472       const PetscInt *perm = permsF ? permsF[p] : NULL;
8473 
8474       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8475       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8476     }
8477     for (p = 0, off = 0; p < numCPoints; p++) {
8478       const PetscInt *perm = permsC ? permsC[p] : NULL;
8479 
8480       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8481       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8482     }
8483     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8484     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8485   }
8486   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8487   /* TODO: flips */
8488   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8489   ierr = MatSetValues(A, numFIndices, findices, numCIndices, cindices, values, mode);
8490   if (ierr) {
8491     PetscMPIInt rank;
8492 
8493     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8494     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8495     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8496     PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8497     PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8498   }
8499   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8500   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8501   PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8502   PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8503   PetscFunctionReturn(PETSC_SUCCESS);
8504 }
8505 
8506 PetscErrorCode DMPlexMatGetClosureIndicesRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, PetscInt point, PetscInt cindices[], PetscInt findices[])
8507 {
8508   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
8509   PetscInt       *cpoints      = NULL;
8510   PetscInt        foffsets[32] = {0}, coffsets[32] = {0};
8511   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
8512   DMPolytopeType  ct;
8513   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
8514 
8515   PetscFunctionBegin;
8516   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
8517   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
8518   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
8519   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
8520   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
8521   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
8522   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
8523   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
8524   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
8525   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
8526   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
8527   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
8528   /* Column indices */
8529   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8530   maxFPoints = numCPoints;
8531   /* Compress out points not in the section */
8532   /*   TODO: Squeeze out points with 0 dof as well */
8533   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
8534   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
8535     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
8536       cpoints[q * 2]     = cpoints[p];
8537       cpoints[q * 2 + 1] = cpoints[p + 1];
8538       ++q;
8539     }
8540   }
8541   numCPoints = q;
8542   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
8543     PetscInt fdof;
8544 
8545     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
8546     if (!dof) continue;
8547     for (f = 0; f < numFields; ++f) {
8548       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
8549       coffsets[f + 1] += fdof;
8550     }
8551     numCIndices += dof;
8552   }
8553   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
8554   /* Row indices */
8555   PetscCall(DMPlexGetCellType(dmc, point, &ct));
8556   {
8557     DMPlexTransform tr;
8558     DMPolytopeType *rct;
8559     PetscInt       *rsize, *rcone, *rornt, Nt;
8560 
8561     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8562     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8563     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8564     numSubcells = rsize[Nt - 1];
8565     PetscCall(DMPlexTransformDestroy(&tr));
8566   }
8567   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8568   for (r = 0, q = 0; r < numSubcells; ++r) {
8569     /* TODO Map from coarse to fine cells */
8570     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8571     /* Compress out points not in the section */
8572     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8573     for (p = 0; p < numFPoints * 2; p += 2) {
8574       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8575         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8576         if (!dof) continue;
8577         for (s = 0; s < q; ++s)
8578           if (fpoints[p] == ftotpoints[s * 2]) break;
8579         if (s < q) continue;
8580         ftotpoints[q * 2]     = fpoints[p];
8581         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8582         ++q;
8583       }
8584     }
8585     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8586   }
8587   numFPoints = q;
8588   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8589     PetscInt fdof;
8590 
8591     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8592     if (!dof) continue;
8593     for (f = 0; f < numFields; ++f) {
8594       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8595       foffsets[f + 1] += fdof;
8596     }
8597     numFIndices += dof;
8598   }
8599   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8600 
8601   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8602   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8603   if (numFields) {
8604     const PetscInt **permsF[32] = {NULL};
8605     const PetscInt **permsC[32] = {NULL};
8606 
8607     for (f = 0; f < numFields; f++) {
8608       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8609       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8610     }
8611     for (p = 0; p < numFPoints; p++) {
8612       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8613       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8614     }
8615     for (p = 0; p < numCPoints; p++) {
8616       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8617       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8618     }
8619     for (f = 0; f < numFields; f++) {
8620       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8621       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8622     }
8623   } else {
8624     const PetscInt **permsF = NULL;
8625     const PetscInt **permsC = NULL;
8626 
8627     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8628     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8629     for (p = 0, off = 0; p < numFPoints; p++) {
8630       const PetscInt *perm = permsF ? permsF[p] : NULL;
8631 
8632       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8633       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8634     }
8635     for (p = 0, off = 0; p < numCPoints; p++) {
8636       const PetscInt *perm = permsC ? permsC[p] : NULL;
8637 
8638       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8639       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8640     }
8641     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8642     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8643   }
8644   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8645   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8646   PetscFunctionReturn(PETSC_SUCCESS);
8647 }
8648 
8649 /*@
8650   DMPlexGetVTKCellHeight - Returns the height in the DAG used to determine which points are cells (normally 0)
8651 
8652   Input Parameter:
8653 . dm - The `DMPLEX` object
8654 
8655   Output Parameter:
8656 . cellHeight - The height of a cell
8657 
8658   Level: developer
8659 
8660 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetVTKCellHeight()`
8661 @*/
8662 PetscErrorCode DMPlexGetVTKCellHeight(DM dm, PetscInt *cellHeight)
8663 {
8664   DM_Plex *mesh = (DM_Plex *)dm->data;
8665 
8666   PetscFunctionBegin;
8667   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8668   PetscAssertPointer(cellHeight, 2);
8669   *cellHeight = mesh->vtkCellHeight;
8670   PetscFunctionReturn(PETSC_SUCCESS);
8671 }
8672 
8673 /*@
8674   DMPlexSetVTKCellHeight - Sets the height in the DAG used to determine which points are cells (normally 0)
8675 
8676   Input Parameters:
8677 + dm         - The `DMPLEX` object
8678 - cellHeight - The height of a cell
8679 
8680   Level: developer
8681 
8682 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetVTKCellHeight()`
8683 @*/
8684 PetscErrorCode DMPlexSetVTKCellHeight(DM dm, PetscInt cellHeight)
8685 {
8686   DM_Plex *mesh = (DM_Plex *)dm->data;
8687 
8688   PetscFunctionBegin;
8689   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8690   mesh->vtkCellHeight = cellHeight;
8691   PetscFunctionReturn(PETSC_SUCCESS);
8692 }
8693 
8694 /*@
8695   DMPlexGetCellTypeStratum - Get the range of cells of a given celltype
8696 
8697   Input Parameters:
8698 + dm - The `DMPLEX` object
8699 - ct - The `DMPolytopeType` of the cell
8700 
8701   Output Parameters:
8702 + start - The first cell of this type, or `NULL`
8703 - end   - The upper bound on this celltype, or `NULL`
8704 
8705   Level: advanced
8706 
8707 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexConstructGhostCells()`, `DMPlexGetDepthStratum()`, `DMPlexGetHeightStratum()`
8708 @*/
8709 PetscErrorCode DMPlexGetCellTypeStratum(DM dm, DMPolytopeType ct, PetscInt *start, PetscInt *end)
8710 {
8711   DM_Plex *mesh = (DM_Plex *)dm->data;
8712   DMLabel  label;
8713   PetscInt pStart, pEnd;
8714 
8715   PetscFunctionBegin;
8716   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8717   if (start) {
8718     PetscAssertPointer(start, 3);
8719     *start = 0;
8720   }
8721   if (end) {
8722     PetscAssertPointer(end, 4);
8723     *end = 0;
8724   }
8725   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8726   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
8727   if (mesh->tr) {
8728     PetscCall(DMPlexTransformGetCellTypeStratum(mesh->tr, ct, start, end));
8729   } else {
8730     PetscCall(DMPlexGetCellTypeLabel(dm, &label));
8731     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named celltype was found");
8732     PetscCall(DMLabelGetStratumBounds(label, ct, start, end));
8733   }
8734   PetscFunctionReturn(PETSC_SUCCESS);
8735 }
8736 
8737 PetscErrorCode DMPlexCreateNumbering_Plex(DM dm, PetscInt pStart, PetscInt pEnd, PetscInt shift, PetscInt *globalSize, PetscSF sf, IS *numbering)
8738 {
8739   PetscSection section, globalSection;
8740   PetscInt    *numbers, p;
8741 
8742   PetscFunctionBegin;
8743   if (PetscDefined(USE_DEBUG)) PetscCall(DMPlexCheckPointSF(dm, sf, PETSC_TRUE));
8744   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
8745   PetscCall(PetscSectionSetChart(section, pStart, pEnd));
8746   for (p = pStart; p < pEnd; ++p) PetscCall(PetscSectionSetDof(section, p, 1));
8747   PetscCall(PetscSectionSetUp(section));
8748   PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &globalSection));
8749   PetscCall(PetscMalloc1(pEnd - pStart, &numbers));
8750   for (p = pStart; p < pEnd; ++p) {
8751     PetscCall(PetscSectionGetOffset(globalSection, p, &numbers[p - pStart]));
8752     if (numbers[p - pStart] < 0) numbers[p - pStart] -= shift;
8753     else numbers[p - pStart] += shift;
8754   }
8755   PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), pEnd - pStart, numbers, PETSC_OWN_POINTER, numbering));
8756   if (globalSize) {
8757     PetscLayout layout;
8758     PetscCall(PetscSectionGetPointLayout(PetscObjectComm((PetscObject)dm), globalSection, &layout));
8759     PetscCall(PetscLayoutGetSize(layout, globalSize));
8760     PetscCall(PetscLayoutDestroy(&layout));
8761   }
8762   PetscCall(PetscSectionDestroy(&section));
8763   PetscCall(PetscSectionDestroy(&globalSection));
8764   PetscFunctionReturn(PETSC_SUCCESS);
8765 }
8766 
8767 /*@
8768   DMPlexCreateCellNumbering - Get a global cell numbering for all cells on this process
8769 
8770   Input Parameters:
8771 + dm         - The `DMPLEX` object
8772 - includeAll - Whether to include all cells, or just the simplex and box cells
8773 
8774   Output Parameter:
8775 . globalCellNumbers - Global cell numbers for all cells on this process
8776 
8777   Level: developer
8778 
8779 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`, `DMPlexGetVertexNumbering()`
8780 @*/
8781 PetscErrorCode DMPlexCreateCellNumbering(DM dm, PetscBool includeAll, IS *globalCellNumbers)
8782 {
8783   PetscInt cellHeight, cStart, cEnd;
8784 
8785   PetscFunctionBegin;
8786   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8787   if (includeAll) PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8788   else PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd));
8789   PetscCall(DMPlexCreateNumbering_Plex(dm, cStart, cEnd, 0, NULL, dm->sf, globalCellNumbers));
8790   PetscFunctionReturn(PETSC_SUCCESS);
8791 }
8792 
8793 /*@
8794   DMPlexGetCellNumbering - Get a global cell numbering for all cells on this process
8795 
8796   Input Parameter:
8797 . dm - The `DMPLEX` object
8798 
8799   Output Parameter:
8800 . globalCellNumbers - Global cell numbers for all cells on this process
8801 
8802   Level: developer
8803 
8804 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreateCellNumbering()`, `DMPlexGetVertexNumbering()`
8805 @*/
8806 PetscErrorCode DMPlexGetCellNumbering(DM dm, IS *globalCellNumbers)
8807 {
8808   DM_Plex *mesh = (DM_Plex *)dm->data;
8809 
8810   PetscFunctionBegin;
8811   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8812   if (!mesh->globalCellNumbers) PetscCall(DMPlexCreateCellNumbering(dm, PETSC_FALSE, &mesh->globalCellNumbers));
8813   *globalCellNumbers = mesh->globalCellNumbers;
8814   PetscFunctionReturn(PETSC_SUCCESS);
8815 }
8816 
8817 PetscErrorCode DMPlexCreateVertexNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalVertexNumbers)
8818 {
8819   PetscInt vStart, vEnd;
8820 
8821   PetscFunctionBegin;
8822   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8823   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8824   PetscCall(DMPlexCreateNumbering_Plex(dm, vStart, vEnd, 0, NULL, dm->sf, globalVertexNumbers));
8825   PetscFunctionReturn(PETSC_SUCCESS);
8826 }
8827 
8828 /*@
8829   DMPlexGetVertexNumbering - Get a global vertex numbering for all vertices on this process
8830 
8831   Input Parameter:
8832 . dm - The `DMPLEX` object
8833 
8834   Output Parameter:
8835 . globalVertexNumbers - Global vertex numbers for all vertices on this process
8836 
8837   Level: developer
8838 
8839 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
8840 @*/
8841 PetscErrorCode DMPlexGetVertexNumbering(DM dm, IS *globalVertexNumbers)
8842 {
8843   DM_Plex *mesh = (DM_Plex *)dm->data;
8844 
8845   PetscFunctionBegin;
8846   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8847   if (!mesh->globalVertexNumbers) PetscCall(DMPlexCreateVertexNumbering_Internal(dm, PETSC_FALSE, &mesh->globalVertexNumbers));
8848   *globalVertexNumbers = mesh->globalVertexNumbers;
8849   PetscFunctionReturn(PETSC_SUCCESS);
8850 }
8851 
8852 /*@
8853   DMPlexCreatePointNumbering - Create a global numbering for all points.
8854 
8855   Collective
8856 
8857   Input Parameter:
8858 . dm - The `DMPLEX` object
8859 
8860   Output Parameter:
8861 . globalPointNumbers - Global numbers for all points on this process
8862 
8863   Level: developer
8864 
8865   Notes:
8866   The point numbering `IS` is parallel, with local portion indexed by local points (see `DMGetLocalSection()`). The global
8867   points are taken as stratified, with each MPI rank owning a contiguous subset of each stratum. In the IS, owned points
8868   will have their non-negative value while points owned by different ranks will be involuted -(idx+1). As an example,
8869   consider a parallel mesh in which the first two elements and first two vertices are owned by rank 0.
8870 
8871   The partitioned mesh is
8872   ```
8873   (2)--0--(3)--1--(4)    (1)--0--(2)
8874   ```
8875   and its global numbering is
8876   ```
8877   (3)--0--(4)--1--(5)--2--(6)
8878   ```
8879   Then the global numbering is provided as
8880   ```
8881   [0] Number of indices in set 5
8882   [0] 0 0
8883   [0] 1 1
8884   [0] 2 3
8885   [0] 3 4
8886   [0] 4 -6
8887   [1] Number of indices in set 3
8888   [1] 0 2
8889   [1] 1 5
8890   [1] 2 6
8891   ```
8892 
8893 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
8894 @*/
8895 PetscErrorCode DMPlexCreatePointNumbering(DM dm, IS *globalPointNumbers)
8896 {
8897   IS        nums[4];
8898   PetscInt  depths[4], gdepths[4], starts[4];
8899   PetscInt  depth, d, shift = 0;
8900   PetscBool empty = PETSC_FALSE;
8901 
8902   PetscFunctionBegin;
8903   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8904   PetscCall(DMPlexGetDepth(dm, &depth));
8905   // For unstratified meshes use dim instead of depth
8906   if (depth < 0) PetscCall(DMGetDimension(dm, &depth));
8907   // If any stratum is empty, we must mark all empty
8908   for (d = 0; d <= depth; ++d) {
8909     PetscInt end;
8910 
8911     depths[d] = depth - d;
8912     PetscCall(DMPlexGetDepthStratum(dm, depths[d], &starts[d], &end));
8913     if (!(starts[d] - end)) empty = PETSC_TRUE;
8914   }
8915   if (empty)
8916     for (d = 0; d <= depth; ++d) {
8917       depths[d] = -1;
8918       starts[d] = -1;
8919     }
8920   else PetscCall(PetscSortIntWithArray(depth + 1, starts, depths));
8921   PetscCall(MPIU_Allreduce(depths, gdepths, depth + 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
8922   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]);
8923   // Note here that 'shift' is collective, so that the numbering is stratified by depth
8924   for (d = 0; d <= depth; ++d) {
8925     PetscInt pStart, pEnd, gsize;
8926 
8927     PetscCall(DMPlexGetDepthStratum(dm, gdepths[d], &pStart, &pEnd));
8928     PetscCall(DMPlexCreateNumbering_Plex(dm, pStart, pEnd, shift, &gsize, dm->sf, &nums[d]));
8929     shift += gsize;
8930   }
8931   PetscCall(ISConcatenate(PETSC_COMM_SELF, depth + 1, nums, globalPointNumbers));
8932   for (d = 0; d <= depth; ++d) PetscCall(ISDestroy(&nums[d]));
8933   PetscFunctionReturn(PETSC_SUCCESS);
8934 }
8935 
8936 /*@
8937   DMPlexCreateEdgeNumbering - Create a global numbering for edges.
8938 
8939   Collective
8940 
8941   Input Parameter:
8942 . dm - The `DMPLEX` object
8943 
8944   Output Parameter:
8945 . globalEdgeNumbers - Global numbers for all edges on this process
8946 
8947   Level: developer
8948 
8949   Notes:
8950   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).
8951 
8952 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`, `DMPlexGetVertexNumbering()`, `DMPlexCreatePointNumbering()`
8953 @*/
8954 PetscErrorCode DMPlexCreateEdgeNumbering(DM dm, IS *globalEdgeNumbers)
8955 {
8956   PetscSF  sf;
8957   PetscInt eStart, eEnd;
8958 
8959   PetscFunctionBegin;
8960   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8961   PetscCall(DMGetPointSF(dm, &sf));
8962   PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
8963   PetscCall(DMPlexCreateNumbering_Plex(dm, eStart, eEnd, 0, NULL, sf, globalEdgeNumbers));
8964   PetscFunctionReturn(PETSC_SUCCESS);
8965 }
8966 
8967 /*@
8968   DMPlexCreateRankField - Create a cell field whose value is the rank of the owner
8969 
8970   Input Parameter:
8971 . dm - The `DMPLEX` object
8972 
8973   Output Parameter:
8974 . ranks - The rank field
8975 
8976   Options Database Key:
8977 . -dm_partition_view - Adds the rank field into the `DM` output from `-dm_view` using the same viewer
8978 
8979   Level: intermediate
8980 
8981 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`
8982 @*/
8983 PetscErrorCode DMPlexCreateRankField(DM dm, Vec *ranks)
8984 {
8985   DM             rdm;
8986   PetscFE        fe;
8987   PetscScalar   *r;
8988   PetscMPIInt    rank;
8989   DMPolytopeType ct;
8990   PetscInt       dim, cStart, cEnd, c;
8991   PetscBool      simplex;
8992 
8993   PetscFunctionBeginUser;
8994   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8995   PetscAssertPointer(ranks, 2);
8996   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
8997   PetscCall(DMClone(dm, &rdm));
8998   PetscCall(DMGetDimension(rdm, &dim));
8999   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
9000   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
9001   simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct) + 1 ? PETSC_TRUE : PETSC_FALSE;
9002   PetscCall(PetscFECreateDefault(PETSC_COMM_SELF, dim, 1, simplex, "PETSc___rank_", -1, &fe));
9003   PetscCall(PetscObjectSetName((PetscObject)fe, "rank"));
9004   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
9005   PetscCall(PetscFEDestroy(&fe));
9006   PetscCall(DMCreateDS(rdm));
9007   PetscCall(DMCreateGlobalVector(rdm, ranks));
9008   PetscCall(PetscObjectSetName((PetscObject)*ranks, "partition"));
9009   PetscCall(VecGetArray(*ranks, &r));
9010   for (c = cStart; c < cEnd; ++c) {
9011     PetscScalar *lr;
9012 
9013     PetscCall(DMPlexPointGlobalRef(rdm, c, r, &lr));
9014     if (lr) *lr = rank;
9015   }
9016   PetscCall(VecRestoreArray(*ranks, &r));
9017   PetscCall(DMDestroy(&rdm));
9018   PetscFunctionReturn(PETSC_SUCCESS);
9019 }
9020 
9021 /*@
9022   DMPlexCreateLabelField - Create a field whose value is the label value for that point
9023 
9024   Input Parameters:
9025 + dm    - The `DMPLEX`
9026 - label - The `DMLabel`
9027 
9028   Output Parameter:
9029 . val - The label value field
9030 
9031   Options Database Key:
9032 . -dm_label_view - Adds the label value field into the `DM` output from `-dm_view` using the same viewer
9033 
9034   Level: intermediate
9035 
9036 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`
9037 @*/
9038 PetscErrorCode DMPlexCreateLabelField(DM dm, DMLabel label, Vec *val)
9039 {
9040   DM             rdm, plex;
9041   Vec            lval;
9042   PetscSection   section;
9043   PetscFE        fe;
9044   PetscScalar   *v;
9045   PetscInt       dim, pStart, pEnd, p, cStart;
9046   DMPolytopeType ct;
9047   char           name[PETSC_MAX_PATH_LEN];
9048   const char    *lname, *prefix;
9049 
9050   PetscFunctionBeginUser;
9051   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9052   PetscAssertPointer(label, 2);
9053   PetscAssertPointer(val, 3);
9054   PetscCall(DMClone(dm, &rdm));
9055   PetscCall(DMConvert(rdm, DMPLEX, &plex));
9056   PetscCall(DMPlexGetHeightStratum(plex, 0, &cStart, NULL));
9057   PetscCall(DMPlexGetCellType(plex, cStart, &ct));
9058   PetscCall(DMDestroy(&plex));
9059   PetscCall(DMGetDimension(rdm, &dim));
9060   PetscCall(DMGetOptionsPrefix(dm, &prefix));
9061   PetscCall(PetscObjectGetName((PetscObject)label, &lname));
9062   PetscCall(PetscSNPrintf(name, sizeof(name), "%s%s_", prefix ? prefix : "", lname));
9063   PetscCall(PetscFECreateByCell(PETSC_COMM_SELF, dim, 1, ct, name, -1, &fe));
9064   PetscCall(PetscObjectSetName((PetscObject)fe, ""));
9065   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
9066   PetscCall(PetscFEDestroy(&fe));
9067   PetscCall(DMCreateDS(rdm));
9068   PetscCall(DMCreateGlobalVector(rdm, val));
9069   PetscCall(DMCreateLocalVector(rdm, &lval));
9070   PetscCall(PetscObjectSetName((PetscObject)*val, lname));
9071   PetscCall(DMGetLocalSection(rdm, &section));
9072   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
9073   PetscCall(VecGetArray(lval, &v));
9074   for (p = pStart; p < pEnd; ++p) {
9075     PetscInt cval, dof, off;
9076 
9077     PetscCall(PetscSectionGetDof(section, p, &dof));
9078     if (!dof) continue;
9079     PetscCall(DMLabelGetValue(label, p, &cval));
9080     PetscCall(PetscSectionGetOffset(section, p, &off));
9081     for (PetscInt d = 0; d < dof; d++) v[off + d] = cval;
9082   }
9083   PetscCall(VecRestoreArray(lval, &v));
9084   PetscCall(DMLocalToGlobal(rdm, lval, INSERT_VALUES, *val));
9085   PetscCall(VecDestroy(&lval));
9086   PetscCall(DMDestroy(&rdm));
9087   PetscFunctionReturn(PETSC_SUCCESS);
9088 }
9089 
9090 /*@
9091   DMPlexCheckSymmetry - Check that the adjacency information in the mesh is symmetric.
9092 
9093   Input Parameter:
9094 . dm - The `DMPLEX` object
9095 
9096   Level: developer
9097 
9098   Notes:
9099   This is a useful diagnostic when creating meshes programmatically.
9100 
9101   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9102 
9103 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9104 @*/
9105 PetscErrorCode DMPlexCheckSymmetry(DM dm)
9106 {
9107   PetscSection    coneSection, supportSection;
9108   const PetscInt *cone, *support;
9109   PetscInt        coneSize, c, supportSize, s;
9110   PetscInt        pStart, pEnd, p, pp, csize, ssize;
9111   PetscBool       storagecheck = PETSC_TRUE;
9112 
9113   PetscFunctionBegin;
9114   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9115   PetscCall(DMViewFromOptions(dm, NULL, "-sym_dm_view"));
9116   PetscCall(DMPlexGetConeSection(dm, &coneSection));
9117   PetscCall(DMPlexGetSupportSection(dm, &supportSection));
9118   /* Check that point p is found in the support of its cone points, and vice versa */
9119   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
9120   for (p = pStart; p < pEnd; ++p) {
9121     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
9122     PetscCall(DMPlexGetCone(dm, p, &cone));
9123     for (c = 0; c < coneSize; ++c) {
9124       PetscBool dup = PETSC_FALSE;
9125       PetscInt  d;
9126       for (d = c - 1; d >= 0; --d) {
9127         if (cone[c] == cone[d]) {
9128           dup = PETSC_TRUE;
9129           break;
9130         }
9131       }
9132       PetscCall(DMPlexGetSupportSize(dm, cone[c], &supportSize));
9133       PetscCall(DMPlexGetSupport(dm, cone[c], &support));
9134       for (s = 0; s < supportSize; ++s) {
9135         if (support[s] == p) break;
9136       }
9137       if ((s >= supportSize) || (dup && (support[s + 1] != p))) {
9138         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", p));
9139         for (s = 0; s < coneSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[s]));
9140         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9141         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", cone[c]));
9142         for (s = 0; s < supportSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[s]));
9143         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9144         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]);
9145         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in support of cone point %" PetscInt_FMT, p, cone[c]);
9146       }
9147     }
9148     PetscCall(DMPlexGetTreeParent(dm, p, &pp, NULL));
9149     if (p != pp) {
9150       storagecheck = PETSC_FALSE;
9151       continue;
9152     }
9153     PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
9154     PetscCall(DMPlexGetSupport(dm, p, &support));
9155     for (s = 0; s < supportSize; ++s) {
9156       PetscCall(DMPlexGetConeSize(dm, support[s], &coneSize));
9157       PetscCall(DMPlexGetCone(dm, support[s], &cone));
9158       for (c = 0; c < coneSize; ++c) {
9159         PetscCall(DMPlexGetTreeParent(dm, cone[c], &pp, NULL));
9160         if (cone[c] != pp) {
9161           c = 0;
9162           break;
9163         }
9164         if (cone[c] == p) break;
9165       }
9166       if (c >= coneSize) {
9167         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", p));
9168         for (c = 0; c < supportSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[c]));
9169         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9170         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", support[s]));
9171         for (c = 0; c < coneSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[c]));
9172         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9173         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in cone of support point %" PetscInt_FMT, p, support[s]);
9174       }
9175     }
9176   }
9177   if (storagecheck) {
9178     PetscCall(PetscSectionGetStorageSize(coneSection, &csize));
9179     PetscCall(PetscSectionGetStorageSize(supportSection, &ssize));
9180     PetscCheck(csize == ssize, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Total cone size %" PetscInt_FMT " != Total support size %" PetscInt_FMT, csize, ssize);
9181   }
9182   PetscFunctionReturn(PETSC_SUCCESS);
9183 }
9184 
9185 /*
9186   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.
9187 */
9188 static PetscErrorCode DMPlexCellUnsplitVertices_Private(DM dm, PetscInt c, DMPolytopeType ct, PetscInt *unsplit)
9189 {
9190   DMPolytopeType  cct;
9191   PetscInt        ptpoints[4];
9192   const PetscInt *cone, *ccone, *ptcone;
9193   PetscInt        coneSize, cp, cconeSize, ccp, npt = 0, pt;
9194 
9195   PetscFunctionBegin;
9196   *unsplit = 0;
9197   switch (ct) {
9198   case DM_POLYTOPE_POINT_PRISM_TENSOR:
9199     ptpoints[npt++] = c;
9200     break;
9201   case DM_POLYTOPE_SEG_PRISM_TENSOR:
9202     PetscCall(DMPlexGetCone(dm, c, &cone));
9203     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9204     for (cp = 0; cp < coneSize; ++cp) {
9205       PetscCall(DMPlexGetCellType(dm, cone[cp], &cct));
9206       if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) ptpoints[npt++] = cone[cp];
9207     }
9208     break;
9209   case DM_POLYTOPE_TRI_PRISM_TENSOR:
9210   case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9211     PetscCall(DMPlexGetCone(dm, c, &cone));
9212     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9213     for (cp = 0; cp < coneSize; ++cp) {
9214       PetscCall(DMPlexGetCone(dm, cone[cp], &ccone));
9215       PetscCall(DMPlexGetConeSize(dm, cone[cp], &cconeSize));
9216       for (ccp = 0; ccp < cconeSize; ++ccp) {
9217         PetscCall(DMPlexGetCellType(dm, ccone[ccp], &cct));
9218         if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) {
9219           PetscInt p;
9220           for (p = 0; p < npt; ++p)
9221             if (ptpoints[p] == ccone[ccp]) break;
9222           if (p == npt) ptpoints[npt++] = ccone[ccp];
9223         }
9224       }
9225     }
9226     break;
9227   default:
9228     break;
9229   }
9230   for (pt = 0; pt < npt; ++pt) {
9231     PetscCall(DMPlexGetCone(dm, ptpoints[pt], &ptcone));
9232     if (ptcone[0] == ptcone[1]) ++(*unsplit);
9233   }
9234   PetscFunctionReturn(PETSC_SUCCESS);
9235 }
9236 
9237 /*@
9238   DMPlexCheckSkeleton - Check that each cell has the correct number of vertices
9239 
9240   Input Parameters:
9241 + dm         - The `DMPLEX` object
9242 - cellHeight - Normally 0
9243 
9244   Level: developer
9245 
9246   Notes:
9247   This is a useful diagnostic when creating meshes programmatically.
9248   Currently applicable only to homogeneous simplex or tensor meshes.
9249 
9250   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9251 
9252 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9253 @*/
9254 PetscErrorCode DMPlexCheckSkeleton(DM dm, PetscInt cellHeight)
9255 {
9256   DMPlexInterpolatedFlag interp;
9257   DMPolytopeType         ct;
9258   PetscInt               vStart, vEnd, cStart, cEnd, c;
9259 
9260   PetscFunctionBegin;
9261   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9262   PetscCall(DMPlexIsInterpolated(dm, &interp));
9263   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9264   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
9265   for (c = cStart; c < cEnd; ++c) {
9266     PetscInt *closure = NULL;
9267     PetscInt  coneSize, closureSize, cl, Nv = 0;
9268 
9269     PetscCall(DMPlexGetCellType(dm, c, &ct));
9270     if (ct == DM_POLYTOPE_UNKNOWN) continue;
9271     if (interp == DMPLEX_INTERPOLATED_FULL) {
9272       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9273       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));
9274     }
9275     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9276     for (cl = 0; cl < closureSize * 2; cl += 2) {
9277       const PetscInt p = closure[cl];
9278       if ((p >= vStart) && (p < vEnd)) ++Nv;
9279     }
9280     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9281     /* Special Case: Tensor faces with identified vertices */
9282     if (Nv < DMPolytopeTypeGetNumVertices(ct)) {
9283       PetscInt unsplit;
9284 
9285       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9286       if (Nv + unsplit == DMPolytopeTypeGetNumVertices(ct)) continue;
9287     }
9288     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));
9289   }
9290   PetscFunctionReturn(PETSC_SUCCESS);
9291 }
9292 
9293 /*@
9294   DMPlexCheckFaces - Check that the faces of each cell give a vertex order this is consistent with what we expect from the cell type
9295 
9296   Collective
9297 
9298   Input Parameters:
9299 + dm         - The `DMPLEX` object
9300 - cellHeight - Normally 0
9301 
9302   Level: developer
9303 
9304   Notes:
9305   This is a useful diagnostic when creating meshes programmatically.
9306   This routine is only relevant for meshes that are fully interpolated across all ranks.
9307   It will error out if a partially interpolated mesh is given on some rank.
9308   It will do nothing for locally uninterpolated mesh (as there is nothing to check).
9309 
9310   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9311 
9312 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMPlexGetVTKCellHeight()`, `DMSetFromOptions()`
9313 @*/
9314 PetscErrorCode DMPlexCheckFaces(DM dm, PetscInt cellHeight)
9315 {
9316   PetscInt               dim, depth, vStart, vEnd, cStart, cEnd, c, h;
9317   DMPlexInterpolatedFlag interpEnum;
9318 
9319   PetscFunctionBegin;
9320   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9321   PetscCall(DMPlexIsInterpolatedCollective(dm, &interpEnum));
9322   if (interpEnum == DMPLEX_INTERPOLATED_NONE) PetscFunctionReturn(PETSC_SUCCESS);
9323   if (interpEnum != DMPLEX_INTERPOLATED_FULL) {
9324     PetscCall(PetscPrintf(PetscObjectComm((PetscObject)dm), "DMPlexCheckFaces() warning: Mesh is only partially interpolated, this is currently not supported"));
9325     PetscFunctionReturn(PETSC_SUCCESS);
9326   }
9327 
9328   PetscCall(DMGetDimension(dm, &dim));
9329   PetscCall(DMPlexGetDepth(dm, &depth));
9330   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
9331   for (h = cellHeight; h < PetscMin(depth, dim); ++h) {
9332     PetscCall(DMPlexGetHeightStratum(dm, h, &cStart, &cEnd));
9333     for (c = cStart; c < cEnd; ++c) {
9334       const PetscInt       *cone, *ornt, *faceSizes, *faces;
9335       const DMPolytopeType *faceTypes;
9336       DMPolytopeType        ct;
9337       PetscInt              numFaces, coneSize, f;
9338       PetscInt             *closure = NULL, closureSize, cl, numCorners = 0, fOff = 0, unsplit;
9339 
9340       PetscCall(DMPlexGetCellType(dm, c, &ct));
9341       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9342       if (unsplit) continue;
9343       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9344       PetscCall(DMPlexGetCone(dm, c, &cone));
9345       PetscCall(DMPlexGetConeOrientation(dm, c, &ornt));
9346       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9347       for (cl = 0; cl < closureSize * 2; cl += 2) {
9348         const PetscInt p = closure[cl];
9349         if ((p >= vStart) && (p < vEnd)) closure[numCorners++] = p;
9350       }
9351       PetscCall(DMPlexGetRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
9352       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);
9353       for (f = 0; f < numFaces; ++f) {
9354         DMPolytopeType fct;
9355         PetscInt      *fclosure = NULL, fclosureSize, cl, fnumCorners = 0, v;
9356 
9357         PetscCall(DMPlexGetCellType(dm, cone[f], &fct));
9358         PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[f], ornt[f], PETSC_TRUE, &fclosureSize, &fclosure));
9359         for (cl = 0; cl < fclosureSize * 2; cl += 2) {
9360           const PetscInt p = fclosure[cl];
9361           if ((p >= vStart) && (p < vEnd)) fclosure[fnumCorners++] = p;
9362         }
9363         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]);
9364         for (v = 0; v < fnumCorners; ++v) {
9365           if (fclosure[v] != faces[fOff + v]) {
9366             PetscInt v1;
9367 
9368             PetscCall(PetscPrintf(PETSC_COMM_SELF, "face closure:"));
9369             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, fclosure[v1]));
9370             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\ncell face:"));
9371             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, faces[fOff + v1]));
9372             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9373             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]);
9374           }
9375         }
9376         PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[f], PETSC_TRUE, &fclosureSize, &fclosure));
9377         fOff += faceSizes[f];
9378       }
9379       PetscCall(DMPlexRestoreRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
9380       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9381     }
9382   }
9383   PetscFunctionReturn(PETSC_SUCCESS);
9384 }
9385 
9386 /*@
9387   DMPlexCheckGeometry - Check the geometry of mesh cells
9388 
9389   Input Parameter:
9390 . dm - The `DMPLEX` object
9391 
9392   Level: developer
9393 
9394   Notes:
9395   This is a useful diagnostic when creating meshes programmatically.
9396 
9397   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9398 
9399 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9400 @*/
9401 PetscErrorCode DMPlexCheckGeometry(DM dm)
9402 {
9403   Vec       coordinates;
9404   PetscReal detJ, J[9], refVol = 1.0;
9405   PetscReal vol;
9406   PetscInt  dim, depth, dE, d, cStart, cEnd, c;
9407 
9408   PetscFunctionBegin;
9409   PetscCall(DMGetDimension(dm, &dim));
9410   PetscCall(DMGetCoordinateDim(dm, &dE));
9411   if (dim != dE) PetscFunctionReturn(PETSC_SUCCESS);
9412   PetscCall(DMPlexGetDepth(dm, &depth));
9413   for (d = 0; d < dim; ++d) refVol *= 2.0;
9414   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
9415   /* Make sure local coordinates are created, because that step is collective */
9416   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
9417   if (!coordinates) PetscFunctionReturn(PETSC_SUCCESS);
9418   for (c = cStart; c < cEnd; ++c) {
9419     DMPolytopeType ct;
9420     PetscInt       unsplit;
9421     PetscBool      ignoreZeroVol = PETSC_FALSE;
9422 
9423     PetscCall(DMPlexGetCellType(dm, c, &ct));
9424     switch (ct) {
9425     case DM_POLYTOPE_SEG_PRISM_TENSOR:
9426     case DM_POLYTOPE_TRI_PRISM_TENSOR:
9427     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9428       ignoreZeroVol = PETSC_TRUE;
9429       break;
9430     default:
9431       break;
9432     }
9433     switch (ct) {
9434     case DM_POLYTOPE_TRI_PRISM:
9435     case DM_POLYTOPE_TRI_PRISM_TENSOR:
9436     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9437     case DM_POLYTOPE_PYRAMID:
9438       continue;
9439     default:
9440       break;
9441     }
9442     PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9443     if (unsplit) continue;
9444     PetscCall(DMPlexComputeCellGeometryFEM(dm, c, NULL, NULL, J, NULL, &detJ));
9445     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);
9446     PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FEM Volume %g\n", c, (double)(detJ * refVol)));
9447     /* This should work with periodicity since DG coordinates should be used */
9448     if (depth > 1) {
9449       PetscCall(DMPlexComputeCellGeometryFVM(dm, c, &vol, NULL, NULL));
9450       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);
9451       PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FVM Volume %g\n", c, (double)vol));
9452     }
9453   }
9454   PetscFunctionReturn(PETSC_SUCCESS);
9455 }
9456 
9457 /*@
9458   DMPlexCheckPointSF - Check that several necessary conditions are met for the point `PetscSF` of this plex.
9459 
9460   Collective
9461 
9462   Input Parameters:
9463 + dm              - The `DMPLEX` object
9464 . pointSF         - The `PetscSF`, or `NULL` for `PointSF` attached to `DM`
9465 - allowExtraRoots - Flag to allow extra points not present in the `DM`
9466 
9467   Level: developer
9468 
9469   Notes:
9470   This is mainly intended for debugging/testing purposes.
9471 
9472   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9473 
9474   Extra roots can come from periodic cuts, where additional points appear on the boundary
9475 
9476 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMGetPointSF()`, `DMSetFromOptions()`
9477 @*/
9478 PetscErrorCode DMPlexCheckPointSF(DM dm, PetscSF pointSF, PetscBool allowExtraRoots)
9479 {
9480   PetscInt           l, nleaves, nroots, overlap;
9481   const PetscInt    *locals;
9482   const PetscSFNode *remotes;
9483   PetscBool          distributed;
9484   MPI_Comm           comm;
9485   PetscMPIInt        rank;
9486 
9487   PetscFunctionBegin;
9488   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9489   if (pointSF) PetscValidHeaderSpecific(pointSF, PETSCSF_CLASSID, 2);
9490   else pointSF = dm->sf;
9491   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9492   PetscCheck(pointSF, comm, PETSC_ERR_ARG_WRONGSTATE, "DMPlex must have Point SF attached");
9493   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9494   {
9495     PetscMPIInt mpiFlag;
9496 
9497     PetscCallMPI(MPI_Comm_compare(comm, PetscObjectComm((PetscObject)pointSF), &mpiFlag));
9498     PetscCheck(mpiFlag == MPI_CONGRUENT || mpiFlag == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "DM and Point SF have different communicators (flag %d)", mpiFlag);
9499   }
9500   PetscCall(PetscSFGetGraph(pointSF, &nroots, &nleaves, &locals, &remotes));
9501   PetscCall(DMPlexIsDistributed(dm, &distributed));
9502   if (!distributed) {
9503     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);
9504     PetscFunctionReturn(PETSC_SUCCESS);
9505   }
9506   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);
9507   PetscCall(DMPlexGetOverlap(dm, &overlap));
9508 
9509   /* Check SF graph is compatible with DMPlex chart */
9510   {
9511     PetscInt pStart, pEnd, maxLeaf;
9512 
9513     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
9514     PetscCall(PetscSFGetLeafRange(pointSF, NULL, &maxLeaf));
9515     PetscCheck(allowExtraRoots || pEnd - pStart == nroots, PETSC_COMM_SELF, PETSC_ERR_PLIB, "pEnd - pStart = %" PetscInt_FMT " != nroots = %" PetscInt_FMT, pEnd - pStart, nroots);
9516     PetscCheck(maxLeaf < pEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "maxLeaf = %" PetscInt_FMT " >= pEnd = %" PetscInt_FMT, maxLeaf, pEnd);
9517   }
9518 
9519   /* Check Point SF has no local points referenced */
9520   for (l = 0; l < nleaves; l++) {
9521     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);
9522   }
9523 
9524   /* Check there are no cells in interface */
9525   if (!overlap) {
9526     PetscInt cellHeight, cStart, cEnd;
9527 
9528     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9529     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9530     for (l = 0; l < nleaves; ++l) {
9531       const PetscInt point = locals ? locals[l] : l;
9532 
9533       PetscCheck(point < cStart || point >= cEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " which is a cell", point);
9534     }
9535   }
9536 
9537   /* If some point is in interface, then all its cone points must be also in interface (either as leaves or roots) */
9538   {
9539     const PetscInt *rootdegree;
9540 
9541     PetscCall(PetscSFComputeDegreeBegin(pointSF, &rootdegree));
9542     PetscCall(PetscSFComputeDegreeEnd(pointSF, &rootdegree));
9543     for (l = 0; l < nleaves; ++l) {
9544       const PetscInt  point = locals ? locals[l] : l;
9545       const PetscInt *cone;
9546       PetscInt        coneSize, c, idx;
9547 
9548       PetscCall(DMPlexGetConeSize(dm, point, &coneSize));
9549       PetscCall(DMPlexGetCone(dm, point, &cone));
9550       for (c = 0; c < coneSize; ++c) {
9551         if (!rootdegree[cone[c]]) {
9552           if (locals) {
9553             PetscCall(PetscFindInt(cone[c], nleaves, locals, &idx));
9554           } else {
9555             idx = (cone[c] < nleaves) ? cone[c] : -1;
9556           }
9557           PetscCheck(idx >= 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " but not %" PetscInt_FMT " from its cone", point, cone[c]);
9558         }
9559       }
9560     }
9561   }
9562   PetscFunctionReturn(PETSC_SUCCESS);
9563 }
9564 
9565 /*@
9566   DMPlexCheckOrphanVertices - Check that no vertices are disconnected from the mesh, unless the mesh only consists of disconnected vertices.
9567 
9568   Collective
9569 
9570   Input Parameter:
9571 . dm - The `DMPLEX` object
9572 
9573   Level: developer
9574 
9575   Notes:
9576   This is mainly intended for debugging/testing purposes.
9577 
9578   Other cell types which are disconnected would be caught by the symmetry and face checks.
9579 
9580   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9581 
9582 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCheck()`, `DMSetFromOptions()`
9583 @*/
9584 PetscErrorCode DMPlexCheckOrphanVertices(DM dm)
9585 {
9586   PetscInt pStart, pEnd, vStart, vEnd;
9587 
9588   PetscFunctionBegin;
9589   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
9590   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
9591   if (pStart == vStart && pEnd == vEnd) PetscFunctionReturn(PETSC_SUCCESS);
9592   for (PetscInt v = vStart; v < vEnd; ++v) {
9593     PetscInt suppSize;
9594 
9595     PetscCall(DMPlexGetSupportSize(dm, v, &suppSize));
9596     PetscCheck(suppSize, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Vertex %" PetscInt_FMT " is disconnected from the mesh", v);
9597   }
9598   PetscFunctionReturn(PETSC_SUCCESS);
9599 }
9600 
9601 /*@
9602   DMPlexCheck - Perform various checks of `DMPLEX` sanity
9603 
9604   Input Parameter:
9605 . dm - The `DMPLEX` object
9606 
9607   Level: developer
9608 
9609   Notes:
9610   This is a useful diagnostic when creating meshes programmatically.
9611 
9612   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9613 
9614   Currently does not include `DMPlexCheckCellShape()`.
9615 
9616 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9617 @*/
9618 PetscErrorCode DMPlexCheck(DM dm)
9619 {
9620   PetscInt cellHeight;
9621 
9622   PetscFunctionBegin;
9623   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9624   PetscCall(DMPlexCheckSymmetry(dm));
9625   PetscCall(DMPlexCheckSkeleton(dm, cellHeight));
9626   PetscCall(DMPlexCheckFaces(dm, cellHeight));
9627   PetscCall(DMPlexCheckGeometry(dm));
9628   PetscCall(DMPlexCheckPointSF(dm, NULL, PETSC_FALSE));
9629   PetscCall(DMPlexCheckInterfaceCones(dm));
9630   PetscCall(DMPlexCheckOrphanVertices(dm));
9631   PetscFunctionReturn(PETSC_SUCCESS);
9632 }
9633 
9634 typedef struct cell_stats {
9635   PetscReal min, max, sum, squaresum;
9636   PetscInt  count;
9637 } cell_stats_t;
9638 
9639 static void MPIAPI cell_stats_reduce(void *a, void *b, int *len, MPI_Datatype *datatype)
9640 {
9641   PetscInt i, N = *len;
9642 
9643   for (i = 0; i < N; i++) {
9644     cell_stats_t *A = (cell_stats_t *)a;
9645     cell_stats_t *B = (cell_stats_t *)b;
9646 
9647     B->min = PetscMin(A->min, B->min);
9648     B->max = PetscMax(A->max, B->max);
9649     B->sum += A->sum;
9650     B->squaresum += A->squaresum;
9651     B->count += A->count;
9652   }
9653 }
9654 
9655 /*@
9656   DMPlexCheckCellShape - Checks the Jacobian of the mapping from reference to real cells and computes some minimal statistics.
9657 
9658   Collective
9659 
9660   Input Parameters:
9661 + dm        - The `DMPLEX` object
9662 . output    - If true, statistics will be displayed on `stdout`
9663 - condLimit - Display all cells above this condition number, or `PETSC_DETERMINE` for no cell output
9664 
9665   Level: developer
9666 
9667   Notes:
9668   This is mainly intended for debugging/testing purposes.
9669 
9670   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9671 
9672 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexComputeOrthogonalQuality()`
9673 @*/
9674 PetscErrorCode DMPlexCheckCellShape(DM dm, PetscBool output, PetscReal condLimit)
9675 {
9676   DM           dmCoarse;
9677   cell_stats_t stats, globalStats;
9678   MPI_Comm     comm = PetscObjectComm((PetscObject)dm);
9679   PetscReal   *J, *invJ, min = 0, max = 0, mean = 0, stdev = 0;
9680   PetscReal    limit = condLimit > 0 ? condLimit : PETSC_MAX_REAL;
9681   PetscInt     cdim, cStart, cEnd, c, eStart, eEnd, count = 0;
9682   PetscMPIInt  rank, size;
9683 
9684   PetscFunctionBegin;
9685   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9686   stats.min = PETSC_MAX_REAL;
9687   stats.max = PETSC_MIN_REAL;
9688   stats.sum = stats.squaresum = 0.;
9689   stats.count                 = 0;
9690 
9691   PetscCallMPI(MPI_Comm_size(comm, &size));
9692   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9693   PetscCall(DMGetCoordinateDim(dm, &cdim));
9694   PetscCall(PetscMalloc2(PetscSqr(cdim), &J, PetscSqr(cdim), &invJ));
9695   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
9696   PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
9697   for (c = cStart; c < cEnd; c++) {
9698     PetscInt  i;
9699     PetscReal frobJ = 0., frobInvJ = 0., cond2, cond, detJ;
9700 
9701     PetscCall(DMPlexComputeCellGeometryAffineFEM(dm, c, NULL, J, invJ, &detJ));
9702     PetscCheck(detJ >= 0.0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Mesh cell %" PetscInt_FMT " is inverted", c);
9703     for (i = 0; i < PetscSqr(cdim); ++i) {
9704       frobJ += J[i] * J[i];
9705       frobInvJ += invJ[i] * invJ[i];
9706     }
9707     cond2 = frobJ * frobInvJ;
9708     cond  = PetscSqrtReal(cond2);
9709 
9710     stats.min = PetscMin(stats.min, cond);
9711     stats.max = PetscMax(stats.max, cond);
9712     stats.sum += cond;
9713     stats.squaresum += cond2;
9714     stats.count++;
9715     if (output && cond > limit) {
9716       PetscSection coordSection;
9717       Vec          coordsLocal;
9718       PetscScalar *coords = NULL;
9719       PetscInt     Nv, d, clSize, cl, *closure = NULL;
9720 
9721       PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
9722       PetscCall(DMGetCoordinateSection(dm, &coordSection));
9723       PetscCall(DMPlexVecGetClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9724       PetscCall(PetscSynchronizedPrintf(comm, "[%d] Cell %" PetscInt_FMT " cond %g\n", rank, c, (double)cond));
9725       for (i = 0; i < Nv / cdim; ++i) {
9726         PetscCall(PetscSynchronizedPrintf(comm, "  Vertex %" PetscInt_FMT ": (", i));
9727         for (d = 0; d < cdim; ++d) {
9728           if (d > 0) PetscCall(PetscSynchronizedPrintf(comm, ", "));
9729           PetscCall(PetscSynchronizedPrintf(comm, "%g", (double)PetscRealPart(coords[i * cdim + d])));
9730         }
9731         PetscCall(PetscSynchronizedPrintf(comm, ")\n"));
9732       }
9733       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9734       for (cl = 0; cl < clSize * 2; cl += 2) {
9735         const PetscInt edge = closure[cl];
9736 
9737         if ((edge >= eStart) && (edge < eEnd)) {
9738           PetscReal len;
9739 
9740           PetscCall(DMPlexComputeCellGeometryFVM(dm, edge, &len, NULL, NULL));
9741           PetscCall(PetscSynchronizedPrintf(comm, "  Edge %" PetscInt_FMT ": length %g\n", edge, (double)len));
9742         }
9743       }
9744       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9745       PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9746     }
9747   }
9748   if (output) PetscCall(PetscSynchronizedFlush(comm, NULL));
9749 
9750   if (size > 1) {
9751     PetscMPIInt  blockLengths[2] = {4, 1};
9752     MPI_Aint     blockOffsets[2] = {offsetof(cell_stats_t, min), offsetof(cell_stats_t, count)};
9753     MPI_Datatype blockTypes[2]   = {MPIU_REAL, MPIU_INT}, statType;
9754     MPI_Op       statReduce;
9755 
9756     PetscCallMPI(MPI_Type_create_struct(2, blockLengths, blockOffsets, blockTypes, &statType));
9757     PetscCallMPI(MPI_Type_commit(&statType));
9758     PetscCallMPI(MPI_Op_create(cell_stats_reduce, PETSC_TRUE, &statReduce));
9759     PetscCallMPI(MPI_Reduce(&stats, &globalStats, 1, statType, statReduce, 0, comm));
9760     PetscCallMPI(MPI_Op_free(&statReduce));
9761     PetscCallMPI(MPI_Type_free(&statType));
9762   } else {
9763     PetscCall(PetscArraycpy(&globalStats, &stats, 1));
9764   }
9765   if (rank == 0) {
9766     count = globalStats.count;
9767     min   = globalStats.min;
9768     max   = globalStats.max;
9769     mean  = globalStats.sum / globalStats.count;
9770     stdev = globalStats.count > 1 ? PetscSqrtReal(PetscMax((globalStats.squaresum - globalStats.count * mean * mean) / (globalStats.count - 1), 0)) : 0.0;
9771   }
9772 
9773   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));
9774   PetscCall(PetscFree2(J, invJ));
9775 
9776   PetscCall(DMGetCoarseDM(dm, &dmCoarse));
9777   if (dmCoarse) {
9778     PetscBool isplex;
9779 
9780     PetscCall(PetscObjectTypeCompare((PetscObject)dmCoarse, DMPLEX, &isplex));
9781     if (isplex) PetscCall(DMPlexCheckCellShape(dmCoarse, output, condLimit));
9782   }
9783   PetscFunctionReturn(PETSC_SUCCESS);
9784 }
9785 
9786 /*@
9787   DMPlexComputeOrthogonalQuality - Compute cell-wise orthogonal quality mesh statistic. Optionally tags all cells with
9788   orthogonal quality below given tolerance.
9789 
9790   Collective
9791 
9792   Input Parameters:
9793 + dm   - The `DMPLEX` object
9794 . fv   - Optional `PetscFV` object for pre-computed cell/face centroid information
9795 - atol - [0, 1] Absolute tolerance for tagging cells.
9796 
9797   Output Parameters:
9798 + OrthQual      - `Vec` containing orthogonal quality per cell
9799 - OrthQualLabel - `DMLabel` tagging cells below atol with `DM_ADAPT_REFINE`
9800 
9801   Options Database Keys:
9802 + -dm_plex_orthogonal_quality_label_view - view OrthQualLabel if label is requested. Currently only `PETSCVIEWERASCII` is supported.
9803 - -dm_plex_orthogonal_quality_vec_view   - view OrthQual vector.
9804 
9805   Level: intermediate
9806 
9807   Notes:
9808   Orthogonal quality is given by the following formula\:
9809 
9810   $ \min \left[ \frac{A_i \cdot f_i}{\|A_i\| \|f_i\|} , \frac{A_i \cdot c_i}{\|A_i\| \|c_i\|} \right]$
9811 
9812   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
9813   is the vector from the current cells centroid to the centroid of its i'th neighbor (which shares a face with the
9814   current cell). This computes the vector similarity between each cell face and its corresponding neighbor centroid by
9815   calculating the cosine of the angle between these vectors.
9816 
9817   Orthogonal quality ranges from 1 (best) to 0 (worst).
9818 
9819   This routine is mainly useful for FVM, however is not restricted to only FVM. The `PetscFV` object is optionally used to check for
9820   pre-computed FVM cell data, but if it is not passed in then this data will be computed.
9821 
9822   Cells are tagged if they have an orthogonal quality less than or equal to the absolute tolerance.
9823 
9824 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCheckCellShape()`, `DMCreateLabel()`, `PetscFV`, `DMLabel`, `Vec`
9825 @*/
9826 PetscErrorCode DMPlexComputeOrthogonalQuality(DM dm, PetscFV fv, PetscReal atol, Vec *OrthQual, DMLabel *OrthQualLabel)
9827 {
9828   PetscInt               nc, cellHeight, cStart, cEnd, cell, cellIter = 0;
9829   PetscInt              *idx;
9830   PetscScalar           *oqVals;
9831   const PetscScalar     *cellGeomArr, *faceGeomArr;
9832   PetscReal             *ci, *fi, *Ai;
9833   MPI_Comm               comm;
9834   Vec                    cellgeom, facegeom;
9835   DM                     dmFace, dmCell;
9836   IS                     glob;
9837   ISLocalToGlobalMapping ltog;
9838   PetscViewer            vwr;
9839 
9840   PetscFunctionBegin;
9841   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9842   if (fv) PetscValidHeaderSpecific(fv, PETSCFV_CLASSID, 2);
9843   PetscAssertPointer(OrthQual, 4);
9844   PetscCheck(atol >= 0.0 && atol <= 1.0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Absolute tolerance %g not in [0,1]", (double)atol);
9845   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9846   PetscCall(DMGetDimension(dm, &nc));
9847   PetscCheck(nc >= 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must have dimension >= 2 (current %" PetscInt_FMT ")", nc);
9848   {
9849     DMPlexInterpolatedFlag interpFlag;
9850 
9851     PetscCall(DMPlexIsInterpolated(dm, &interpFlag));
9852     if (interpFlag != DMPLEX_INTERPOLATED_FULL) {
9853       PetscMPIInt rank;
9854 
9855       PetscCallMPI(MPI_Comm_rank(comm, &rank));
9856       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must be fully interpolated, DM on rank %d is not fully interpolated", rank);
9857     }
9858   }
9859   if (OrthQualLabel) {
9860     PetscAssertPointer(OrthQualLabel, 5);
9861     PetscCall(DMCreateLabel(dm, "Orthogonal_Quality"));
9862     PetscCall(DMGetLabel(dm, "Orthogonal_Quality", OrthQualLabel));
9863   } else {
9864     *OrthQualLabel = NULL;
9865   }
9866   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9867   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9868   PetscCall(DMPlexCreateCellNumbering(dm, PETSC_TRUE, &glob));
9869   PetscCall(ISLocalToGlobalMappingCreateIS(glob, &ltog));
9870   PetscCall(ISLocalToGlobalMappingSetType(ltog, ISLOCALTOGLOBALMAPPINGHASH));
9871   PetscCall(VecCreate(comm, OrthQual));
9872   PetscCall(VecSetType(*OrthQual, VECSTANDARD));
9873   PetscCall(VecSetSizes(*OrthQual, cEnd - cStart, PETSC_DETERMINE));
9874   PetscCall(VecSetLocalToGlobalMapping(*OrthQual, ltog));
9875   PetscCall(VecSetUp(*OrthQual));
9876   PetscCall(ISDestroy(&glob));
9877   PetscCall(ISLocalToGlobalMappingDestroy(&ltog));
9878   PetscCall(DMPlexGetDataFVM(dm, fv, &cellgeom, &facegeom, NULL));
9879   PetscCall(VecGetArrayRead(cellgeom, &cellGeomArr));
9880   PetscCall(VecGetArrayRead(facegeom, &faceGeomArr));
9881   PetscCall(VecGetDM(cellgeom, &dmCell));
9882   PetscCall(VecGetDM(facegeom, &dmFace));
9883   PetscCall(PetscMalloc5(cEnd - cStart, &idx, cEnd - cStart, &oqVals, nc, &ci, nc, &fi, nc, &Ai));
9884   for (cell = cStart; cell < cEnd; cellIter++, cell++) {
9885     PetscInt         cellneigh, cellneighiter = 0, adjSize = PETSC_DETERMINE;
9886     PetscInt         cellarr[2], *adj = NULL;
9887     PetscScalar     *cArr, *fArr;
9888     PetscReal        minvalc = 1.0, minvalf = 1.0;
9889     PetscFVCellGeom *cg;
9890 
9891     idx[cellIter] = cell - cStart;
9892     cellarr[0]    = cell;
9893     /* Make indexing into cellGeom easier */
9894     PetscCall(DMPlexPointLocalRead(dmCell, cell, cellGeomArr, &cg));
9895     PetscCall(DMPlexGetAdjacency_Internal(dm, cell, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &adjSize, &adj));
9896     /* Technically 1 too big, but easier than fiddling with empty adjacency array */
9897     PetscCall(PetscCalloc2(adjSize, &cArr, adjSize, &fArr));
9898     for (cellneigh = 0; cellneigh < adjSize; cellneighiter++, cellneigh++) {
9899       PetscInt         i;
9900       const PetscInt   neigh  = adj[cellneigh];
9901       PetscReal        normci = 0, normfi = 0, normai = 0;
9902       PetscFVCellGeom *cgneigh;
9903       PetscFVFaceGeom *fg;
9904 
9905       /* Don't count ourselves in the neighbor list */
9906       if (neigh == cell) continue;
9907       PetscCall(DMPlexPointLocalRead(dmCell, neigh, cellGeomArr, &cgneigh));
9908       cellarr[1] = neigh;
9909       {
9910         PetscInt        numcovpts;
9911         const PetscInt *covpts;
9912 
9913         PetscCall(DMPlexGetMeet(dm, 2, cellarr, &numcovpts, &covpts));
9914         PetscCall(DMPlexPointLocalRead(dmFace, covpts[0], faceGeomArr, &fg));
9915         PetscCall(DMPlexRestoreMeet(dm, 2, cellarr, &numcovpts, &covpts));
9916       }
9917 
9918       /* Compute c_i, f_i and their norms */
9919       for (i = 0; i < nc; i++) {
9920         ci[i] = cgneigh->centroid[i] - cg->centroid[i];
9921         fi[i] = fg->centroid[i] - cg->centroid[i];
9922         Ai[i] = fg->normal[i];
9923         normci += PetscPowReal(ci[i], 2);
9924         normfi += PetscPowReal(fi[i], 2);
9925         normai += PetscPowReal(Ai[i], 2);
9926       }
9927       normci = PetscSqrtReal(normci);
9928       normfi = PetscSqrtReal(normfi);
9929       normai = PetscSqrtReal(normai);
9930 
9931       /* Normalize and compute for each face-cell-normal pair */
9932       for (i = 0; i < nc; i++) {
9933         ci[i] = ci[i] / normci;
9934         fi[i] = fi[i] / normfi;
9935         Ai[i] = Ai[i] / normai;
9936         /* PetscAbs because I don't know if normals are guaranteed to point out */
9937         cArr[cellneighiter] += PetscAbs(Ai[i] * ci[i]);
9938         fArr[cellneighiter] += PetscAbs(Ai[i] * fi[i]);
9939       }
9940       if (PetscRealPart(cArr[cellneighiter]) < minvalc) minvalc = PetscRealPart(cArr[cellneighiter]);
9941       if (PetscRealPart(fArr[cellneighiter]) < minvalf) minvalf = PetscRealPart(fArr[cellneighiter]);
9942     }
9943     PetscCall(PetscFree(adj));
9944     PetscCall(PetscFree2(cArr, fArr));
9945     /* Defer to cell if they're equal */
9946     oqVals[cellIter] = PetscMin(minvalf, minvalc);
9947     if (OrthQualLabel) {
9948       if (PetscRealPart(oqVals[cellIter]) <= atol) PetscCall(DMLabelSetValue(*OrthQualLabel, cell, DM_ADAPT_REFINE));
9949     }
9950   }
9951   PetscCall(VecSetValuesLocal(*OrthQual, cEnd - cStart, idx, oqVals, INSERT_VALUES));
9952   PetscCall(VecAssemblyBegin(*OrthQual));
9953   PetscCall(VecAssemblyEnd(*OrthQual));
9954   PetscCall(VecRestoreArrayRead(cellgeom, &cellGeomArr));
9955   PetscCall(VecRestoreArrayRead(facegeom, &faceGeomArr));
9956   PetscCall(PetscOptionsCreateViewer(comm, NULL, NULL, "-dm_plex_orthogonal_quality_label_view", &vwr, NULL, NULL));
9957   if (OrthQualLabel) {
9958     if (vwr) PetscCall(DMLabelView(*OrthQualLabel, vwr));
9959   }
9960   PetscCall(PetscFree5(idx, oqVals, ci, fi, Ai));
9961   PetscCall(PetscViewerDestroy(&vwr));
9962   PetscCall(VecViewFromOptions(*OrthQual, NULL, "-dm_plex_orthogonal_quality_vec_view"));
9963   PetscFunctionReturn(PETSC_SUCCESS);
9964 }
9965 
9966 /* this is here instead of DMGetOutputDM because output DM still has constraints in the local indices that affect
9967  * interpolator construction */
9968 static PetscErrorCode DMGetFullDM(DM dm, DM *odm)
9969 {
9970   PetscSection section, newSection, gsection;
9971   PetscSF      sf;
9972   PetscBool    hasConstraints, ghasConstraints;
9973 
9974   PetscFunctionBegin;
9975   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9976   PetscAssertPointer(odm, 2);
9977   PetscCall(DMGetLocalSection(dm, &section));
9978   PetscCall(PetscSectionHasConstraints(section, &hasConstraints));
9979   PetscCall(MPIU_Allreduce(&hasConstraints, &ghasConstraints, 1, MPIU_BOOL, MPI_LOR, PetscObjectComm((PetscObject)dm)));
9980   if (!ghasConstraints) {
9981     PetscCall(PetscObjectReference((PetscObject)dm));
9982     *odm = dm;
9983     PetscFunctionReturn(PETSC_SUCCESS);
9984   }
9985   PetscCall(DMClone(dm, odm));
9986   PetscCall(DMCopyFields(dm, *odm));
9987   PetscCall(DMGetLocalSection(*odm, &newSection));
9988   PetscCall(DMGetPointSF(*odm, &sf));
9989   PetscCall(PetscSectionCreateGlobalSection(newSection, sf, PETSC_TRUE, PETSC_TRUE, PETSC_FALSE, &gsection));
9990   PetscCall(DMSetGlobalSection(*odm, gsection));
9991   PetscCall(PetscSectionDestroy(&gsection));
9992   PetscFunctionReturn(PETSC_SUCCESS);
9993 }
9994 
9995 static PetscErrorCode DMCreateAffineInterpolationCorrection_Plex(DM dmc, DM dmf, Vec *shift)
9996 {
9997   DM        dmco, dmfo;
9998   Mat       interpo;
9999   Vec       rscale;
10000   Vec       cglobalo, clocal;
10001   Vec       fglobal, fglobalo, flocal;
10002   PetscBool regular;
10003 
10004   PetscFunctionBegin;
10005   PetscCall(DMGetFullDM(dmc, &dmco));
10006   PetscCall(DMGetFullDM(dmf, &dmfo));
10007   PetscCall(DMSetCoarseDM(dmfo, dmco));
10008   PetscCall(DMPlexGetRegularRefinement(dmf, &regular));
10009   PetscCall(DMPlexSetRegularRefinement(dmfo, regular));
10010   PetscCall(DMCreateInterpolation(dmco, dmfo, &interpo, &rscale));
10011   PetscCall(DMCreateGlobalVector(dmco, &cglobalo));
10012   PetscCall(DMCreateLocalVector(dmc, &clocal));
10013   PetscCall(VecSet(cglobalo, 0.));
10014   PetscCall(VecSet(clocal, 0.));
10015   PetscCall(DMCreateGlobalVector(dmf, &fglobal));
10016   PetscCall(DMCreateGlobalVector(dmfo, &fglobalo));
10017   PetscCall(DMCreateLocalVector(dmf, &flocal));
10018   PetscCall(VecSet(fglobal, 0.));
10019   PetscCall(VecSet(fglobalo, 0.));
10020   PetscCall(VecSet(flocal, 0.));
10021   PetscCall(DMPlexInsertBoundaryValues(dmc, PETSC_TRUE, clocal, 0., NULL, NULL, NULL));
10022   PetscCall(DMLocalToGlobalBegin(dmco, clocal, INSERT_VALUES, cglobalo));
10023   PetscCall(DMLocalToGlobalEnd(dmco, clocal, INSERT_VALUES, cglobalo));
10024   PetscCall(MatMult(interpo, cglobalo, fglobalo));
10025   PetscCall(DMGlobalToLocalBegin(dmfo, fglobalo, INSERT_VALUES, flocal));
10026   PetscCall(DMGlobalToLocalEnd(dmfo, fglobalo, INSERT_VALUES, flocal));
10027   PetscCall(DMLocalToGlobalBegin(dmf, flocal, INSERT_VALUES, fglobal));
10028   PetscCall(DMLocalToGlobalEnd(dmf, flocal, INSERT_VALUES, fglobal));
10029   *shift = fglobal;
10030   PetscCall(VecDestroy(&flocal));
10031   PetscCall(VecDestroy(&fglobalo));
10032   PetscCall(VecDestroy(&clocal));
10033   PetscCall(VecDestroy(&cglobalo));
10034   PetscCall(VecDestroy(&rscale));
10035   PetscCall(MatDestroy(&interpo));
10036   PetscCall(DMDestroy(&dmfo));
10037   PetscCall(DMDestroy(&dmco));
10038   PetscFunctionReturn(PETSC_SUCCESS);
10039 }
10040 
10041 PETSC_INTERN PetscErrorCode DMInterpolateSolution_Plex(DM coarse, DM fine, Mat interp, Vec coarseSol, Vec fineSol)
10042 {
10043   PetscObject shifto;
10044   Vec         shift;
10045 
10046   PetscFunctionBegin;
10047   if (!interp) {
10048     Vec rscale;
10049 
10050     PetscCall(DMCreateInterpolation(coarse, fine, &interp, &rscale));
10051     PetscCall(VecDestroy(&rscale));
10052   } else {
10053     PetscCall(PetscObjectReference((PetscObject)interp));
10054   }
10055   PetscCall(PetscObjectQuery((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", &shifto));
10056   if (!shifto) {
10057     PetscCall(DMCreateAffineInterpolationCorrection_Plex(coarse, fine, &shift));
10058     PetscCall(PetscObjectCompose((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", (PetscObject)shift));
10059     shifto = (PetscObject)shift;
10060     PetscCall(VecDestroy(&shift));
10061   }
10062   shift = (Vec)shifto;
10063   PetscCall(MatInterpolate(interp, coarseSol, fineSol));
10064   PetscCall(VecAXPY(fineSol, 1.0, shift));
10065   PetscCall(MatDestroy(&interp));
10066   PetscFunctionReturn(PETSC_SUCCESS);
10067 }
10068 
10069 /* Pointwise interpolation
10070      Just code FEM for now
10071      u^f = I u^c
10072      sum_k u^f_k phi^f_k = I sum_j u^c_j phi^c_j
10073      u^f_i = sum_j psi^f_i I phi^c_j u^c_j
10074      I_{ij} = psi^f_i phi^c_j
10075 */
10076 PetscErrorCode DMCreateInterpolation_Plex(DM dmCoarse, DM dmFine, Mat *interpolation, Vec *scaling)
10077 {
10078   PetscSection gsc, gsf;
10079   PetscInt     m, n;
10080   void        *ctx;
10081   DM           cdm;
10082   PetscBool    regular, ismatis, isRefined = dmCoarse->data == dmFine->data ? PETSC_FALSE : PETSC_TRUE;
10083 
10084   PetscFunctionBegin;
10085   PetscCall(DMGetGlobalSection(dmFine, &gsf));
10086   PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
10087   PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
10088   PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
10089 
10090   PetscCall(PetscStrcmp(dmCoarse->mattype, MATIS, &ismatis));
10091   PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), interpolation));
10092   PetscCall(MatSetSizes(*interpolation, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
10093   PetscCall(MatSetType(*interpolation, ismatis ? MATAIJ : dmCoarse->mattype));
10094   PetscCall(DMGetApplicationContext(dmFine, &ctx));
10095 
10096   PetscCall(DMGetCoarseDM(dmFine, &cdm));
10097   PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
10098   if (!isRefined || (regular && cdm == dmCoarse)) PetscCall(DMPlexComputeInterpolatorNested(dmCoarse, dmFine, isRefined, *interpolation, ctx));
10099   else PetscCall(DMPlexComputeInterpolatorGeneral(dmCoarse, dmFine, *interpolation, ctx));
10100   PetscCall(MatViewFromOptions(*interpolation, NULL, "-interp_mat_view"));
10101   if (scaling) {
10102     /* Use naive scaling */
10103     PetscCall(DMCreateInterpolationScale(dmCoarse, dmFine, *interpolation, scaling));
10104   }
10105   PetscFunctionReturn(PETSC_SUCCESS);
10106 }
10107 
10108 PetscErrorCode DMCreateInjection_Plex(DM dmCoarse, DM dmFine, Mat *mat)
10109 {
10110   VecScatter ctx;
10111 
10112   PetscFunctionBegin;
10113   PetscCall(DMPlexComputeInjectorFEM(dmCoarse, dmFine, &ctx, NULL));
10114   PetscCall(MatCreateScatter(PetscObjectComm((PetscObject)ctx), ctx, mat));
10115   PetscCall(VecScatterDestroy(&ctx));
10116   PetscFunctionReturn(PETSC_SUCCESS);
10117 }
10118 
10119 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[])
10120 {
10121   const PetscInt f  = (PetscInt)PetscRealPart(constants[numConstants]);
10122   const PetscInt Nc = uOff[f + 1] - uOff[f];
10123   for (PetscInt c = 0; c < Nc; ++c) g0[c * Nc + c] = 1.0;
10124 }
10125 
10126 PetscErrorCode DMCreateMassMatrixLumped_Plex(DM dm, Vec *lmass, Vec *mass)
10127 {
10128   DM           dmc;
10129   PetscDS      ds;
10130   Vec          ones, locmass;
10131   IS           cellIS;
10132   PetscFormKey key;
10133   PetscInt     depth;
10134 
10135   PetscFunctionBegin;
10136   PetscCall(DMClone(dm, &dmc));
10137   PetscCall(DMCopyDisc(dm, dmc));
10138   PetscCall(DMGetDS(dmc, &ds));
10139   for (PetscInt f = 0; f < dmc->Nf; ++f) PetscCall(PetscDSSetJacobian(ds, f, f, g0_identity_private, NULL, NULL, NULL));
10140   if (mass) PetscCall(DMCreateGlobalVector(dm, mass));
10141   if (lmass) PetscCall(DMCreateLocalVector(dm, &locmass));
10142   else PetscCall(DMGetLocalVector(dm, &locmass));
10143   PetscCall(DMGetLocalVector(dm, &ones));
10144   PetscCall(DMPlexGetDepth(dm, &depth));
10145   PetscCall(DMGetStratumIS(dm, "depth", depth, &cellIS));
10146   PetscCall(VecSet(locmass, 0.0));
10147   PetscCall(VecSet(ones, 1.0));
10148   key.label = NULL;
10149   key.value = 0;
10150   key.field = 0;
10151   key.part  = 0;
10152   PetscCall(DMPlexComputeJacobian_Action_Internal(dmc, key, cellIS, 0.0, 0.0, ones, NULL, ones, locmass, NULL));
10153   PetscCall(ISDestroy(&cellIS));
10154   if (mass) {
10155     PetscCall(DMLocalToGlobalBegin(dm, locmass, ADD_VALUES, *mass));
10156     PetscCall(DMLocalToGlobalEnd(dm, locmass, ADD_VALUES, *mass));
10157   }
10158   PetscCall(DMRestoreLocalVector(dm, &ones));
10159   if (lmass) *lmass = locmass;
10160   else PetscCall(DMRestoreLocalVector(dm, &locmass));
10161   PetscCall(DMDestroy(&dmc));
10162   PetscFunctionReturn(PETSC_SUCCESS);
10163 }
10164 
10165 PetscErrorCode DMCreateMassMatrix_Plex(DM dmCoarse, DM dmFine, Mat *mass)
10166 {
10167   PetscSection gsc, gsf;
10168   PetscInt     m, n;
10169   void        *ctx;
10170   DM           cdm;
10171   PetscBool    regular;
10172 
10173   PetscFunctionBegin;
10174   if (dmFine == dmCoarse) {
10175     DM            dmc;
10176     PetscDS       ds;
10177     PetscWeakForm wf;
10178     Vec           u;
10179     IS            cellIS;
10180     PetscFormKey  key;
10181     PetscInt      depth;
10182 
10183     PetscCall(DMClone(dmFine, &dmc));
10184     PetscCall(DMCopyDisc(dmFine, dmc));
10185     PetscCall(DMGetDS(dmc, &ds));
10186     PetscCall(PetscDSGetWeakForm(ds, &wf));
10187     PetscCall(PetscWeakFormClear(wf));
10188     for (PetscInt f = 0; f < dmc->Nf; ++f) PetscCall(PetscDSSetJacobian(ds, f, f, g0_identity_private, NULL, NULL, NULL));
10189     PetscCall(DMCreateMatrix(dmc, mass));
10190     PetscCall(DMGetLocalVector(dmc, &u));
10191     PetscCall(DMPlexGetDepth(dmc, &depth));
10192     PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
10193     PetscCall(MatZeroEntries(*mass));
10194     key.label = NULL;
10195     key.value = 0;
10196     key.field = 0;
10197     key.part  = 0;
10198     PetscCall(DMPlexComputeJacobian_Internal(dmc, key, cellIS, 0.0, 0.0, u, NULL, *mass, *mass, NULL));
10199     PetscCall(ISDestroy(&cellIS));
10200     PetscCall(DMRestoreLocalVector(dmc, &u));
10201     PetscCall(DMDestroy(&dmc));
10202   } else {
10203     PetscCall(DMGetGlobalSection(dmFine, &gsf));
10204     PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
10205     PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
10206     PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
10207 
10208     PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), mass));
10209     PetscCall(MatSetSizes(*mass, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
10210     PetscCall(MatSetType(*mass, dmCoarse->mattype));
10211     PetscCall(DMGetApplicationContext(dmFine, &ctx));
10212 
10213     PetscCall(DMGetCoarseDM(dmFine, &cdm));
10214     PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
10215     if (regular && cdm == dmCoarse) PetscCall(DMPlexComputeMassMatrixNested(dmCoarse, dmFine, *mass, ctx));
10216     else PetscCall(DMPlexComputeMassMatrixGeneral(dmCoarse, dmFine, *mass, ctx));
10217   }
10218   PetscCall(MatViewFromOptions(*mass, NULL, "-mass_mat_view"));
10219   PetscFunctionReturn(PETSC_SUCCESS);
10220 }
10221 
10222 /*@
10223   DMPlexGetRegularRefinement - Get the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
10224 
10225   Input Parameter:
10226 . dm - The `DMPLEX` object
10227 
10228   Output Parameter:
10229 . regular - The flag
10230 
10231   Level: intermediate
10232 
10233 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetRegularRefinement()`
10234 @*/
10235 PetscErrorCode DMPlexGetRegularRefinement(DM dm, PetscBool *regular)
10236 {
10237   PetscFunctionBegin;
10238   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10239   PetscAssertPointer(regular, 2);
10240   *regular = ((DM_Plex *)dm->data)->regularRefinement;
10241   PetscFunctionReturn(PETSC_SUCCESS);
10242 }
10243 
10244 /*@
10245   DMPlexSetRegularRefinement - Set the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
10246 
10247   Input Parameters:
10248 + dm      - The `DMPLEX` object
10249 - regular - The flag
10250 
10251   Level: intermediate
10252 
10253 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetRegularRefinement()`
10254 @*/
10255 PetscErrorCode DMPlexSetRegularRefinement(DM dm, PetscBool regular)
10256 {
10257   PetscFunctionBegin;
10258   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10259   ((DM_Plex *)dm->data)->regularRefinement = regular;
10260   PetscFunctionReturn(PETSC_SUCCESS);
10261 }
10262 
10263 /*@
10264   DMPlexGetAnchors - Get the layout of the anchor (point-to-point) constraints.  Typically, the user will not have to
10265   call DMPlexGetAnchors() directly: if there are anchors, then `DMPlexGetAnchors()` is called during `DMGetDefaultConstraints()`.
10266 
10267   Not Collective
10268 
10269   Input Parameter:
10270 . dm - The `DMPLEX` object
10271 
10272   Output Parameters:
10273 + anchorSection - If not `NULL`, set to the section describing which points anchor the constrained points.
10274 - anchorIS      - If not `NULL`, set to the list of anchors indexed by `anchorSection`
10275 
10276   Level: intermediate
10277 
10278 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`, `IS`, `PetscSection`
10279 @*/
10280 PetscErrorCode DMPlexGetAnchors(DM dm, PetscSection *anchorSection, IS *anchorIS)
10281 {
10282   DM_Plex *plex = (DM_Plex *)dm->data;
10283 
10284   PetscFunctionBegin;
10285   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10286   if (!plex->anchorSection && !plex->anchorIS && plex->createanchors) PetscCall((*plex->createanchors)(dm));
10287   if (anchorSection) *anchorSection = plex->anchorSection;
10288   if (anchorIS) *anchorIS = plex->anchorIS;
10289   PetscFunctionReturn(PETSC_SUCCESS);
10290 }
10291 
10292 /*@
10293   DMPlexSetAnchors - Set the layout of the local anchor (point-to-point) constraints.
10294 
10295   Collective
10296 
10297   Input Parameters:
10298 + dm            - The `DMPLEX` object
10299 . anchorSection - The section that describes the mapping from constrained points to the anchor points listed in anchorIS.
10300                   Must have a local communicator (`PETSC_COMM_SELF` or derivative).
10301 - anchorIS      - The list of all anchor points.  Must have a local communicator (`PETSC_COMM_SELF` or derivative).
10302 
10303   Level: intermediate
10304 
10305   Notes:
10306   Unlike boundary conditions, when a point's degrees of freedom in a section are constrained to
10307   an outside value, the anchor constraints set a point's degrees of freedom to be a linear
10308   combination of other points' degrees of freedom.
10309 
10310   After specifying the layout of constraints with `DMPlexSetAnchors()`, one specifies the constraints by calling
10311   `DMGetDefaultConstraints()` and filling in the entries in the constraint matrix.
10312 
10313   The reference counts of `anchorSection` and `anchorIS` are incremented.
10314 
10315 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`
10316 @*/
10317 PetscErrorCode DMPlexSetAnchors(DM dm, PetscSection anchorSection, IS anchorIS)
10318 {
10319   DM_Plex    *plex = (DM_Plex *)dm->data;
10320   PetscMPIInt result;
10321 
10322   PetscFunctionBegin;
10323   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10324   if (anchorSection) {
10325     PetscValidHeaderSpecific(anchorSection, PETSC_SECTION_CLASSID, 2);
10326     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorSection), &result));
10327     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor section must have local communicator");
10328   }
10329   if (anchorIS) {
10330     PetscValidHeaderSpecific(anchorIS, IS_CLASSID, 3);
10331     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorIS), &result));
10332     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor IS must have local communicator");
10333   }
10334 
10335   PetscCall(PetscObjectReference((PetscObject)anchorSection));
10336   PetscCall(PetscSectionDestroy(&plex->anchorSection));
10337   plex->anchorSection = anchorSection;
10338 
10339   PetscCall(PetscObjectReference((PetscObject)anchorIS));
10340   PetscCall(ISDestroy(&plex->anchorIS));
10341   plex->anchorIS = anchorIS;
10342 
10343   if (PetscUnlikelyDebug(anchorIS && anchorSection)) {
10344     PetscInt        size, a, pStart, pEnd;
10345     const PetscInt *anchors;
10346 
10347     PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
10348     PetscCall(ISGetLocalSize(anchorIS, &size));
10349     PetscCall(ISGetIndices(anchorIS, &anchors));
10350     for (a = 0; a < size; a++) {
10351       PetscInt p;
10352 
10353       p = anchors[a];
10354       if (p >= pStart && p < pEnd) {
10355         PetscInt dof;
10356 
10357         PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
10358         if (dof) {
10359           PetscCall(ISRestoreIndices(anchorIS, &anchors));
10360           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Point %" PetscInt_FMT " cannot be constrained and an anchor", p);
10361         }
10362       }
10363     }
10364     PetscCall(ISRestoreIndices(anchorIS, &anchors));
10365   }
10366   /* reset the generic constraints */
10367   PetscCall(DMSetDefaultConstraints(dm, NULL, NULL, NULL));
10368   PetscFunctionReturn(PETSC_SUCCESS);
10369 }
10370 
10371 static PetscErrorCode DMPlexCreateConstraintSection_Anchors(DM dm, PetscSection section, PetscSection *cSec)
10372 {
10373   PetscSection anchorSection;
10374   PetscInt     pStart, pEnd, sStart, sEnd, p, dof, numFields, f;
10375 
10376   PetscFunctionBegin;
10377   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10378   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
10379   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, cSec));
10380   PetscCall(PetscSectionGetNumFields(section, &numFields));
10381   if (numFields) {
10382     PetscInt f;
10383     PetscCall(PetscSectionSetNumFields(*cSec, numFields));
10384 
10385     for (f = 0; f < numFields; f++) {
10386       PetscInt numComp;
10387 
10388       PetscCall(PetscSectionGetFieldComponents(section, f, &numComp));
10389       PetscCall(PetscSectionSetFieldComponents(*cSec, f, numComp));
10390     }
10391   }
10392   PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
10393   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
10394   pStart = PetscMax(pStart, sStart);
10395   pEnd   = PetscMin(pEnd, sEnd);
10396   pEnd   = PetscMax(pStart, pEnd);
10397   PetscCall(PetscSectionSetChart(*cSec, pStart, pEnd));
10398   for (p = pStart; p < pEnd; p++) {
10399     PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
10400     if (dof) {
10401       PetscCall(PetscSectionGetDof(section, p, &dof));
10402       PetscCall(PetscSectionSetDof(*cSec, p, dof));
10403       for (f = 0; f < numFields; f++) {
10404         PetscCall(PetscSectionGetFieldDof(section, p, f, &dof));
10405         PetscCall(PetscSectionSetFieldDof(*cSec, p, f, dof));
10406       }
10407     }
10408   }
10409   PetscCall(PetscSectionSetUp(*cSec));
10410   PetscCall(PetscObjectSetName((PetscObject)*cSec, "Constraint Section"));
10411   PetscFunctionReturn(PETSC_SUCCESS);
10412 }
10413 
10414 static PetscErrorCode DMPlexCreateConstraintMatrix_Anchors(DM dm, PetscSection section, PetscSection cSec, Mat *cMat)
10415 {
10416   PetscSection    aSec;
10417   PetscInt        pStart, pEnd, p, sStart, sEnd, dof, aDof, aOff, off, nnz, annz, m, n, q, a, offset, *i, *j;
10418   const PetscInt *anchors;
10419   PetscInt        numFields, f;
10420   IS              aIS;
10421   MatType         mtype;
10422   PetscBool       iscuda, iskokkos;
10423 
10424   PetscFunctionBegin;
10425   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10426   PetscCall(PetscSectionGetStorageSize(cSec, &m));
10427   PetscCall(PetscSectionGetStorageSize(section, &n));
10428   PetscCall(MatCreate(PETSC_COMM_SELF, cMat));
10429   PetscCall(MatSetSizes(*cMat, m, n, m, n));
10430   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJCUSPARSE, &iscuda));
10431   if (!iscuda) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJCUSPARSE, &iscuda));
10432   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJKOKKOS, &iskokkos));
10433   if (!iskokkos) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJKOKKOS, &iskokkos));
10434   if (iscuda) mtype = MATSEQAIJCUSPARSE;
10435   else if (iskokkos) mtype = MATSEQAIJKOKKOS;
10436   else mtype = MATSEQAIJ;
10437   PetscCall(MatSetType(*cMat, mtype));
10438   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
10439   PetscCall(ISGetIndices(aIS, &anchors));
10440   /* cSec will be a subset of aSec and section */
10441   PetscCall(PetscSectionGetChart(cSec, &pStart, &pEnd));
10442   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
10443   PetscCall(PetscMalloc1(m + 1, &i));
10444   i[0] = 0;
10445   PetscCall(PetscSectionGetNumFields(section, &numFields));
10446   for (p = pStart; p < pEnd; p++) {
10447     PetscInt rDof, rOff, r;
10448 
10449     PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10450     if (!rDof) continue;
10451     PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10452     if (numFields) {
10453       for (f = 0; f < numFields; f++) {
10454         annz = 0;
10455         for (r = 0; r < rDof; r++) {
10456           a = anchors[rOff + r];
10457           if (a < sStart || a >= sEnd) continue;
10458           PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
10459           annz += aDof;
10460         }
10461         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
10462         PetscCall(PetscSectionGetFieldOffset(cSec, p, f, &off));
10463         for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
10464       }
10465     } else {
10466       annz = 0;
10467       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10468       for (q = 0; q < dof; q++) {
10469         a = anchors[rOff + q];
10470         if (a < sStart || a >= sEnd) continue;
10471         PetscCall(PetscSectionGetDof(section, a, &aDof));
10472         annz += aDof;
10473       }
10474       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10475       PetscCall(PetscSectionGetOffset(cSec, p, &off));
10476       for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
10477     }
10478   }
10479   nnz = i[m];
10480   PetscCall(PetscMalloc1(nnz, &j));
10481   offset = 0;
10482   for (p = pStart; p < pEnd; p++) {
10483     if (numFields) {
10484       for (f = 0; f < numFields; f++) {
10485         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
10486         for (q = 0; q < dof; q++) {
10487           PetscInt rDof, rOff, r;
10488           PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10489           PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10490           for (r = 0; r < rDof; r++) {
10491             PetscInt s;
10492 
10493             a = anchors[rOff + r];
10494             if (a < sStart || a >= sEnd) continue;
10495             PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
10496             PetscCall(PetscSectionGetFieldOffset(section, a, f, &aOff));
10497             for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
10498           }
10499         }
10500       }
10501     } else {
10502       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10503       for (q = 0; q < dof; q++) {
10504         PetscInt rDof, rOff, r;
10505         PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10506         PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10507         for (r = 0; r < rDof; r++) {
10508           PetscInt s;
10509 
10510           a = anchors[rOff + r];
10511           if (a < sStart || a >= sEnd) continue;
10512           PetscCall(PetscSectionGetDof(section, a, &aDof));
10513           PetscCall(PetscSectionGetOffset(section, a, &aOff));
10514           for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
10515         }
10516       }
10517     }
10518   }
10519   PetscCall(MatSeqAIJSetPreallocationCSR(*cMat, i, j, NULL));
10520   PetscCall(PetscFree(i));
10521   PetscCall(PetscFree(j));
10522   PetscCall(ISRestoreIndices(aIS, &anchors));
10523   PetscFunctionReturn(PETSC_SUCCESS);
10524 }
10525 
10526 PetscErrorCode DMCreateDefaultConstraints_Plex(DM dm)
10527 {
10528   DM_Plex     *plex = (DM_Plex *)dm->data;
10529   PetscSection anchorSection, section, cSec;
10530   Mat          cMat;
10531 
10532   PetscFunctionBegin;
10533   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10534   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
10535   if (anchorSection) {
10536     PetscInt Nf;
10537 
10538     PetscCall(DMGetLocalSection(dm, &section));
10539     PetscCall(DMPlexCreateConstraintSection_Anchors(dm, section, &cSec));
10540     PetscCall(DMPlexCreateConstraintMatrix_Anchors(dm, section, cSec, &cMat));
10541     PetscCall(DMGetNumFields(dm, &Nf));
10542     if (Nf && plex->computeanchormatrix) PetscCall((*plex->computeanchormatrix)(dm, section, cSec, cMat));
10543     PetscCall(DMSetDefaultConstraints(dm, cSec, cMat, NULL));
10544     PetscCall(PetscSectionDestroy(&cSec));
10545     PetscCall(MatDestroy(&cMat));
10546   }
10547   PetscFunctionReturn(PETSC_SUCCESS);
10548 }
10549 
10550 PetscErrorCode DMCreateSubDomainDM_Plex(DM dm, DMLabel label, PetscInt value, IS *is, DM *subdm)
10551 {
10552   IS           subis;
10553   PetscSection section, subsection;
10554 
10555   PetscFunctionBegin;
10556   PetscCall(DMGetLocalSection(dm, &section));
10557   PetscCheck(section, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set default section for DM before splitting subdomain");
10558   PetscCheck(subdm, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set output subDM for splitting subdomain");
10559   /* Create subdomain */
10560   PetscCall(DMPlexFilter(dm, label, value, PETSC_FALSE, PETSC_FALSE, NULL, subdm));
10561   /* Create submodel */
10562   PetscCall(DMPlexGetSubpointIS(*subdm, &subis));
10563   PetscCall(PetscSectionCreateSubmeshSection(section, subis, &subsection));
10564   PetscCall(DMSetLocalSection(*subdm, subsection));
10565   PetscCall(PetscSectionDestroy(&subsection));
10566   PetscCall(DMCopyDisc(dm, *subdm));
10567   /* Create map from submodel to global model */
10568   if (is) {
10569     PetscSection    sectionGlobal, subsectionGlobal;
10570     IS              spIS;
10571     const PetscInt *spmap;
10572     PetscInt       *subIndices;
10573     PetscInt        subSize = 0, subOff = 0, pStart, pEnd, p;
10574     PetscInt        Nf, f, bs = -1, bsLocal[2], bsMinMax[2];
10575 
10576     PetscCall(DMPlexGetSubpointIS(*subdm, &spIS));
10577     PetscCall(ISGetIndices(spIS, &spmap));
10578     PetscCall(PetscSectionGetNumFields(section, &Nf));
10579     PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
10580     PetscCall(DMGetGlobalSection(*subdm, &subsectionGlobal));
10581     PetscCall(PetscSectionGetChart(subsection, &pStart, &pEnd));
10582     for (p = pStart; p < pEnd; ++p) {
10583       PetscInt gdof, pSubSize = 0;
10584 
10585       PetscCall(PetscSectionGetDof(sectionGlobal, p, &gdof));
10586       if (gdof > 0) {
10587         for (f = 0; f < Nf; ++f) {
10588           PetscInt fdof, fcdof;
10589 
10590           PetscCall(PetscSectionGetFieldDof(subsection, p, f, &fdof));
10591           PetscCall(PetscSectionGetFieldConstraintDof(subsection, p, f, &fcdof));
10592           pSubSize += fdof - fcdof;
10593         }
10594         subSize += pSubSize;
10595         if (pSubSize) {
10596           if (bs < 0) {
10597             bs = pSubSize;
10598           } else if (bs != pSubSize) {
10599             /* Layout does not admit a pointwise block size */
10600             bs = 1;
10601           }
10602         }
10603       }
10604     }
10605     /* Must have same blocksize on all procs (some might have no points) */
10606     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs;
10607     bsLocal[1] = bs;
10608     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
10609     if (bsMinMax[0] != bsMinMax[1]) {
10610       bs = 1;
10611     } else {
10612       bs = bsMinMax[0];
10613     }
10614     PetscCall(PetscMalloc1(subSize, &subIndices));
10615     for (p = pStart; p < pEnd; ++p) {
10616       PetscInt gdof, goff;
10617 
10618       PetscCall(PetscSectionGetDof(subsectionGlobal, p, &gdof));
10619       if (gdof > 0) {
10620         const PetscInt point = spmap[p];
10621 
10622         PetscCall(PetscSectionGetOffset(sectionGlobal, point, &goff));
10623         for (f = 0; f < Nf; ++f) {
10624           PetscInt fdof, fcdof, fc, f2, poff = 0;
10625 
10626           /* Can get rid of this loop by storing field information in the global section */
10627           for (f2 = 0; f2 < f; ++f2) {
10628             PetscCall(PetscSectionGetFieldDof(section, p, f2, &fdof));
10629             PetscCall(PetscSectionGetFieldConstraintDof(section, p, f2, &fcdof));
10630             poff += fdof - fcdof;
10631           }
10632           PetscCall(PetscSectionGetFieldDof(section, p, f, &fdof));
10633           PetscCall(PetscSectionGetFieldConstraintDof(section, p, f, &fcdof));
10634           for (fc = 0; fc < fdof - fcdof; ++fc, ++subOff) subIndices[subOff] = goff + poff + fc;
10635         }
10636       }
10637     }
10638     PetscCall(ISRestoreIndices(spIS, &spmap));
10639     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), subSize, subIndices, PETSC_OWN_POINTER, is));
10640     if (bs > 1) {
10641       /* We need to check that the block size does not come from non-contiguous fields */
10642       PetscInt i, j, set = 1;
10643       for (i = 0; i < subSize; i += bs) {
10644         for (j = 0; j < bs; ++j) {
10645           if (subIndices[i + j] != subIndices[i] + j) {
10646             set = 0;
10647             break;
10648           }
10649         }
10650       }
10651       if (set) PetscCall(ISSetBlockSize(*is, bs));
10652     }
10653     /* Attach nullspace */
10654     for (f = 0; f < Nf; ++f) {
10655       (*subdm)->nullspaceConstructors[f] = dm->nullspaceConstructors[f];
10656       if ((*subdm)->nullspaceConstructors[f]) break;
10657     }
10658     if (f < Nf) {
10659       MatNullSpace nullSpace;
10660       PetscCall((*(*subdm)->nullspaceConstructors[f])(*subdm, f, f, &nullSpace));
10661 
10662       PetscCall(PetscObjectCompose((PetscObject)*is, "nullspace", (PetscObject)nullSpace));
10663       PetscCall(MatNullSpaceDestroy(&nullSpace));
10664     }
10665   }
10666   PetscFunctionReturn(PETSC_SUCCESS);
10667 }
10668 
10669 /*@
10670   DMPlexMonitorThroughput - Report the cell throughput of FE integration
10671 
10672   Input Parameters:
10673 + dm    - The `DM`
10674 - dummy - unused argument
10675 
10676   Options Database Key:
10677 . -dm_plex_monitor_throughput - Activate the monitor
10678 
10679   Level: developer
10680 
10681 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexCreate()`
10682 @*/
10683 PetscErrorCode DMPlexMonitorThroughput(DM dm, void *dummy)
10684 {
10685   PetscLogHandler default_handler;
10686 
10687   PetscFunctionBegin;
10688   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10689   PetscCall(PetscLogGetDefaultHandler(&default_handler));
10690   if (default_handler) {
10691     PetscLogEvent      event;
10692     PetscEventPerfInfo eventInfo;
10693     PetscReal          cellRate, flopRate;
10694     PetscInt           cStart, cEnd, Nf, N;
10695     const char        *name;
10696 
10697     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
10698     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
10699     PetscCall(DMGetNumFields(dm, &Nf));
10700     PetscCall(PetscLogEventGetId("DMPlexResidualFE", &event));
10701     PetscCall(PetscLogEventGetPerfInfo(PETSC_DEFAULT, event, &eventInfo));
10702     N        = (cEnd - cStart) * Nf * eventInfo.count;
10703     flopRate = eventInfo.flops / eventInfo.time;
10704     cellRate = N / eventInfo.time;
10705     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)));
10706   } else {
10707     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.");
10708   }
10709   PetscFunctionReturn(PETSC_SUCCESS);
10710 }
10711