xref: /petsc/src/dm/impls/plex/plex.c (revision 5e8b5fd9abe00a37558533ec5365ad37a0791173)
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   PetscInt        Nct, cS = PETSC_MAX_INT, cE = 0;
89 
90   PetscFunctionBegin;
91   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
92   PetscCall(DMLabelGetValueIS(ctLabel, &valueIS));
93   PetscCall(ISGetLocalSize(valueIS, &Nct));
94   PetscCall(ISGetIndices(valueIS, &ctypes));
95   if (!Nct) cS = cE = 0;
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   }
114   PetscCall(ISDestroy(&valueIS));
115   // Reset label for fast lookup
116   PetscCall(DMLabelMakeAllInvalid_Internal(ctLabel));
117   if (cStart) *cStart = cS;
118   if (cEnd) *cEnd = cE;
119   PetscFunctionReturn(PETSC_SUCCESS);
120 }
121 
122 PetscErrorCode DMPlexGetFieldTypes_Internal(DM dm, PetscSection section, PetscInt field, PetscInt *types, PetscInt **ssStart, PetscInt **ssEnd, PetscViewerVTKFieldType **sft)
123 {
124   PetscInt                 cdim, pStart, pEnd, vStart, vEnd, cStart, cEnd, c, depth, cellHeight, t;
125   PetscInt                *sStart, *sEnd;
126   PetscViewerVTKFieldType *ft;
127   PetscInt                 vcdof[DM_NUM_POLYTOPES + 1], globalvcdof[DM_NUM_POLYTOPES + 1];
128   DMLabel                  depthLabel, ctLabel;
129 
130   PetscFunctionBegin;
131   /* the vcdof and globalvcdof are sized to allow every polytope type and simple vertex at DM_NUM_POLYTOPES */
132   PetscCall(PetscArrayzero(vcdof, DM_NUM_POLYTOPES + 1));
133   PetscCall(DMGetCoordinateDim(dm, &cdim));
134   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
135   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
136   if (field >= 0) {
137     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, vStart, field, &vcdof[DM_NUM_POLYTOPES]));
138   } else {
139     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetDof(section, vStart, &vcdof[DM_NUM_POLYTOPES]));
140   }
141 
142   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
143   PetscCall(DMPlexGetDepth(dm, &depth));
144   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
145   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
146   for (c = 0; c < DM_NUM_POLYTOPES; ++c) {
147     const DMPolytopeType ict = (DMPolytopeType)c;
148     PetscInt             dep;
149 
150     if (ict == DM_POLYTOPE_FV_GHOST) continue;
151     PetscCall(DMLabelGetStratumBounds(ctLabel, ict, &cStart, &cEnd));
152     if (pStart >= 0) {
153       PetscCall(DMLabelGetValue(depthLabel, cStart, &dep));
154       if (dep != depth - cellHeight) continue;
155     }
156     if (field >= 0) {
157       if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, cStart, field, &vcdof[c]));
158     } else {
159       if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetDof(section, cStart, &vcdof[c]));
160     }
161   }
162 
163   PetscCall(MPIU_Allreduce(vcdof, globalvcdof, DM_NUM_POLYTOPES + 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
164   *types = 0;
165 
166   for (c = 0; c < DM_NUM_POLYTOPES + 1; ++c) {
167     if (globalvcdof[c]) ++(*types);
168   }
169 
170   PetscCall(PetscMalloc3(*types, &sStart, *types, &sEnd, *types, &ft));
171   t = 0;
172   if (globalvcdof[DM_NUM_POLYTOPES]) {
173     sStart[t] = vStart;
174     sEnd[t]   = vEnd;
175     ft[t]     = (globalvcdof[t] == cdim) ? PETSC_VTK_POINT_VECTOR_FIELD : PETSC_VTK_POINT_FIELD;
176     ++t;
177   }
178 
179   for (c = 0; c < DM_NUM_POLYTOPES; ++c) {
180     if (globalvcdof[c]) {
181       const DMPolytopeType ict = (DMPolytopeType)c;
182 
183       PetscCall(DMLabelGetStratumBounds(ctLabel, ict, &cStart, &cEnd));
184       sStart[t] = cStart;
185       sEnd[t]   = cEnd;
186       ft[t]     = (globalvcdof[c] == cdim) ? PETSC_VTK_CELL_VECTOR_FIELD : PETSC_VTK_CELL_FIELD;
187       ++t;
188     }
189   }
190 
191   if (!*types) {
192     if (field >= 0) {
193       const char *fieldname;
194 
195       PetscCall(PetscSectionGetFieldName(section, field, &fieldname));
196       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section field %" PetscInt_FMT " \"%s\"\n", field, fieldname));
197     } else {
198       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section\n"));
199     }
200   }
201 
202   *ssStart = sStart;
203   *ssEnd   = sEnd;
204   *sft     = ft;
205   PetscFunctionReturn(PETSC_SUCCESS);
206 }
207 
208 PetscErrorCode DMPlexRestoreFieldTypes_Internal(DM dm, PetscSection section, PetscInt field, PetscInt *types, PetscInt **sStart, PetscInt **sEnd, PetscViewerVTKFieldType **ft)
209 {
210   PetscFunctionBegin;
211   PetscCall(PetscFree3(*sStart, *sEnd, *ft));
212   PetscFunctionReturn(PETSC_SUCCESS);
213 }
214 
215 PetscErrorCode DMPlexGetFieldType_Internal(DM dm, PetscSection section, PetscInt field, PetscInt *sStart, PetscInt *sEnd, PetscViewerVTKFieldType *ft)
216 {
217   PetscInt cdim, pStart, pEnd, vStart, vEnd, cStart, cEnd;
218   PetscInt vcdof[2] = {0, 0}, globalvcdof[2];
219 
220   PetscFunctionBegin;
221   *ft = PETSC_VTK_INVALID;
222   PetscCall(DMGetCoordinateDim(dm, &cdim));
223   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
224   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
225   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
226   if (field >= 0) {
227     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, vStart, field, &vcdof[0]));
228     if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, cStart, field, &vcdof[1]));
229   } else {
230     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetDof(section, vStart, &vcdof[0]));
231     if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetDof(section, cStart, &vcdof[1]));
232   }
233   PetscCall(MPIU_Allreduce(vcdof, globalvcdof, 2, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
234   if (globalvcdof[0]) {
235     *sStart = vStart;
236     *sEnd   = vEnd;
237     if (globalvcdof[0] == cdim) *ft = PETSC_VTK_POINT_VECTOR_FIELD;
238     else *ft = PETSC_VTK_POINT_FIELD;
239   } else if (globalvcdof[1]) {
240     *sStart = cStart;
241     *sEnd   = cEnd;
242     if (globalvcdof[1] == cdim) *ft = PETSC_VTK_CELL_VECTOR_FIELD;
243     else *ft = PETSC_VTK_CELL_FIELD;
244   } else {
245     if (field >= 0) {
246       const char *fieldname;
247 
248       PetscCall(PetscSectionGetFieldName(section, field, &fieldname));
249       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section field %" PetscInt_FMT " \"%s\"\n", field, fieldname));
250     } else {
251       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section\n"));
252     }
253   }
254   PetscFunctionReturn(PETSC_SUCCESS);
255 }
256 
257 /*@
258   DMPlexVecView1D - Plot many 1D solutions on the same line graph
259 
260   Collective
261 
262   Input Parameters:
263 + dm     - The `DMPLEX` object
264 . n      - The number of vectors
265 . u      - The array of local vectors
266 - viewer - The `PetscViewer`
267 
268   Level: advanced
269 
270 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `VecViewFromOptions()`, `VecView()`
271 @*/
272 PetscErrorCode DMPlexVecView1D(DM dm, PetscInt n, Vec u[], PetscViewer viewer)
273 {
274   PetscDS            ds;
275   PetscDraw          draw = NULL;
276   PetscDrawLG        lg;
277   Vec                coordinates;
278   const PetscScalar *coords, **sol;
279   PetscReal         *vals;
280   PetscInt          *Nc;
281   PetscInt           Nf, f, c, Nl, l, i, vStart, vEnd, v;
282   char             **names;
283 
284   PetscFunctionBegin;
285   PetscCall(DMGetDS(dm, &ds));
286   PetscCall(PetscDSGetNumFields(ds, &Nf));
287   PetscCall(PetscDSGetTotalComponents(ds, &Nl));
288   PetscCall(PetscDSGetComponents(ds, &Nc));
289 
290   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
291   if (!draw) PetscFunctionReturn(PETSC_SUCCESS);
292   PetscCall(PetscDrawLGCreate(draw, n * Nl, &lg));
293 
294   PetscCall(PetscMalloc3(n, &sol, n * Nl, &names, n * Nl, &vals));
295   for (i = 0, l = 0; i < n; ++i) {
296     const char *vname;
297 
298     PetscCall(PetscObjectGetName((PetscObject)u[i], &vname));
299     for (f = 0; f < Nf; ++f) {
300       PetscObject disc;
301       const char *fname;
302       char        tmpname[PETSC_MAX_PATH_LEN];
303 
304       PetscCall(PetscDSGetDiscretization(ds, f, &disc));
305       /* TODO Create names for components */
306       for (c = 0; c < Nc[f]; ++c, ++l) {
307         PetscCall(PetscObjectGetName(disc, &fname));
308         PetscCall(PetscStrncpy(tmpname, vname, sizeof(tmpname)));
309         PetscCall(PetscStrlcat(tmpname, ":", sizeof(tmpname)));
310         PetscCall(PetscStrlcat(tmpname, fname, sizeof(tmpname)));
311         PetscCall(PetscStrallocpy(tmpname, &names[l]));
312       }
313     }
314   }
315   PetscCall(PetscDrawLGSetLegend(lg, (const char *const *)names));
316   /* Just add P_1 support for now */
317   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
318   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
319   PetscCall(VecGetArrayRead(coordinates, &coords));
320   for (i = 0; i < n; ++i) PetscCall(VecGetArrayRead(u[i], &sol[i]));
321   for (v = vStart; v < vEnd; ++v) {
322     PetscScalar *x, *svals;
323 
324     PetscCall(DMPlexPointLocalRead(dm, v, coords, &x));
325     for (i = 0; i < n; ++i) {
326       PetscCall(DMPlexPointLocalRead(dm, v, sol[i], &svals));
327       for (l = 0; l < Nl; ++l) vals[i * Nl + l] = PetscRealPart(svals[l]);
328     }
329     PetscCall(PetscDrawLGAddCommonPoint(lg, PetscRealPart(x[0]), vals));
330   }
331   PetscCall(VecRestoreArrayRead(coordinates, &coords));
332   for (i = 0; i < n; ++i) PetscCall(VecRestoreArrayRead(u[i], &sol[i]));
333   for (l = 0; l < n * Nl; ++l) PetscCall(PetscFree(names[l]));
334   PetscCall(PetscFree3(sol, names, vals));
335 
336   PetscCall(PetscDrawLGDraw(lg));
337   PetscCall(PetscDrawLGDestroy(&lg));
338   PetscFunctionReturn(PETSC_SUCCESS);
339 }
340 
341 static PetscErrorCode VecView_Plex_Local_Draw_1D(Vec u, PetscViewer viewer)
342 {
343   DM dm;
344 
345   PetscFunctionBegin;
346   PetscCall(VecGetDM(u, &dm));
347   PetscCall(DMPlexVecView1D(dm, 1, &u, viewer));
348   PetscFunctionReturn(PETSC_SUCCESS);
349 }
350 
351 static PetscErrorCode VecView_Plex_Local_Draw_2D(Vec v, PetscViewer viewer)
352 {
353   DM                 dm;
354   PetscSection       s;
355   PetscDraw          draw, popup;
356   DM                 cdm;
357   PetscSection       coordSection;
358   Vec                coordinates;
359   const PetscScalar *array;
360   PetscReal          lbound[3], ubound[3];
361   PetscReal          vbound[2], time;
362   PetscBool          flg;
363   PetscInt           dim, Nf, f, Nc, comp, vStart, vEnd, cStart, cEnd, c, N, level, step, w = 0;
364   const char        *name;
365   char               title[PETSC_MAX_PATH_LEN];
366 
367   PetscFunctionBegin;
368   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
369   PetscCall(VecGetDM(v, &dm));
370   PetscCall(DMGetCoordinateDim(dm, &dim));
371   PetscCall(DMGetLocalSection(dm, &s));
372   PetscCall(PetscSectionGetNumFields(s, &Nf));
373   PetscCall(DMGetCoarsenLevel(dm, &level));
374   PetscCall(DMGetCoordinateDM(dm, &cdm));
375   PetscCall(DMGetLocalSection(cdm, &coordSection));
376   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
377   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
378   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
379 
380   PetscCall(PetscObjectGetName((PetscObject)v, &name));
381   PetscCall(DMGetOutputSequenceNumber(dm, &step, &time));
382 
383   PetscCall(VecGetLocalSize(coordinates, &N));
384   PetscCall(DMGetBoundingBox(dm, lbound, ubound));
385   PetscCall(PetscDrawClear(draw));
386 
387   /* Could implement something like DMDASelectFields() */
388   for (f = 0; f < Nf; ++f) {
389     DM          fdm = dm;
390     Vec         fv  = v;
391     IS          fis;
392     char        prefix[PETSC_MAX_PATH_LEN];
393     const char *fname;
394 
395     PetscCall(PetscSectionGetFieldComponents(s, f, &Nc));
396     PetscCall(PetscSectionGetFieldName(s, f, &fname));
397 
398     if (v->hdr.prefix) PetscCall(PetscStrncpy(prefix, v->hdr.prefix, sizeof(prefix)));
399     else prefix[0] = '\0';
400     if (Nf > 1) {
401       PetscCall(DMCreateSubDM(dm, 1, &f, &fis, &fdm));
402       PetscCall(VecGetSubVector(v, fis, &fv));
403       PetscCall(PetscStrlcat(prefix, fname, sizeof(prefix)));
404       PetscCall(PetscStrlcat(prefix, "_", sizeof(prefix)));
405     }
406     for (comp = 0; comp < Nc; ++comp, ++w) {
407       PetscInt nmax = 2;
408 
409       PetscCall(PetscViewerDrawGetDraw(viewer, w, &draw));
410       if (Nc > 1) PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s_%" PetscInt_FMT " Step: %" PetscInt_FMT " Time: %.4g", name, fname, comp, step, (double)time));
411       else PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s Step: %" PetscInt_FMT " Time: %.4g", name, fname, step, (double)time));
412       PetscCall(PetscDrawSetTitle(draw, title));
413 
414       /* TODO Get max and min only for this component */
415       PetscCall(PetscOptionsGetRealArray(NULL, prefix, "-vec_view_bounds", vbound, &nmax, &flg));
416       if (!flg) {
417         PetscCall(VecMin(fv, NULL, &vbound[0]));
418         PetscCall(VecMax(fv, NULL, &vbound[1]));
419         if (vbound[1] <= vbound[0]) vbound[1] = vbound[0] + 1.0;
420       }
421 
422       PetscCall(PetscDrawGetPopup(draw, &popup));
423       PetscCall(PetscDrawScalePopup(popup, vbound[0], vbound[1]));
424       PetscCall(PetscDrawSetCoordinates(draw, lbound[0], lbound[1], ubound[0], ubound[1]));
425       PetscCall(VecGetArrayRead(fv, &array));
426       for (c = cStart; c < cEnd; ++c) {
427         PetscScalar       *coords = NULL, *a = NULL;
428         const PetscScalar *coords_arr;
429         PetscBool          isDG;
430         PetscInt           numCoords, color[4] = {-1, -1, -1, -1};
431 
432         PetscCall(DMPlexPointLocalRead(fdm, c, array, &a));
433         if (a) {
434           color[0] = PetscDrawRealToColor(PetscRealPart(a[comp]), vbound[0], vbound[1]);
435           color[1] = color[2] = color[3] = color[0];
436         } else {
437           PetscScalar *vals = NULL;
438           PetscInt     numVals, va;
439 
440           PetscCall(DMPlexVecGetClosure(fdm, NULL, fv, c, &numVals, &vals));
441           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);
442           switch (numVals / Nc) {
443           case 3: /* P1 Triangle */
444           case 4: /* P1 Quadrangle */
445             for (va = 0; va < numVals / Nc; ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp]), vbound[0], vbound[1]);
446             break;
447           case 6: /* P2 Triangle */
448           case 8: /* P2 Quadrangle */
449             for (va = 0; va < numVals / (Nc * 2); ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp + numVals / (Nc * 2)]), vbound[0], vbound[1]);
450             break;
451           default:
452             SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of values for cell closure %" PetscInt_FMT " cannot be handled", numVals / Nc);
453           }
454           PetscCall(DMPlexVecRestoreClosure(fdm, NULL, fv, c, &numVals, &vals));
455         }
456         PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
457         switch (numCoords) {
458         case 6:
459         case 12: /* Localized triangle */
460           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]));
461           break;
462         case 8:
463         case 16: /* Localized quadrilateral */
464           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]));
465           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]));
466           break;
467         default:
468           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells with %" PetscInt_FMT " coordinates", numCoords);
469         }
470         PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
471       }
472       PetscCall(VecRestoreArrayRead(fv, &array));
473       PetscCall(PetscDrawFlush(draw));
474       PetscCall(PetscDrawPause(draw));
475       PetscCall(PetscDrawSave(draw));
476     }
477     if (Nf > 1) {
478       PetscCall(VecRestoreSubVector(v, fis, &fv));
479       PetscCall(ISDestroy(&fis));
480       PetscCall(DMDestroy(&fdm));
481     }
482   }
483   PetscFunctionReturn(PETSC_SUCCESS);
484 }
485 
486 static PetscErrorCode VecView_Plex_Local_Draw(Vec v, PetscViewer viewer)
487 {
488   DM        dm;
489   PetscDraw draw;
490   PetscInt  dim;
491   PetscBool isnull;
492 
493   PetscFunctionBegin;
494   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
495   PetscCall(PetscDrawIsNull(draw, &isnull));
496   if (isnull) PetscFunctionReturn(PETSC_SUCCESS);
497 
498   PetscCall(VecGetDM(v, &dm));
499   PetscCall(DMGetCoordinateDim(dm, &dim));
500   switch (dim) {
501   case 1:
502     PetscCall(VecView_Plex_Local_Draw_1D(v, viewer));
503     break;
504   case 2:
505     PetscCall(VecView_Plex_Local_Draw_2D(v, viewer));
506     break;
507   default:
508     SETERRQ(PetscObjectComm((PetscObject)v), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT ". Try PETSCVIEWERGLVIS", dim);
509   }
510   PetscFunctionReturn(PETSC_SUCCESS);
511 }
512 
513 static PetscErrorCode VecView_Plex_Local_VTK(Vec v, PetscViewer viewer)
514 {
515   DM                      dm;
516   Vec                     locv;
517   const char             *name;
518   PetscSection            section;
519   PetscInt                pStart, pEnd;
520   PetscInt                numFields;
521   PetscViewerVTKFieldType ft;
522 
523   PetscFunctionBegin;
524   PetscCall(VecGetDM(v, &dm));
525   PetscCall(DMCreateLocalVector(dm, &locv)); /* VTK viewer requires exclusive ownership of the vector */
526   PetscCall(PetscObjectGetName((PetscObject)v, &name));
527   PetscCall(PetscObjectSetName((PetscObject)locv, name));
528   PetscCall(VecCopy(v, locv));
529   PetscCall(DMGetLocalSection(dm, &section));
530   PetscCall(PetscSectionGetNumFields(section, &numFields));
531   if (!numFields) {
532     PetscCall(DMPlexGetFieldType_Internal(dm, section, PETSC_DETERMINE, &pStart, &pEnd, &ft));
533     PetscCall(PetscViewerVTKAddField(viewer, (PetscObject)dm, DMPlexVTKWriteAll, PETSC_DEFAULT, ft, PETSC_TRUE, (PetscObject)locv));
534   } else {
535     PetscInt f;
536 
537     for (f = 0; f < numFields; f++) {
538       PetscCall(DMPlexGetFieldType_Internal(dm, section, f, &pStart, &pEnd, &ft));
539       if (ft == PETSC_VTK_INVALID) continue;
540       PetscCall(PetscObjectReference((PetscObject)locv));
541       PetscCall(PetscViewerVTKAddField(viewer, (PetscObject)dm, DMPlexVTKWriteAll, f, ft, PETSC_TRUE, (PetscObject)locv));
542     }
543     PetscCall(VecDestroy(&locv));
544   }
545   PetscFunctionReturn(PETSC_SUCCESS);
546 }
547 
548 PetscErrorCode VecView_Plex_Local(Vec v, PetscViewer viewer)
549 {
550   DM        dm;
551   PetscBool isvtk, ishdf5, isdraw, isglvis, iscgns;
552 
553   PetscFunctionBegin;
554   PetscCall(VecGetDM(v, &dm));
555   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
556   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
557   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
558   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
559   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
560   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
561   if (isvtk || ishdf5 || isdraw || isglvis || iscgns) {
562     PetscInt    i, numFields;
563     PetscObject fe;
564     PetscBool   fem  = PETSC_FALSE;
565     Vec         locv = v;
566     const char *name;
567     PetscInt    step;
568     PetscReal   time;
569 
570     PetscCall(DMGetNumFields(dm, &numFields));
571     for (i = 0; i < numFields; i++) {
572       PetscCall(DMGetField(dm, i, NULL, &fe));
573       if (fe->classid == PETSCFE_CLASSID) {
574         fem = PETSC_TRUE;
575         break;
576       }
577     }
578     if (fem) {
579       PetscObject isZero;
580 
581       PetscCall(DMGetLocalVector(dm, &locv));
582       PetscCall(PetscObjectGetName((PetscObject)v, &name));
583       PetscCall(PetscObjectSetName((PetscObject)locv, name));
584       PetscCall(PetscObjectQuery((PetscObject)v, "__Vec_bc_zero__", &isZero));
585       PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", isZero));
586       PetscCall(VecCopy(v, locv));
587       PetscCall(DMGetOutputSequenceNumber(dm, NULL, &time));
588       PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_TRUE, locv, time, NULL, NULL, NULL));
589     }
590     if (isvtk) {
591       PetscCall(VecView_Plex_Local_VTK(locv, viewer));
592     } else if (ishdf5) {
593 #if defined(PETSC_HAVE_HDF5)
594       PetscCall(VecView_Plex_Local_HDF5_Internal(locv, viewer));
595 #else
596       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
597 #endif
598     } else if (isdraw) {
599       PetscCall(VecView_Plex_Local_Draw(locv, viewer));
600     } else if (isglvis) {
601       PetscCall(DMGetOutputSequenceNumber(dm, &step, NULL));
602       PetscCall(PetscViewerGLVisSetSnapId(viewer, step));
603       PetscCall(VecView_GLVis(locv, viewer));
604     } else if (iscgns) {
605 #if defined(PETSC_HAVE_CGNS)
606       PetscCall(VecView_Plex_Local_CGNS(locv, viewer));
607 #else
608       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "CGNS not supported in this build.\nPlease reconfigure using --download-cgns");
609 #endif
610     }
611     if (fem) {
612       PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", NULL));
613       PetscCall(DMRestoreLocalVector(dm, &locv));
614     }
615   } else {
616     PetscBool isseq;
617 
618     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
619     if (isseq) PetscCall(VecView_Seq(v, viewer));
620     else PetscCall(VecView_MPI(v, viewer));
621   }
622   PetscFunctionReturn(PETSC_SUCCESS);
623 }
624 
625 PetscErrorCode VecView_Plex(Vec v, PetscViewer viewer)
626 {
627   DM        dm;
628   PetscBool isvtk, ishdf5, isdraw, isglvis, isexodusii, iscgns;
629 
630   PetscFunctionBegin;
631   PetscCall(VecGetDM(v, &dm));
632   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
633   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
634   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
635   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
636   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
637   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
638   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodusii));
639   if (isvtk || isdraw || isglvis || iscgns) {
640     Vec         locv;
641     PetscObject isZero;
642     const char *name;
643 
644     PetscCall(DMGetLocalVector(dm, &locv));
645     PetscCall(PetscObjectGetName((PetscObject)v, &name));
646     PetscCall(PetscObjectSetName((PetscObject)locv, name));
647     PetscCall(DMGlobalToLocalBegin(dm, v, INSERT_VALUES, locv));
648     PetscCall(DMGlobalToLocalEnd(dm, v, INSERT_VALUES, locv));
649     PetscCall(PetscObjectQuery((PetscObject)v, "__Vec_bc_zero__", &isZero));
650     PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", isZero));
651     PetscCall(VecView_Plex_Local(locv, viewer));
652     PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", NULL));
653     PetscCall(DMRestoreLocalVector(dm, &locv));
654   } else if (ishdf5) {
655 #if defined(PETSC_HAVE_HDF5)
656     PetscCall(VecView_Plex_HDF5_Internal(v, viewer));
657 #else
658     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
659 #endif
660   } else if (isexodusii) {
661 #if defined(PETSC_HAVE_EXODUSII)
662     PetscCall(VecView_PlexExodusII_Internal(v, viewer));
663 #else
664     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
665 #endif
666   } else {
667     PetscBool isseq;
668 
669     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
670     if (isseq) PetscCall(VecView_Seq(v, viewer));
671     else PetscCall(VecView_MPI(v, viewer));
672   }
673   PetscFunctionReturn(PETSC_SUCCESS);
674 }
675 
676 PetscErrorCode VecView_Plex_Native(Vec originalv, PetscViewer viewer)
677 {
678   DM                dm;
679   MPI_Comm          comm;
680   PetscViewerFormat format;
681   Vec               v;
682   PetscBool         isvtk, ishdf5;
683 
684   PetscFunctionBegin;
685   PetscCall(VecGetDM(originalv, &dm));
686   PetscCall(PetscObjectGetComm((PetscObject)originalv, &comm));
687   PetscCheck(dm, comm, PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
688   PetscCall(PetscViewerGetFormat(viewer, &format));
689   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
690   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
691   if (format == PETSC_VIEWER_NATIVE) {
692     /* Natural ordering is the common case for DMDA, NATIVE means plain vector, for PLEX is the opposite */
693     /* this need a better fix */
694     if (dm->useNatural) {
695       if (dm->sfNatural) {
696         const char *vecname;
697         PetscInt    n, nroots;
698 
699         PetscCall(VecGetLocalSize(originalv, &n));
700         PetscCall(PetscSFGetGraph(dm->sfNatural, &nroots, NULL, NULL, NULL));
701         if (n == nroots) {
702           PetscCall(DMPlexCreateNaturalVector(dm, &v));
703           PetscCall(DMPlexGlobalToNaturalBegin(dm, originalv, v));
704           PetscCall(DMPlexGlobalToNaturalEnd(dm, originalv, v));
705           PetscCall(PetscObjectGetName((PetscObject)originalv, &vecname));
706           PetscCall(PetscObjectSetName((PetscObject)v, vecname));
707         } else SETERRQ(comm, PETSC_ERR_ARG_WRONG, "DM global to natural SF only handles global vectors");
708       } else SETERRQ(comm, PETSC_ERR_ARG_WRONGSTATE, "DM global to natural SF was not created");
709     } else v = originalv;
710   } else v = originalv;
711 
712   if (ishdf5) {
713 #if defined(PETSC_HAVE_HDF5)
714     PetscCall(VecView_Plex_HDF5_Native_Internal(v, viewer));
715 #else
716     SETERRQ(comm, PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
717 #endif
718   } else if (isvtk) {
719     SETERRQ(comm, PETSC_ERR_SUP, "VTK format does not support viewing in natural order. Please switch to HDF5.");
720   } else {
721     PetscBool isseq;
722 
723     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
724     if (isseq) PetscCall(VecView_Seq(v, viewer));
725     else PetscCall(VecView_MPI(v, viewer));
726   }
727   if (v != originalv) PetscCall(VecDestroy(&v));
728   PetscFunctionReturn(PETSC_SUCCESS);
729 }
730 
731 PetscErrorCode VecLoad_Plex_Local(Vec v, PetscViewer viewer)
732 {
733   DM        dm;
734   PetscBool ishdf5;
735 
736   PetscFunctionBegin;
737   PetscCall(VecGetDM(v, &dm));
738   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
739   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
740   if (ishdf5) {
741     DM          dmBC;
742     Vec         gv;
743     const char *name;
744 
745     PetscCall(DMGetOutputDM(dm, &dmBC));
746     PetscCall(DMGetGlobalVector(dmBC, &gv));
747     PetscCall(PetscObjectGetName((PetscObject)v, &name));
748     PetscCall(PetscObjectSetName((PetscObject)gv, name));
749     PetscCall(VecLoad_Default(gv, viewer));
750     PetscCall(DMGlobalToLocalBegin(dmBC, gv, INSERT_VALUES, v));
751     PetscCall(DMGlobalToLocalEnd(dmBC, gv, INSERT_VALUES, v));
752     PetscCall(DMRestoreGlobalVector(dmBC, &gv));
753   } else PetscCall(VecLoad_Default(v, viewer));
754   PetscFunctionReturn(PETSC_SUCCESS);
755 }
756 
757 PetscErrorCode VecLoad_Plex(Vec v, PetscViewer viewer)
758 {
759   DM        dm;
760   PetscBool ishdf5, isexodusii;
761 
762   PetscFunctionBegin;
763   PetscCall(VecGetDM(v, &dm));
764   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
765   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
766   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodusii));
767   if (ishdf5) {
768 #if defined(PETSC_HAVE_HDF5)
769     PetscCall(VecLoad_Plex_HDF5_Internal(v, viewer));
770 #else
771     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
772 #endif
773   } else if (isexodusii) {
774 #if defined(PETSC_HAVE_EXODUSII)
775     PetscCall(VecLoad_PlexExodusII_Internal(v, viewer));
776 #else
777     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
778 #endif
779   } else PetscCall(VecLoad_Default(v, viewer));
780   PetscFunctionReturn(PETSC_SUCCESS);
781 }
782 
783 PetscErrorCode VecLoad_Plex_Native(Vec originalv, PetscViewer viewer)
784 {
785   DM                dm;
786   PetscViewerFormat format;
787   PetscBool         ishdf5;
788 
789   PetscFunctionBegin;
790   PetscCall(VecGetDM(originalv, &dm));
791   PetscCheck(dm, PetscObjectComm((PetscObject)originalv), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
792   PetscCall(PetscViewerGetFormat(viewer, &format));
793   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
794   if (format == PETSC_VIEWER_NATIVE) {
795     if (dm->useNatural) {
796       if (dm->sfNatural) {
797         if (ishdf5) {
798 #if defined(PETSC_HAVE_HDF5)
799           Vec         v;
800           const char *vecname;
801 
802           PetscCall(DMPlexCreateNaturalVector(dm, &v));
803           PetscCall(PetscObjectGetName((PetscObject)originalv, &vecname));
804           PetscCall(PetscObjectSetName((PetscObject)v, vecname));
805           PetscCall(VecLoad_Plex_HDF5_Native_Internal(v, viewer));
806           PetscCall(DMPlexNaturalToGlobalBegin(dm, v, originalv));
807           PetscCall(DMPlexNaturalToGlobalEnd(dm, v, originalv));
808           PetscCall(VecDestroy(&v));
809 #else
810           SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
811 #endif
812         } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Reading in natural order is not supported for anything but HDF5.");
813       }
814     } else PetscCall(VecLoad_Default(originalv, viewer));
815   }
816   PetscFunctionReturn(PETSC_SUCCESS);
817 }
818 
819 PETSC_UNUSED static PetscErrorCode DMPlexView_Ascii_Geometry(DM dm, PetscViewer viewer)
820 {
821   PetscSection       coordSection;
822   Vec                coordinates;
823   DMLabel            depthLabel, celltypeLabel;
824   const char        *name[4];
825   const PetscScalar *a;
826   PetscInt           dim, pStart, pEnd, cStart, cEnd, c;
827 
828   PetscFunctionBegin;
829   PetscCall(DMGetDimension(dm, &dim));
830   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
831   PetscCall(DMGetCoordinateSection(dm, &coordSection));
832   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
833   PetscCall(DMPlexGetCellTypeLabel(dm, &celltypeLabel));
834   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
835   PetscCall(PetscSectionGetChart(coordSection, &pStart, &pEnd));
836   PetscCall(VecGetArrayRead(coordinates, &a));
837   name[0]       = "vertex";
838   name[1]       = "edge";
839   name[dim - 1] = "face";
840   name[dim]     = "cell";
841   for (c = cStart; c < cEnd; ++c) {
842     PetscInt *closure = NULL;
843     PetscInt  closureSize, cl, ct;
844 
845     PetscCall(DMLabelGetValue(celltypeLabel, c, &ct));
846     PetscCall(PetscViewerASCIIPrintf(viewer, "Geometry for cell %" PetscInt_FMT " polytope type %s:\n", c, DMPolytopeTypes[ct]));
847     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
848     PetscCall(PetscViewerASCIIPushTab(viewer));
849     for (cl = 0; cl < closureSize * 2; cl += 2) {
850       PetscInt point = closure[cl], depth, dof, off, d, p;
851 
852       if ((point < pStart) || (point >= pEnd)) continue;
853       PetscCall(PetscSectionGetDof(coordSection, point, &dof));
854       if (!dof) continue;
855       PetscCall(DMLabelGetValue(depthLabel, point, &depth));
856       PetscCall(PetscSectionGetOffset(coordSection, point, &off));
857       PetscCall(PetscViewerASCIIPrintf(viewer, "%s %" PetscInt_FMT " coords:", name[depth], point));
858       for (p = 0; p < dof / dim; ++p) {
859         PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
860         for (d = 0; d < dim; ++d) {
861           if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
862           PetscCall(PetscViewerASCIIPrintf(viewer, "%g", (double)PetscRealPart(a[off + p * dim + d])));
863         }
864         PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
865       }
866       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
867     }
868     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
869     PetscCall(PetscViewerASCIIPopTab(viewer));
870   }
871   PetscCall(VecRestoreArrayRead(coordinates, &a));
872   PetscFunctionReturn(PETSC_SUCCESS);
873 }
874 
875 typedef enum {
876   CS_CARTESIAN,
877   CS_POLAR,
878   CS_CYLINDRICAL,
879   CS_SPHERICAL
880 } CoordSystem;
881 const char *CoordSystems[] = {"cartesian", "polar", "cylindrical", "spherical", "CoordSystem", "CS_", NULL};
882 
883 static PetscErrorCode DMPlexView_Ascii_Coordinates(PetscViewer viewer, CoordSystem cs, PetscInt dim, const PetscScalar x[])
884 {
885   PetscInt i;
886 
887   PetscFunctionBegin;
888   if (dim > 3) {
889     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double)PetscRealPart(x[i])));
890   } else {
891     PetscReal coords[3], trcoords[3] = {0., 0., 0.};
892 
893     for (i = 0; i < dim; ++i) coords[i] = PetscRealPart(x[i]);
894     switch (cs) {
895     case CS_CARTESIAN:
896       for (i = 0; i < dim; ++i) trcoords[i] = coords[i];
897       break;
898     case CS_POLAR:
899       PetscCheck(dim == 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Polar coordinates are for 2 dimension, not %" PetscInt_FMT, dim);
900       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
901       trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
902       break;
903     case CS_CYLINDRICAL:
904       PetscCheck(dim == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cylindrical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
905       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
906       trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
907       trcoords[2] = coords[2];
908       break;
909     case CS_SPHERICAL:
910       PetscCheck(dim == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Spherical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
911       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]) + PetscSqr(coords[2]));
912       trcoords[1] = PetscAtan2Real(PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1])), coords[2]);
913       trcoords[2] = PetscAtan2Real(coords[1], coords[0]);
914       break;
915     }
916     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double)trcoords[i]));
917   }
918   PetscFunctionReturn(PETSC_SUCCESS);
919 }
920 
921 static PetscErrorCode DMPlexView_Ascii(DM dm, PetscViewer viewer)
922 {
923   DM_Plex          *mesh = (DM_Plex *)dm->data;
924   DM                cdm, cdmCell;
925   PetscSection      coordSection, coordSectionCell;
926   Vec               coordinates, coordinatesCell;
927   PetscViewerFormat format;
928 
929   PetscFunctionBegin;
930   PetscCall(PetscViewerGetFormat(viewer, &format));
931   if (format == PETSC_VIEWER_ASCII_INFO_DETAIL) {
932     const char *name;
933     PetscInt    dim, cellHeight, maxConeSize, maxSupportSize;
934     PetscInt    pStart, pEnd, p, numLabels, l;
935     PetscMPIInt rank, size;
936 
937     PetscCall(DMGetCoordinateDM(dm, &cdm));
938     PetscCall(DMGetCoordinateSection(dm, &coordSection));
939     PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
940     PetscCall(DMGetCellCoordinateDM(dm, &cdmCell));
941     PetscCall(DMGetCellCoordinateSection(dm, &coordSectionCell));
942     PetscCall(DMGetCellCoordinatesLocal(dm, &coordinatesCell));
943     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
944     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
945     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
946     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
947     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
948     PetscCall(DMGetDimension(dm, &dim));
949     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
950     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
951     else PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
952     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
953     PetscCall(PetscViewerASCIIPrintf(viewer, "Supports:\n"));
954     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
955     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max support size: %" PetscInt_FMT "\n", rank, maxSupportSize));
956     for (p = pStart; p < pEnd; ++p) {
957       PetscInt dof, off, s;
958 
959       PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
960       PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
961       for (s = off; s < off + dof; ++s) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d]: %" PetscInt_FMT " ----> %" PetscInt_FMT "\n", rank, p, mesh->supports[s]));
962     }
963     PetscCall(PetscViewerFlush(viewer));
964     PetscCall(PetscViewerASCIIPrintf(viewer, "Cones:\n"));
965     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max cone size: %" PetscInt_FMT "\n", rank, maxConeSize));
966     for (p = pStart; p < pEnd; ++p) {
967       PetscInt dof, off, c;
968 
969       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
970       PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
971       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]));
972     }
973     PetscCall(PetscViewerFlush(viewer));
974     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
975     if (coordSection && coordinates) {
976       CoordSystem        cs = CS_CARTESIAN;
977       const PetscScalar *array, *arrayCell = NULL;
978       PetscInt           Nf, Nc, pvStart, pvEnd, pcStart = PETSC_MAX_INT, pcEnd = PETSC_MIN_INT, pStart, pEnd, p;
979       PetscMPIInt        rank;
980       const char        *name;
981 
982       PetscCall(PetscOptionsGetEnum(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_coord_system", CoordSystems, (PetscEnum *)&cs, NULL));
983       PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)viewer), &rank));
984       PetscCall(PetscSectionGetNumFields(coordSection, &Nf));
985       PetscCheck(Nf == 1, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Coordinate section should have 1 field, not %" PetscInt_FMT, Nf);
986       PetscCall(PetscSectionGetFieldComponents(coordSection, 0, &Nc));
987       PetscCall(PetscSectionGetChart(coordSection, &pvStart, &pvEnd));
988       if (coordSectionCell) PetscCall(PetscSectionGetChart(coordSectionCell, &pcStart, &pcEnd));
989       pStart = PetscMin(pvStart, pcStart);
990       pEnd   = PetscMax(pvEnd, pcEnd);
991       PetscCall(PetscObjectGetName((PetscObject)coordinates, &name));
992       PetscCall(PetscViewerASCIIPrintf(viewer, "%s with %" PetscInt_FMT " fields\n", name, Nf));
993       PetscCall(PetscViewerASCIIPrintf(viewer, "  field 0 with %" PetscInt_FMT " components\n", Nc));
994       if (cs != CS_CARTESIAN) PetscCall(PetscViewerASCIIPrintf(viewer, "  output coordinate system: %s\n", CoordSystems[cs]));
995 
996       PetscCall(VecGetArrayRead(coordinates, &array));
997       if (coordinatesCell) PetscCall(VecGetArrayRead(coordinatesCell, &arrayCell));
998       PetscCall(PetscViewerASCIIPushSynchronized(viewer));
999       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "Process %d:\n", rank));
1000       for (p = pStart; p < pEnd; ++p) {
1001         PetscInt dof, off;
1002 
1003         if (p >= pvStart && p < pvEnd) {
1004           PetscCall(PetscSectionGetDof(coordSection, p, &dof));
1005           PetscCall(PetscSectionGetOffset(coordSection, p, &off));
1006           if (dof) {
1007             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dim %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
1008             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &array[off]));
1009             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
1010           }
1011         }
1012         if (cdmCell && p >= pcStart && p < pcEnd) {
1013           PetscCall(PetscSectionGetDof(coordSectionCell, p, &dof));
1014           PetscCall(PetscSectionGetOffset(coordSectionCell, p, &off));
1015           if (dof) {
1016             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dim %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
1017             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &arrayCell[off]));
1018             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
1019           }
1020         }
1021       }
1022       PetscCall(PetscViewerFlush(viewer));
1023       PetscCall(PetscViewerASCIIPopSynchronized(viewer));
1024       PetscCall(VecRestoreArrayRead(coordinates, &array));
1025       if (coordinatesCell) PetscCall(VecRestoreArrayRead(coordinatesCell, &arrayCell));
1026     }
1027     PetscCall(DMGetNumLabels(dm, &numLabels));
1028     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
1029     for (l = 0; l < numLabels; ++l) {
1030       DMLabel     label;
1031       PetscBool   isdepth;
1032       const char *name;
1033 
1034       PetscCall(DMGetLabelName(dm, l, &name));
1035       PetscCall(PetscStrcmp(name, "depth", &isdepth));
1036       if (isdepth) continue;
1037       PetscCall(DMGetLabel(dm, name, &label));
1038       PetscCall(DMLabelView(label, viewer));
1039     }
1040     if (size > 1) {
1041       PetscSF sf;
1042 
1043       PetscCall(DMGetPointSF(dm, &sf));
1044       PetscCall(PetscSFView(sf, viewer));
1045     }
1046     if (mesh->periodic.face_sfs)
1047       for (PetscInt i = 0; i < mesh->periodic.num_face_sfs; i++) PetscCall(PetscSFView(mesh->periodic.face_sfs[i], viewer));
1048     PetscCall(PetscViewerFlush(viewer));
1049   } else if (format == PETSC_VIEWER_ASCII_LATEX) {
1050     const char  *name, *color;
1051     const char  *defcolors[3]  = {"gray", "orange", "green"};
1052     const char  *deflcolors[4] = {"blue", "cyan", "red", "magenta"};
1053     char         lname[PETSC_MAX_PATH_LEN];
1054     PetscReal    scale      = 2.0;
1055     PetscReal    tikzscale  = 1.0;
1056     PetscBool    useNumbers = PETSC_TRUE, drawNumbers[4], drawColors[4], useLabels, useColors, plotEdges, drawHasse = PETSC_FALSE;
1057     double       tcoords[3];
1058     PetscScalar *coords;
1059     PetscInt     numLabels, l, numColors, numLColors, dim, d, depth, cStart, cEnd, c, vStart, vEnd, v, eStart = 0, eEnd = 0, e, p, n;
1060     PetscMPIInt  rank, size;
1061     char       **names, **colors, **lcolors;
1062     PetscBool    flg, lflg;
1063     PetscBT      wp = NULL;
1064     PetscInt     pEnd, pStart;
1065 
1066     PetscCall(DMGetCoordinateDM(dm, &cdm));
1067     PetscCall(DMGetCoordinateSection(dm, &coordSection));
1068     PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1069     PetscCall(DMGetCellCoordinateDM(dm, &cdmCell));
1070     PetscCall(DMGetCellCoordinateSection(dm, &coordSectionCell));
1071     PetscCall(DMGetCellCoordinatesLocal(dm, &coordinatesCell));
1072     PetscCall(DMGetDimension(dm, &dim));
1073     PetscCall(DMPlexGetDepth(dm, &depth));
1074     PetscCall(DMGetNumLabels(dm, &numLabels));
1075     numLabels  = PetscMax(numLabels, 10);
1076     numColors  = 10;
1077     numLColors = 10;
1078     PetscCall(PetscCalloc3(numLabels, &names, numColors, &colors, numLColors, &lcolors));
1079     PetscCall(PetscOptionsGetReal(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_scale", &scale, NULL));
1080     PetscCall(PetscOptionsGetReal(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_tikzscale", &tikzscale, NULL));
1081     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_numbers", &useNumbers, NULL));
1082     for (d = 0; d < 4; ++d) drawNumbers[d] = useNumbers;
1083     for (d = 0; d < 4; ++d) drawColors[d] = PETSC_TRUE;
1084     n = 4;
1085     PetscCall(PetscOptionsGetBoolArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_numbers_depth", drawNumbers, &n, &flg));
1086     PetscCheck(!flg || n == dim + 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim + 1);
1087     n = 4;
1088     PetscCall(PetscOptionsGetBoolArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_colors_depth", drawColors, &n, &flg));
1089     PetscCheck(!flg || n == dim + 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim + 1);
1090     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_labels", names, &numLabels, &useLabels));
1091     if (!useLabels) numLabels = 0;
1092     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_colors", colors, &numColors, &useColors));
1093     if (!useColors) {
1094       numColors = 3;
1095       for (c = 0; c < numColors; ++c) PetscCall(PetscStrallocpy(defcolors[c], &colors[c]));
1096     }
1097     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_lcolors", lcolors, &numLColors, &useColors));
1098     if (!useColors) {
1099       numLColors = 4;
1100       for (c = 0; c < numLColors; ++c) PetscCall(PetscStrallocpy(deflcolors[c], &lcolors[c]));
1101     }
1102     PetscCall(PetscOptionsGetString(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_label_filter", lname, sizeof(lname), &lflg));
1103     plotEdges = (PetscBool)(depth > 1 && drawNumbers[1] && dim < 3);
1104     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_edges", &plotEdges, &flg));
1105     PetscCheck(!flg || !plotEdges || depth >= dim, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Mesh must be interpolated");
1106     if (depth < dim) plotEdges = PETSC_FALSE;
1107     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_hasse", &drawHasse, NULL));
1108 
1109     /* filter points with labelvalue != labeldefaultvalue */
1110     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
1111     PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1112     PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
1113     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1114     if (lflg) {
1115       DMLabel lbl;
1116 
1117       PetscCall(DMGetLabel(dm, lname, &lbl));
1118       if (lbl) {
1119         PetscInt val, defval;
1120 
1121         PetscCall(DMLabelGetDefaultValue(lbl, &defval));
1122         PetscCall(PetscBTCreate(pEnd - pStart, &wp));
1123         for (c = pStart; c < pEnd; c++) {
1124           PetscInt *closure = NULL;
1125           PetscInt  closureSize;
1126 
1127           PetscCall(DMLabelGetValue(lbl, c, &val));
1128           if (val == defval) continue;
1129 
1130           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1131           for (p = 0; p < closureSize * 2; p += 2) PetscCall(PetscBTSet(wp, closure[p] - pStart));
1132           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1133         }
1134       }
1135     }
1136 
1137     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1138     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
1139     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1140     PetscCall(PetscViewerASCIIPrintf(viewer, "\
1141 \\documentclass[tikz]{standalone}\n\n\
1142 \\usepackage{pgflibraryshapes}\n\
1143 \\usetikzlibrary{backgrounds}\n\
1144 \\usetikzlibrary{arrows}\n\
1145 \\begin{document}\n"));
1146     if (size > 1) {
1147       PetscCall(PetscViewerASCIIPrintf(viewer, "%s for process ", name));
1148       for (p = 0; p < size; ++p) {
1149         if (p) PetscCall(PetscViewerASCIIPrintf(viewer, (p == size - 1) ? ", and " : ", "));
1150         PetscCall(PetscViewerASCIIPrintf(viewer, "{\\textcolor{%s}%" PetscInt_FMT "}", colors[p % numColors], p));
1151       }
1152       PetscCall(PetscViewerASCIIPrintf(viewer, ".\n\n\n"));
1153     }
1154     if (drawHasse) {
1155       PetscInt maxStratum = PetscMax(vEnd - vStart, PetscMax(eEnd - eStart, cEnd - cStart));
1156 
1157       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vStart}{%" PetscInt_FMT "}\n", vStart));
1158       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vEnd}{%" PetscInt_FMT "}\n", vEnd - 1));
1159       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numVertices}{%" PetscInt_FMT "}\n", vEnd - vStart));
1160       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vShift}{%.2f}\n", 3 + (maxStratum - (vEnd - vStart)) / 2.));
1161       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eStart}{%" PetscInt_FMT "}\n", eStart));
1162       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eEnd}{%" PetscInt_FMT "}\n", eEnd - 1));
1163       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eShift}{%.2f}\n", 3 + (maxStratum - (eEnd - eStart)) / 2.));
1164       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numEdges}{%" PetscInt_FMT "}\n", eEnd - eStart));
1165       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cStart}{%" PetscInt_FMT "}\n", cStart));
1166       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cEnd}{%" PetscInt_FMT "}\n", cEnd - 1));
1167       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numCells}{%" PetscInt_FMT "}\n", cEnd - cStart));
1168       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cShift}{%.2f}\n", 3 + (maxStratum - (cEnd - cStart)) / 2.));
1169     }
1170     PetscCall(PetscViewerASCIIPrintf(viewer, "\\begin{tikzpicture}[scale = %g,font=\\fontsize{8}{8}\\selectfont]\n", (double)tikzscale));
1171 
1172     /* Plot vertices */
1173     PetscCall(VecGetArray(coordinates, &coords));
1174     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
1175     for (v = vStart; v < vEnd; ++v) {
1176       PetscInt  off, dof, d;
1177       PetscBool isLabeled = PETSC_FALSE;
1178 
1179       if (wp && !PetscBTLookup(wp, v - pStart)) continue;
1180       PetscCall(PetscSectionGetDof(coordSection, v, &dof));
1181       PetscCall(PetscSectionGetOffset(coordSection, v, &off));
1182       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1183       PetscCheck(dof <= 3, PETSC_COMM_SELF, PETSC_ERR_PLIB, "coordSection vertex %" PetscInt_FMT " has dof %" PetscInt_FMT " > 3", v, dof);
1184       for (d = 0; d < dof; ++d) {
1185         tcoords[d] = (double)(scale * PetscRealPart(coords[off + d]));
1186         tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1187       }
1188       /* Rotate coordinates since PGF makes z point out of the page instead of up */
1189       if (dim == 3) {
1190         PetscReal tmp = tcoords[1];
1191         tcoords[1]    = tcoords[2];
1192         tcoords[2]    = -tmp;
1193       }
1194       for (d = 0; d < dof; ++d) {
1195         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1196         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)tcoords[d]));
1197       }
1198       if (drawHasse) color = colors[0 % numColors];
1199       else color = colors[rank % numColors];
1200       for (l = 0; l < numLabels; ++l) {
1201         PetscInt val;
1202         PetscCall(DMGetLabelValue(dm, names[l], v, &val));
1203         if (val >= 0) {
1204           color     = lcolors[l % numLColors];
1205           isLabeled = PETSC_TRUE;
1206           break;
1207         }
1208       }
1209       if (drawNumbers[0]) {
1210         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", v, rank, color, v));
1211       } else if (drawColors[0]) {
1212         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", v, rank, !isLabeled ? 1 : 2, color));
1213       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", v, rank));
1214     }
1215     PetscCall(VecRestoreArray(coordinates, &coords));
1216     PetscCall(PetscViewerFlush(viewer));
1217     /* Plot edges */
1218     if (plotEdges) {
1219       PetscCall(VecGetArray(coordinates, &coords));
1220       PetscCall(PetscViewerASCIIPrintf(viewer, "\\path\n"));
1221       for (e = eStart; e < eEnd; ++e) {
1222         const PetscInt *cone;
1223         PetscInt        coneSize, offA, offB, dof, d;
1224 
1225         if (wp && !PetscBTLookup(wp, e - pStart)) continue;
1226         PetscCall(DMPlexGetConeSize(dm, e, &coneSize));
1227         PetscCheck(coneSize == 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Edge %" PetscInt_FMT " cone should have two vertices, not %" PetscInt_FMT, e, coneSize);
1228         PetscCall(DMPlexGetCone(dm, e, &cone));
1229         PetscCall(PetscSectionGetDof(coordSection, cone[0], &dof));
1230         PetscCall(PetscSectionGetOffset(coordSection, cone[0], &offA));
1231         PetscCall(PetscSectionGetOffset(coordSection, cone[1], &offB));
1232         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "("));
1233         for (d = 0; d < dof; ++d) {
1234           tcoords[d] = (double)(0.5 * scale * PetscRealPart(coords[offA + d] + coords[offB + d]));
1235           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1236         }
1237         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1238         if (dim == 3) {
1239           PetscReal tmp = tcoords[1];
1240           tcoords[1]    = tcoords[2];
1241           tcoords[2]    = -tmp;
1242         }
1243         for (d = 0; d < dof; ++d) {
1244           if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1245           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)tcoords[d]));
1246         }
1247         if (drawHasse) color = colors[1 % numColors];
1248         else color = colors[rank % numColors];
1249         for (l = 0; l < numLabels; ++l) {
1250           PetscInt val;
1251           PetscCall(DMGetLabelValue(dm, names[l], e, &val));
1252           if (val >= 0) {
1253             color = lcolors[l % numLColors];
1254             break;
1255           }
1256         }
1257         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "} --\n", e, rank, color, e));
1258       }
1259       PetscCall(VecRestoreArray(coordinates, &coords));
1260       PetscCall(PetscViewerFlush(viewer));
1261       PetscCall(PetscViewerASCIIPrintf(viewer, "(0,0);\n"));
1262     }
1263     /* Plot cells */
1264     if (dim == 3 || !drawNumbers[1]) {
1265       for (e = eStart; e < eEnd; ++e) {
1266         const PetscInt *cone;
1267 
1268         if (wp && !PetscBTLookup(wp, e - pStart)) continue;
1269         color = colors[rank % numColors];
1270         for (l = 0; l < numLabels; ++l) {
1271           PetscInt val;
1272           PetscCall(DMGetLabelValue(dm, names[l], e, &val));
1273           if (val >= 0) {
1274             color = lcolors[l % numLColors];
1275             break;
1276           }
1277         }
1278         PetscCall(DMPlexGetCone(dm, e, &cone));
1279         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", color, cone[0], rank, cone[1], rank));
1280       }
1281     } else {
1282       DMPolytopeType ct;
1283 
1284       /* Drawing a 2D polygon */
1285       for (c = cStart; c < cEnd; ++c) {
1286         if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1287         PetscCall(DMPlexGetCellType(dm, c, &ct));
1288         if (DMPolytopeTypeIsHybrid(ct)) {
1289           const PetscInt *cone;
1290           PetscInt        coneSize, e;
1291 
1292           PetscCall(DMPlexGetCone(dm, c, &cone));
1293           PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
1294           for (e = 0; e < coneSize; ++e) {
1295             const PetscInt *econe;
1296 
1297             PetscCall(DMPlexGetCone(dm, cone[e], &econe));
1298             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));
1299           }
1300         } else {
1301           PetscInt *closure = NULL;
1302           PetscInt  closureSize, Nv = 0, v;
1303 
1304           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1305           for (p = 0; p < closureSize * 2; p += 2) {
1306             const PetscInt point = closure[p];
1307 
1308             if ((point >= vStart) && (point < vEnd)) closure[Nv++] = point;
1309           }
1310           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] ", colors[rank % numColors]));
1311           for (v = 0; v <= Nv; ++v) {
1312             const PetscInt vertex = closure[v % Nv];
1313 
1314             if (v > 0) {
1315               if (plotEdges) {
1316                 const PetscInt *edge;
1317                 PetscInt        endpoints[2], ne;
1318 
1319                 endpoints[0] = closure[v - 1];
1320                 endpoints[1] = vertex;
1321                 PetscCall(DMPlexGetJoin(dm, 2, endpoints, &ne, &edge));
1322                 PetscCheck(ne == 1, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Could not find edge for vertices %" PetscInt_FMT ", %" PetscInt_FMT, endpoints[0], endpoints[1]);
1323                 PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- (%" PetscInt_FMT "_%d) -- ", edge[0], rank));
1324                 PetscCall(DMPlexRestoreJoin(dm, 2, endpoints, &ne, &edge));
1325               } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- "));
1326             }
1327             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "(%" PetscInt_FMT "_%d)", vertex, rank));
1328           }
1329           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ";\n"));
1330           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1331         }
1332       }
1333     }
1334     for (c = cStart; c < cEnd; ++c) {
1335       double             ccoords[3] = {0.0, 0.0, 0.0};
1336       PetscBool          isLabeled  = PETSC_FALSE;
1337       PetscScalar       *cellCoords = NULL;
1338       const PetscScalar *array;
1339       PetscInt           numCoords, cdim, d;
1340       PetscBool          isDG;
1341 
1342       if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1343       PetscCall(DMGetCoordinateDim(dm, &cdim));
1344       PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1345       PetscCheck(!(numCoords % cdim), PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "coordinate dim %" PetscInt_FMT " does not divide numCoords %" PetscInt_FMT, cdim, numCoords);
1346       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1347       for (p = 0; p < numCoords / cdim; ++p) {
1348         for (d = 0; d < cdim; ++d) {
1349           tcoords[d] = (double)(scale * PetscRealPart(cellCoords[p * cdim + d]));
1350           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1351         }
1352         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1353         if (cdim == 3) {
1354           PetscReal tmp = tcoords[1];
1355           tcoords[1]    = tcoords[2];
1356           tcoords[2]    = -tmp;
1357         }
1358         for (d = 0; d < dim; ++d) ccoords[d] += tcoords[d];
1359       }
1360       for (d = 0; d < cdim; ++d) ccoords[d] /= (numCoords / cdim);
1361       PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1362       for (d = 0; d < cdim; ++d) {
1363         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1364         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)ccoords[d]));
1365       }
1366       if (drawHasse) color = colors[depth % numColors];
1367       else color = colors[rank % numColors];
1368       for (l = 0; l < numLabels; ++l) {
1369         PetscInt val;
1370         PetscCall(DMGetLabelValue(dm, names[l], c, &val));
1371         if (val >= 0) {
1372           color     = lcolors[l % numLColors];
1373           isLabeled = PETSC_TRUE;
1374           break;
1375         }
1376       }
1377       if (drawNumbers[dim]) {
1378         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", c, rank, color, c));
1379       } else if (drawColors[dim]) {
1380         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", c, rank, !isLabeled ? 1 : 2, color));
1381       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", c, rank));
1382     }
1383     if (drawHasse) {
1384       color = colors[depth % numColors];
1385       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Cells\n"));
1386       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\c in {\\cStart,...,\\cEnd}\n"));
1387       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1388       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\c_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\cShift+\\c-\\cStart,0) {\\c};\n", rank, color));
1389       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1390 
1391       color = colors[1 % numColors];
1392       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Edges\n"));
1393       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\e in {\\eStart,...,\\eEnd}\n"));
1394       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1395       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\e_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\eShift+\\e-\\eStart,1) {\\e};\n", rank, color));
1396       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1397 
1398       color = colors[0 % numColors];
1399       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Vertices\n"));
1400       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\v in {\\vStart,...,\\vEnd}\n"));
1401       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1402       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\v_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\vShift+\\v-\\vStart,2) {\\v};\n", rank, color));
1403       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1404 
1405       for (p = pStart; p < pEnd; ++p) {
1406         const PetscInt *cone;
1407         PetscInt        coneSize, cp;
1408 
1409         PetscCall(DMPlexGetCone(dm, p, &cone));
1410         PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
1411         for (cp = 0; cp < coneSize; ++cp) PetscCall(PetscViewerASCIIPrintf(viewer, "\\draw[->, shorten >=1pt] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", cone[cp], rank, p, rank));
1412       }
1413     }
1414     PetscCall(PetscViewerFlush(viewer));
1415     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
1416     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{tikzpicture}\n"));
1417     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{document}\n"));
1418     for (l = 0; l < numLabels; ++l) PetscCall(PetscFree(names[l]));
1419     for (c = 0; c < numColors; ++c) PetscCall(PetscFree(colors[c]));
1420     for (c = 0; c < numLColors; ++c) PetscCall(PetscFree(lcolors[c]));
1421     PetscCall(PetscFree3(names, colors, lcolors));
1422     PetscCall(PetscBTDestroy(&wp));
1423   } else if (format == PETSC_VIEWER_LOAD_BALANCE) {
1424     Vec                    cown, acown;
1425     VecScatter             sct;
1426     ISLocalToGlobalMapping g2l;
1427     IS                     gid, acis;
1428     MPI_Comm               comm, ncomm = MPI_COMM_NULL;
1429     MPI_Group              ggroup, ngroup;
1430     PetscScalar           *array, nid;
1431     const PetscInt        *idxs;
1432     PetscInt              *idxs2, *start, *adjacency, *work;
1433     PetscInt64             lm[3], gm[3];
1434     PetscInt               i, c, cStart, cEnd, cum, numVertices, ect, ectn, cellHeight;
1435     PetscMPIInt            d1, d2, rank;
1436 
1437     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1438     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1439 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1440     PetscCallMPI(MPI_Comm_split_type(comm, MPI_COMM_TYPE_SHARED, rank, MPI_INFO_NULL, &ncomm));
1441 #endif
1442     if (ncomm != MPI_COMM_NULL) {
1443       PetscCallMPI(MPI_Comm_group(comm, &ggroup));
1444       PetscCallMPI(MPI_Comm_group(ncomm, &ngroup));
1445       d1 = 0;
1446       PetscCallMPI(MPI_Group_translate_ranks(ngroup, 1, &d1, ggroup, &d2));
1447       nid = d2;
1448       PetscCallMPI(MPI_Group_free(&ggroup));
1449       PetscCallMPI(MPI_Group_free(&ngroup));
1450       PetscCallMPI(MPI_Comm_free(&ncomm));
1451     } else nid = 0.0;
1452 
1453     /* Get connectivity */
1454     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1455     PetscCall(DMPlexCreatePartitionerGraph(dm, cellHeight, &numVertices, &start, &adjacency, &gid));
1456 
1457     /* filter overlapped local cells */
1458     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
1459     PetscCall(ISGetIndices(gid, &idxs));
1460     PetscCall(ISGetLocalSize(gid, &cum));
1461     PetscCall(PetscMalloc1(cum, &idxs2));
1462     for (c = cStart, cum = 0; c < cEnd; c++) {
1463       if (idxs[c - cStart] < 0) continue;
1464       idxs2[cum++] = idxs[c - cStart];
1465     }
1466     PetscCall(ISRestoreIndices(gid, &idxs));
1467     PetscCheck(numVertices == cum, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Unexpected %" PetscInt_FMT " != %" PetscInt_FMT, numVertices, cum);
1468     PetscCall(ISDestroy(&gid));
1469     PetscCall(ISCreateGeneral(comm, numVertices, idxs2, PETSC_OWN_POINTER, &gid));
1470 
1471     /* support for node-aware cell locality */
1472     PetscCall(ISCreateGeneral(comm, start[numVertices], adjacency, PETSC_USE_POINTER, &acis));
1473     PetscCall(VecCreateSeq(PETSC_COMM_SELF, start[numVertices], &acown));
1474     PetscCall(VecCreateMPI(comm, numVertices, PETSC_DECIDE, &cown));
1475     PetscCall(VecGetArray(cown, &array));
1476     for (c = 0; c < numVertices; c++) array[c] = nid;
1477     PetscCall(VecRestoreArray(cown, &array));
1478     PetscCall(VecScatterCreate(cown, acis, acown, NULL, &sct));
1479     PetscCall(VecScatterBegin(sct, cown, acown, INSERT_VALUES, SCATTER_FORWARD));
1480     PetscCall(VecScatterEnd(sct, cown, acown, INSERT_VALUES, SCATTER_FORWARD));
1481     PetscCall(ISDestroy(&acis));
1482     PetscCall(VecScatterDestroy(&sct));
1483     PetscCall(VecDestroy(&cown));
1484 
1485     /* compute edgeCut */
1486     for (c = 0, cum = 0; c < numVertices; c++) cum = PetscMax(cum, start[c + 1] - start[c]);
1487     PetscCall(PetscMalloc1(cum, &work));
1488     PetscCall(ISLocalToGlobalMappingCreateIS(gid, &g2l));
1489     PetscCall(ISLocalToGlobalMappingSetType(g2l, ISLOCALTOGLOBALMAPPINGHASH));
1490     PetscCall(ISDestroy(&gid));
1491     PetscCall(VecGetArray(acown, &array));
1492     for (c = 0, ect = 0, ectn = 0; c < numVertices; c++) {
1493       PetscInt totl;
1494 
1495       totl = start[c + 1] - start[c];
1496       PetscCall(ISGlobalToLocalMappingApply(g2l, IS_GTOLM_MASK, totl, adjacency + start[c], NULL, work));
1497       for (i = 0; i < totl; i++) {
1498         if (work[i] < 0) {
1499           ect += 1;
1500           ectn += (array[i + start[c]] != nid) ? 0 : 1;
1501         }
1502       }
1503     }
1504     PetscCall(PetscFree(work));
1505     PetscCall(VecRestoreArray(acown, &array));
1506     lm[0] = numVertices > 0 ? numVertices : PETSC_MAX_INT;
1507     lm[1] = -numVertices;
1508     PetscCall(MPIU_Allreduce(lm, gm, 2, MPIU_INT64, MPI_MIN, comm));
1509     PetscCall(PetscViewerASCIIPrintf(viewer, "  Cell balance: %.2f (max %" PetscInt_FMT ", min %" PetscInt_FMT, -((double)gm[1]) / ((double)gm[0]), -(PetscInt)gm[1], (PetscInt)gm[0]));
1510     lm[0] = ect;                     /* edgeCut */
1511     lm[1] = ectn;                    /* node-aware edgeCut */
1512     lm[2] = numVertices > 0 ? 0 : 1; /* empty processes */
1513     PetscCall(MPIU_Allreduce(lm, gm, 3, MPIU_INT64, MPI_SUM, comm));
1514     PetscCall(PetscViewerASCIIPrintf(viewer, ", empty %" PetscInt_FMT ")\n", (PetscInt)gm[2]));
1515 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1516     PetscCall(PetscViewerASCIIPrintf(viewer, "  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n", (PetscInt)(gm[0] / 2), gm[0] ? ((double)gm[1]) / ((double)gm[0]) : 1.));
1517 #else
1518     PetscCall(PetscViewerASCIIPrintf(viewer, "  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n", (PetscInt)(gm[0] / 2), 0.0));
1519 #endif
1520     PetscCall(ISLocalToGlobalMappingDestroy(&g2l));
1521     PetscCall(PetscFree(start));
1522     PetscCall(PetscFree(adjacency));
1523     PetscCall(VecDestroy(&acown));
1524   } else {
1525     const char    *name;
1526     PetscInt      *sizes, *hybsizes, *ghostsizes;
1527     PetscInt       locDepth, depth, cellHeight, dim, d;
1528     PetscInt       pStart, pEnd, p, gcStart, gcEnd, gcNum;
1529     PetscInt       numLabels, l, maxSize = 17;
1530     DMPolytopeType ct0 = DM_POLYTOPE_UNKNOWN;
1531     MPI_Comm       comm;
1532     PetscMPIInt    size, rank;
1533 
1534     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1535     PetscCallMPI(MPI_Comm_size(comm, &size));
1536     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1537     PetscCall(DMGetDimension(dm, &dim));
1538     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1539     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1540     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
1541     else PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
1542     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
1543     PetscCall(DMPlexGetDepth(dm, &locDepth));
1544     PetscCall(MPIU_Allreduce(&locDepth, &depth, 1, MPIU_INT, MPI_MAX, comm));
1545     PetscCall(DMPlexGetCellTypeStratum(dm, DM_POLYTOPE_FV_GHOST, &gcStart, &gcEnd));
1546     gcNum = gcEnd - gcStart;
1547     if (size < maxSize) PetscCall(PetscCalloc3(size, &sizes, size, &hybsizes, size, &ghostsizes));
1548     else PetscCall(PetscCalloc3(3, &sizes, 3, &hybsizes, 3, &ghostsizes));
1549     for (d = 0; d <= depth; d++) {
1550       PetscInt Nc[2] = {0, 0}, ict;
1551 
1552       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
1553       if (pStart < pEnd) PetscCall(DMPlexGetCellType(dm, pStart, &ct0));
1554       ict = ct0;
1555       PetscCallMPI(MPI_Bcast(&ict, 1, MPIU_INT, 0, comm));
1556       ct0 = (DMPolytopeType)ict;
1557       for (p = pStart; p < pEnd; ++p) {
1558         DMPolytopeType ct;
1559 
1560         PetscCall(DMPlexGetCellType(dm, p, &ct));
1561         if (ct == ct0) ++Nc[0];
1562         else ++Nc[1];
1563       }
1564       if (size < maxSize) {
1565         PetscCallMPI(MPI_Gather(&Nc[0], 1, MPIU_INT, sizes, 1, MPIU_INT, 0, comm));
1566         PetscCallMPI(MPI_Gather(&Nc[1], 1, MPIU_INT, hybsizes, 1, MPIU_INT, 0, comm));
1567         if (d == depth) PetscCallMPI(MPI_Gather(&gcNum, 1, MPIU_INT, ghostsizes, 1, MPIU_INT, 0, comm));
1568         PetscCall(PetscViewerASCIIPrintf(viewer, "  Number of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1569         for (p = 0; p < size; ++p) {
1570           if (rank == 0) {
1571             PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT, sizes[p] + hybsizes[p]));
1572             if (hybsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT ")", hybsizes[p]));
1573             if (ghostsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "]", ghostsizes[p]));
1574           }
1575         }
1576       } else {
1577         PetscInt locMinMax[2];
1578 
1579         locMinMax[0] = Nc[0] + Nc[1];
1580         locMinMax[1] = Nc[0] + Nc[1];
1581         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, sizes));
1582         locMinMax[0] = Nc[1];
1583         locMinMax[1] = Nc[1];
1584         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, hybsizes));
1585         if (d == depth) {
1586           locMinMax[0] = gcNum;
1587           locMinMax[1] = gcNum;
1588           PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, ghostsizes));
1589         }
1590         PetscCall(PetscViewerASCIIPrintf(viewer, "  Min/Max of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1591         PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT "/%" PetscInt_FMT, sizes[0], sizes[1]));
1592         if (hybsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT "/%" PetscInt_FMT ")", hybsizes[0], hybsizes[1]));
1593         if (ghostsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "/%" PetscInt_FMT "]", ghostsizes[0], ghostsizes[1]));
1594       }
1595       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
1596     }
1597     PetscCall(PetscFree3(sizes, hybsizes, ghostsizes));
1598     {
1599       const PetscReal *maxCell;
1600       const PetscReal *L;
1601       PetscBool        localized;
1602 
1603       PetscCall(DMGetPeriodicity(dm, &maxCell, NULL, &L));
1604       PetscCall(DMGetCoordinatesLocalized(dm, &localized));
1605       if (L || localized) {
1606         PetscCall(PetscViewerASCIIPrintf(viewer, "Periodic mesh"));
1607         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1608         if (L) {
1609           PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
1610           for (d = 0; d < dim; ++d) {
1611             if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1612             PetscCall(PetscViewerASCIIPrintf(viewer, "%s", L[d] > 0.0 ? "PERIODIC" : "NONE"));
1613           }
1614           PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
1615         }
1616         PetscCall(PetscViewerASCIIPrintf(viewer, " coordinates %s\n", localized ? "localized" : "not localized"));
1617         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1618       }
1619     }
1620     PetscCall(DMGetNumLabels(dm, &numLabels));
1621     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
1622     for (l = 0; l < numLabels; ++l) {
1623       DMLabel         label;
1624       const char     *name;
1625       IS              valueIS;
1626       const PetscInt *values;
1627       PetscInt        numValues, v;
1628 
1629       PetscCall(DMGetLabelName(dm, l, &name));
1630       PetscCall(DMGetLabel(dm, name, &label));
1631       PetscCall(DMLabelGetNumValues(label, &numValues));
1632       PetscCall(PetscViewerASCIIPrintf(viewer, "  %s: %" PetscInt_FMT " strata with value/size (", name, numValues));
1633       PetscCall(DMLabelGetValueIS(label, &valueIS));
1634       PetscCall(ISGetIndices(valueIS, &values));
1635       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1636       for (v = 0; v < numValues; ++v) {
1637         PetscInt size;
1638 
1639         PetscCall(DMLabelGetStratumSize(label, values[v], &size));
1640         if (v > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1641         PetscCall(PetscViewerASCIIPrintf(viewer, "%" PetscInt_FMT " (%" PetscInt_FMT ")", values[v], size));
1642       }
1643       PetscCall(PetscViewerASCIIPrintf(viewer, ")\n"));
1644       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1645       PetscCall(ISRestoreIndices(valueIS, &values));
1646       PetscCall(ISDestroy(&valueIS));
1647     }
1648     {
1649       char    **labelNames;
1650       PetscInt  Nl = numLabels;
1651       PetscBool flg;
1652 
1653       PetscCall(PetscMalloc1(Nl, &labelNames));
1654       PetscCall(PetscOptionsGetStringArray(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_plex_view_labels", labelNames, &Nl, &flg));
1655       for (l = 0; l < Nl; ++l) {
1656         DMLabel label;
1657 
1658         PetscCall(DMHasLabel(dm, labelNames[l], &flg));
1659         if (flg) {
1660           PetscCall(DMGetLabel(dm, labelNames[l], &label));
1661           PetscCall(DMLabelView(label, viewer));
1662         }
1663         PetscCall(PetscFree(labelNames[l]));
1664       }
1665       PetscCall(PetscFree(labelNames));
1666     }
1667     /* If no fields are specified, people do not want to see adjacency */
1668     if (dm->Nf) {
1669       PetscInt f;
1670 
1671       for (f = 0; f < dm->Nf; ++f) {
1672         const char *name;
1673 
1674         PetscCall(PetscObjectGetName(dm->fields[f].disc, &name));
1675         if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Field %s:\n", name));
1676         PetscCall(PetscViewerASCIIPushTab(viewer));
1677         if (dm->fields[f].label) PetscCall(DMLabelView(dm->fields[f].label, viewer));
1678         if (dm->fields[f].adjacency[0]) {
1679           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM++\n"));
1680           else PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM\n"));
1681         } else {
1682           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FEM\n"));
1683           else PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FUNKY\n"));
1684         }
1685         PetscCall(PetscViewerASCIIPopTab(viewer));
1686       }
1687     }
1688     PetscCall(DMGetCoarseDM(dm, &cdm));
1689     if (cdm) {
1690       PetscCall(PetscViewerASCIIPushTab(viewer));
1691       PetscCall(PetscViewerASCIIPrintf(viewer, "Defined by transform from:\n"));
1692       PetscCall(DMPlexView_Ascii(cdm, viewer));
1693       PetscCall(PetscViewerASCIIPopTab(viewer));
1694     }
1695   }
1696   PetscFunctionReturn(PETSC_SUCCESS);
1697 }
1698 
1699 static PetscErrorCode DMPlexDrawCell(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[])
1700 {
1701   DMPolytopeType ct;
1702   PetscMPIInt    rank;
1703   PetscInt       cdim;
1704 
1705   PetscFunctionBegin;
1706   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1707   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1708   PetscCall(DMGetCoordinateDim(dm, &cdim));
1709   switch (ct) {
1710   case DM_POLYTOPE_SEGMENT:
1711   case DM_POLYTOPE_POINT_PRISM_TENSOR:
1712     switch (cdim) {
1713     case 1: {
1714       const PetscReal y  = 0.5;  /* TODO Put it in the middle of the viewport */
1715       const PetscReal dy = 0.05; /* TODO Make it a fraction of the total length */
1716 
1717       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y, PetscRealPart(coords[1]), y, PETSC_DRAW_BLACK));
1718       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y + dy, PetscRealPart(coords[0]), y - dy, PETSC_DRAW_BLACK));
1719       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[1]), y + dy, PetscRealPart(coords[1]), y - dy, PETSC_DRAW_BLACK));
1720     } break;
1721     case 2: {
1722       const PetscReal dx = (PetscRealPart(coords[3]) - PetscRealPart(coords[1]));
1723       const PetscReal dy = (PetscRealPart(coords[2]) - PetscRealPart(coords[0]));
1724       const PetscReal l  = 0.1 / PetscSqrtReal(dx * dx + dy * dy);
1725 
1726       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1727       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));
1728       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));
1729     } break;
1730     default:
1731       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of dimension %" PetscInt_FMT, cdim);
1732     }
1733     break;
1734   case DM_POLYTOPE_TRIANGLE:
1735     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));
1736     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1737     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1738     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1739     break;
1740   case DM_POLYTOPE_QUADRILATERAL:
1741     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));
1742     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));
1743     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1744     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1745     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_BLACK));
1746     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1747     break;
1748   case DM_POLYTOPE_SEG_PRISM_TENSOR:
1749     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));
1750     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));
1751     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1752     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_BLACK));
1753     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1754     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1755     break;
1756   case DM_POLYTOPE_FV_GHOST:
1757     break;
1758   default:
1759     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1760   }
1761   PetscFunctionReturn(PETSC_SUCCESS);
1762 }
1763 
1764 static PetscErrorCode DrawPolygon_Private(DM dm, PetscDraw draw, PetscInt cell, PetscInt Nv, const PetscReal refVertices[], const PetscScalar coords[], PetscInt edgeDiv, PetscReal refCoords[], PetscReal edgeCoords[])
1765 {
1766   PetscReal   centroid[2] = {0., 0.};
1767   PetscMPIInt rank;
1768   PetscInt    fillColor;
1769 
1770   PetscFunctionBegin;
1771   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1772   fillColor = PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2;
1773   for (PetscInt v = 0; v < Nv; ++v) {
1774     centroid[0] += PetscRealPart(coords[v * 2 + 0]) / Nv;
1775     centroid[1] += PetscRealPart(coords[v * 2 + 1]) / Nv;
1776   }
1777   for (PetscInt e = 0; e < Nv; ++e) {
1778     refCoords[0] = refVertices[e * 2 + 0];
1779     refCoords[1] = refVertices[e * 2 + 1];
1780     for (PetscInt d = 1; d <= edgeDiv; ++d) {
1781       refCoords[d * 2 + 0] = refCoords[0] + (refVertices[(e + 1) % Nv * 2 + 0] - refCoords[0]) * d / edgeDiv;
1782       refCoords[d * 2 + 1] = refCoords[1] + (refVertices[(e + 1) % Nv * 2 + 1] - refCoords[1]) * d / edgeDiv;
1783     }
1784     PetscCall(DMPlexReferenceToCoordinates(dm, cell, edgeDiv + 1, refCoords, edgeCoords));
1785     for (PetscInt d = 0; d < edgeDiv; ++d) {
1786       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));
1787       PetscCall(PetscDrawLine(draw, edgeCoords[d * 2 + 0], edgeCoords[d * 2 + 1], edgeCoords[(d + 1) * 2 + 0], edgeCoords[(d + 1) * 2 + 1], PETSC_DRAW_BLACK));
1788     }
1789   }
1790   PetscFunctionReturn(PETSC_SUCCESS);
1791 }
1792 
1793 static PetscErrorCode DMPlexDrawCellHighOrder(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[], PetscInt edgeDiv, PetscReal refCoords[], PetscReal edgeCoords[])
1794 {
1795   DMPolytopeType ct;
1796 
1797   PetscFunctionBegin;
1798   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1799   switch (ct) {
1800   case DM_POLYTOPE_TRIANGLE: {
1801     PetscReal refVertices[6] = {-1., -1., 1., -1., -1., 1.};
1802 
1803     PetscCall(DrawPolygon_Private(dm, draw, cell, 3, refVertices, coords, edgeDiv, refCoords, edgeCoords));
1804   } break;
1805   case DM_POLYTOPE_QUADRILATERAL: {
1806     PetscReal refVertices[8] = {-1., -1., 1., -1., 1., 1., -1., 1.};
1807 
1808     PetscCall(DrawPolygon_Private(dm, draw, cell, 4, refVertices, coords, edgeDiv, refCoords, edgeCoords));
1809   } break;
1810   default:
1811     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1812   }
1813   PetscFunctionReturn(PETSC_SUCCESS);
1814 }
1815 
1816 static PetscErrorCode DMPlexView_Draw(DM dm, PetscViewer viewer)
1817 {
1818   PetscDraw    draw;
1819   DM           cdm;
1820   PetscSection coordSection;
1821   Vec          coordinates;
1822   PetscReal    xyl[3], xyr[3];
1823   PetscReal   *refCoords, *edgeCoords;
1824   PetscBool    isnull, drawAffine;
1825   PetscInt     dim, vStart, vEnd, cStart, cEnd, c, cDegree, edgeDiv;
1826 
1827   PetscFunctionBegin;
1828   PetscCall(DMGetCoordinateDim(dm, &dim));
1829   PetscCheck(dim <= 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT, dim);
1830   PetscCall(DMGetCoordinateDegree_Internal(dm, &cDegree));
1831   drawAffine = cDegree > 1 ? PETSC_FALSE : PETSC_TRUE;
1832   edgeDiv    = cDegree + 1;
1833   PetscCall(PetscOptionsGetBool(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_view_draw_affine", &drawAffine, NULL));
1834   if (!drawAffine) PetscCall(PetscMalloc2((edgeDiv + 1) * dim, &refCoords, (edgeDiv + 1) * dim, &edgeCoords));
1835   PetscCall(DMGetCoordinateDM(dm, &cdm));
1836   PetscCall(DMGetLocalSection(cdm, &coordSection));
1837   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1838   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1839   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1840 
1841   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
1842   PetscCall(PetscDrawIsNull(draw, &isnull));
1843   if (isnull) PetscFunctionReturn(PETSC_SUCCESS);
1844   PetscCall(PetscDrawSetTitle(draw, "Mesh"));
1845 
1846   PetscCall(DMGetBoundingBox(dm, xyl, xyr));
1847   PetscCall(PetscDrawSetCoordinates(draw, xyl[0], xyl[1], xyr[0], xyr[1]));
1848   PetscCall(PetscDrawClear(draw));
1849 
1850   for (c = cStart; c < cEnd; ++c) {
1851     PetscScalar       *coords = NULL;
1852     const PetscScalar *coords_arr;
1853     PetscInt           numCoords;
1854     PetscBool          isDG;
1855 
1856     PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
1857     if (drawAffine) PetscCall(DMPlexDrawCell(dm, draw, c, coords));
1858     else PetscCall(DMPlexDrawCellHighOrder(dm, draw, c, coords, edgeDiv, refCoords, edgeCoords));
1859     PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
1860   }
1861   if (!drawAffine) PetscCall(PetscFree2(refCoords, edgeCoords));
1862   PetscCall(PetscDrawFlush(draw));
1863   PetscCall(PetscDrawPause(draw));
1864   PetscCall(PetscDrawSave(draw));
1865   PetscFunctionReturn(PETSC_SUCCESS);
1866 }
1867 
1868 static PetscErrorCode DMPlexCreateHighOrderSurrogate_Internal(DM dm, DM *hdm)
1869 {
1870   DM           odm = dm, rdm = dm, cdm;
1871   PetscFE      fe;
1872   PetscSpace   sp;
1873   PetscClassId id;
1874   PetscInt     degree;
1875   PetscBool    hoView = PETSC_TRUE;
1876 
1877   PetscFunctionBegin;
1878   PetscObjectOptionsBegin((PetscObject)dm);
1879   PetscCall(PetscOptionsBool("-dm_plex_high_order_view", "Subsample to view meshes with high order coordinates", "DMPlexCreateHighOrderSurrogate_Internal", hoView, &hoView, NULL));
1880   PetscOptionsEnd();
1881   PetscCall(PetscObjectReference((PetscObject)dm));
1882   *hdm = dm;
1883   if (!hoView) PetscFunctionReturn(PETSC_SUCCESS);
1884   PetscCall(DMGetCoordinateDM(dm, &cdm));
1885   PetscCall(DMGetField(cdm, 0, NULL, (PetscObject *)&fe));
1886   PetscCall(PetscObjectGetClassId((PetscObject)fe, &id));
1887   if (id != PETSCFE_CLASSID) PetscFunctionReturn(PETSC_SUCCESS);
1888   PetscCall(PetscFEGetBasisSpace(fe, &sp));
1889   PetscCall(PetscSpaceGetDegree(sp, &degree, NULL));
1890   for (PetscInt r = 0, rd = PetscCeilReal(((PetscReal)degree) / 2.); r < (PetscInt)PetscCeilReal(PetscLog2Real(degree)); ++r, rd = PetscCeilReal(((PetscReal)rd) / 2.)) {
1891     DM  cdm, rcdm;
1892     Mat In;
1893     Vec cl, rcl;
1894 
1895     PetscCall(DMRefine(odm, PetscObjectComm((PetscObject)odm), &rdm));
1896     PetscCall(DMPlexCreateCoordinateSpace(rdm, rd, PETSC_FALSE, NULL));
1897     PetscCall(PetscObjectSetName((PetscObject)rdm, "Refined Mesh with Linear Coordinates"));
1898     PetscCall(DMGetCoordinateDM(odm, &cdm));
1899     PetscCall(DMGetCoordinateDM(rdm, &rcdm));
1900     PetscCall(DMGetCoordinatesLocal(odm, &cl));
1901     PetscCall(DMGetCoordinatesLocal(rdm, &rcl));
1902     PetscCall(DMSetCoarseDM(rcdm, cdm));
1903     PetscCall(DMCreateInterpolation(cdm, rcdm, &In, NULL));
1904     PetscCall(MatMult(In, cl, rcl));
1905     PetscCall(MatDestroy(&In));
1906     PetscCall(DMSetCoordinatesLocal(rdm, rcl));
1907     PetscCall(DMDestroy(&odm));
1908     odm = rdm;
1909   }
1910   *hdm = rdm;
1911   PetscFunctionReturn(PETSC_SUCCESS);
1912 }
1913 
1914 #if defined(PETSC_HAVE_EXODUSII)
1915   #include <exodusII.h>
1916   #include <petscviewerexodusii.h>
1917 #endif
1918 
1919 PetscErrorCode DMView_Plex(DM dm, PetscViewer viewer)
1920 {
1921   PetscBool iascii, ishdf5, isvtk, isdraw, flg, isglvis, isexodus, iscgns;
1922   char      name[PETSC_MAX_PATH_LEN];
1923 
1924   PetscFunctionBegin;
1925   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1926   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1927   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &iascii));
1928   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
1929   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1930   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
1931   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
1932   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodus));
1933   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
1934   if (iascii) {
1935     PetscViewerFormat format;
1936     PetscCall(PetscViewerGetFormat(viewer, &format));
1937     if (format == PETSC_VIEWER_ASCII_GLVIS) PetscCall(DMPlexView_GLVis(dm, viewer));
1938     else PetscCall(DMPlexView_Ascii(dm, viewer));
1939   } else if (ishdf5) {
1940 #if defined(PETSC_HAVE_HDF5)
1941     PetscCall(DMPlexView_HDF5_Internal(dm, viewer));
1942 #else
1943     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1944 #endif
1945   } else if (isvtk) {
1946     PetscCall(DMPlexVTKWriteAll((PetscObject)dm, viewer));
1947   } else if (isdraw) {
1948     DM hdm;
1949 
1950     PetscCall(DMPlexCreateHighOrderSurrogate_Internal(dm, &hdm));
1951     PetscCall(DMPlexView_Draw(hdm, viewer));
1952     PetscCall(DMDestroy(&hdm));
1953   } else if (isglvis) {
1954     PetscCall(DMPlexView_GLVis(dm, viewer));
1955 #if defined(PETSC_HAVE_EXODUSII)
1956   } else if (isexodus) {
1957     /*
1958       exodusII requires that all sets be part of exactly one cell set.
1959       If the dm does not have a "Cell Sets" label defined, we create one
1960       with ID 1, containing all cells.
1961       Note that if the Cell Sets label is defined but does not cover all cells,
1962       we may still have a problem. This should probably be checked here or in the viewer;
1963     */
1964     PetscInt numCS;
1965     PetscCall(DMGetLabelSize(dm, "Cell Sets", &numCS));
1966     if (!numCS) {
1967       PetscInt cStart, cEnd, c;
1968       PetscCall(DMCreateLabel(dm, "Cell Sets"));
1969       PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1970       for (c = cStart; c < cEnd; ++c) PetscCall(DMSetLabelValue(dm, "Cell Sets", c, 1));
1971     }
1972     PetscCall(DMView_PlexExodusII(dm, viewer));
1973 #endif
1974 #if defined(PETSC_HAVE_CGNS)
1975   } else if (iscgns) {
1976     PetscCall(DMView_PlexCGNS(dm, viewer));
1977 #endif
1978   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex writing", ((PetscObject)viewer)->type_name);
1979   /* Optionally view the partition */
1980   PetscCall(PetscOptionsHasName(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_partition_view", &flg));
1981   if (flg) {
1982     Vec ranks;
1983     PetscCall(DMPlexCreateRankField(dm, &ranks));
1984     PetscCall(VecView(ranks, viewer));
1985     PetscCall(VecDestroy(&ranks));
1986   }
1987   /* Optionally view a label */
1988   PetscCall(PetscOptionsGetString(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_label_view", name, sizeof(name), &flg));
1989   if (flg) {
1990     DMLabel label;
1991     Vec     val;
1992 
1993     PetscCall(DMGetLabel(dm, name, &label));
1994     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Label %s provided to -dm_label_view does not exist in this DM", name);
1995     PetscCall(DMPlexCreateLabelField(dm, label, &val));
1996     PetscCall(VecView(val, viewer));
1997     PetscCall(VecDestroy(&val));
1998   }
1999   PetscFunctionReturn(PETSC_SUCCESS);
2000 }
2001 
2002 /*@
2003   DMPlexTopologyView - Saves a `DMPLEX` topology into a file
2004 
2005   Collective
2006 
2007   Input Parameters:
2008 + dm     - The `DM` whose topology is to be saved
2009 - viewer - The `PetscViewer` to save it in
2010 
2011   Level: advanced
2012 
2013 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexTopologyLoad()`, `PetscViewer`
2014 @*/
2015 PetscErrorCode DMPlexTopologyView(DM dm, PetscViewer viewer)
2016 {
2017   PetscBool ishdf5;
2018 
2019   PetscFunctionBegin;
2020   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2021   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2022   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2023   PetscCall(PetscLogEventBegin(DMPLEX_TopologyView, viewer, 0, 0, 0));
2024   if (ishdf5) {
2025 #if defined(PETSC_HAVE_HDF5)
2026     PetscViewerFormat format;
2027     PetscCall(PetscViewerGetFormat(viewer, &format));
2028     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2029       IS globalPointNumbering;
2030 
2031       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
2032       PetscCall(DMPlexTopologyView_HDF5_Internal(dm, globalPointNumbering, viewer));
2033       PetscCall(ISDestroy(&globalPointNumbering));
2034     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
2035 #else
2036     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2037 #endif
2038   }
2039   PetscCall(PetscLogEventEnd(DMPLEX_TopologyView, viewer, 0, 0, 0));
2040   PetscFunctionReturn(PETSC_SUCCESS);
2041 }
2042 
2043 /*@
2044   DMPlexCoordinatesView - Saves `DMPLEX` coordinates into a file
2045 
2046   Collective
2047 
2048   Input Parameters:
2049 + dm     - The `DM` whose coordinates are to be saved
2050 - viewer - The `PetscViewer` for saving
2051 
2052   Level: advanced
2053 
2054 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexLabelsView()`, `DMPlexCoordinatesLoad()`, `PetscViewer`
2055 @*/
2056 PetscErrorCode DMPlexCoordinatesView(DM dm, PetscViewer viewer)
2057 {
2058   PetscBool ishdf5;
2059 
2060   PetscFunctionBegin;
2061   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2062   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2063   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2064   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
2065   if (ishdf5) {
2066 #if defined(PETSC_HAVE_HDF5)
2067     PetscViewerFormat format;
2068     PetscCall(PetscViewerGetFormat(viewer, &format));
2069     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2070       PetscCall(DMPlexCoordinatesView_HDF5_Internal(dm, viewer));
2071     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
2072 #else
2073     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2074 #endif
2075   }
2076   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
2077   PetscFunctionReturn(PETSC_SUCCESS);
2078 }
2079 
2080 /*@
2081   DMPlexLabelsView - Saves `DMPLEX` labels into a file
2082 
2083   Collective
2084 
2085   Input Parameters:
2086 + dm     - The `DM` whose labels are to be saved
2087 - viewer - The `PetscViewer` for saving
2088 
2089   Level: advanced
2090 
2091 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsLoad()`, `PetscViewer`
2092 @*/
2093 PetscErrorCode DMPlexLabelsView(DM dm, PetscViewer viewer)
2094 {
2095   PetscBool ishdf5;
2096 
2097   PetscFunctionBegin;
2098   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2099   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2100   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2101   PetscCall(PetscLogEventBegin(DMPLEX_LabelsView, viewer, 0, 0, 0));
2102   if (ishdf5) {
2103 #if defined(PETSC_HAVE_HDF5)
2104     IS                globalPointNumbering;
2105     PetscViewerFormat format;
2106 
2107     PetscCall(PetscViewerGetFormat(viewer, &format));
2108     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2109       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
2110       PetscCall(DMPlexLabelsView_HDF5_Internal(dm, globalPointNumbering, viewer));
2111       PetscCall(ISDestroy(&globalPointNumbering));
2112     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2113 #else
2114     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2115 #endif
2116   }
2117   PetscCall(PetscLogEventEnd(DMPLEX_LabelsView, viewer, 0, 0, 0));
2118   PetscFunctionReturn(PETSC_SUCCESS);
2119 }
2120 
2121 /*@
2122   DMPlexSectionView - Saves a section associated with a `DMPLEX`
2123 
2124   Collective
2125 
2126   Input Parameters:
2127 + dm        - The `DM` that contains the topology on which the section to be saved is defined
2128 . viewer    - The `PetscViewer` for saving
2129 - sectiondm - The `DM` that contains the section to be saved, can be `NULL`
2130 
2131   Level: advanced
2132 
2133   Notes:
2134   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.
2135 
2136   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.
2137 
2138 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`, `PetscSectionView()`, `DMPlexSectionLoad()`, `PetscViewer`
2139 @*/
2140 PetscErrorCode DMPlexSectionView(DM dm, PetscViewer viewer, DM sectiondm)
2141 {
2142   PetscBool ishdf5;
2143 
2144   PetscFunctionBegin;
2145   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2146   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2147   if (!sectiondm) sectiondm = dm;
2148   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2149   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2150   PetscCall(PetscLogEventBegin(DMPLEX_SectionView, viewer, 0, 0, 0));
2151   if (ishdf5) {
2152 #if defined(PETSC_HAVE_HDF5)
2153     PetscCall(DMPlexSectionView_HDF5_Internal(dm, viewer, sectiondm));
2154 #else
2155     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2156 #endif
2157   }
2158   PetscCall(PetscLogEventEnd(DMPLEX_SectionView, viewer, 0, 0, 0));
2159   PetscFunctionReturn(PETSC_SUCCESS);
2160 }
2161 
2162 /*@
2163   DMPlexGlobalVectorView - Saves a global vector
2164 
2165   Collective
2166 
2167   Input Parameters:
2168 + dm        - The `DM` that represents the topology
2169 . viewer    - The `PetscViewer` to save data with
2170 . sectiondm - The `DM` that contains the global section on which vec is defined, can be `NULL`
2171 - vec       - The global vector to be saved
2172 
2173   Level: advanced
2174 
2175   Notes:
2176   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.
2177 
2178   Calling sequence:
2179 .vb
2180        DMCreate(PETSC_COMM_WORLD, &dm);
2181        DMSetType(dm, DMPLEX);
2182        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2183        DMClone(dm, &sectiondm);
2184        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2185        PetscSectionCreate(PETSC_COMM_WORLD, &section);
2186        DMPlexGetChart(sectiondm, &pStart, &pEnd);
2187        PetscSectionSetChart(section, pStart, pEnd);
2188        PetscSectionSetUp(section);
2189        DMSetLocalSection(sectiondm, section);
2190        PetscSectionDestroy(&section);
2191        DMGetGlobalVector(sectiondm, &vec);
2192        PetscObjectSetName((PetscObject)vec, "vec_name");
2193        DMPlexTopologyView(dm, viewer);
2194        DMPlexSectionView(dm, viewer, sectiondm);
2195        DMPlexGlobalVectorView(dm, viewer, sectiondm, vec);
2196        DMRestoreGlobalVector(sectiondm, &vec);
2197        DMDestroy(&sectiondm);
2198        DMDestroy(&dm);
2199 .ve
2200 
2201 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexLocalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2202 @*/
2203 PetscErrorCode DMPlexGlobalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2204 {
2205   PetscBool ishdf5;
2206 
2207   PetscFunctionBegin;
2208   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2209   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2210   if (!sectiondm) sectiondm = dm;
2211   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2212   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2213   /* Check consistency */
2214   {
2215     PetscSection section;
2216     PetscBool    includesConstraints;
2217     PetscInt     m, m1;
2218 
2219     PetscCall(VecGetLocalSize(vec, &m1));
2220     PetscCall(DMGetGlobalSection(sectiondm, &section));
2221     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2222     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2223     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2224     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2225   }
2226   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2227   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorView, viewer, 0, 0, 0));
2228   if (ishdf5) {
2229 #if defined(PETSC_HAVE_HDF5)
2230     PetscCall(DMPlexGlobalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2231 #else
2232     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2233 #endif
2234   }
2235   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorView, viewer, 0, 0, 0));
2236   PetscFunctionReturn(PETSC_SUCCESS);
2237 }
2238 
2239 /*@
2240   DMPlexLocalVectorView - Saves a local vector
2241 
2242   Collective
2243 
2244   Input Parameters:
2245 + dm        - The `DM` that represents the topology
2246 . viewer    - The `PetscViewer` to save data with
2247 . sectiondm - The `DM` that contains the local section on which `vec` is defined, can be `NULL`
2248 - vec       - The local vector to be saved
2249 
2250   Level: advanced
2251 
2252   Note:
2253   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.
2254 
2255   Calling sequence:
2256 .vb
2257        DMCreate(PETSC_COMM_WORLD, &dm);
2258        DMSetType(dm, DMPLEX);
2259        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2260        DMClone(dm, &sectiondm);
2261        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2262        PetscSectionCreate(PETSC_COMM_WORLD, &section);
2263        DMPlexGetChart(sectiondm, &pStart, &pEnd);
2264        PetscSectionSetChart(section, pStart, pEnd);
2265        PetscSectionSetUp(section);
2266        DMSetLocalSection(sectiondm, section);
2267        DMGetLocalVector(sectiondm, &vec);
2268        PetscObjectSetName((PetscObject)vec, "vec_name");
2269        DMPlexTopologyView(dm, viewer);
2270        DMPlexSectionView(dm, viewer, sectiondm);
2271        DMPlexLocalVectorView(dm, viewer, sectiondm, vec);
2272        DMRestoreLocalVector(sectiondm, &vec);
2273        DMDestroy(&sectiondm);
2274        DMDestroy(&dm);
2275 .ve
2276 
2277 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexGlobalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2278 @*/
2279 PetscErrorCode DMPlexLocalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2280 {
2281   PetscBool ishdf5;
2282 
2283   PetscFunctionBegin;
2284   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2285   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2286   if (!sectiondm) sectiondm = dm;
2287   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2288   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2289   /* Check consistency */
2290   {
2291     PetscSection section;
2292     PetscBool    includesConstraints;
2293     PetscInt     m, m1;
2294 
2295     PetscCall(VecGetLocalSize(vec, &m1));
2296     PetscCall(DMGetLocalSection(sectiondm, &section));
2297     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2298     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2299     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2300     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2301   }
2302   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2303   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorView, viewer, 0, 0, 0));
2304   if (ishdf5) {
2305 #if defined(PETSC_HAVE_HDF5)
2306     PetscCall(DMPlexLocalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2307 #else
2308     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2309 #endif
2310   }
2311   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorView, viewer, 0, 0, 0));
2312   PetscFunctionReturn(PETSC_SUCCESS);
2313 }
2314 
2315 PetscErrorCode DMLoad_Plex(DM dm, PetscViewer viewer)
2316 {
2317   PetscBool ishdf5;
2318 
2319   PetscFunctionBegin;
2320   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2321   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2322   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2323   if (ishdf5) {
2324 #if defined(PETSC_HAVE_HDF5)
2325     PetscViewerFormat format;
2326     PetscCall(PetscViewerGetFormat(viewer, &format));
2327     if (format == PETSC_VIEWER_HDF5_XDMF || format == PETSC_VIEWER_HDF5_VIZ) {
2328       PetscCall(DMPlexLoad_HDF5_Xdmf_Internal(dm, viewer));
2329     } else if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2330       PetscCall(DMPlexLoad_HDF5_Internal(dm, viewer));
2331     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2332     PetscFunctionReturn(PETSC_SUCCESS);
2333 #else
2334     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2335 #endif
2336   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex loading", ((PetscObject)viewer)->type_name);
2337 }
2338 
2339 /*@
2340   DMPlexTopologyLoad - Loads a topology into a `DMPLEX`
2341 
2342   Collective
2343 
2344   Input Parameters:
2345 + dm     - The `DM` into which the topology is loaded
2346 - viewer - The `PetscViewer` for the saved topology
2347 
2348   Output Parameter:
2349 . globalToLocalPointSF - The `PetscSF` that pushes points in [0, N) to the associated points in the loaded `DMPLEX`, where N is the global number of points; `NULL` if unneeded
2350 
2351   Level: advanced
2352 
2353 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2354           `PetscViewer`, `PetscSF`
2355 @*/
2356 PetscErrorCode DMPlexTopologyLoad(DM dm, PetscViewer viewer, PetscSF *globalToLocalPointSF)
2357 {
2358   PetscBool ishdf5;
2359 
2360   PetscFunctionBegin;
2361   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2362   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2363   if (globalToLocalPointSF) PetscAssertPointer(globalToLocalPointSF, 3);
2364   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2365   PetscCall(PetscLogEventBegin(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2366   if (ishdf5) {
2367 #if defined(PETSC_HAVE_HDF5)
2368     PetscViewerFormat format;
2369     PetscCall(PetscViewerGetFormat(viewer, &format));
2370     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2371       PetscCall(DMPlexTopologyLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2372     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2373 #else
2374     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2375 #endif
2376   }
2377   PetscCall(PetscLogEventEnd(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2378   PetscFunctionReturn(PETSC_SUCCESS);
2379 }
2380 
2381 /*@
2382   DMPlexCoordinatesLoad - Loads coordinates into a `DMPLEX`
2383 
2384   Collective
2385 
2386   Input Parameters:
2387 + dm                   - The `DM` into which the coordinates are loaded
2388 . viewer               - The `PetscViewer` for the saved coordinates
2389 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad()` when loading dm from viewer
2390 
2391   Level: advanced
2392 
2393 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2394           `PetscSF`, `PetscViewer`
2395 @*/
2396 PetscErrorCode DMPlexCoordinatesLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2397 {
2398   PetscBool ishdf5;
2399 
2400   PetscFunctionBegin;
2401   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2402   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2403   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2404   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2405   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesLoad, viewer, 0, 0, 0));
2406   if (ishdf5) {
2407 #if defined(PETSC_HAVE_HDF5)
2408     PetscViewerFormat format;
2409     PetscCall(PetscViewerGetFormat(viewer, &format));
2410     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2411       PetscCall(DMPlexCoordinatesLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2412     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2413 #else
2414     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2415 #endif
2416   }
2417   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesLoad, viewer, 0, 0, 0));
2418   PetscFunctionReturn(PETSC_SUCCESS);
2419 }
2420 
2421 /*@
2422   DMPlexLabelsLoad - Loads labels into a `DMPLEX`
2423 
2424   Collective
2425 
2426   Input Parameters:
2427 + dm                   - The `DM` into which the labels are loaded
2428 . viewer               - The `PetscViewer` for the saved labels
2429 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad()` when loading `dm` from viewer
2430 
2431   Level: advanced
2432 
2433   Note:
2434   The `PetscSF` argument must not be NULL if the `DM` is distributed, otherwise an error occurs.
2435 
2436 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2437           `PetscSF`, `PetscViewer`
2438 @*/
2439 PetscErrorCode DMPlexLabelsLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2440 {
2441   PetscBool ishdf5;
2442 
2443   PetscFunctionBegin;
2444   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2445   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2446   if (globalToLocalPointSF) PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2447   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2448   PetscCall(PetscLogEventBegin(DMPLEX_LabelsLoad, viewer, 0, 0, 0));
2449   if (ishdf5) {
2450 #if defined(PETSC_HAVE_HDF5)
2451     PetscViewerFormat format;
2452 
2453     PetscCall(PetscViewerGetFormat(viewer, &format));
2454     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2455       PetscCall(DMPlexLabelsLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2456     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2457 #else
2458     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2459 #endif
2460   }
2461   PetscCall(PetscLogEventEnd(DMPLEX_LabelsLoad, viewer, 0, 0, 0));
2462   PetscFunctionReturn(PETSC_SUCCESS);
2463 }
2464 
2465 /*@
2466   DMPlexSectionLoad - Loads section into a `DMPLEX`
2467 
2468   Collective
2469 
2470   Input Parameters:
2471 + dm                   - The `DM` that represents the topology
2472 . viewer               - The `PetscViewer` that represents the on-disk section (sectionA)
2473 . sectiondm            - The `DM` into which the on-disk section (sectionA) is migrated, can be `NULL`
2474 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad(`) when loading dm from viewer
2475 
2476   Output Parameters:
2477 + 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)
2478 - 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)
2479 
2480   Level: advanced
2481 
2482   Notes:
2483   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.
2484 
2485   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.
2486 
2487   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.
2488 
2489   Example using 2 processes:
2490 .vb
2491   NX (number of points on dm): 4
2492   sectionA                   : the on-disk section
2493   vecA                       : a vector associated with sectionA
2494   sectionB                   : sectiondm's local section constructed in this function
2495   vecB (local)               : a vector associated with sectiondm's local section
2496   vecB (global)              : a vector associated with sectiondm's global section
2497 
2498                                      rank 0    rank 1
2499   vecA (global)                  : [.0 .4 .1 | .2 .3]        <- to be loaded in DMPlexGlobalVectorLoad() or DMPlexLocalVectorLoad()
2500   sectionA->atlasOff             :       0 2 | 1             <- loaded in PetscSectionLoad()
2501   sectionA->atlasDof             :       1 3 | 1             <- loaded in PetscSectionLoad()
2502   sectionA's global point numbers:       0 2 | 3             <- loaded in DMPlexSectionLoad()
2503   [0, NX)                        :       0 1 | 2 3           <- conceptual partition used in globalToLocalPointSF
2504   sectionB's global point numbers:     0 1 3 | 3 2           <- associated with [0, NX) by globalToLocalPointSF
2505   sectionB->atlasDof             :     1 0 1 | 1 3
2506   sectionB->atlasOff (no perm)   :     0 1 1 | 0 1
2507   vecB (local)                   :   [.0 .4] | [.4 .1 .2 .3] <- to be constructed by calling DMPlexLocalVectorLoad() with localDofSF
2508   vecB (global)                  :    [.0 .4 | .1 .2 .3]     <- to be constructed by calling DMPlexGlobalVectorLoad() with globalDofSF
2509 .ve
2510   where "|" represents a partition of loaded data, and global point 3 is assumed to be owned by rank 0.
2511 
2512 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`, `PetscSectionLoad()`, `DMPlexSectionView()`, `PetscSF`, `PetscViewer`
2513 @*/
2514 PetscErrorCode DMPlexSectionLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF globalToLocalPointSF, PetscSF *globalDofSF, PetscSF *localDofSF)
2515 {
2516   PetscBool ishdf5;
2517 
2518   PetscFunctionBegin;
2519   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2520   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2521   if (!sectiondm) sectiondm = dm;
2522   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2523   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 4);
2524   if (globalDofSF) PetscAssertPointer(globalDofSF, 5);
2525   if (localDofSF) PetscAssertPointer(localDofSF, 6);
2526   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2527   PetscCall(PetscLogEventBegin(DMPLEX_SectionLoad, viewer, 0, 0, 0));
2528   if (ishdf5) {
2529 #if defined(PETSC_HAVE_HDF5)
2530     PetscCall(DMPlexSectionLoad_HDF5_Internal(dm, viewer, sectiondm, globalToLocalPointSF, globalDofSF, localDofSF));
2531 #else
2532     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2533 #endif
2534   }
2535   PetscCall(PetscLogEventEnd(DMPLEX_SectionLoad, viewer, 0, 0, 0));
2536   PetscFunctionReturn(PETSC_SUCCESS);
2537 }
2538 
2539 /*@
2540   DMPlexGlobalVectorLoad - Loads on-disk vector data into a global vector
2541 
2542   Collective
2543 
2544   Input Parameters:
2545 + dm        - The `DM` that represents the topology
2546 . viewer    - The `PetscViewer` that represents the on-disk vector data
2547 . sectiondm - The `DM` that contains the global section on which vec is defined, can be `NULL`
2548 . sf        - The `PetscSF` that migrates the on-disk vector data into vec
2549 - vec       - The global vector to set values of
2550 
2551   Level: advanced
2552 
2553   Notes:
2554   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.
2555 
2556   Calling sequence:
2557 .vb
2558        DMCreate(PETSC_COMM_WORLD, &dm);
2559        DMSetType(dm, DMPLEX);
2560        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2561        DMPlexTopologyLoad(dm, viewer, &sfX);
2562        DMClone(dm, &sectiondm);
2563        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2564        DMPlexSectionLoad(dm, viewer, sectiondm, sfX, &gsf, NULL);
2565        DMGetGlobalVector(sectiondm, &vec);
2566        PetscObjectSetName((PetscObject)vec, "vec_name");
2567        DMPlexGlobalVectorLoad(dm, viewer, sectiondm, gsf, vec);
2568        DMRestoreGlobalVector(sectiondm, &vec);
2569        PetscSFDestroy(&gsf);
2570        PetscSFDestroy(&sfX);
2571        DMDestroy(&sectiondm);
2572        DMDestroy(&dm);
2573 .ve
2574 
2575 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexLocalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`,
2576           `PetscSF`, `PetscViewer`
2577 @*/
2578 PetscErrorCode DMPlexGlobalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2579 {
2580   PetscBool ishdf5;
2581 
2582   PetscFunctionBegin;
2583   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2584   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2585   if (!sectiondm) sectiondm = dm;
2586   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2587   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2588   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2589   /* Check consistency */
2590   {
2591     PetscSection section;
2592     PetscBool    includesConstraints;
2593     PetscInt     m, m1;
2594 
2595     PetscCall(VecGetLocalSize(vec, &m1));
2596     PetscCall(DMGetGlobalSection(sectiondm, &section));
2597     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2598     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2599     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2600     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2601   }
2602   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2603   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorLoad, viewer, 0, 0, 0));
2604   if (ishdf5) {
2605 #if defined(PETSC_HAVE_HDF5)
2606     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2607 #else
2608     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2609 #endif
2610   }
2611   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorLoad, viewer, 0, 0, 0));
2612   PetscFunctionReturn(PETSC_SUCCESS);
2613 }
2614 
2615 /*@
2616   DMPlexLocalVectorLoad - Loads on-disk vector data into a local vector
2617 
2618   Collective
2619 
2620   Input Parameters:
2621 + dm        - The `DM` that represents the topology
2622 . viewer    - The `PetscViewer` that represents the on-disk vector data
2623 . sectiondm - The `DM` that contains the local section on which vec is defined, can be `NULL`
2624 . sf        - The `PetscSF` that migrates the on-disk vector data into vec
2625 - vec       - The local vector to set values of
2626 
2627   Level: advanced
2628 
2629   Notes:
2630   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.
2631 
2632   Calling sequence:
2633 .vb
2634        DMCreate(PETSC_COMM_WORLD, &dm);
2635        DMSetType(dm, DMPLEX);
2636        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2637        DMPlexTopologyLoad(dm, viewer, &sfX);
2638        DMClone(dm, &sectiondm);
2639        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2640        DMPlexSectionLoad(dm, viewer, sectiondm, sfX, NULL, &lsf);
2641        DMGetLocalVector(sectiondm, &vec);
2642        PetscObjectSetName((PetscObject)vec, "vec_name");
2643        DMPlexLocalVectorLoad(dm, viewer, sectiondm, lsf, vec);
2644        DMRestoreLocalVector(sectiondm, &vec);
2645        PetscSFDestroy(&lsf);
2646        PetscSFDestroy(&sfX);
2647        DMDestroy(&sectiondm);
2648        DMDestroy(&dm);
2649 .ve
2650 
2651 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`,
2652           `PetscSF`, `PetscViewer`
2653 @*/
2654 PetscErrorCode DMPlexLocalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2655 {
2656   PetscBool ishdf5;
2657 
2658   PetscFunctionBegin;
2659   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2660   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2661   if (!sectiondm) sectiondm = dm;
2662   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2663   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2664   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2665   /* Check consistency */
2666   {
2667     PetscSection section;
2668     PetscBool    includesConstraints;
2669     PetscInt     m, m1;
2670 
2671     PetscCall(VecGetLocalSize(vec, &m1));
2672     PetscCall(DMGetLocalSection(sectiondm, &section));
2673     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2674     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2675     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2676     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2677   }
2678   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2679   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorLoad, viewer, 0, 0, 0));
2680   if (ishdf5) {
2681 #if defined(PETSC_HAVE_HDF5)
2682     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2683 #else
2684     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2685 #endif
2686   }
2687   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorLoad, viewer, 0, 0, 0));
2688   PetscFunctionReturn(PETSC_SUCCESS);
2689 }
2690 
2691 PetscErrorCode DMDestroy_Plex(DM dm)
2692 {
2693   DM_Plex *mesh = (DM_Plex *)dm->data;
2694 
2695   PetscFunctionBegin;
2696   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMSetUpGLVisViewer_C", NULL));
2697   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertBoundaryValues_C", NULL));
2698   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMCreateNeumannOverlap_C", NULL));
2699   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMInterpolateSolution_C", NULL));
2700   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertTimeDerivativeBoundaryValues_C", NULL));
2701   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", NULL));
2702   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeGetDefault_C", NULL));
2703   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeSetDefault_C", NULL));
2704   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "MatComputeNeumannOverlap_C", NULL));
2705   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderGetDefault_C", NULL));
2706   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderSetDefault_C", NULL));
2707   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionGetDefault_C", NULL));
2708   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionSetDefault_C", NULL));
2709   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionGetType_C", NULL));
2710   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionSetType_C", NULL));
2711   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", NULL));
2712   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexSetOverlap_C", NULL));
2713   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetUseCeed_C", NULL));
2714   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexSetUseCeed_C", NULL));
2715   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMGetIsoperiodicPointSF_C", NULL));
2716   if (--mesh->refct > 0) PetscFunctionReturn(PETSC_SUCCESS);
2717   PetscCall(PetscSectionDestroy(&mesh->coneSection));
2718   PetscCall(PetscFree(mesh->cones));
2719   PetscCall(PetscFree(mesh->coneOrientations));
2720   PetscCall(PetscSectionDestroy(&mesh->supportSection));
2721   PetscCall(PetscSectionDestroy(&mesh->subdomainSection));
2722   PetscCall(PetscFree(mesh->supports));
2723   PetscCall(PetscFree(mesh->cellTypes));
2724   PetscCall(DMPlexTransformDestroy(&mesh->tr));
2725   PetscCall(PetscFree(mesh->tetgenOpts));
2726   PetscCall(PetscFree(mesh->triangleOpts));
2727   PetscCall(PetscFree(mesh->transformType));
2728   PetscCall(PetscFree(mesh->distributionName));
2729   PetscCall(PetscPartitionerDestroy(&mesh->partitioner));
2730   PetscCall(DMLabelDestroy(&mesh->subpointMap));
2731   PetscCall(ISDestroy(&mesh->subpointIS));
2732   PetscCall(ISDestroy(&mesh->globalVertexNumbers));
2733   PetscCall(ISDestroy(&mesh->globalCellNumbers));
2734   if (mesh->periodic.face_sfs) {
2735     for (PetscInt i = 0; i < mesh->periodic.num_face_sfs; i++) PetscCall(PetscSFDestroy(&mesh->periodic.face_sfs[i]));
2736     PetscCall(PetscFree(mesh->periodic.face_sfs));
2737   }
2738   PetscCall(PetscSFDestroy(&mesh->periodic.composed_sf));
2739   if (mesh->periodic.periodic_points) {
2740     for (PetscInt i = 0; i < mesh->periodic.num_face_sfs; i++) PetscCall(ISDestroy(&mesh->periodic.periodic_points[i]));
2741     PetscCall(PetscFree(mesh->periodic.periodic_points));
2742   }
2743   if (mesh->periodic.transform) PetscCall(PetscFree(mesh->periodic.transform));
2744   PetscCall(PetscSectionDestroy(&mesh->anchorSection));
2745   PetscCall(ISDestroy(&mesh->anchorIS));
2746   PetscCall(PetscSectionDestroy(&mesh->parentSection));
2747   PetscCall(PetscFree(mesh->parents));
2748   PetscCall(PetscFree(mesh->childIDs));
2749   PetscCall(PetscSectionDestroy(&mesh->childSection));
2750   PetscCall(PetscFree(mesh->children));
2751   PetscCall(DMDestroy(&mesh->referenceTree));
2752   PetscCall(PetscGridHashDestroy(&mesh->lbox));
2753   PetscCall(PetscFree(mesh->neighbors));
2754   if (mesh->metricCtx) PetscCall(PetscFree(mesh->metricCtx));
2755   /* This was originally freed in DMDestroy(), but that prevents reference counting of backend objects */
2756   PetscCall(PetscFree(mesh));
2757   PetscFunctionReturn(PETSC_SUCCESS);
2758 }
2759 
2760 PetscErrorCode DMCreateMatrix_Plex(DM dm, Mat *J)
2761 {
2762   PetscSection           sectionGlobal, sectionLocal;
2763   PetscInt               bs = -1, mbs;
2764   PetscInt               localSize, localStart = 0;
2765   PetscBool              isShell, isBlock, isSeqBlock, isMPIBlock, isSymBlock, isSymSeqBlock, isSymMPIBlock, isMatIS;
2766   MatType                mtype;
2767   ISLocalToGlobalMapping ltog;
2768 
2769   PetscFunctionBegin;
2770   PetscCall(MatInitializePackage());
2771   mtype = dm->mattype;
2772   PetscCall(DMGetLocalSection(dm, &sectionLocal));
2773   PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
2774   /* PetscCall(PetscSectionGetStorageSize(sectionGlobal, &localSize)); */
2775   PetscCall(PetscSectionGetConstrainedStorageSize(sectionGlobal, &localSize));
2776   PetscCallMPI(MPI_Exscan(&localSize, &localStart, 1, MPIU_INT, MPI_SUM, PetscObjectComm((PetscObject)dm)));
2777   PetscCall(MatCreate(PetscObjectComm((PetscObject)dm), J));
2778   PetscCall(MatSetSizes(*J, localSize, localSize, PETSC_DETERMINE, PETSC_DETERMINE));
2779   PetscCall(MatSetType(*J, mtype));
2780   PetscCall(MatSetFromOptions(*J));
2781   PetscCall(MatGetBlockSize(*J, &mbs));
2782   if (mbs > 1) bs = mbs;
2783   PetscCall(PetscStrcmp(mtype, MATSHELL, &isShell));
2784   PetscCall(PetscStrcmp(mtype, MATBAIJ, &isBlock));
2785   PetscCall(PetscStrcmp(mtype, MATSEQBAIJ, &isSeqBlock));
2786   PetscCall(PetscStrcmp(mtype, MATMPIBAIJ, &isMPIBlock));
2787   PetscCall(PetscStrcmp(mtype, MATSBAIJ, &isSymBlock));
2788   PetscCall(PetscStrcmp(mtype, MATSEQSBAIJ, &isSymSeqBlock));
2789   PetscCall(PetscStrcmp(mtype, MATMPISBAIJ, &isSymMPIBlock));
2790   PetscCall(PetscStrcmp(mtype, MATIS, &isMatIS));
2791   if (!isShell) {
2792     PetscBool fillMatrix = (PetscBool)(!dm->prealloc_only && !isMatIS);
2793     PetscInt *dnz, *onz, *dnzu, *onzu, bsLocal[2], bsMinMax[2], *pblocks;
2794     PetscInt  pStart, pEnd, p, dof, cdof, num_fields;
2795 
2796     PetscCall(DMGetLocalToGlobalMapping(dm, &ltog));
2797 
2798     PetscCall(PetscCalloc1(localSize, &pblocks));
2799     PetscCall(PetscSectionGetChart(sectionGlobal, &pStart, &pEnd));
2800     PetscCall(PetscSectionGetNumFields(sectionGlobal, &num_fields));
2801     for (p = pStart; p < pEnd; ++p) {
2802       switch (dm->blocking_type) {
2803       case DM_BLOCKING_TOPOLOGICAL_POINT: { // One block per topological point
2804         PetscInt bdof, offset;
2805 
2806         PetscCall(PetscSectionGetDof(sectionGlobal, p, &dof));
2807         PetscCall(PetscSectionGetOffset(sectionGlobal, p, &offset));
2808         PetscCall(PetscSectionGetConstraintDof(sectionGlobal, p, &cdof));
2809         for (PetscInt i = 0; i < dof - cdof; ++i) pblocks[offset - localStart + i] = dof - cdof;
2810         // Signal block concatenation
2811         if (dof - cdof && sectionLocal->blockStarts && !PetscBTLookup(sectionLocal->blockStarts, p)) pblocks[offset - localStart] = -(dof - cdof);
2812         dof  = dof < 0 ? -(dof + 1) : dof;
2813         bdof = cdof && (dof - cdof) ? 1 : dof;
2814         if (dof) {
2815           if (bs < 0) {
2816             bs = bdof;
2817           } else if (bs != bdof) {
2818             bs = 1;
2819           }
2820         }
2821       } break;
2822       case DM_BLOCKING_FIELD_NODE: {
2823         for (PetscInt field = 0; field < num_fields; field++) {
2824           PetscInt num_comp, bdof, offset;
2825           PetscCall(PetscSectionGetFieldComponents(sectionGlobal, field, &num_comp));
2826           PetscCall(PetscSectionGetFieldDof(sectionGlobal, p, field, &dof));
2827           if (dof < 0) continue;
2828           PetscCall(PetscSectionGetFieldOffset(sectionGlobal, p, field, &offset));
2829           PetscCall(PetscSectionGetFieldConstraintDof(sectionGlobal, p, field, &cdof));
2830           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);
2831           PetscInt num_nodes = dof / num_comp;
2832           for (PetscInt i = 0; i < dof - cdof; i++) pblocks[offset - localStart + i] = (dof - cdof) / num_nodes;
2833           // Handle possibly constant block size (unlikely)
2834           bdof = cdof && (dof - cdof) ? 1 : dof;
2835           if (dof) {
2836             if (bs < 0) {
2837               bs = bdof;
2838             } else if (bs != bdof) {
2839               bs = 1;
2840             }
2841           }
2842         }
2843       } break;
2844       }
2845     }
2846     /* Must have same blocksize on all procs (some might have no points) */
2847     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs;
2848     bsLocal[1] = bs;
2849     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
2850     if (bsMinMax[0] != bsMinMax[1]) bs = 1;
2851     else bs = bsMinMax[0];
2852     bs = PetscMax(1, bs);
2853     PetscCall(MatSetLocalToGlobalMapping(*J, ltog, ltog));
2854     if (dm->prealloc_skip) { // User will likely use MatSetPreallocationCOO(), but still set structural parameters
2855       PetscCall(MatSetBlockSize(*J, bs));
2856       PetscCall(MatSetUp(*J));
2857     } else {
2858       PetscCall(PetscCalloc4(localSize / bs, &dnz, localSize / bs, &onz, localSize / bs, &dnzu, localSize / bs, &onzu));
2859       PetscCall(DMPlexPreallocateOperator(dm, bs, dnz, onz, dnzu, onzu, *J, fillMatrix));
2860       PetscCall(PetscFree4(dnz, onz, dnzu, onzu));
2861     }
2862     { // Consolidate blocks
2863       PetscInt nblocks = 0;
2864       for (PetscInt i = 0; i < localSize; i += PetscMax(1, pblocks[i])) {
2865         if (pblocks[i] == 0) continue;
2866         // Negative block size indicates the blocks should be concatenated
2867         if (pblocks[i] < 0) {
2868           pblocks[i] = -pblocks[i];
2869           pblocks[nblocks - 1] += pblocks[i];
2870         } else {
2871           pblocks[nblocks++] = pblocks[i]; // nblocks always <= i
2872         }
2873         for (PetscInt j = 1; j < pblocks[i]; j++) PetscCheck(pblocks[i + j] == pblocks[i], PETSC_COMM_SELF, PETSC_ERR_PLIB, "Block of size %" PetscInt_FMT " mismatches entry %" PetscInt_FMT, pblocks[i], pblocks[i + j]);
2874       }
2875       PetscCall(MatSetVariableBlockSizes(*J, nblocks, pblocks));
2876     }
2877     PetscCall(PetscFree(pblocks));
2878   }
2879   PetscCall(MatSetDM(*J, dm));
2880   PetscFunctionReturn(PETSC_SUCCESS);
2881 }
2882 
2883 /*@
2884   DMPlexGetSubdomainSection - Returns the section associated with the subdomain
2885 
2886   Not Collective
2887 
2888   Input Parameter:
2889 . dm - The `DMPLEX`
2890 
2891   Output Parameter:
2892 . subsection - The subdomain section
2893 
2894   Level: developer
2895 
2896 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `PetscSection`
2897 @*/
2898 PetscErrorCode DMPlexGetSubdomainSection(DM dm, PetscSection *subsection)
2899 {
2900   DM_Plex *mesh = (DM_Plex *)dm->data;
2901 
2902   PetscFunctionBegin;
2903   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2904   if (!mesh->subdomainSection) {
2905     PetscSection section;
2906     PetscSF      sf;
2907 
2908     PetscCall(PetscSFCreate(PETSC_COMM_SELF, &sf));
2909     PetscCall(DMGetLocalSection(dm, &section));
2910     PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_TRUE, PETSC_FALSE, PETSC_TRUE, &mesh->subdomainSection));
2911     PetscCall(PetscSFDestroy(&sf));
2912   }
2913   *subsection = mesh->subdomainSection;
2914   PetscFunctionReturn(PETSC_SUCCESS);
2915 }
2916 
2917 /*@
2918   DMPlexGetChart - Return the interval for all mesh points [`pStart`, `pEnd`)
2919 
2920   Not Collective
2921 
2922   Input Parameter:
2923 . dm - The `DMPLEX`
2924 
2925   Output Parameters:
2926 + pStart - The first mesh point
2927 - pEnd   - The upper bound for mesh points
2928 
2929   Level: beginner
2930 
2931 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetChart()`
2932 @*/
2933 PetscErrorCode DMPlexGetChart(DM dm, PetscInt *pStart, PetscInt *pEnd)
2934 {
2935   DM_Plex *mesh = (DM_Plex *)dm->data;
2936 
2937   PetscFunctionBegin;
2938   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2939   if (mesh->tr) PetscCall(DMPlexTransformGetChart(mesh->tr, pStart, pEnd));
2940   else PetscCall(PetscSectionGetChart(mesh->coneSection, pStart, pEnd));
2941   PetscFunctionReturn(PETSC_SUCCESS);
2942 }
2943 
2944 /*@
2945   DMPlexSetChart - Set the interval for all mesh points [`pStart`, `pEnd`)
2946 
2947   Not Collective
2948 
2949   Input Parameters:
2950 + dm     - The `DMPLEX`
2951 . pStart - The first mesh point
2952 - pEnd   - The upper bound for mesh points
2953 
2954   Level: beginner
2955 
2956 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetChart()`
2957 @*/
2958 PetscErrorCode DMPlexSetChart(DM dm, PetscInt pStart, PetscInt pEnd)
2959 {
2960   DM_Plex *mesh = (DM_Plex *)dm->data;
2961 
2962   PetscFunctionBegin;
2963   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2964   PetscCall(PetscSectionSetChart(mesh->coneSection, pStart, pEnd));
2965   PetscCall(PetscSectionSetChart(mesh->supportSection, pStart, pEnd));
2966   PetscCall(PetscFree(mesh->cellTypes));
2967   PetscFunctionReturn(PETSC_SUCCESS);
2968 }
2969 
2970 /*@
2971   DMPlexGetConeSize - Return the number of in-edges for this point in the DAG
2972 
2973   Not Collective
2974 
2975   Input Parameters:
2976 + dm - The `DMPLEX`
2977 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
2978 
2979   Output Parameter:
2980 . size - The cone size for point `p`
2981 
2982   Level: beginner
2983 
2984 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
2985 @*/
2986 PetscErrorCode DMPlexGetConeSize(DM dm, PetscInt p, PetscInt *size)
2987 {
2988   DM_Plex *mesh = (DM_Plex *)dm->data;
2989 
2990   PetscFunctionBegin;
2991   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2992   PetscAssertPointer(size, 3);
2993   if (mesh->tr) PetscCall(DMPlexTransformGetConeSize(mesh->tr, p, size));
2994   else PetscCall(PetscSectionGetDof(mesh->coneSection, p, size));
2995   PetscFunctionReturn(PETSC_SUCCESS);
2996 }
2997 
2998 /*@
2999   DMPlexSetConeSize - Set the number of in-edges for this point in the DAG
3000 
3001   Not Collective
3002 
3003   Input Parameters:
3004 + dm   - The `DMPLEX`
3005 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3006 - size - The cone size for point `p`
3007 
3008   Level: beginner
3009 
3010   Note:
3011   This should be called after `DMPlexSetChart()`.
3012 
3013 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetConeSize()`, `DMPlexSetChart()`
3014 @*/
3015 PetscErrorCode DMPlexSetConeSize(DM dm, PetscInt p, PetscInt size)
3016 {
3017   DM_Plex *mesh = (DM_Plex *)dm->data;
3018 
3019   PetscFunctionBegin;
3020   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3021   PetscCheck(!mesh->tr, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Cannot call DMPlexSetConeSize() on a mesh with a transform defined.");
3022   PetscCall(PetscSectionSetDof(mesh->coneSection, p, size));
3023   PetscFunctionReturn(PETSC_SUCCESS);
3024 }
3025 
3026 /*@C
3027   DMPlexGetCone - Return the points on the in-edges for this point in the DAG
3028 
3029   Not Collective
3030 
3031   Input Parameters:
3032 + dm - The `DMPLEX`
3033 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3034 
3035   Output Parameter:
3036 . cone - An array of points which are on the in-edges for point `p`
3037 
3038   Level: beginner
3039 
3040   Fortran Notes:
3041   You must also call `DMPlexRestoreCone()` after you finish using the returned array.
3042   `DMPlexRestoreCone()` is not needed/available in C.
3043 
3044 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSize()`, `DMPlexSetCone()`, `DMPlexGetConeTuple()`, `DMPlexSetChart()`, `DMPlexRestoreCone()`
3045 @*/
3046 PetscErrorCode DMPlexGetCone(DM dm, PetscInt p, const PetscInt *cone[])
3047 {
3048   DM_Plex *mesh = (DM_Plex *)dm->data;
3049   PetscInt off;
3050 
3051   PetscFunctionBegin;
3052   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3053   PetscAssertPointer(cone, 3);
3054   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3055   *cone = PetscSafePointerPlusOffset(mesh->cones, off);
3056   PetscFunctionReturn(PETSC_SUCCESS);
3057 }
3058 
3059 /*@C
3060   DMPlexGetConeTuple - Return the points on the in-edges of several points in the DAG
3061 
3062   Not Collective
3063 
3064   Input Parameters:
3065 + dm - The `DMPLEX`
3066 - p  - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3067 
3068   Output Parameters:
3069 + pConesSection - `PetscSection` describing the layout of `pCones`
3070 - pCones        - An array of points which are on the in-edges for the point set `p`
3071 
3072   Level: intermediate
3073 
3074 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeRecursive()`, `DMPlexSetChart()`, `PetscSection`, `IS`
3075 @*/
3076 PetscErrorCode DMPlexGetConeTuple(DM dm, IS p, PetscSection *pConesSection, IS *pCones)
3077 {
3078   PetscSection cs, newcs;
3079   PetscInt    *cones;
3080   PetscInt    *newarr = NULL;
3081   PetscInt     n;
3082 
3083   PetscFunctionBegin;
3084   PetscCall(DMPlexGetCones(dm, &cones));
3085   PetscCall(DMPlexGetConeSection(dm, &cs));
3086   PetscCall(PetscSectionExtractDofsFromArray(cs, MPIU_INT, cones, p, &newcs, pCones ? ((void **)&newarr) : NULL));
3087   if (pConesSection) *pConesSection = newcs;
3088   if (pCones) {
3089     PetscCall(PetscSectionGetStorageSize(newcs, &n));
3090     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)p), n, newarr, PETSC_OWN_POINTER, pCones));
3091   }
3092   PetscFunctionReturn(PETSC_SUCCESS);
3093 }
3094 
3095 /*@
3096   DMPlexGetConeRecursiveVertices - Expand each given point into its cone points and do that recursively until we end up just with vertices.
3097 
3098   Not Collective
3099 
3100   Input Parameters:
3101 + dm     - The `DMPLEX`
3102 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3103 
3104   Output Parameter:
3105 . expandedPoints - An array of vertices recursively expanded from input points
3106 
3107   Level: advanced
3108 
3109   Notes:
3110   Like `DMPlexGetConeRecursive()` but returns only the 0-depth `IS` (i.e. vertices only) and no sections.
3111 
3112   There is no corresponding Restore function, just call `ISDestroy()` on the returned `IS` to deallocate.
3113 
3114 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexRestoreConeRecursive()`,
3115           `DMPlexGetDepth()`, `IS`
3116 @*/
3117 PetscErrorCode DMPlexGetConeRecursiveVertices(DM dm, IS points, IS *expandedPoints)
3118 {
3119   IS      *expandedPointsAll;
3120   PetscInt depth;
3121 
3122   PetscFunctionBegin;
3123   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3124   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
3125   PetscAssertPointer(expandedPoints, 3);
3126   PetscCall(DMPlexGetConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
3127   *expandedPoints = expandedPointsAll[0];
3128   PetscCall(PetscObjectReference((PetscObject)expandedPointsAll[0]));
3129   PetscCall(DMPlexRestoreConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
3130   PetscFunctionReturn(PETSC_SUCCESS);
3131 }
3132 
3133 /*@
3134   DMPlexGetConeRecursive - Expand each given point into its cone points and do that recursively until we end up just with vertices (DAG points of depth 0, i.e. without cones).
3135 
3136   Not Collective
3137 
3138   Input Parameters:
3139 + dm     - The `DMPLEX`
3140 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3141 
3142   Output Parameters:
3143 + depth          - (optional) Size of the output arrays, equal to `DMPLEX` depth, returned by `DMPlexGetDepth()`
3144 . expandedPoints - (optional) An array of index sets with recursively expanded cones
3145 - sections       - (optional) An array of sections which describe mappings from points to their cone points
3146 
3147   Level: advanced
3148 
3149   Notes:
3150   Like `DMPlexGetConeTuple()` but recursive.
3151 
3152   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.
3153   For example, for d=0 it contains only vertices, for d=1 it can contain vertices and edges, etc.
3154 
3155   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\:
3156   (1) DAG points in `expandedPoints`[d+1] with `depth` d+1 to their cone points in `expandedPoints`[d];
3157   (2) DAG points in `expandedPoints`[d+1] with `depth` in [0,d] to the same points in `expandedPoints`[d].
3158 
3159 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexRestoreConeRecursive()`, `DMPlexGetConeRecursiveVertices()`,
3160           `DMPlexGetDepth()`, `PetscSection`, `IS`
3161 @*/
3162 PetscErrorCode DMPlexGetConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
3163 {
3164   const PetscInt *arr0 = NULL, *cone = NULL;
3165   PetscInt       *arr = NULL, *newarr = NULL;
3166   PetscInt        d, depth_, i, n, newn, cn, co, start, end;
3167   IS             *expandedPoints_;
3168   PetscSection   *sections_;
3169 
3170   PetscFunctionBegin;
3171   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3172   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
3173   if (depth) PetscAssertPointer(depth, 3);
3174   if (expandedPoints) PetscAssertPointer(expandedPoints, 4);
3175   if (sections) PetscAssertPointer(sections, 5);
3176   PetscCall(ISGetLocalSize(points, &n));
3177   PetscCall(ISGetIndices(points, &arr0));
3178   PetscCall(DMPlexGetDepth(dm, &depth_));
3179   PetscCall(PetscCalloc1(depth_, &expandedPoints_));
3180   PetscCall(PetscCalloc1(depth_, &sections_));
3181   arr = (PetscInt *)arr0; /* this is ok because first generation of arr is not modified */
3182   for (d = depth_ - 1; d >= 0; d--) {
3183     PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &sections_[d]));
3184     PetscCall(PetscSectionSetChart(sections_[d], 0, n));
3185     for (i = 0; i < n; i++) {
3186       PetscCall(DMPlexGetDepthStratum(dm, d + 1, &start, &end));
3187       if (arr[i] >= start && arr[i] < end) {
3188         PetscCall(DMPlexGetConeSize(dm, arr[i], &cn));
3189         PetscCall(PetscSectionSetDof(sections_[d], i, cn));
3190       } else {
3191         PetscCall(PetscSectionSetDof(sections_[d], i, 1));
3192       }
3193     }
3194     PetscCall(PetscSectionSetUp(sections_[d]));
3195     PetscCall(PetscSectionGetStorageSize(sections_[d], &newn));
3196     PetscCall(PetscMalloc1(newn, &newarr));
3197     for (i = 0; i < n; i++) {
3198       PetscCall(PetscSectionGetDof(sections_[d], i, &cn));
3199       PetscCall(PetscSectionGetOffset(sections_[d], i, &co));
3200       if (cn > 1) {
3201         PetscCall(DMPlexGetCone(dm, arr[i], &cone));
3202         PetscCall(PetscMemcpy(&newarr[co], cone, cn * sizeof(PetscInt)));
3203       } else {
3204         newarr[co] = arr[i];
3205       }
3206     }
3207     PetscCall(ISCreateGeneral(PETSC_COMM_SELF, newn, newarr, PETSC_OWN_POINTER, &expandedPoints_[d]));
3208     arr = newarr;
3209     n   = newn;
3210   }
3211   PetscCall(ISRestoreIndices(points, &arr0));
3212   *depth = depth_;
3213   if (expandedPoints) *expandedPoints = expandedPoints_;
3214   else {
3215     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&expandedPoints_[d]));
3216     PetscCall(PetscFree(expandedPoints_));
3217   }
3218   if (sections) *sections = sections_;
3219   else {
3220     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&sections_[d]));
3221     PetscCall(PetscFree(sections_));
3222   }
3223   PetscFunctionReturn(PETSC_SUCCESS);
3224 }
3225 
3226 /*@
3227   DMPlexRestoreConeRecursive - Deallocates arrays created by `DMPlexGetConeRecursive()`
3228 
3229   Not Collective
3230 
3231   Input Parameters:
3232 + dm     - The `DMPLEX`
3233 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3234 
3235   Output Parameters:
3236 + depth          - (optional) Size of the output arrays, equal to `DMPLEX` depth, returned by `DMPlexGetDepth()`
3237 . expandedPoints - (optional) An array of recursively expanded cones
3238 - sections       - (optional) An array of sections which describe mappings from points to their cone points
3239 
3240   Level: advanced
3241 
3242   Note:
3243   See `DMPlexGetConeRecursive()`
3244 
3245 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexGetConeRecursiveVertices()`,
3246           `DMPlexGetDepth()`, `IS`, `PetscSection`
3247 @*/
3248 PetscErrorCode DMPlexRestoreConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
3249 {
3250   PetscInt d, depth_;
3251 
3252   PetscFunctionBegin;
3253   PetscCall(DMPlexGetDepth(dm, &depth_));
3254   PetscCheck(!depth || *depth == depth_, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "depth changed since last call to DMPlexGetConeRecursive");
3255   if (depth) *depth = 0;
3256   if (expandedPoints) {
3257     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&((*expandedPoints)[d])));
3258     PetscCall(PetscFree(*expandedPoints));
3259   }
3260   if (sections) {
3261     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&((*sections)[d])));
3262     PetscCall(PetscFree(*sections));
3263   }
3264   PetscFunctionReturn(PETSC_SUCCESS);
3265 }
3266 
3267 /*@
3268   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
3269 
3270   Not Collective
3271 
3272   Input Parameters:
3273 + dm   - The `DMPLEX`
3274 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3275 - cone - An array of points which are on the in-edges for point `p`
3276 
3277   Level: beginner
3278 
3279   Note:
3280   This should be called after all calls to `DMPlexSetConeSize()` and `DMSetUp()`.
3281 
3282 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`, `DMPlexSetSupport()`, `DMPlexSetSupportSize()`
3283 @*/
3284 PetscErrorCode DMPlexSetCone(DM dm, PetscInt p, const PetscInt cone[])
3285 {
3286   DM_Plex *mesh = (DM_Plex *)dm->data;
3287   PetscInt dof, off, c;
3288 
3289   PetscFunctionBegin;
3290   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3291   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3292   if (dof) PetscAssertPointer(cone, 3);
3293   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3294   if (PetscDefined(USE_DEBUG)) {
3295     PetscInt pStart, pEnd;
3296     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3297     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);
3298     for (c = 0; c < dof; ++c) {
3299       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);
3300       mesh->cones[off + c] = cone[c];
3301     }
3302   } else {
3303     for (c = 0; c < dof; ++c) mesh->cones[off + c] = cone[c];
3304   }
3305   PetscFunctionReturn(PETSC_SUCCESS);
3306 }
3307 
3308 /*@C
3309   DMPlexGetConeOrientation - Return the orientations on the in-edges for this point in the DAG
3310 
3311   Not Collective
3312 
3313   Input Parameters:
3314 + dm - The `DMPLEX`
3315 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3316 
3317   Output Parameter:
3318 . coneOrientation - An array of orientations which are on the in-edges for point `p`. An orientation is an
3319                     integer giving the prescription for cone traversal.
3320 
3321   Level: beginner
3322 
3323   Note:
3324   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3325   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3326   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3327   with the identity.
3328 
3329   Fortran Notes:
3330   You must also call `DMPlexRestoreConeOrientation()` after you finish using the returned array.
3331   `DMPlexRestoreConeOrientation()` is not needed/available in C.
3332 
3333 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPolytopeTypeComposeOrientation()`, `DMPolytopeTypeComposeOrientationInv()`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetCone()`, `DMPlexSetChart()`
3334 @*/
3335 PetscErrorCode DMPlexGetConeOrientation(DM dm, PetscInt p, const PetscInt *coneOrientation[])
3336 {
3337   DM_Plex *mesh = (DM_Plex *)dm->data;
3338   PetscInt off;
3339 
3340   PetscFunctionBegin;
3341   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3342   if (PetscDefined(USE_DEBUG)) {
3343     PetscInt dof;
3344     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3345     if (dof) PetscAssertPointer(coneOrientation, 3);
3346   }
3347   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3348 
3349   *coneOrientation = &mesh->coneOrientations[off];
3350   PetscFunctionReturn(PETSC_SUCCESS);
3351 }
3352 
3353 /*@
3354   DMPlexSetConeOrientation - Set the orientations on the in-edges for this point in the DAG
3355 
3356   Not Collective
3357 
3358   Input Parameters:
3359 + dm              - The `DMPLEX`
3360 . p               - The point, which must lie in the chart set with `DMPlexSetChart()`
3361 - coneOrientation - An array of orientations
3362 
3363   Level: beginner
3364 
3365   Notes:
3366   This should be called after all calls to `DMPlexSetConeSize()` and `DMSetUp()`.
3367 
3368   The meaning of coneOrientation is detailed in `DMPlexGetConeOrientation()`.
3369 
3370 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetConeOrientation()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3371 @*/
3372 PetscErrorCode DMPlexSetConeOrientation(DM dm, PetscInt p, const PetscInt coneOrientation[])
3373 {
3374   DM_Plex *mesh = (DM_Plex *)dm->data;
3375   PetscInt pStart, pEnd;
3376   PetscInt dof, off, c;
3377 
3378   PetscFunctionBegin;
3379   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3380   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3381   if (dof) PetscAssertPointer(coneOrientation, 3);
3382   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3383   if (PetscDefined(USE_DEBUG)) {
3384     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3385     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);
3386     for (c = 0; c < dof; ++c) {
3387       PetscInt cdof, o = coneOrientation[c];
3388 
3389       PetscCall(PetscSectionGetDof(mesh->coneSection, mesh->cones[off + c], &cdof));
3390       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);
3391       mesh->coneOrientations[off + c] = o;
3392     }
3393   } else {
3394     for (c = 0; c < dof; ++c) mesh->coneOrientations[off + c] = coneOrientation[c];
3395   }
3396   PetscFunctionReturn(PETSC_SUCCESS);
3397 }
3398 
3399 /*@
3400   DMPlexInsertCone - Insert a point into the in-edges for the point p in the DAG
3401 
3402   Not Collective
3403 
3404   Input Parameters:
3405 + dm        - The `DMPLEX`
3406 . p         - The point, which must lie in the chart set with `DMPlexSetChart()`
3407 . conePos   - The local index in the cone where the point should be put
3408 - conePoint - The mesh point to insert
3409 
3410   Level: beginner
3411 
3412 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3413 @*/
3414 PetscErrorCode DMPlexInsertCone(DM dm, PetscInt p, PetscInt conePos, PetscInt conePoint)
3415 {
3416   DM_Plex *mesh = (DM_Plex *)dm->data;
3417   PetscInt pStart, pEnd;
3418   PetscInt dof, off;
3419 
3420   PetscFunctionBegin;
3421   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3422   if (PetscDefined(USE_DEBUG)) {
3423     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3424     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);
3425     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);
3426     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3427     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);
3428   }
3429   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3430   mesh->cones[off + conePos] = conePoint;
3431   PetscFunctionReturn(PETSC_SUCCESS);
3432 }
3433 
3434 /*@
3435   DMPlexInsertConeOrientation - Insert a point orientation for the in-edge for the point p in the DAG
3436 
3437   Not Collective
3438 
3439   Input Parameters:
3440 + dm              - The `DMPLEX`
3441 . p               - The point, which must lie in the chart set with `DMPlexSetChart()`
3442 . conePos         - The local index in the cone where the point should be put
3443 - coneOrientation - The point orientation to insert
3444 
3445   Level: beginner
3446 
3447   Note:
3448   The meaning of coneOrientation values is detailed in `DMPlexGetConeOrientation()`.
3449 
3450 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3451 @*/
3452 PetscErrorCode DMPlexInsertConeOrientation(DM dm, PetscInt p, PetscInt conePos, PetscInt coneOrientation)
3453 {
3454   DM_Plex *mesh = (DM_Plex *)dm->data;
3455   PetscInt pStart, pEnd;
3456   PetscInt dof, off;
3457 
3458   PetscFunctionBegin;
3459   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3460   if (PetscDefined(USE_DEBUG)) {
3461     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3462     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);
3463     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3464     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);
3465   }
3466   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3467   mesh->coneOrientations[off + conePos] = coneOrientation;
3468   PetscFunctionReturn(PETSC_SUCCESS);
3469 }
3470 
3471 /*@C
3472   DMPlexGetOrientedCone - Return the points and orientations on the in-edges for this point in the DAG
3473 
3474   Not collective
3475 
3476   Input Parameters:
3477 + dm - The DMPlex
3478 - p  - The point, which must lie in the chart set with DMPlexSetChart()
3479 
3480   Output Parameters:
3481 + cone - An array of points which are on the in-edges for point `p`
3482 - ornt - An array of orientations which are on the in-edges for point `p`. An orientation is an
3483         integer giving the prescription for cone traversal.
3484 
3485   Level: beginner
3486 
3487   Notes:
3488   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3489   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3490   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3491   with the identity.
3492 
3493   Fortran Notes:
3494   You must also call `DMPlexRestoreCone()` after you finish using the returned array.
3495   `DMPlexRestoreCone()` is not needed/available in C.
3496 
3497 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreOrientedCone()`, `DMPlexGetConeSize()`, `DMPlexGetCone()`, `DMPlexGetChart()`
3498 @*/
3499 PetscErrorCode DMPlexGetOrientedCone(DM dm, PetscInt p, const PetscInt *cone[], const PetscInt *ornt[])
3500 {
3501   DM_Plex *mesh = (DM_Plex *)dm->data;
3502 
3503   PetscFunctionBegin;
3504   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3505   if (mesh->tr) {
3506     PetscCall(DMPlexTransformGetCone(mesh->tr, p, cone, ornt));
3507   } else {
3508     PetscInt off;
3509     if (PetscDefined(USE_DEBUG)) {
3510       PetscInt dof;
3511       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3512       if (dof) {
3513         if (cone) PetscAssertPointer(cone, 3);
3514         if (ornt) PetscAssertPointer(ornt, 4);
3515       }
3516     }
3517     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3518     if (cone) *cone = PetscSafePointerPlusOffset(mesh->cones, off);
3519     if (ornt) *ornt = PetscSafePointerPlusOffset(mesh->coneOrientations, off);
3520   }
3521   PetscFunctionReturn(PETSC_SUCCESS);
3522 }
3523 
3524 /*@C
3525   DMPlexRestoreOrientedCone - Restore the points and orientations on the in-edges for this point in the DAG
3526 
3527   Not Collective
3528 
3529   Input Parameters:
3530 + dm   - The DMPlex
3531 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3532 . cone - An array of points which are on the in-edges for point p
3533 - ornt - An array of orientations which are on the in-edges for point `p`. An orientation is an
3534         integer giving the prescription for cone traversal.
3535 
3536   Level: beginner
3537 
3538   Notes:
3539   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3540   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3541   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3542   with the identity.
3543 
3544   Fortran Notes:
3545   You must also call `DMPlexRestoreCone()` after you finish using the returned array.
3546   `DMPlexRestoreCone()` is not needed/available in C.
3547 
3548 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetOrientedCone()`, `DMPlexGetConeSize()`, `DMPlexGetCone()`, `DMPlexGetChart()`
3549 @*/
3550 PetscErrorCode DMPlexRestoreOrientedCone(DM dm, PetscInt p, const PetscInt *cone[], const PetscInt *ornt[])
3551 {
3552   DM_Plex *mesh = (DM_Plex *)dm->data;
3553 
3554   PetscFunctionBegin;
3555   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3556   if (mesh->tr) PetscCall(DMPlexTransformRestoreCone(mesh->tr, p, cone, ornt));
3557   PetscFunctionReturn(PETSC_SUCCESS);
3558 }
3559 
3560 /*@
3561   DMPlexGetSupportSize - Return the number of out-edges for this point in the DAG
3562 
3563   Not Collective
3564 
3565   Input Parameters:
3566 + dm - The `DMPLEX`
3567 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3568 
3569   Output Parameter:
3570 . size - The support size for point `p`
3571 
3572   Level: beginner
3573 
3574 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`, `DMPlexGetConeSize()`
3575 @*/
3576 PetscErrorCode DMPlexGetSupportSize(DM dm, PetscInt p, PetscInt *size)
3577 {
3578   DM_Plex *mesh = (DM_Plex *)dm->data;
3579 
3580   PetscFunctionBegin;
3581   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3582   PetscAssertPointer(size, 3);
3583   PetscCall(PetscSectionGetDof(mesh->supportSection, p, size));
3584   PetscFunctionReturn(PETSC_SUCCESS);
3585 }
3586 
3587 /*@
3588   DMPlexSetSupportSize - Set the number of out-edges for this point in the DAG
3589 
3590   Not Collective
3591 
3592   Input Parameters:
3593 + dm   - The `DMPLEX`
3594 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3595 - size - The support size for point `p`
3596 
3597   Level: beginner
3598 
3599   Note:
3600   This should be called after `DMPlexSetChart()`.
3601 
3602 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetSupportSize()`, `DMPlexSetChart()`
3603 @*/
3604 PetscErrorCode DMPlexSetSupportSize(DM dm, PetscInt p, PetscInt size)
3605 {
3606   DM_Plex *mesh = (DM_Plex *)dm->data;
3607 
3608   PetscFunctionBegin;
3609   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3610   PetscCall(PetscSectionSetDof(mesh->supportSection, p, size));
3611   PetscFunctionReturn(PETSC_SUCCESS);
3612 }
3613 
3614 /*@C
3615   DMPlexGetSupport - Return the points on the out-edges for this point in the DAG
3616 
3617   Not Collective
3618 
3619   Input Parameters:
3620 + dm - The `DMPLEX`
3621 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3622 
3623   Output Parameter:
3624 . support - An array of points which are on the out-edges for point `p`
3625 
3626   Level: beginner
3627 
3628   Fortran Notes:
3629   You must also call `DMPlexRestoreSupport()` after you finish using the returned array.
3630   `DMPlexRestoreSupport()` is not needed/available in C.
3631 
3632 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSupportSize()`, `DMPlexSetSupport()`, `DMPlexGetCone()`, `DMPlexSetChart()`
3633 @*/
3634 PetscErrorCode DMPlexGetSupport(DM dm, PetscInt p, const PetscInt *support[])
3635 {
3636   DM_Plex *mesh = (DM_Plex *)dm->data;
3637   PetscInt off;
3638 
3639   PetscFunctionBegin;
3640   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3641   PetscAssertPointer(support, 3);
3642   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3643   *support = PetscSafePointerPlusOffset(mesh->supports, off);
3644   PetscFunctionReturn(PETSC_SUCCESS);
3645 }
3646 
3647 /*@
3648   DMPlexSetSupport - Set the points on the out-edges for this point in the DAG, that is the list of points that this point covers
3649 
3650   Not Collective
3651 
3652   Input Parameters:
3653 + dm      - The `DMPLEX`
3654 . p       - The point, which must lie in the chart set with `DMPlexSetChart()`
3655 - support - An array of points which are on the out-edges for point `p`
3656 
3657   Level: beginner
3658 
3659   Note:
3660   This should be called after all calls to `DMPlexSetSupportSize()` and `DMSetUp()`.
3661 
3662 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetCone()`, `DMPlexSetConeSize()`, `DMPlexCreate()`, `DMPlexGetSupport()`, `DMPlexSetChart()`, `DMPlexSetSupportSize()`, `DMSetUp()`
3663 @*/
3664 PetscErrorCode DMPlexSetSupport(DM dm, PetscInt p, const PetscInt support[])
3665 {
3666   DM_Plex *mesh = (DM_Plex *)dm->data;
3667   PetscInt pStart, pEnd;
3668   PetscInt dof, off, c;
3669 
3670   PetscFunctionBegin;
3671   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3672   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3673   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3674   if (dof) PetscAssertPointer(support, 3);
3675   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3676   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);
3677   for (c = 0; c < dof; ++c) {
3678     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);
3679     mesh->supports[off + c] = support[c];
3680   }
3681   PetscFunctionReturn(PETSC_SUCCESS);
3682 }
3683 
3684 /*@
3685   DMPlexInsertSupport - Insert a point into the out-edges for the point p in the DAG
3686 
3687   Not Collective
3688 
3689   Input Parameters:
3690 + dm           - The `DMPLEX`
3691 . p            - The point, which must lie in the chart set with `DMPlexSetChart()`
3692 . supportPos   - The local index in the cone where the point should be put
3693 - supportPoint - The mesh point to insert
3694 
3695   Level: beginner
3696 
3697 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3698 @*/
3699 PetscErrorCode DMPlexInsertSupport(DM dm, PetscInt p, PetscInt supportPos, PetscInt supportPoint)
3700 {
3701   DM_Plex *mesh = (DM_Plex *)dm->data;
3702   PetscInt pStart, pEnd;
3703   PetscInt dof, off;
3704 
3705   PetscFunctionBegin;
3706   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3707   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3708   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3709   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3710   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);
3711   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);
3712   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);
3713   mesh->supports[off + supportPos] = supportPoint;
3714   PetscFunctionReturn(PETSC_SUCCESS);
3715 }
3716 
3717 /* Converts an orientation o in the current numbering to the previous scheme used in Plex */
3718 PetscInt DMPolytopeConvertNewOrientation_Internal(DMPolytopeType ct, PetscInt o)
3719 {
3720   switch (ct) {
3721   case DM_POLYTOPE_SEGMENT:
3722     if (o == -1) return -2;
3723     break;
3724   case DM_POLYTOPE_TRIANGLE:
3725     if (o == -3) return -1;
3726     if (o == -2) return -3;
3727     if (o == -1) return -2;
3728     break;
3729   case DM_POLYTOPE_QUADRILATERAL:
3730     if (o == -4) return -2;
3731     if (o == -3) return -1;
3732     if (o == -2) return -4;
3733     if (o == -1) return -3;
3734     break;
3735   default:
3736     return o;
3737   }
3738   return o;
3739 }
3740 
3741 /* Converts an orientation o in the previous scheme used in Plex to the current numbering */
3742 PetscInt DMPolytopeConvertOldOrientation_Internal(DMPolytopeType ct, PetscInt o)
3743 {
3744   switch (ct) {
3745   case DM_POLYTOPE_SEGMENT:
3746     if ((o == -2) || (o == 1)) return -1;
3747     if (o == -1) return 0;
3748     break;
3749   case DM_POLYTOPE_TRIANGLE:
3750     if (o == -3) return -2;
3751     if (o == -2) return -1;
3752     if (o == -1) return -3;
3753     break;
3754   case DM_POLYTOPE_QUADRILATERAL:
3755     if (o == -4) return -2;
3756     if (o == -3) return -1;
3757     if (o == -2) return -4;
3758     if (o == -1) return -3;
3759     break;
3760   default:
3761     return o;
3762   }
3763   return o;
3764 }
3765 
3766 /* Takes in a mesh whose orientations are in the previous scheme and converts them all to the current numbering */
3767 PetscErrorCode DMPlexConvertOldOrientations_Internal(DM dm)
3768 {
3769   PetscInt pStart, pEnd, p;
3770 
3771   PetscFunctionBegin;
3772   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3773   for (p = pStart; p < pEnd; ++p) {
3774     const PetscInt *cone, *ornt;
3775     PetscInt        coneSize, c;
3776 
3777     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
3778     PetscCall(DMPlexGetCone(dm, p, &cone));
3779     PetscCall(DMPlexGetConeOrientation(dm, p, &ornt));
3780     for (c = 0; c < coneSize; ++c) {
3781       DMPolytopeType ct;
3782       const PetscInt o = ornt[c];
3783 
3784       PetscCall(DMPlexGetCellType(dm, cone[c], &ct));
3785       switch (ct) {
3786       case DM_POLYTOPE_SEGMENT:
3787         if ((o == -2) || (o == 1)) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3788         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, 0));
3789         break;
3790       case DM_POLYTOPE_TRIANGLE:
3791         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3792         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3793         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3794         break;
3795       case DM_POLYTOPE_QUADRILATERAL:
3796         if (o == -4) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3797         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3798         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -4));
3799         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3800         break;
3801       default:
3802         break;
3803       }
3804     }
3805   }
3806   PetscFunctionReturn(PETSC_SUCCESS);
3807 }
3808 
3809 static inline PetscErrorCode DMPlexGetTransitiveClosure_Hot_Private(DM dm, PetscInt p, PetscBool useCone, PetscInt *size, const PetscInt *arr[], const PetscInt *ornt[])
3810 {
3811   DM_Plex *mesh = (DM_Plex *)dm->data;
3812 
3813   PetscFunctionBeginHot;
3814   if (PetscDefined(USE_DEBUG) || mesh->tr) {
3815     if (useCone) {
3816       PetscCall(DMPlexGetConeSize(dm, p, size));
3817       PetscCall(DMPlexGetOrientedCone(dm, p, arr, ornt));
3818     } else {
3819       PetscCall(DMPlexGetSupportSize(dm, p, size));
3820       PetscCall(DMPlexGetSupport(dm, p, arr));
3821     }
3822   } else {
3823     if (useCone) {
3824       const PetscSection s   = mesh->coneSection;
3825       const PetscInt     ps  = p - s->pStart;
3826       const PetscInt     off = s->atlasOff[ps];
3827 
3828       *size = s->atlasDof[ps];
3829       *arr  = mesh->cones + off;
3830       *ornt = mesh->coneOrientations + off;
3831     } else {
3832       const PetscSection s   = mesh->supportSection;
3833       const PetscInt     ps  = p - s->pStart;
3834       const PetscInt     off = s->atlasOff[ps];
3835 
3836       *size = s->atlasDof[ps];
3837       *arr  = mesh->supports + off;
3838     }
3839   }
3840   PetscFunctionReturn(PETSC_SUCCESS);
3841 }
3842 
3843 static inline PetscErrorCode DMPlexRestoreTransitiveClosure_Hot_Private(DM dm, PetscInt p, PetscBool useCone, PetscInt *size, const PetscInt *arr[], const PetscInt *ornt[])
3844 {
3845   DM_Plex *mesh = (DM_Plex *)dm->data;
3846 
3847   PetscFunctionBeginHot;
3848   if (PetscDefined(USE_DEBUG) || mesh->tr) {
3849     if (useCone) PetscCall(DMPlexRestoreOrientedCone(dm, p, arr, ornt));
3850   }
3851   PetscFunctionReturn(PETSC_SUCCESS);
3852 }
3853 
3854 static PetscErrorCode DMPlexGetTransitiveClosure_Depth1_Private(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3855 {
3856   DMPolytopeType  ct = DM_POLYTOPE_UNKNOWN;
3857   PetscInt       *closure;
3858   const PetscInt *tmp = NULL, *tmpO = NULL;
3859   PetscInt        off = 0, tmpSize, t;
3860 
3861   PetscFunctionBeginHot;
3862   if (ornt) {
3863     PetscCall(DMPlexGetCellType(dm, p, &ct));
3864     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;
3865   }
3866   if (*points) {
3867     closure = *points;
3868   } else {
3869     PetscInt maxConeSize, maxSupportSize;
3870     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3871     PetscCall(DMGetWorkArray(dm, 2 * (PetscMax(maxConeSize, maxSupportSize) + 1), MPIU_INT, &closure));
3872   }
3873   PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, p, useCone, &tmpSize, &tmp, &tmpO));
3874   if (ct == DM_POLYTOPE_UNKNOWN) {
3875     closure[off++] = p;
3876     closure[off++] = 0;
3877     for (t = 0; t < tmpSize; ++t) {
3878       closure[off++] = tmp[t];
3879       closure[off++] = tmpO ? tmpO[t] : 0;
3880     }
3881   } else {
3882     const PetscInt *arr = DMPolytopeTypeGetArrangement(ct, ornt);
3883 
3884     /* We assume that cells with a valid type have faces with a valid type */
3885     closure[off++] = p;
3886     closure[off++] = ornt;
3887     for (t = 0; t < tmpSize; ++t) {
3888       DMPolytopeType ft;
3889 
3890       PetscCall(DMPlexGetCellType(dm, tmp[t], &ft));
3891       closure[off++] = tmp[arr[t]];
3892       closure[off++] = tmpO ? DMPolytopeTypeComposeOrientation(ft, ornt, tmpO[t]) : 0;
3893     }
3894   }
3895   PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, p, useCone, &tmpSize, &tmp, &tmpO));
3896   if (numPoints) *numPoints = tmpSize + 1;
3897   if (points) *points = closure;
3898   PetscFunctionReturn(PETSC_SUCCESS);
3899 }
3900 
3901 /* We need a special tensor version because we want to allow duplicate points in the endcaps for hybrid cells */
3902 static PetscErrorCode DMPlexTransitiveClosure_Tensor_Internal(DM dm, PetscInt point, DMPolytopeType ct, PetscInt o, PetscBool useCone, PetscInt *numPoints, PetscInt **points)
3903 {
3904   const PetscInt *arr = DMPolytopeTypeGetArrangement(ct, o);
3905   const PetscInt *cone, *ornt;
3906   PetscInt       *pts, *closure = NULL;
3907   DMPolytopeType  ft;
3908   PetscInt        maxConeSize, maxSupportSize, coneSeries, supportSeries, maxSize;
3909   PetscInt        dim, coneSize, c, d, clSize, cl;
3910 
3911   PetscFunctionBeginHot;
3912   PetscCall(DMGetDimension(dm, &dim));
3913   PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, point, PETSC_TRUE, &coneSize, &cone, &ornt));
3914   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3915   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, dim + 1) - 1) / (maxConeSize - 1)) : dim + 1;
3916   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, dim + 1) - 1) / (maxSupportSize - 1)) : dim + 1;
3917   maxSize       = PetscMax(coneSeries, supportSeries);
3918   if (*points) {
3919     pts = *points;
3920   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &pts));
3921   c        = 0;
3922   pts[c++] = point;
3923   pts[c++] = o;
3924   PetscCall(DMPlexGetCellType(dm, cone[arr[0 * 2 + 0]], &ft));
3925   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[0 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[0 * 2 + 1], ornt[0]), useCone, &clSize, &closure));
3926   for (cl = 0; cl < clSize * 2; cl += 2) {
3927     pts[c++] = closure[cl];
3928     pts[c++] = closure[cl + 1];
3929   }
3930   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[1 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[1 * 2 + 1], ornt[1]), useCone, &clSize, &closure));
3931   for (cl = 0; cl < clSize * 2; cl += 2) {
3932     pts[c++] = closure[cl];
3933     pts[c++] = closure[cl + 1];
3934   }
3935   PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[0], useCone, &clSize, &closure));
3936   for (d = 2; d < coneSize; ++d) {
3937     PetscCall(DMPlexGetCellType(dm, cone[arr[d * 2 + 0]], &ft));
3938     pts[c++] = cone[arr[d * 2 + 0]];
3939     pts[c++] = DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]);
3940   }
3941   PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, point, PETSC_TRUE, &coneSize, &cone, &ornt));
3942   if (dim >= 3) {
3943     for (d = 2; d < coneSize; ++d) {
3944       const PetscInt  fpoint = cone[arr[d * 2 + 0]];
3945       const PetscInt *fcone, *fornt;
3946       PetscInt        fconeSize, fc, i;
3947 
3948       PetscCall(DMPlexGetCellType(dm, fpoint, &ft));
3949       const PetscInt *farr = DMPolytopeTypeGetArrangement(ft, DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]));
3950       PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, fpoint, PETSC_TRUE, &fconeSize, &fcone, &fornt));
3951       for (fc = 0; fc < fconeSize; ++fc) {
3952         const PetscInt cp = fcone[farr[fc * 2 + 0]];
3953         const PetscInt co = farr[fc * 2 + 1];
3954 
3955         for (i = 0; i < c; i += 2)
3956           if (pts[i] == cp) break;
3957         if (i == c) {
3958           PetscCall(DMPlexGetCellType(dm, cp, &ft));
3959           pts[c++] = cp;
3960           pts[c++] = DMPolytopeTypeComposeOrientation(ft, co, fornt[farr[fc * 2 + 0]]);
3961         }
3962       }
3963       PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, fpoint, PETSC_TRUE, &fconeSize, &fcone, &fornt));
3964     }
3965   }
3966   *numPoints = c / 2;
3967   *points    = pts;
3968   PetscFunctionReturn(PETSC_SUCCESS);
3969 }
3970 
3971 PetscErrorCode DMPlexGetTransitiveClosure_Internal(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3972 {
3973   DMPolytopeType ct;
3974   PetscInt      *closure, *fifo;
3975   PetscInt       closureSize = 0, fifoStart = 0, fifoSize = 0;
3976   PetscInt       maxConeSize, maxSupportSize, coneSeries, supportSeries;
3977   PetscInt       depth, maxSize;
3978 
3979   PetscFunctionBeginHot;
3980   PetscCall(DMPlexGetDepth(dm, &depth));
3981   if (depth == 1) {
3982     PetscCall(DMPlexGetTransitiveClosure_Depth1_Private(dm, p, ornt, useCone, numPoints, points));
3983     PetscFunctionReturn(PETSC_SUCCESS);
3984   }
3985   PetscCall(DMPlexGetCellType(dm, p, &ct));
3986   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;
3987   if (DMPolytopeTypeIsHybrid(ct) && ct != DM_POLYTOPE_POINT_PRISM_TENSOR) {
3988     PetscCall(DMPlexTransitiveClosure_Tensor_Internal(dm, p, ct, ornt, useCone, numPoints, points));
3989     PetscFunctionReturn(PETSC_SUCCESS);
3990   }
3991   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3992   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, depth + 1) - 1) / (maxConeSize - 1)) : depth + 1;
3993   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, depth + 1) - 1) / (maxSupportSize - 1)) : depth + 1;
3994   maxSize       = PetscMax(coneSeries, supportSeries);
3995   PetscCall(DMGetWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
3996   if (*points) {
3997     closure = *points;
3998   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &closure));
3999   closure[closureSize++] = p;
4000   closure[closureSize++] = ornt;
4001   fifo[fifoSize++]       = p;
4002   fifo[fifoSize++]       = ornt;
4003   fifo[fifoSize++]       = ct;
4004   /* Should kick out early when depth is reached, rather than checking all vertices for empty cones */
4005   while (fifoSize - fifoStart) {
4006     const PetscInt       q    = fifo[fifoStart++];
4007     const PetscInt       o    = fifo[fifoStart++];
4008     const DMPolytopeType qt   = (DMPolytopeType)fifo[fifoStart++];
4009     const PetscInt      *qarr = DMPolytopeTypeGetArrangement(qt, o);
4010     const PetscInt      *tmp, *tmpO = NULL;
4011     PetscInt             tmpSize, t;
4012 
4013     if (PetscDefined(USE_DEBUG)) {
4014       PetscInt nO = DMPolytopeTypeGetNumArrangements(qt) / 2;
4015       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);
4016     }
4017     PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, q, useCone, &tmpSize, &tmp, &tmpO));
4018     for (t = 0; t < tmpSize; ++t) {
4019       const PetscInt ip = useCone && qarr ? qarr[t * 2] : t;
4020       const PetscInt io = useCone && qarr ? qarr[t * 2 + 1] : 0;
4021       const PetscInt cp = tmp[ip];
4022       PetscCall(DMPlexGetCellType(dm, cp, &ct));
4023       const PetscInt co = tmpO ? DMPolytopeTypeComposeOrientation(ct, io, tmpO[ip]) : 0;
4024       PetscInt       c;
4025 
4026       /* Check for duplicate */
4027       for (c = 0; c < closureSize; c += 2) {
4028         if (closure[c] == cp) break;
4029       }
4030       if (c == closureSize) {
4031         closure[closureSize++] = cp;
4032         closure[closureSize++] = co;
4033         fifo[fifoSize++]       = cp;
4034         fifo[fifoSize++]       = co;
4035         fifo[fifoSize++]       = ct;
4036       }
4037     }
4038     PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, q, useCone, &tmpSize, &tmp, &tmpO));
4039   }
4040   PetscCall(DMRestoreWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
4041   if (numPoints) *numPoints = closureSize / 2;
4042   if (points) *points = closure;
4043   PetscFunctionReturn(PETSC_SUCCESS);
4044 }
4045 
4046 /*@C
4047   DMPlexGetTransitiveClosure - Return the points on the transitive closure of the in-edges or out-edges for this point in the DAG
4048 
4049   Not Collective
4050 
4051   Input Parameters:
4052 + dm      - The `DMPLEX`
4053 . p       - The mesh point
4054 - useCone - `PETSC_TRUE` for the closure, otherwise return the star
4055 
4056   Input/Output Parameter:
4057 . points - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...];
4058            if `NULL` on input, internal storage will be returned, otherwise the provided array is used
4059 
4060   Output Parameter:
4061 . numPoints - The number of points in the closure, so points[] is of size 2*`numPoints`
4062 
4063   Level: beginner
4064 
4065   Note:
4066   If using internal storage (points is `NULL` on input), each call overwrites the last output.
4067 
4068   Fortran Notes:
4069   The `numPoints` argument is not present in the Fortran binding since it is internal to the array.
4070 
4071 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
4072 @*/
4073 PetscErrorCode DMPlexGetTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
4074 {
4075   PetscFunctionBeginHot;
4076   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4077   if (numPoints) PetscAssertPointer(numPoints, 4);
4078   if (points) PetscAssertPointer(points, 5);
4079   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, p, 0, useCone, numPoints, points));
4080   PetscFunctionReturn(PETSC_SUCCESS);
4081 }
4082 
4083 /*@C
4084   DMPlexRestoreTransitiveClosure - Restore the array of points on the transitive closure of the in-edges or out-edges for this point in the DAG
4085 
4086   Not Collective
4087 
4088   Input Parameters:
4089 + dm        - The `DMPLEX`
4090 . p         - The mesh point
4091 . useCone   - `PETSC_TRUE` for the closure, otherwise return the star
4092 . numPoints - The number of points in the closure, so points[] is of size 2*`numPoints`
4093 - points    - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...]
4094 
4095   Level: beginner
4096 
4097   Note:
4098   If not using internal storage (points is not `NULL` on input), this call is unnecessary
4099 
4100 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
4101 @*/
4102 PetscErrorCode DMPlexRestoreTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
4103 {
4104   PetscFunctionBeginHot;
4105   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4106   if (numPoints) *numPoints = 0;
4107   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, points));
4108   PetscFunctionReturn(PETSC_SUCCESS);
4109 }
4110 
4111 /*@
4112   DMPlexGetMaxSizes - Return the maximum number of in-edges (cone) and out-edges (support) for any point in the DAG
4113 
4114   Not Collective
4115 
4116   Input Parameter:
4117 . dm - The `DMPLEX`
4118 
4119   Output Parameters:
4120 + maxConeSize    - The maximum number of in-edges
4121 - maxSupportSize - The maximum number of out-edges
4122 
4123   Level: beginner
4124 
4125 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
4126 @*/
4127 PetscErrorCode DMPlexGetMaxSizes(DM dm, PetscInt *maxConeSize, PetscInt *maxSupportSize)
4128 {
4129   DM_Plex *mesh = (DM_Plex *)dm->data;
4130 
4131   PetscFunctionBegin;
4132   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4133   if (maxConeSize) PetscCall(PetscSectionGetMaxDof(mesh->coneSection, maxConeSize));
4134   if (maxSupportSize) PetscCall(PetscSectionGetMaxDof(mesh->supportSection, maxSupportSize));
4135   PetscFunctionReturn(PETSC_SUCCESS);
4136 }
4137 
4138 PetscErrorCode DMSetUp_Plex(DM dm)
4139 {
4140   DM_Plex *mesh = (DM_Plex *)dm->data;
4141   PetscInt size, maxSupportSize;
4142 
4143   PetscFunctionBegin;
4144   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4145   PetscCall(PetscSectionSetUp(mesh->coneSection));
4146   PetscCall(PetscSectionGetStorageSize(mesh->coneSection, &size));
4147   PetscCall(PetscMalloc1(size, &mesh->cones));
4148   PetscCall(PetscCalloc1(size, &mesh->coneOrientations));
4149   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
4150   if (maxSupportSize) {
4151     PetscCall(PetscSectionSetUp(mesh->supportSection));
4152     PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &size));
4153     PetscCall(PetscMalloc1(size, &mesh->supports));
4154   }
4155   PetscFunctionReturn(PETSC_SUCCESS);
4156 }
4157 
4158 PetscErrorCode DMCreateSubDM_Plex(DM dm, PetscInt numFields, const PetscInt fields[], IS *is, DM *subdm)
4159 {
4160   PetscFunctionBegin;
4161   if (subdm) PetscCall(DMClone(dm, subdm));
4162   PetscCall(DMCreateSectionSubDM(dm, numFields, fields, NULL, NULL, is, subdm));
4163   if (subdm) (*subdm)->useNatural = dm->useNatural;
4164   if (dm->useNatural && dm->sfMigration) {
4165     PetscSF sfNatural;
4166 
4167     (*subdm)->sfMigration = dm->sfMigration;
4168     PetscCall(PetscObjectReference((PetscObject)dm->sfMigration));
4169     PetscCall(DMPlexCreateGlobalToNaturalSF(*subdm, NULL, (*subdm)->sfMigration, &sfNatural));
4170     (*subdm)->sfNatural = sfNatural;
4171   }
4172   PetscFunctionReturn(PETSC_SUCCESS);
4173 }
4174 
4175 PetscErrorCode DMCreateSuperDM_Plex(DM dms[], PetscInt len, IS **is, DM *superdm)
4176 {
4177   PetscInt i = 0;
4178 
4179   PetscFunctionBegin;
4180   PetscCall(DMClone(dms[0], superdm));
4181   PetscCall(DMCreateSectionSuperDM(dms, len, is, superdm));
4182   (*superdm)->useNatural = PETSC_FALSE;
4183   for (i = 0; i < len; i++) {
4184     if (dms[i]->useNatural && dms[i]->sfMigration) {
4185       PetscSF sfNatural;
4186 
4187       (*superdm)->sfMigration = dms[i]->sfMigration;
4188       PetscCall(PetscObjectReference((PetscObject)dms[i]->sfMigration));
4189       (*superdm)->useNatural = PETSC_TRUE;
4190       PetscCall(DMPlexCreateGlobalToNaturalSF(*superdm, NULL, (*superdm)->sfMigration, &sfNatural));
4191       (*superdm)->sfNatural = sfNatural;
4192       break;
4193     }
4194   }
4195   PetscFunctionReturn(PETSC_SUCCESS);
4196 }
4197 
4198 /*@
4199   DMPlexSymmetrize - Create support (out-edge) information from cone (in-edge) information
4200 
4201   Not Collective
4202 
4203   Input Parameter:
4204 . dm - The `DMPLEX`
4205 
4206   Level: beginner
4207 
4208   Note:
4209   This should be called after all calls to `DMPlexSetCone()`
4210 
4211 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMPlexSetCone()`
4212 @*/
4213 PetscErrorCode DMPlexSymmetrize(DM dm)
4214 {
4215   DM_Plex  *mesh = (DM_Plex *)dm->data;
4216   PetscInt *offsets;
4217   PetscInt  supportSize;
4218   PetscInt  pStart, pEnd, p;
4219 
4220   PetscFunctionBegin;
4221   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4222   PetscCheck(!mesh->supports, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONGSTATE, "Supports were already setup in this DMPlex");
4223   PetscCall(PetscLogEventBegin(DMPLEX_Symmetrize, dm, 0, 0, 0));
4224   /* Calculate support sizes */
4225   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4226   for (p = pStart; p < pEnd; ++p) {
4227     PetscInt dof, off, c;
4228 
4229     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
4230     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
4231     for (c = off; c < off + dof; ++c) PetscCall(PetscSectionAddDof(mesh->supportSection, mesh->cones[c], 1));
4232   }
4233   PetscCall(PetscSectionSetUp(mesh->supportSection));
4234   /* Calculate supports */
4235   PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &supportSize));
4236   PetscCall(PetscMalloc1(supportSize, &mesh->supports));
4237   PetscCall(PetscCalloc1(pEnd - pStart, &offsets));
4238   for (p = pStart; p < pEnd; ++p) {
4239     PetscInt dof, off, c;
4240 
4241     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
4242     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
4243     for (c = off; c < off + dof; ++c) {
4244       const PetscInt q = mesh->cones[c];
4245       PetscInt       offS;
4246 
4247       PetscCall(PetscSectionGetOffset(mesh->supportSection, q, &offS));
4248 
4249       mesh->supports[offS + offsets[q]] = p;
4250       ++offsets[q];
4251     }
4252   }
4253   PetscCall(PetscFree(offsets));
4254   PetscCall(PetscLogEventEnd(DMPLEX_Symmetrize, dm, 0, 0, 0));
4255   PetscFunctionReturn(PETSC_SUCCESS);
4256 }
4257 
4258 static PetscErrorCode DMPlexCreateDepthStratum(DM dm, DMLabel label, PetscInt depth, PetscInt pStart, PetscInt pEnd)
4259 {
4260   IS stratumIS;
4261 
4262   PetscFunctionBegin;
4263   if (pStart >= pEnd) PetscFunctionReturn(PETSC_SUCCESS);
4264   if (PetscDefined(USE_DEBUG)) {
4265     PetscInt  qStart, qEnd, numLevels, level;
4266     PetscBool overlap = PETSC_FALSE;
4267     PetscCall(DMLabelGetNumValues(label, &numLevels));
4268     for (level = 0; level < numLevels; level++) {
4269       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4270       if ((pStart >= qStart && pStart < qEnd) || (pEnd > qStart && pEnd <= qEnd)) {
4271         overlap = PETSC_TRUE;
4272         break;
4273       }
4274     }
4275     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);
4276   }
4277   PetscCall(ISCreateStride(PETSC_COMM_SELF, pEnd - pStart, pStart, 1, &stratumIS));
4278   PetscCall(DMLabelSetStratumIS(label, depth, stratumIS));
4279   PetscCall(ISDestroy(&stratumIS));
4280   PetscFunctionReturn(PETSC_SUCCESS);
4281 }
4282 
4283 static PetscErrorCode DMPlexStratify_CellType_Private(DM dm, DMLabel label)
4284 {
4285   PetscInt *pMin, *pMax;
4286   PetscInt  pStart, pEnd;
4287   PetscInt  dmin = PETSC_MAX_INT, dmax = PETSC_MIN_INT;
4288 
4289   PetscFunctionBegin;
4290   {
4291     DMLabel label2;
4292 
4293     PetscCall(DMPlexGetCellTypeLabel(dm, &label2));
4294     PetscCall(PetscObjectViewFromOptions((PetscObject)label2, NULL, "-ct_view"));
4295   }
4296   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4297   for (PetscInt p = pStart; p < pEnd; ++p) {
4298     DMPolytopeType ct;
4299 
4300     PetscCall(DMPlexGetCellType(dm, p, &ct));
4301     dmin = PetscMin(DMPolytopeTypeGetDim(ct), dmin);
4302     dmax = PetscMax(DMPolytopeTypeGetDim(ct), dmax);
4303   }
4304   PetscCall(PetscMalloc2(dmax + 1, &pMin, dmax + 1, &pMax));
4305   for (PetscInt d = dmin; d <= dmax; ++d) {
4306     pMin[d] = PETSC_MAX_INT;
4307     pMax[d] = PETSC_MIN_INT;
4308   }
4309   for (PetscInt p = pStart; p < pEnd; ++p) {
4310     DMPolytopeType ct;
4311     PetscInt       d;
4312 
4313     PetscCall(DMPlexGetCellType(dm, p, &ct));
4314     d       = DMPolytopeTypeGetDim(ct);
4315     pMin[d] = PetscMin(p, pMin[d]);
4316     pMax[d] = PetscMax(p, pMax[d]);
4317   }
4318   for (PetscInt d = dmin; d <= dmax; ++d) {
4319     if (pMin[d] > pMax[d]) continue;
4320     PetscCall(DMPlexCreateDepthStratum(dm, label, d, pMin[d], pMax[d] + 1));
4321   }
4322   PetscCall(PetscFree2(pMin, pMax));
4323   PetscFunctionReturn(PETSC_SUCCESS);
4324 }
4325 
4326 static PetscErrorCode DMPlexStratify_Topological_Private(DM dm, DMLabel label)
4327 {
4328   PetscInt pStart, pEnd;
4329   PetscInt numRoots = 0, numLeaves = 0;
4330 
4331   PetscFunctionBegin;
4332   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4333   {
4334     /* Initialize roots and count leaves */
4335     PetscInt sMin = PETSC_MAX_INT;
4336     PetscInt sMax = PETSC_MIN_INT;
4337     PetscInt coneSize, supportSize;
4338 
4339     for (PetscInt p = pStart; p < pEnd; ++p) {
4340       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4341       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
4342       if (!coneSize && supportSize) {
4343         sMin = PetscMin(p, sMin);
4344         sMax = PetscMax(p, sMax);
4345         ++numRoots;
4346       } else if (!supportSize && coneSize) {
4347         ++numLeaves;
4348       } else if (!supportSize && !coneSize) {
4349         /* Isolated points */
4350         sMin = PetscMin(p, sMin);
4351         sMax = PetscMax(p, sMax);
4352       }
4353     }
4354     PetscCall(DMPlexCreateDepthStratum(dm, label, 0, sMin, sMax + 1));
4355   }
4356 
4357   if (numRoots + numLeaves == (pEnd - pStart)) {
4358     PetscInt sMin = PETSC_MAX_INT;
4359     PetscInt sMax = PETSC_MIN_INT;
4360     PetscInt coneSize, supportSize;
4361 
4362     for (PetscInt p = pStart; p < pEnd; ++p) {
4363       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4364       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
4365       if (!supportSize && coneSize) {
4366         sMin = PetscMin(p, sMin);
4367         sMax = PetscMax(p, sMax);
4368       }
4369     }
4370     PetscCall(DMPlexCreateDepthStratum(dm, label, 1, sMin, sMax + 1));
4371   } else {
4372     PetscInt level = 0;
4373     PetscInt qStart, qEnd;
4374 
4375     PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4376     while (qEnd > qStart) {
4377       PetscInt sMin = PETSC_MAX_INT;
4378       PetscInt sMax = PETSC_MIN_INT;
4379 
4380       for (PetscInt q = qStart; q < qEnd; ++q) {
4381         const PetscInt *support;
4382         PetscInt        supportSize;
4383 
4384         PetscCall(DMPlexGetSupportSize(dm, q, &supportSize));
4385         PetscCall(DMPlexGetSupport(dm, q, &support));
4386         for (PetscInt s = 0; s < supportSize; ++s) {
4387           sMin = PetscMin(support[s], sMin);
4388           sMax = PetscMax(support[s], sMax);
4389         }
4390       }
4391       PetscCall(DMLabelGetNumValues(label, &level));
4392       PetscCall(DMPlexCreateDepthStratum(dm, label, level, sMin, sMax + 1));
4393       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4394     }
4395   }
4396   PetscFunctionReturn(PETSC_SUCCESS);
4397 }
4398 
4399 /*@
4400   DMPlexStratify - Computes the strata for all points in the `DMPLEX`
4401 
4402   Collective
4403 
4404   Input Parameter:
4405 . dm - The `DMPLEX`
4406 
4407   Level: beginner
4408 
4409   Notes:
4410   The strata group all points of the same grade, and this function calculates the strata. This
4411   grade can be seen as the height (or depth) of the point in the DAG.
4412 
4413   The DAG for most topologies is a graded poset (https://en.wikipedia.org/wiki/Graded_poset), and
4414   can be illustrated by a Hasse Diagram (https://en.wikipedia.org/wiki/Hasse_diagram).
4415   Concretely, `DMPlexStratify()` creates a new label named "depth" containing the depth in the DAG of each point. For cell-vertex
4416   meshes, vertices are depth 0 and cells are depth 1. For fully interpolated meshes, depth 0 for vertices, 1 for edges, and so on
4417   until cells have depth equal to the dimension of the mesh. The depth label can be accessed through `DMPlexGetDepthLabel()` or `DMPlexGetDepthStratum()`, or
4418   manually via `DMGetLabel()`.  The height is defined implicitly by height = maxDimension - depth, and can be accessed
4419   via `DMPlexGetHeightStratum()`.  For example, cells have height 0 and faces have height 1.
4420 
4421   The depth of a point is calculated by executing a breadth-first search (BFS) on the DAG. This could produce surprising results
4422   if run on a partially interpolated mesh, meaning one that had some edges and faces, but not others. For example, suppose that
4423   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
4424   to interpolate only that one (e0), so that
4425 .vb
4426   cone(c0) = {e0, v2}
4427   cone(e0) = {v0, v1}
4428 .ve
4429   If `DMPlexStratify()` is run on this mesh, it will give depths
4430 .vb
4431    depth 0 = {v0, v1, v2}
4432    depth 1 = {e0, c0}
4433 .ve
4434   where the triangle has been given depth 1, instead of 2, because it is reachable from vertex v2.
4435 
4436   `DMPlexStratify()` should be called after all calls to `DMPlexSymmetrize()`
4437 
4438 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexComputeCellTypes()`
4439 @*/
4440 PetscErrorCode DMPlexStratify(DM dm)
4441 {
4442   DM_Plex  *mesh = (DM_Plex *)dm->data;
4443   DMLabel   label;
4444   PetscBool flg = PETSC_FALSE;
4445 
4446   PetscFunctionBegin;
4447   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4448   PetscCall(PetscLogEventBegin(DMPLEX_Stratify, dm, 0, 0, 0));
4449 
4450   // Create depth label
4451   PetscCall(DMRemoveLabel(dm, "depth", NULL));
4452   PetscCall(DMCreateLabel(dm, "depth"));
4453   PetscCall(DMPlexGetDepthLabel(dm, &label));
4454 
4455   PetscCall(PetscOptionsGetBool(NULL, dm->hdr.prefix, "-dm_plex_stratify_celltype", &flg, NULL));
4456   if (flg) PetscCall(DMPlexStratify_CellType_Private(dm, label));
4457   else PetscCall(DMPlexStratify_Topological_Private(dm, label));
4458 
4459   { /* just in case there is an empty process */
4460     PetscInt numValues, maxValues = 0, v;
4461 
4462     PetscCall(DMLabelGetNumValues(label, &numValues));
4463     PetscCall(MPIU_Allreduce(&numValues, &maxValues, 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
4464     for (v = numValues; v < maxValues; v++) PetscCall(DMLabelAddStratum(label, v));
4465   }
4466   PetscCall(PetscObjectStateGet((PetscObject)label, &mesh->depthState));
4467   PetscCall(PetscLogEventEnd(DMPLEX_Stratify, dm, 0, 0, 0));
4468   PetscFunctionReturn(PETSC_SUCCESS);
4469 }
4470 
4471 PetscErrorCode DMPlexComputeCellType_Internal(DM dm, PetscInt p, PetscInt pdepth, DMPolytopeType *pt)
4472 {
4473   DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4474   PetscInt       dim, depth, pheight, coneSize;
4475 
4476   PetscFunctionBeginHot;
4477   PetscCall(DMGetDimension(dm, &dim));
4478   PetscCall(DMPlexGetDepth(dm, &depth));
4479   PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4480   pheight = depth - pdepth;
4481   if (depth <= 1) {
4482     switch (pdepth) {
4483     case 0:
4484       ct = DM_POLYTOPE_POINT;
4485       break;
4486     case 1:
4487       switch (coneSize) {
4488       case 2:
4489         ct = DM_POLYTOPE_SEGMENT;
4490         break;
4491       case 3:
4492         ct = DM_POLYTOPE_TRIANGLE;
4493         break;
4494       case 4:
4495         switch (dim) {
4496         case 2:
4497           ct = DM_POLYTOPE_QUADRILATERAL;
4498           break;
4499         case 3:
4500           ct = DM_POLYTOPE_TETRAHEDRON;
4501           break;
4502         default:
4503           break;
4504         }
4505         break;
4506       case 5:
4507         ct = DM_POLYTOPE_PYRAMID;
4508         break;
4509       case 6:
4510         ct = DM_POLYTOPE_TRI_PRISM_TENSOR;
4511         break;
4512       case 8:
4513         ct = DM_POLYTOPE_HEXAHEDRON;
4514         break;
4515       default:
4516         break;
4517       }
4518     }
4519   } else {
4520     if (pdepth == 0) {
4521       ct = DM_POLYTOPE_POINT;
4522     } else if (pheight == 0) {
4523       switch (dim) {
4524       case 1:
4525         switch (coneSize) {
4526         case 2:
4527           ct = DM_POLYTOPE_SEGMENT;
4528           break;
4529         default:
4530           break;
4531         }
4532         break;
4533       case 2:
4534         switch (coneSize) {
4535         case 3:
4536           ct = DM_POLYTOPE_TRIANGLE;
4537           break;
4538         case 4:
4539           ct = DM_POLYTOPE_QUADRILATERAL;
4540           break;
4541         default:
4542           break;
4543         }
4544         break;
4545       case 3:
4546         switch (coneSize) {
4547         case 4:
4548           ct = DM_POLYTOPE_TETRAHEDRON;
4549           break;
4550         case 5: {
4551           const PetscInt *cone;
4552           PetscInt        faceConeSize;
4553 
4554           PetscCall(DMPlexGetCone(dm, p, &cone));
4555           PetscCall(DMPlexGetConeSize(dm, cone[0], &faceConeSize));
4556           switch (faceConeSize) {
4557           case 3:
4558             ct = DM_POLYTOPE_TRI_PRISM_TENSOR;
4559             break;
4560           case 4:
4561             ct = DM_POLYTOPE_PYRAMID;
4562             break;
4563           }
4564         } break;
4565         case 6:
4566           ct = DM_POLYTOPE_HEXAHEDRON;
4567           break;
4568         default:
4569           break;
4570         }
4571         break;
4572       default:
4573         break;
4574       }
4575     } else if (pheight > 0) {
4576       switch (coneSize) {
4577       case 2:
4578         ct = DM_POLYTOPE_SEGMENT;
4579         break;
4580       case 3:
4581         ct = DM_POLYTOPE_TRIANGLE;
4582         break;
4583       case 4:
4584         ct = DM_POLYTOPE_QUADRILATERAL;
4585         break;
4586       default:
4587         break;
4588       }
4589     }
4590   }
4591   *pt = ct;
4592   PetscFunctionReturn(PETSC_SUCCESS);
4593 }
4594 
4595 /*@
4596   DMPlexComputeCellTypes - Infer the polytope type of every cell using its dimension and cone size.
4597 
4598   Collective
4599 
4600   Input Parameter:
4601 . dm - The `DMPLEX`
4602 
4603   Level: developer
4604 
4605   Note:
4606   This function is normally called automatically when a cell type is requested. It creates an
4607   internal `DMLabel` named "celltype" which can be directly accessed using `DMGetLabel()`. A user may disable
4608   automatic creation by creating the label manually, using `DMCreateLabel`(dm, "celltype").
4609 
4610   `DMPlexComputeCellTypes()` should be called after all calls to `DMPlexSymmetrize()` and `DMPlexStratify()`
4611 
4612 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexStratify()`, `DMGetLabel()`, `DMCreateLabel()`
4613 @*/
4614 PetscErrorCode DMPlexComputeCellTypes(DM dm)
4615 {
4616   DM_Plex *mesh;
4617   DMLabel  ctLabel;
4618   PetscInt pStart, pEnd, p;
4619 
4620   PetscFunctionBegin;
4621   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4622   mesh = (DM_Plex *)dm->data;
4623   PetscCall(DMCreateLabel(dm, "celltype"));
4624   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
4625   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4626   PetscCall(PetscFree(mesh->cellTypes));
4627   PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
4628   for (p = pStart; p < pEnd; ++p) {
4629     DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4630     PetscInt       pdepth;
4631 
4632     PetscCall(DMPlexGetPointDepth(dm, p, &pdepth));
4633     PetscCall(DMPlexComputeCellType_Internal(dm, p, pdepth, &ct));
4634     PetscCheck(ct != DM_POLYTOPE_UNKNOWN && ct != DM_POLYTOPE_UNKNOWN_CELL && ct != DM_POLYTOPE_UNKNOWN_FACE, PETSC_COMM_SELF, PETSC_ERR_SUP, "Point %" PetscInt_FMT " is screwed up", p);
4635     PetscCall(DMLabelSetValue(ctLabel, p, ct));
4636     mesh->cellTypes[p - pStart].value_as_uint8 = ct;
4637   }
4638   PetscCall(PetscObjectStateGet((PetscObject)ctLabel, &mesh->celltypeState));
4639   PetscCall(PetscObjectViewFromOptions((PetscObject)ctLabel, NULL, "-dm_plex_celltypes_view"));
4640   PetscFunctionReturn(PETSC_SUCCESS);
4641 }
4642 
4643 /*@C
4644   DMPlexGetJoin - Get an array for the join of the set of points
4645 
4646   Not Collective
4647 
4648   Input Parameters:
4649 + dm        - The `DMPLEX` object
4650 . numPoints - The number of input points for the join
4651 - points    - The input points
4652 
4653   Output Parameters:
4654 + numCoveredPoints - The number of points in the join
4655 - coveredPoints    - The points in the join
4656 
4657   Level: intermediate
4658 
4659   Note:
4660   Currently, this is restricted to a single level join
4661 
4662   Fortran Notes:
4663   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4664 
4665 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4666 @*/
4667 PetscErrorCode DMPlexGetJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4668 {
4669   DM_Plex  *mesh = (DM_Plex *)dm->data;
4670   PetscInt *join[2];
4671   PetscInt  joinSize, i = 0;
4672   PetscInt  dof, off, p, c, m;
4673   PetscInt  maxSupportSize;
4674 
4675   PetscFunctionBegin;
4676   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4677   PetscAssertPointer(points, 3);
4678   PetscAssertPointer(numCoveredPoints, 4);
4679   PetscAssertPointer(coveredPoints, 5);
4680   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
4681   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[0]));
4682   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[1]));
4683   /* Copy in support of first point */
4684   PetscCall(PetscSectionGetDof(mesh->supportSection, points[0], &dof));
4685   PetscCall(PetscSectionGetOffset(mesh->supportSection, points[0], &off));
4686   for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = mesh->supports[off + joinSize];
4687   /* Check each successive support */
4688   for (p = 1; p < numPoints; ++p) {
4689     PetscInt newJoinSize = 0;
4690 
4691     PetscCall(PetscSectionGetDof(mesh->supportSection, points[p], &dof));
4692     PetscCall(PetscSectionGetOffset(mesh->supportSection, points[p], &off));
4693     for (c = 0; c < dof; ++c) {
4694       const PetscInt point = mesh->supports[off + c];
4695 
4696       for (m = 0; m < joinSize; ++m) {
4697         if (point == join[i][m]) {
4698           join[1 - i][newJoinSize++] = point;
4699           break;
4700         }
4701       }
4702     }
4703     joinSize = newJoinSize;
4704     i        = 1 - i;
4705   }
4706   *numCoveredPoints = joinSize;
4707   *coveredPoints    = join[i];
4708   PetscCall(DMRestoreWorkArray(dm, maxSupportSize, MPIU_INT, &join[1 - i]));
4709   PetscFunctionReturn(PETSC_SUCCESS);
4710 }
4711 
4712 /*@C
4713   DMPlexRestoreJoin - Restore an array for the join of the set of points
4714 
4715   Not Collective
4716 
4717   Input Parameters:
4718 + dm        - The `DMPLEX` object
4719 . numPoints - The number of input points for the join
4720 - points    - The input points
4721 
4722   Output Parameters:
4723 + numCoveredPoints - The number of points in the join
4724 - coveredPoints    - The points in the join
4725 
4726   Level: intermediate
4727 
4728   Fortran Notes:
4729   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4730 
4731 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetJoin()`, `DMPlexGetFullJoin()`, `DMPlexGetMeet()`
4732 @*/
4733 PetscErrorCode DMPlexRestoreJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4734 {
4735   PetscFunctionBegin;
4736   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4737   if (points) PetscAssertPointer(points, 3);
4738   if (numCoveredPoints) PetscAssertPointer(numCoveredPoints, 4);
4739   PetscAssertPointer(coveredPoints, 5);
4740   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
4741   if (numCoveredPoints) *numCoveredPoints = 0;
4742   PetscFunctionReturn(PETSC_SUCCESS);
4743 }
4744 
4745 /*@C
4746   DMPlexGetFullJoin - Get an array for the join of the set of points
4747 
4748   Not Collective
4749 
4750   Input Parameters:
4751 + dm        - The `DMPLEX` object
4752 . numPoints - The number of input points for the join
4753 - points    - The input points
4754 
4755   Output Parameters:
4756 + numCoveredPoints - The number of points in the join
4757 - coveredPoints    - The points in the join
4758 
4759   Level: intermediate
4760 
4761   Fortran Notes:
4762   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4763 
4764 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetJoin()`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4765 @*/
4766 PetscErrorCode DMPlexGetFullJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4767 {
4768   PetscInt *offsets, **closures;
4769   PetscInt *join[2];
4770   PetscInt  depth = 0, maxSize, joinSize = 0, i = 0;
4771   PetscInt  p, d, c, m, ms;
4772 
4773   PetscFunctionBegin;
4774   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4775   PetscAssertPointer(points, 3);
4776   PetscAssertPointer(numCoveredPoints, 4);
4777   PetscAssertPointer(coveredPoints, 5);
4778 
4779   PetscCall(DMPlexGetDepth(dm, &depth));
4780   PetscCall(PetscCalloc1(numPoints, &closures));
4781   PetscCall(DMGetWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
4782   PetscCall(DMPlexGetMaxSizes(dm, NULL, &ms));
4783   maxSize = (ms > 1) ? ((PetscPowInt(ms, depth + 1) - 1) / (ms - 1)) : depth + 1;
4784   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[0]));
4785   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[1]));
4786 
4787   for (p = 0; p < numPoints; ++p) {
4788     PetscInt closureSize;
4789 
4790     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_FALSE, &closureSize, &closures[p]));
4791 
4792     offsets[p * (depth + 2) + 0] = 0;
4793     for (d = 0; d < depth + 1; ++d) {
4794       PetscInt pStart, pEnd, i;
4795 
4796       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
4797       for (i = offsets[p * (depth + 2) + d]; i < closureSize; ++i) {
4798         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
4799           offsets[p * (depth + 2) + d + 1] = i;
4800           break;
4801         }
4802       }
4803       if (i == closureSize) offsets[p * (depth + 2) + d + 1] = i;
4804     }
4805     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);
4806   }
4807   for (d = 0; d < depth + 1; ++d) {
4808     PetscInt dof;
4809 
4810     /* Copy in support of first point */
4811     dof = offsets[d + 1] - offsets[d];
4812     for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = closures[0][(offsets[d] + joinSize) * 2];
4813     /* Check each successive cone */
4814     for (p = 1; p < numPoints && joinSize; ++p) {
4815       PetscInt newJoinSize = 0;
4816 
4817       dof = offsets[p * (depth + 2) + d + 1] - offsets[p * (depth + 2) + d];
4818       for (c = 0; c < dof; ++c) {
4819         const PetscInt point = closures[p][(offsets[p * (depth + 2) + d] + c) * 2];
4820 
4821         for (m = 0; m < joinSize; ++m) {
4822           if (point == join[i][m]) {
4823             join[1 - i][newJoinSize++] = point;
4824             break;
4825           }
4826         }
4827       }
4828       joinSize = newJoinSize;
4829       i        = 1 - i;
4830     }
4831     if (joinSize) break;
4832   }
4833   *numCoveredPoints = joinSize;
4834   *coveredPoints    = join[i];
4835   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_FALSE, NULL, &closures[p]));
4836   PetscCall(PetscFree(closures));
4837   PetscCall(DMRestoreWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
4838   PetscCall(DMRestoreWorkArray(dm, ms, MPIU_INT, &join[1 - i]));
4839   PetscFunctionReturn(PETSC_SUCCESS);
4840 }
4841 
4842 /*@C
4843   DMPlexGetMeet - Get an array for the meet of the set of points
4844 
4845   Not Collective
4846 
4847   Input Parameters:
4848 + dm        - The `DMPLEX` object
4849 . numPoints - The number of input points for the meet
4850 - points    - The input points
4851 
4852   Output Parameters:
4853 + numCoveringPoints - The number of points in the meet
4854 - coveringPoints    - The points in the meet
4855 
4856   Level: intermediate
4857 
4858   Note:
4859   Currently, this is restricted to a single level meet
4860 
4861   Fortran Notes:
4862   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4863 
4864 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
4865 @*/
4866 PetscErrorCode DMPlexGetMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveringPoints, const PetscInt **coveringPoints)
4867 {
4868   DM_Plex  *mesh = (DM_Plex *)dm->data;
4869   PetscInt *meet[2];
4870   PetscInt  meetSize, i = 0;
4871   PetscInt  dof, off, p, c, m;
4872   PetscInt  maxConeSize;
4873 
4874   PetscFunctionBegin;
4875   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4876   PetscAssertPointer(points, 3);
4877   PetscAssertPointer(numCoveringPoints, 4);
4878   PetscAssertPointer(coveringPoints, 5);
4879   PetscCall(PetscSectionGetMaxDof(mesh->coneSection, &maxConeSize));
4880   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[0]));
4881   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[1]));
4882   /* Copy in cone of first point */
4883   PetscCall(PetscSectionGetDof(mesh->coneSection, points[0], &dof));
4884   PetscCall(PetscSectionGetOffset(mesh->coneSection, points[0], &off));
4885   for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = mesh->cones[off + meetSize];
4886   /* Check each successive cone */
4887   for (p = 1; p < numPoints; ++p) {
4888     PetscInt newMeetSize = 0;
4889 
4890     PetscCall(PetscSectionGetDof(mesh->coneSection, points[p], &dof));
4891     PetscCall(PetscSectionGetOffset(mesh->coneSection, points[p], &off));
4892     for (c = 0; c < dof; ++c) {
4893       const PetscInt point = mesh->cones[off + c];
4894 
4895       for (m = 0; m < meetSize; ++m) {
4896         if (point == meet[i][m]) {
4897           meet[1 - i][newMeetSize++] = point;
4898           break;
4899         }
4900       }
4901     }
4902     meetSize = newMeetSize;
4903     i        = 1 - i;
4904   }
4905   *numCoveringPoints = meetSize;
4906   *coveringPoints    = meet[i];
4907   PetscCall(DMRestoreWorkArray(dm, maxConeSize, MPIU_INT, &meet[1 - i]));
4908   PetscFunctionReturn(PETSC_SUCCESS);
4909 }
4910 
4911 /*@C
4912   DMPlexRestoreMeet - Restore an array for the meet of the set of points
4913 
4914   Not Collective
4915 
4916   Input Parameters:
4917 + dm        - The `DMPLEX` object
4918 . numPoints - The number of input points for the meet
4919 - points    - The input points
4920 
4921   Output Parameters:
4922 + numCoveredPoints - The number of points in the meet
4923 - coveredPoints    - The points in the meet
4924 
4925   Level: intermediate
4926 
4927   Fortran Notes:
4928   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4929 
4930 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetMeet()`, `DMPlexGetFullMeet()`, `DMPlexGetJoin()`
4931 @*/
4932 PetscErrorCode DMPlexRestoreMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4933 {
4934   PetscFunctionBegin;
4935   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4936   if (points) PetscAssertPointer(points, 3);
4937   if (numCoveredPoints) PetscAssertPointer(numCoveredPoints, 4);
4938   PetscAssertPointer(coveredPoints, 5);
4939   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
4940   if (numCoveredPoints) *numCoveredPoints = 0;
4941   PetscFunctionReturn(PETSC_SUCCESS);
4942 }
4943 
4944 /*@C
4945   DMPlexGetFullMeet - Get an array for the meet of the set of points
4946 
4947   Not Collective
4948 
4949   Input Parameters:
4950 + dm        - The `DMPLEX` object
4951 . numPoints - The number of input points for the meet
4952 - points    - The input points
4953 
4954   Output Parameters:
4955 + numCoveredPoints - The number of points in the meet
4956 - coveredPoints    - The points in the meet
4957 
4958   Level: intermediate
4959 
4960   Fortran Notes:
4961   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4962 
4963 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetMeet()`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
4964 @*/
4965 PetscErrorCode DMPlexGetFullMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4966 {
4967   PetscInt *offsets, **closures;
4968   PetscInt *meet[2];
4969   PetscInt  height = 0, maxSize, meetSize = 0, i = 0;
4970   PetscInt  p, h, c, m, mc;
4971 
4972   PetscFunctionBegin;
4973   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4974   PetscAssertPointer(points, 3);
4975   PetscAssertPointer(numCoveredPoints, 4);
4976   PetscAssertPointer(coveredPoints, 5);
4977 
4978   PetscCall(DMPlexGetDepth(dm, &height));
4979   PetscCall(PetscMalloc1(numPoints, &closures));
4980   PetscCall(DMGetWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
4981   PetscCall(DMPlexGetMaxSizes(dm, &mc, NULL));
4982   maxSize = (mc > 1) ? ((PetscPowInt(mc, height + 1) - 1) / (mc - 1)) : height + 1;
4983   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[0]));
4984   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[1]));
4985 
4986   for (p = 0; p < numPoints; ++p) {
4987     PetscInt closureSize;
4988 
4989     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_TRUE, &closureSize, &closures[p]));
4990 
4991     offsets[p * (height + 2) + 0] = 0;
4992     for (h = 0; h < height + 1; ++h) {
4993       PetscInt pStart, pEnd, i;
4994 
4995       PetscCall(DMPlexGetHeightStratum(dm, h, &pStart, &pEnd));
4996       for (i = offsets[p * (height + 2) + h]; i < closureSize; ++i) {
4997         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
4998           offsets[p * (height + 2) + h + 1] = i;
4999           break;
5000         }
5001       }
5002       if (i == closureSize) offsets[p * (height + 2) + h + 1] = i;
5003     }
5004     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);
5005   }
5006   for (h = 0; h < height + 1; ++h) {
5007     PetscInt dof;
5008 
5009     /* Copy in cone of first point */
5010     dof = offsets[h + 1] - offsets[h];
5011     for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = closures[0][(offsets[h] + meetSize) * 2];
5012     /* Check each successive cone */
5013     for (p = 1; p < numPoints && meetSize; ++p) {
5014       PetscInt newMeetSize = 0;
5015 
5016       dof = offsets[p * (height + 2) + h + 1] - offsets[p * (height + 2) + h];
5017       for (c = 0; c < dof; ++c) {
5018         const PetscInt point = closures[p][(offsets[p * (height + 2) + h] + c) * 2];
5019 
5020         for (m = 0; m < meetSize; ++m) {
5021           if (point == meet[i][m]) {
5022             meet[1 - i][newMeetSize++] = point;
5023             break;
5024           }
5025         }
5026       }
5027       meetSize = newMeetSize;
5028       i        = 1 - i;
5029     }
5030     if (meetSize) break;
5031   }
5032   *numCoveredPoints = meetSize;
5033   *coveredPoints    = meet[i];
5034   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_TRUE, NULL, &closures[p]));
5035   PetscCall(PetscFree(closures));
5036   PetscCall(DMRestoreWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
5037   PetscCall(DMRestoreWorkArray(dm, mc, MPIU_INT, &meet[1 - i]));
5038   PetscFunctionReturn(PETSC_SUCCESS);
5039 }
5040 
5041 /*@C
5042   DMPlexEqual - Determine if two `DM` have the same topology
5043 
5044   Not Collective
5045 
5046   Input Parameters:
5047 + dmA - A `DMPLEX` object
5048 - dmB - A `DMPLEX` object
5049 
5050   Output Parameter:
5051 . equal - `PETSC_TRUE` if the topologies are identical
5052 
5053   Level: intermediate
5054 
5055   Note:
5056   We are not solving graph isomorphism, so we do not permute.
5057 
5058 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCone()`
5059 @*/
5060 PetscErrorCode DMPlexEqual(DM dmA, DM dmB, PetscBool *equal)
5061 {
5062   PetscInt depth, depthB, pStart, pEnd, pStartB, pEndB, p;
5063 
5064   PetscFunctionBegin;
5065   PetscValidHeaderSpecific(dmA, DM_CLASSID, 1);
5066   PetscValidHeaderSpecific(dmB, DM_CLASSID, 2);
5067   PetscAssertPointer(equal, 3);
5068 
5069   *equal = PETSC_FALSE;
5070   PetscCall(DMPlexGetDepth(dmA, &depth));
5071   PetscCall(DMPlexGetDepth(dmB, &depthB));
5072   if (depth != depthB) PetscFunctionReturn(PETSC_SUCCESS);
5073   PetscCall(DMPlexGetChart(dmA, &pStart, &pEnd));
5074   PetscCall(DMPlexGetChart(dmB, &pStartB, &pEndB));
5075   if ((pStart != pStartB) || (pEnd != pEndB)) PetscFunctionReturn(PETSC_SUCCESS);
5076   for (p = pStart; p < pEnd; ++p) {
5077     const PetscInt *cone, *coneB, *ornt, *orntB, *support, *supportB;
5078     PetscInt        coneSize, coneSizeB, c, supportSize, supportSizeB, s;
5079 
5080     PetscCall(DMPlexGetConeSize(dmA, p, &coneSize));
5081     PetscCall(DMPlexGetCone(dmA, p, &cone));
5082     PetscCall(DMPlexGetConeOrientation(dmA, p, &ornt));
5083     PetscCall(DMPlexGetConeSize(dmB, p, &coneSizeB));
5084     PetscCall(DMPlexGetCone(dmB, p, &coneB));
5085     PetscCall(DMPlexGetConeOrientation(dmB, p, &orntB));
5086     if (coneSize != coneSizeB) PetscFunctionReturn(PETSC_SUCCESS);
5087     for (c = 0; c < coneSize; ++c) {
5088       if (cone[c] != coneB[c]) PetscFunctionReturn(PETSC_SUCCESS);
5089       if (ornt[c] != orntB[c]) PetscFunctionReturn(PETSC_SUCCESS);
5090     }
5091     PetscCall(DMPlexGetSupportSize(dmA, p, &supportSize));
5092     PetscCall(DMPlexGetSupport(dmA, p, &support));
5093     PetscCall(DMPlexGetSupportSize(dmB, p, &supportSizeB));
5094     PetscCall(DMPlexGetSupport(dmB, p, &supportB));
5095     if (supportSize != supportSizeB) PetscFunctionReturn(PETSC_SUCCESS);
5096     for (s = 0; s < supportSize; ++s) {
5097       if (support[s] != supportB[s]) PetscFunctionReturn(PETSC_SUCCESS);
5098     }
5099   }
5100   *equal = PETSC_TRUE;
5101   PetscFunctionReturn(PETSC_SUCCESS);
5102 }
5103 
5104 /*@C
5105   DMPlexGetNumFaceVertices - Returns the number of vertices on a face
5106 
5107   Not Collective
5108 
5109   Input Parameters:
5110 + dm         - The `DMPLEX`
5111 . cellDim    - The cell dimension
5112 - numCorners - The number of vertices on a cell
5113 
5114   Output Parameter:
5115 . numFaceVertices - The number of vertices on a face
5116 
5117   Level: developer
5118 
5119   Note:
5120   Of course this can only work for a restricted set of symmetric shapes
5121 
5122 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCone()`
5123 @*/
5124 PetscErrorCode DMPlexGetNumFaceVertices(DM dm, PetscInt cellDim, PetscInt numCorners, PetscInt *numFaceVertices)
5125 {
5126   MPI_Comm comm;
5127 
5128   PetscFunctionBegin;
5129   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
5130   PetscAssertPointer(numFaceVertices, 4);
5131   switch (cellDim) {
5132   case 0:
5133     *numFaceVertices = 0;
5134     break;
5135   case 1:
5136     *numFaceVertices = 1;
5137     break;
5138   case 2:
5139     switch (numCorners) {
5140     case 3:                 /* triangle */
5141       *numFaceVertices = 2; /* Edge has 2 vertices */
5142       break;
5143     case 4:                 /* quadrilateral */
5144       *numFaceVertices = 2; /* Edge has 2 vertices */
5145       break;
5146     case 6:                 /* quadratic triangle, tri and quad cohesive Lagrange cells */
5147       *numFaceVertices = 3; /* Edge has 3 vertices */
5148       break;
5149     case 9:                 /* quadratic quadrilateral, quadratic quad cohesive Lagrange cells */
5150       *numFaceVertices = 3; /* Edge has 3 vertices */
5151       break;
5152     default:
5153       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
5154     }
5155     break;
5156   case 3:
5157     switch (numCorners) {
5158     case 4:                 /* tetradehdron */
5159       *numFaceVertices = 3; /* Face has 3 vertices */
5160       break;
5161     case 6:                 /* tet cohesive cells */
5162       *numFaceVertices = 4; /* Face has 4 vertices */
5163       break;
5164     case 8:                 /* hexahedron */
5165       *numFaceVertices = 4; /* Face has 4 vertices */
5166       break;
5167     case 9:                 /* tet cohesive Lagrange cells */
5168       *numFaceVertices = 6; /* Face has 6 vertices */
5169       break;
5170     case 10:                /* quadratic tetrahedron */
5171       *numFaceVertices = 6; /* Face has 6 vertices */
5172       break;
5173     case 12:                /* hex cohesive Lagrange cells */
5174       *numFaceVertices = 6; /* Face has 6 vertices */
5175       break;
5176     case 18:                /* quadratic tet cohesive Lagrange cells */
5177       *numFaceVertices = 6; /* Face has 6 vertices */
5178       break;
5179     case 27:                /* quadratic hexahedron, quadratic hex cohesive Lagrange cells */
5180       *numFaceVertices = 9; /* Face has 9 vertices */
5181       break;
5182     default:
5183       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
5184     }
5185     break;
5186   default:
5187     SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid cell dimension %" PetscInt_FMT, cellDim);
5188   }
5189   PetscFunctionReturn(PETSC_SUCCESS);
5190 }
5191 
5192 /*@
5193   DMPlexGetDepthLabel - Get the `DMLabel` recording the depth of each point
5194 
5195   Not Collective
5196 
5197   Input Parameter:
5198 . dm - The `DMPLEX` object
5199 
5200   Output Parameter:
5201 . depthLabel - The `DMLabel` recording point depth
5202 
5203   Level: developer
5204 
5205 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepth()`, `DMPlexGetHeightStratum()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`,
5206 @*/
5207 PetscErrorCode DMPlexGetDepthLabel(DM dm, DMLabel *depthLabel)
5208 {
5209   PetscFunctionBegin;
5210   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5211   PetscAssertPointer(depthLabel, 2);
5212   *depthLabel = dm->depthLabel;
5213   PetscFunctionReturn(PETSC_SUCCESS);
5214 }
5215 
5216 /*@
5217   DMPlexGetDepth - Get the depth of the DAG representing this mesh
5218 
5219   Not Collective
5220 
5221   Input Parameter:
5222 . dm - The `DMPLEX` object
5223 
5224   Output Parameter:
5225 . depth - The number of strata (breadth first levels) in the DAG
5226 
5227   Level: developer
5228 
5229   Notes:
5230   This returns maximum of point depths over all points, i.e. maximum value of the label returned by `DMPlexGetDepthLabel()`.
5231 
5232   The point depth is described more in detail in `DMPlexGetDepthStratum()`.
5233 
5234   An empty mesh gives -1.
5235 
5236 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepthLabel()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`
5237 @*/
5238 PetscErrorCode DMPlexGetDepth(DM dm, PetscInt *depth)
5239 {
5240   DM_Plex *mesh = (DM_Plex *)dm->data;
5241   DMLabel  label;
5242   PetscInt d = 0;
5243 
5244   PetscFunctionBegin;
5245   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5246   PetscAssertPointer(depth, 2);
5247   if (mesh->tr) {
5248     PetscCall(DMPlexTransformGetDepth(mesh->tr, depth));
5249   } else {
5250     PetscCall(DMPlexGetDepthLabel(dm, &label));
5251     if (label) PetscCall(DMLabelGetNumValues(label, &d));
5252     *depth = d - 1;
5253   }
5254   PetscFunctionReturn(PETSC_SUCCESS);
5255 }
5256 
5257 /*@
5258   DMPlexGetDepthStratum - Get the bounds [`start`, `end`) for all points at a certain depth.
5259 
5260   Not Collective
5261 
5262   Input Parameters:
5263 + dm    - The `DMPLEX` object
5264 - depth - The requested depth
5265 
5266   Output Parameters:
5267 + start - The first point at this `depth`
5268 - end   - One beyond the last point at this `depth`
5269 
5270   Level: developer
5271 
5272   Notes:
5273   Depth indexing is related to topological dimension.  Depth stratum 0 contains the lowest topological dimension points,
5274   often "vertices".  If the mesh is "interpolated" (see `DMPlexInterpolate()`), then depth stratum 1 contains the next
5275   higher dimension, e.g., "edges".
5276 
5277 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetHeightStratum()`, `DMPlexGetCellTypeStratum()`, `DMPlexGetDepth()`, `DMPlexGetDepthLabel()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`, `DMPlexInterpolate()`
5278 @*/
5279 PetscErrorCode DMPlexGetDepthStratum(DM dm, PetscInt depth, PetscInt *start, PetscInt *end)
5280 {
5281   DM_Plex *mesh = (DM_Plex *)dm->data;
5282   DMLabel  label;
5283   PetscInt pStart, pEnd;
5284 
5285   PetscFunctionBegin;
5286   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5287   if (start) {
5288     PetscAssertPointer(start, 3);
5289     *start = 0;
5290   }
5291   if (end) {
5292     PetscAssertPointer(end, 4);
5293     *end = 0;
5294   }
5295   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
5296   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
5297   if (depth < 0) {
5298     if (start) *start = pStart;
5299     if (end) *end = pEnd;
5300     PetscFunctionReturn(PETSC_SUCCESS);
5301   }
5302   if (mesh->tr) {
5303     PetscCall(DMPlexTransformGetDepthStratum(mesh->tr, depth, start, end));
5304   } else {
5305     PetscCall(DMPlexGetDepthLabel(dm, &label));
5306     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named depth was found");
5307     PetscCall(DMLabelGetStratumBounds(label, depth, start, end));
5308   }
5309   PetscFunctionReturn(PETSC_SUCCESS);
5310 }
5311 
5312 /*@
5313   DMPlexGetHeightStratum - Get the bounds [`start`, `end`) for all points at a certain height.
5314 
5315   Not Collective
5316 
5317   Input Parameters:
5318 + dm     - The `DMPLEX` object
5319 - height - The requested height
5320 
5321   Output Parameters:
5322 + start - The first point at this `height`
5323 - end   - One beyond the last point at this `height`
5324 
5325   Level: developer
5326 
5327   Notes:
5328   Height indexing is related to topological codimension.  Height stratum 0 contains the highest topological dimension
5329   points, often called "cells" or "elements".  If the mesh is "interpolated" (see `DMPlexInterpolate()`), then height
5330   stratum 1 contains the boundary of these "cells", often called "faces" or "facets".
5331 
5332 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepthStratum()`, `DMPlexGetCellTypeStratum()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
5333 @*/
5334 PetscErrorCode DMPlexGetHeightStratum(DM dm, PetscInt height, PetscInt *start, PetscInt *end)
5335 {
5336   DMLabel  label;
5337   PetscInt depth, pStart, pEnd;
5338 
5339   PetscFunctionBegin;
5340   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5341   if (start) {
5342     PetscAssertPointer(start, 3);
5343     *start = 0;
5344   }
5345   if (end) {
5346     PetscAssertPointer(end, 4);
5347     *end = 0;
5348   }
5349   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
5350   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
5351   if (height < 0) {
5352     if (start) *start = pStart;
5353     if (end) *end = pEnd;
5354     PetscFunctionReturn(PETSC_SUCCESS);
5355   }
5356   PetscCall(DMPlexGetDepthLabel(dm, &label));
5357   if (label) PetscCall(DMLabelGetNumValues(label, &depth));
5358   else PetscCall(DMGetDimension(dm, &depth));
5359   PetscCheck(depth >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Depth not yet computed");
5360   PetscCall(DMPlexGetDepthStratum(dm, depth - 1 - height, start, end));
5361   PetscFunctionReturn(PETSC_SUCCESS);
5362 }
5363 
5364 /*@
5365   DMPlexGetPointDepth - Get the `depth` of a given point
5366 
5367   Not Collective
5368 
5369   Input Parameters:
5370 + dm    - The `DMPLEX` object
5371 - point - The point
5372 
5373   Output Parameter:
5374 . depth - The depth of the `point`
5375 
5376   Level: intermediate
5377 
5378 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
5379 @*/
5380 PetscErrorCode DMPlexGetPointDepth(DM dm, PetscInt point, PetscInt *depth)
5381 {
5382   PetscFunctionBegin;
5383   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5384   PetscAssertPointer(depth, 3);
5385   PetscCall(DMLabelGetValue(dm->depthLabel, point, depth));
5386   PetscFunctionReturn(PETSC_SUCCESS);
5387 }
5388 
5389 /*@
5390   DMPlexGetPointHeight - Get the `height` of a given point
5391 
5392   Not Collective
5393 
5394   Input Parameters:
5395 + dm    - The `DMPLEX` object
5396 - point - The point
5397 
5398   Output Parameter:
5399 . height - The height of the `point`
5400 
5401   Level: intermediate
5402 
5403 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointDepth()`
5404 @*/
5405 PetscErrorCode DMPlexGetPointHeight(DM dm, PetscInt point, PetscInt *height)
5406 {
5407   PetscInt n, pDepth;
5408 
5409   PetscFunctionBegin;
5410   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5411   PetscAssertPointer(height, 3);
5412   PetscCall(DMLabelGetNumValues(dm->depthLabel, &n));
5413   PetscCall(DMLabelGetValue(dm->depthLabel, point, &pDepth));
5414   *height = n - 1 - pDepth; /* DAG depth is n-1 */
5415   PetscFunctionReturn(PETSC_SUCCESS);
5416 }
5417 
5418 /*@
5419   DMPlexGetCellTypeLabel - Get the `DMLabel` recording the polytope type of each cell
5420 
5421   Not Collective
5422 
5423   Input Parameter:
5424 . dm - The `DMPLEX` object
5425 
5426   Output Parameter:
5427 . celltypeLabel - The `DMLabel` recording cell polytope type
5428 
5429   Level: developer
5430 
5431   Note:
5432   This function will trigger automatica computation of cell types. This can be disabled by calling
5433   `DMCreateLabel`(dm, "celltype") beforehand.
5434 
5435 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMCreateLabel()`
5436 @*/
5437 PetscErrorCode DMPlexGetCellTypeLabel(DM dm, DMLabel *celltypeLabel)
5438 {
5439   PetscFunctionBegin;
5440   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5441   PetscAssertPointer(celltypeLabel, 2);
5442   if (!dm->celltypeLabel) PetscCall(DMPlexComputeCellTypes(dm));
5443   *celltypeLabel = dm->celltypeLabel;
5444   PetscFunctionReturn(PETSC_SUCCESS);
5445 }
5446 
5447 /*@
5448   DMPlexGetCellType - Get the polytope type of a given cell
5449 
5450   Not Collective
5451 
5452   Input Parameters:
5453 + dm   - The `DMPLEX` object
5454 - cell - The cell
5455 
5456   Output Parameter:
5457 . celltype - The polytope type of the cell
5458 
5459   Level: intermediate
5460 
5461 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPolytopeType`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`
5462 @*/
5463 PetscErrorCode DMPlexGetCellType(DM dm, PetscInt cell, DMPolytopeType *celltype)
5464 {
5465   DM_Plex *mesh = (DM_Plex *)dm->data;
5466   DMLabel  label;
5467   PetscInt ct;
5468 
5469   PetscFunctionBegin;
5470   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5471   PetscAssertPointer(celltype, 3);
5472   if (mesh->tr) {
5473     PetscCall(DMPlexTransformGetCellType(mesh->tr, cell, celltype));
5474   } else {
5475     PetscInt pStart, pEnd;
5476 
5477     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, NULL));
5478     if (!mesh->cellTypes) { /* XXX remove? optimize? */
5479       PetscCall(PetscSectionGetChart(mesh->coneSection, NULL, &pEnd));
5480       PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
5481       PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5482       for (PetscInt p = pStart; p < pEnd; p++) {
5483         PetscCall(DMLabelGetValue(label, p, &ct));
5484         mesh->cellTypes[p - pStart].value_as_uint8 = (DMPolytopeType)ct;
5485       }
5486     }
5487     *celltype = (DMPolytopeType)mesh->cellTypes[cell - pStart].value_as_uint8;
5488     if (PetscDefined(USE_DEBUG)) {
5489       PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5490       PetscCall(DMLabelGetValue(label, cell, &ct));
5491       PetscCheck(ct >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Cell %" PetscInt_FMT " has not been assigned a cell type", cell);
5492       PetscCheck(ct == (PetscInt)*celltype, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid cellType for %" PetscInt_FMT ": %d != %" PetscInt_FMT, cell, (int)*celltype, ct);
5493     }
5494   }
5495   PetscFunctionReturn(PETSC_SUCCESS);
5496 }
5497 
5498 /*@
5499   DMPlexSetCellType - Set the polytope type of a given cell
5500 
5501   Not Collective
5502 
5503   Input Parameters:
5504 + dm       - The `DMPLEX` object
5505 . cell     - The cell
5506 - celltype - The polytope type of the cell
5507 
5508   Level: advanced
5509 
5510   Note:
5511   By default, cell types will be automatically computed using `DMPlexComputeCellTypes()` before this function
5512   is executed. This function will override the computed type. However, if automatic classification will not succeed
5513   and a user wants to manually specify all types, the classification must be disabled by calling
5514   DMCreateLabel(dm, "celltype") before getting or setting any cell types.
5515 
5516 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexComputeCellTypes()`, `DMCreateLabel()`
5517 @*/
5518 PetscErrorCode DMPlexSetCellType(DM dm, PetscInt cell, DMPolytopeType celltype)
5519 {
5520   DM_Plex *mesh = (DM_Plex *)dm->data;
5521   DMLabel  label;
5522   PetscInt pStart, pEnd;
5523 
5524   PetscFunctionBegin;
5525   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5526   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
5527   PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5528   PetscCall(DMLabelSetValue(label, cell, celltype));
5529   if (!mesh->cellTypes) PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
5530   mesh->cellTypes[cell - pStart].value_as_uint8 = celltype;
5531   PetscFunctionReturn(PETSC_SUCCESS);
5532 }
5533 
5534 PetscErrorCode DMCreateCoordinateDM_Plex(DM dm, DM *cdm)
5535 {
5536   PetscSection section;
5537   PetscInt     maxHeight;
5538   const char  *prefix;
5539 
5540   PetscFunctionBegin;
5541   PetscCall(DMClone(dm, cdm));
5542   PetscCall(PetscObjectGetOptionsPrefix((PetscObject)dm, &prefix));
5543   PetscCall(PetscObjectSetOptionsPrefix((PetscObject)*cdm, prefix));
5544   PetscCall(PetscObjectAppendOptionsPrefix((PetscObject)*cdm, "cdm_"));
5545   PetscCall(DMPlexGetMaxProjectionHeight(dm, &maxHeight));
5546   PetscCall(DMPlexSetMaxProjectionHeight(*cdm, maxHeight));
5547   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
5548   PetscCall(DMSetLocalSection(*cdm, section));
5549   PetscCall(PetscSectionDestroy(&section));
5550 
5551   PetscCall(DMSetNumFields(*cdm, 1));
5552   PetscCall(DMCreateDS(*cdm));
5553   (*cdm)->cloneOpts = PETSC_TRUE;
5554   if (dm->setfromoptionscalled) PetscCall(DMSetFromOptions(*cdm));
5555   PetscFunctionReturn(PETSC_SUCCESS);
5556 }
5557 
5558 PetscErrorCode DMCreateCoordinateField_Plex(DM dm, DMField *field)
5559 {
5560   Vec coordsLocal, cellCoordsLocal;
5561   DM  coordsDM, cellCoordsDM;
5562 
5563   PetscFunctionBegin;
5564   *field = NULL;
5565   PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
5566   PetscCall(DMGetCoordinateDM(dm, &coordsDM));
5567   PetscCall(DMGetCellCoordinatesLocal(dm, &cellCoordsLocal));
5568   PetscCall(DMGetCellCoordinateDM(dm, &cellCoordsDM));
5569   if (coordsLocal && coordsDM) {
5570     if (cellCoordsLocal && cellCoordsDM) PetscCall(DMFieldCreateDSWithDG(coordsDM, cellCoordsDM, 0, coordsLocal, cellCoordsLocal, field));
5571     else PetscCall(DMFieldCreateDS(coordsDM, 0, coordsLocal, field));
5572   }
5573   PetscFunctionReturn(PETSC_SUCCESS);
5574 }
5575 
5576 /*@C
5577   DMPlexGetConeSection - Return a section which describes the layout of cone data
5578 
5579   Not Collective
5580 
5581   Input Parameter:
5582 . dm - The `DMPLEX` object
5583 
5584   Output Parameter:
5585 . section - The `PetscSection` object
5586 
5587   Level: developer
5588 
5589 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSupportSection()`, `DMPlexGetCones()`, `DMPlexGetConeOrientations()`, `PetscSection`
5590 @*/
5591 PetscErrorCode DMPlexGetConeSection(DM dm, PetscSection *section)
5592 {
5593   DM_Plex *mesh = (DM_Plex *)dm->data;
5594 
5595   PetscFunctionBegin;
5596   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5597   if (section) *section = mesh->coneSection;
5598   PetscFunctionReturn(PETSC_SUCCESS);
5599 }
5600 
5601 /*@C
5602   DMPlexGetSupportSection - Return a section which describes the layout of support data
5603 
5604   Not Collective
5605 
5606   Input Parameter:
5607 . dm - The `DMPLEX` object
5608 
5609   Output Parameter:
5610 . section - The `PetscSection` object
5611 
5612   Level: developer
5613 
5614 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `PetscSection`
5615 @*/
5616 PetscErrorCode DMPlexGetSupportSection(DM dm, PetscSection *section)
5617 {
5618   DM_Plex *mesh = (DM_Plex *)dm->data;
5619 
5620   PetscFunctionBegin;
5621   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5622   if (section) *section = mesh->supportSection;
5623   PetscFunctionReturn(PETSC_SUCCESS);
5624 }
5625 
5626 /*@C
5627   DMPlexGetCones - Return cone data
5628 
5629   Not Collective
5630 
5631   Input Parameter:
5632 . dm - The `DMPLEX` object
5633 
5634   Output Parameter:
5635 . cones - The cone for each point
5636 
5637   Level: developer
5638 
5639 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`
5640 @*/
5641 PetscErrorCode DMPlexGetCones(DM dm, PetscInt *cones[])
5642 {
5643   DM_Plex *mesh = (DM_Plex *)dm->data;
5644 
5645   PetscFunctionBegin;
5646   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5647   if (cones) *cones = mesh->cones;
5648   PetscFunctionReturn(PETSC_SUCCESS);
5649 }
5650 
5651 /*@C
5652   DMPlexGetConeOrientations - Return cone orientation data
5653 
5654   Not Collective
5655 
5656   Input Parameter:
5657 . dm - The `DMPLEX` object
5658 
5659   Output Parameter:
5660 . coneOrientations - The array of cone orientations for all points
5661 
5662   Level: developer
5663 
5664   Notes:
5665   The `PetscSection` returned by `DMPlexGetConeSection()` partitions coneOrientations into cone orientations of particular points as returned by `DMPlexGetConeOrientation()`.
5666 
5667   The meaning of coneOrientations values is detailed in `DMPlexGetConeOrientation()`.
5668 
5669 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `DMPlexGetConeOrientation()`, `PetscSection`
5670 @*/
5671 PetscErrorCode DMPlexGetConeOrientations(DM dm, PetscInt *coneOrientations[])
5672 {
5673   DM_Plex *mesh = (DM_Plex *)dm->data;
5674 
5675   PetscFunctionBegin;
5676   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5677   if (coneOrientations) *coneOrientations = mesh->coneOrientations;
5678   PetscFunctionReturn(PETSC_SUCCESS);
5679 }
5680 
5681 /******************************** FEM Support **********************************/
5682 
5683 PetscErrorCode DMPlexGetAllCells_Internal(DM plex, IS *cellIS)
5684 {
5685   PetscInt depth;
5686 
5687   PetscFunctionBegin;
5688   PetscCall(DMPlexGetDepth(plex, &depth));
5689   PetscCall(DMGetStratumIS(plex, "dim", depth, cellIS));
5690   if (!*cellIS) PetscCall(DMGetStratumIS(plex, "depth", depth, cellIS));
5691   PetscFunctionReturn(PETSC_SUCCESS);
5692 }
5693 
5694 PetscErrorCode DMPlexGetAllFaces_Internal(DM plex, IS *faceIS)
5695 {
5696   PetscInt depth;
5697 
5698   PetscFunctionBegin;
5699   PetscCall(DMPlexGetDepth(plex, &depth));
5700   PetscCall(DMGetStratumIS(plex, "dim", depth - 1, faceIS));
5701   if (!*faceIS) PetscCall(DMGetStratumIS(plex, "depth", depth - 1, faceIS));
5702   PetscFunctionReturn(PETSC_SUCCESS);
5703 }
5704 
5705 /*
5706  Returns number of components and tensor degree for the field.  For interpolated meshes, line should be a point
5707  representing a line in the section.
5708 */
5709 static PetscErrorCode PetscSectionFieldGetTensorDegree_Private(DM dm, PetscSection section, PetscInt field, PetscInt line, PetscInt *Nc, PetscInt *k, PetscBool *continuous, PetscBool *tensor)
5710 {
5711   PetscObject  obj;
5712   PetscClassId id;
5713   PetscFE      fe = NULL;
5714 
5715   PetscFunctionBeginHot;
5716   PetscCall(PetscSectionGetFieldComponents(section, field, Nc));
5717   PetscCall(DMGetField(dm, field, NULL, &obj));
5718   PetscCall(PetscObjectGetClassId(obj, &id));
5719   if (id == PETSCFE_CLASSID) fe = (PetscFE)obj;
5720 
5721   if (!fe) {
5722     /* Assume the full interpolated mesh is in the chart; lines in particular */
5723     /* An order k SEM disc has k-1 dofs on an edge */
5724     PetscCall(PetscSectionGetFieldDof(section, line, field, k));
5725     *k = *k / *Nc + 1;
5726   } else {
5727     PetscInt       dual_space_size, dim;
5728     PetscDualSpace dsp;
5729 
5730     PetscCall(DMGetDimension(dm, &dim));
5731     PetscCall(PetscFEGetDualSpace(fe, &dsp));
5732     PetscCall(PetscDualSpaceGetDimension(dsp, &dual_space_size));
5733     *k = (PetscInt)PetscCeilReal(PetscPowReal(dual_space_size / *Nc, 1.0 / dim)) - 1;
5734     PetscCall(PetscDualSpaceLagrangeGetContinuity(dsp, continuous));
5735     PetscCall(PetscDualSpaceLagrangeGetTensor(dsp, tensor));
5736   }
5737   PetscFunctionReturn(PETSC_SUCCESS);
5738 }
5739 
5740 static PetscErrorCode GetFieldSize_Private(PetscInt dim, PetscInt k, PetscBool tensor, PetscInt *dof)
5741 {
5742   PetscFunctionBeginHot;
5743   if (tensor) {
5744     *dof = PetscPowInt(k + 1, dim);
5745   } else {
5746     switch (dim) {
5747     case 1:
5748       *dof = k + 1;
5749       break;
5750     case 2:
5751       *dof = ((k + 1) * (k + 2)) / 2;
5752       break;
5753     case 3:
5754       *dof = ((k + 1) * (k + 2) * (k + 3)) / 6;
5755       break;
5756     default:
5757       *dof = 0;
5758     }
5759   }
5760   PetscFunctionReturn(PETSC_SUCCESS);
5761 }
5762 
5763 /*@
5764 
5765   DMPlexSetClosurePermutationTensor - Create a permutation from the default (BFS) point ordering in the closure, to a
5766   lexicographic ordering over the tensor product cell (i.e., line, quad, hex, etc.), and set this permutation in the
5767   section provided (or the section of the `DM`).
5768 
5769   Input Parameters:
5770 + dm      - The `DM`
5771 . point   - Either a cell (highest dim point) or an edge (dim 1 point), or `PETSC_DETERMINE`
5772 - section - The `PetscSection` to reorder, or `NULL` for the default section
5773 
5774   Example:
5775   A typical interpolated single-quad mesh might order points as
5776 .vb
5777   [c0, v1, v2, v3, v4, e5, e6, e7, e8]
5778 
5779   v4 -- e6 -- v3
5780   |           |
5781   e7    c0    e8
5782   |           |
5783   v1 -- e5 -- v2
5784 .ve
5785 
5786   (There is no significance to the ordering described here.)  The default section for a Q3 quad might typically assign
5787   dofs in the order of points, e.g.,
5788 .vb
5789     c0 -> [0,1,2,3]
5790     v1 -> [4]
5791     ...
5792     e5 -> [8, 9]
5793 .ve
5794 
5795   which corresponds to the dofs
5796 .vb
5797     6   10  11  7
5798     13  2   3   15
5799     12  0   1   14
5800     4   8   9   5
5801 .ve
5802 
5803   The closure in BFS ordering works through height strata (cells, edges, vertices) to produce the ordering
5804 .vb
5805   0 1 2 3 8 9 14 15 11 10 13 12 4 5 7 6
5806 .ve
5807 
5808   After calling DMPlexSetClosurePermutationTensor(), the closure will be ordered lexicographically,
5809 .vb
5810    4 8 9 5 12 0 1 14 13 2 3 15 6 10 11 7
5811 .ve
5812 
5813   Level: developer
5814 
5815   Notes:
5816   The point is used to determine the number of dofs/field on an edge. For SEM, this is related to the polynomial
5817   degree of the basis.
5818 
5819   This is required to run with libCEED.
5820 
5821 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMGetLocalSection()`, `PetscSectionSetClosurePermutation()`, `DMSetGlobalSection()`
5822 @*/
5823 PetscErrorCode DMPlexSetClosurePermutationTensor(DM dm, PetscInt point, PetscSection section)
5824 {
5825   DMLabel   label;
5826   PetscInt  dim, depth = -1, eStart = -1, Nf;
5827   PetscBool continuous = PETSC_TRUE, tensor = PETSC_TRUE;
5828 
5829   PetscFunctionBegin;
5830   PetscCall(DMGetDimension(dm, &dim));
5831   if (dim < 1) PetscFunctionReturn(PETSC_SUCCESS);
5832   if (point < 0) {
5833     PetscInt sStart, sEnd;
5834 
5835     PetscCall(DMPlexGetDepthStratum(dm, 1, &sStart, &sEnd));
5836     point = sEnd - sStart ? sStart : point;
5837   }
5838   PetscCall(DMPlexGetDepthLabel(dm, &label));
5839   if (point >= 0) PetscCall(DMLabelGetValue(label, point, &depth));
5840   if (!section) PetscCall(DMGetLocalSection(dm, &section));
5841   if (depth == 1) {
5842     eStart = point;
5843   } else if (depth == dim) {
5844     const PetscInt *cone;
5845 
5846     PetscCall(DMPlexGetCone(dm, point, &cone));
5847     if (dim == 2) eStart = cone[0];
5848     else if (dim == 3) {
5849       const PetscInt *cone2;
5850       PetscCall(DMPlexGetCone(dm, cone[0], &cone2));
5851       eStart = cone2[0];
5852     } 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);
5853   } 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);
5854 
5855   PetscCall(PetscSectionGetNumFields(section, &Nf));
5856   for (PetscInt d = 1; d <= dim; d++) {
5857     PetscInt  k, f, Nc, c, i, j, size = 0, offset = 0, foffset = 0;
5858     PetscInt *perm;
5859 
5860     for (f = 0; f < Nf; ++f) {
5861       PetscInt dof;
5862 
5863       PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
5864       PetscCheck(dim == 1 || tensor || !continuous, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Continuous field %" PetscInt_FMT " must have a tensor product discretization", f);
5865       if (!continuous && d < dim) continue;
5866       PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
5867       size += dof * Nc;
5868     }
5869     PetscCall(PetscMalloc1(size, &perm));
5870     for (f = 0; f < Nf; ++f) {
5871       switch (d) {
5872       case 1:
5873         PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
5874         if (!continuous && d < dim) continue;
5875         /*
5876          Original ordering is [ edge of length k-1; vtx0; vtx1 ]
5877          We want              [ vtx0; edge of length k-1; vtx1 ]
5878          */
5879         if (continuous) {
5880           for (c = 0; c < Nc; c++, offset++) perm[offset] = (k - 1) * Nc + c + foffset;
5881           for (i = 0; i < k - 1; i++)
5882             for (c = 0; c < Nc; c++, offset++) perm[offset] = i * Nc + c + foffset;
5883           for (c = 0; c < Nc; c++, offset++) perm[offset] = k * Nc + c + foffset;
5884           foffset = offset;
5885         } else {
5886           PetscInt dof;
5887 
5888           PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
5889           for (i = 0; i < dof * Nc; ++i, ++offset) perm[offset] = i + foffset;
5890           foffset = offset;
5891         }
5892         break;
5893       case 2:
5894         /* The original quad closure is oriented clockwise, {f, e_b, e_r, e_t, e_l, v_lb, v_rb, v_tr, v_tl} */
5895         PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
5896         if (!continuous && d < dim) continue;
5897         /* The SEM order is
5898 
5899          v_lb, {e_b}, v_rb,
5900          e^{(k-1)-i}_l, {f^{i*(k-1)}}, e^i_r,
5901          v_lt, reverse {e_t}, v_rt
5902          */
5903         if (continuous) {
5904           const PetscInt of   = 0;
5905           const PetscInt oeb  = of + PetscSqr(k - 1);
5906           const PetscInt oer  = oeb + (k - 1);
5907           const PetscInt oet  = oer + (k - 1);
5908           const PetscInt oel  = oet + (k - 1);
5909           const PetscInt ovlb = oel + (k - 1);
5910           const PetscInt ovrb = ovlb + 1;
5911           const PetscInt ovrt = ovrb + 1;
5912           const PetscInt ovlt = ovrt + 1;
5913           PetscInt       o;
5914 
5915           /* bottom */
5916           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlb * Nc + c + foffset;
5917           for (o = oeb; o < oer; ++o)
5918             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5919           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrb * Nc + c + foffset;
5920           /* middle */
5921           for (i = 0; i < k - 1; ++i) {
5922             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oel + (k - 2) - i) * Nc + c + foffset;
5923             for (o = of + (k - 1) * i; o < of + (k - 1) * (i + 1); ++o)
5924               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5925             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oer + i) * Nc + c + foffset;
5926           }
5927           /* top */
5928           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlt * Nc + c + foffset;
5929           for (o = oel - 1; o >= oet; --o)
5930             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5931           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrt * Nc + c + foffset;
5932           foffset = offset;
5933         } else {
5934           PetscInt dof;
5935 
5936           PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
5937           for (i = 0; i < dof * Nc; ++i, ++offset) perm[offset] = i + foffset;
5938           foffset = offset;
5939         }
5940         break;
5941       case 3:
5942         /* The original hex closure is
5943 
5944          {c,
5945          f_b, f_t, f_f, f_b, f_r, f_l,
5946          e_bl, e_bb, e_br, e_bf,  e_tf, e_tr, e_tb, e_tl,  e_rf, e_lf, e_lb, e_rb,
5947          v_blf, v_blb, v_brb, v_brf, v_tlf, v_trf, v_trb, v_tlb}
5948          */
5949         PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
5950         if (!continuous && d < dim) continue;
5951         /* The SEM order is
5952          Bottom Slice
5953          v_blf, {e^{(k-1)-n}_bf}, v_brf,
5954          e^{i}_bl, f^{n*(k-1)+(k-1)-i}_b, e^{(k-1)-i}_br,
5955          v_blb, {e_bb}, v_brb,
5956 
5957          Middle Slice (j)
5958          {e^{(k-1)-j}_lf}, {f^{j*(k-1)+n}_f}, e^j_rf,
5959          f^{i*(k-1)+j}_l, {c^{(j*(k-1) + i)*(k-1)+n}_t}, f^{j*(k-1)+i}_r,
5960          e^j_lb, {f^{j*(k-1)+(k-1)-n}_b}, e^{(k-1)-j}_rb,
5961 
5962          Top Slice
5963          v_tlf, {e_tf}, v_trf,
5964          e^{(k-1)-i}_tl, {f^{i*(k-1)}_t}, e^{i}_tr,
5965          v_tlb, {e^{(k-1)-n}_tb}, v_trb,
5966          */
5967         if (continuous) {
5968           const PetscInt oc    = 0;
5969           const PetscInt ofb   = oc + PetscSqr(k - 1) * (k - 1);
5970           const PetscInt oft   = ofb + PetscSqr(k - 1);
5971           const PetscInt off   = oft + PetscSqr(k - 1);
5972           const PetscInt ofk   = off + PetscSqr(k - 1);
5973           const PetscInt ofr   = ofk + PetscSqr(k - 1);
5974           const PetscInt ofl   = ofr + PetscSqr(k - 1);
5975           const PetscInt oebl  = ofl + PetscSqr(k - 1);
5976           const PetscInt oebb  = oebl + (k - 1);
5977           const PetscInt oebr  = oebb + (k - 1);
5978           const PetscInt oebf  = oebr + (k - 1);
5979           const PetscInt oetf  = oebf + (k - 1);
5980           const PetscInt oetr  = oetf + (k - 1);
5981           const PetscInt oetb  = oetr + (k - 1);
5982           const PetscInt oetl  = oetb + (k - 1);
5983           const PetscInt oerf  = oetl + (k - 1);
5984           const PetscInt oelf  = oerf + (k - 1);
5985           const PetscInt oelb  = oelf + (k - 1);
5986           const PetscInt oerb  = oelb + (k - 1);
5987           const PetscInt ovblf = oerb + (k - 1);
5988           const PetscInt ovblb = ovblf + 1;
5989           const PetscInt ovbrb = ovblb + 1;
5990           const PetscInt ovbrf = ovbrb + 1;
5991           const PetscInt ovtlf = ovbrf + 1;
5992           const PetscInt ovtrf = ovtlf + 1;
5993           const PetscInt ovtrb = ovtrf + 1;
5994           const PetscInt ovtlb = ovtrb + 1;
5995           PetscInt       o, n;
5996 
5997           /* Bottom Slice */
5998           /*   bottom */
5999           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblf * Nc + c + foffset;
6000           for (o = oetf - 1; o >= oebf; --o)
6001             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6002           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrf * Nc + c + foffset;
6003           /*   middle */
6004           for (i = 0; i < k - 1; ++i) {
6005             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebl + i) * Nc + c + foffset;
6006             for (n = 0; n < k - 1; ++n) {
6007               o = ofb + n * (k - 1) + i;
6008               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6009             }
6010             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebr + (k - 2) - i) * Nc + c + foffset;
6011           }
6012           /*   top */
6013           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblb * Nc + c + foffset;
6014           for (o = oebb; o < oebr; ++o)
6015             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6016           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrb * Nc + c + foffset;
6017 
6018           /* Middle Slice */
6019           for (j = 0; j < k - 1; ++j) {
6020             /*   bottom */
6021             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelf + (k - 2) - j) * Nc + c + foffset;
6022             for (o = off + j * (k - 1); o < off + (j + 1) * (k - 1); ++o)
6023               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6024             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerf + j) * Nc + c + foffset;
6025             /*   middle */
6026             for (i = 0; i < k - 1; ++i) {
6027               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofl + i * (k - 1) + j) * Nc + c + foffset;
6028               for (n = 0; n < k - 1; ++n)
6029                 for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oc + (j * (k - 1) + i) * (k - 1) + n) * Nc + c + foffset;
6030               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofr + j * (k - 1) + i) * Nc + c + foffset;
6031             }
6032             /*   top */
6033             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelb + j) * Nc + c + foffset;
6034             for (o = ofk + j * (k - 1) + (k - 2); o >= ofk + j * (k - 1); --o)
6035               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6036             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerb + (k - 2) - j) * Nc + c + foffset;
6037           }
6038 
6039           /* Top Slice */
6040           /*   bottom */
6041           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlf * Nc + c + foffset;
6042           for (o = oetf; o < oetr; ++o)
6043             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6044           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrf * Nc + c + foffset;
6045           /*   middle */
6046           for (i = 0; i < k - 1; ++i) {
6047             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetl + (k - 2) - i) * Nc + c + foffset;
6048             for (n = 0; n < k - 1; ++n)
6049               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oft + i * (k - 1) + n) * Nc + c + foffset;
6050             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetr + i) * Nc + c + foffset;
6051           }
6052           /*   top */
6053           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlb * Nc + c + foffset;
6054           for (o = oetl - 1; o >= oetb; --o)
6055             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6056           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrb * Nc + c + foffset;
6057 
6058           foffset = offset;
6059         } else {
6060           PetscInt dof;
6061 
6062           PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
6063           for (i = 0; i < dof * Nc; ++i, ++offset) perm[offset] = i + foffset;
6064           foffset = offset;
6065         }
6066         break;
6067       default:
6068         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "No spectral ordering for dimension %" PetscInt_FMT, d);
6069       }
6070     }
6071     PetscCheck(offset == size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Number of permutation entries %" PetscInt_FMT " != %" PetscInt_FMT, offset, size);
6072     /* Check permutation */
6073     {
6074       PetscInt *check;
6075 
6076       PetscCall(PetscMalloc1(size, &check));
6077       for (i = 0; i < size; ++i) {
6078         check[i] = -1;
6079         PetscCheck(perm[i] >= 0 && perm[i] < size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid permutation index p[%" PetscInt_FMT "] = %" PetscInt_FMT, i, perm[i]);
6080       }
6081       for (i = 0; i < size; ++i) check[perm[i]] = i;
6082       for (i = 0; i < size; ++i) PetscCheck(check[i] >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Missing permutation index %" PetscInt_FMT, i);
6083       PetscCall(PetscFree(check));
6084     }
6085     PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size, PETSC_OWN_POINTER, perm));
6086     if (d == dim) { // Add permutation for localized (in case this is a coordinate DM)
6087       PetscInt *loc_perm;
6088       PetscCall(PetscMalloc1(size * 2, &loc_perm));
6089       for (PetscInt i = 0; i < size; i++) {
6090         loc_perm[i]        = perm[i];
6091         loc_perm[size + i] = size + perm[i];
6092       }
6093       PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size * 2, PETSC_OWN_POINTER, loc_perm));
6094     }
6095   }
6096   PetscFunctionReturn(PETSC_SUCCESS);
6097 }
6098 
6099 PetscErrorCode DMPlexGetPointDualSpaceFEM(DM dm, PetscInt point, PetscInt field, PetscDualSpace *dspace)
6100 {
6101   PetscDS  prob;
6102   PetscInt depth, Nf, h;
6103   DMLabel  label;
6104 
6105   PetscFunctionBeginHot;
6106   PetscCall(DMGetDS(dm, &prob));
6107   Nf      = prob->Nf;
6108   label   = dm->depthLabel;
6109   *dspace = NULL;
6110   if (field < Nf) {
6111     PetscObject disc = prob->disc[field];
6112 
6113     if (disc->classid == PETSCFE_CLASSID) {
6114       PetscDualSpace dsp;
6115 
6116       PetscCall(PetscFEGetDualSpace((PetscFE)disc, &dsp));
6117       PetscCall(DMLabelGetNumValues(label, &depth));
6118       PetscCall(DMLabelGetValue(label, point, &h));
6119       h = depth - 1 - h;
6120       if (h) {
6121         PetscCall(PetscDualSpaceGetHeightSubspace(dsp, h, dspace));
6122       } else {
6123         *dspace = dsp;
6124       }
6125     }
6126   }
6127   PetscFunctionReturn(PETSC_SUCCESS);
6128 }
6129 
6130 static inline PetscErrorCode DMPlexVecGetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6131 {
6132   PetscScalar       *array;
6133   const PetscScalar *vArray;
6134   const PetscInt    *cone, *coneO;
6135   PetscInt           pStart, pEnd, p, numPoints, size = 0, offset = 0;
6136 
6137   PetscFunctionBeginHot;
6138   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6139   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
6140   PetscCall(DMPlexGetCone(dm, point, &cone));
6141   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
6142   if (!values || !*values) {
6143     if ((point >= pStart) && (point < pEnd)) {
6144       PetscInt dof;
6145 
6146       PetscCall(PetscSectionGetDof(section, point, &dof));
6147       size += dof;
6148     }
6149     for (p = 0; p < numPoints; ++p) {
6150       const PetscInt cp = cone[p];
6151       PetscInt       dof;
6152 
6153       if ((cp < pStart) || (cp >= pEnd)) continue;
6154       PetscCall(PetscSectionGetDof(section, cp, &dof));
6155       size += dof;
6156     }
6157     if (!values) {
6158       if (csize) *csize = size;
6159       PetscFunctionReturn(PETSC_SUCCESS);
6160     }
6161     PetscCall(DMGetWorkArray(dm, size, MPIU_SCALAR, &array));
6162   } else {
6163     array = *values;
6164   }
6165   size = 0;
6166   PetscCall(VecGetArrayRead(v, &vArray));
6167   if ((point >= pStart) && (point < pEnd)) {
6168     PetscInt           dof, off, d;
6169     const PetscScalar *varr;
6170 
6171     PetscCall(PetscSectionGetDof(section, point, &dof));
6172     PetscCall(PetscSectionGetOffset(section, point, &off));
6173     varr = PetscSafePointerPlusOffset(vArray, off);
6174     for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
6175     size += dof;
6176   }
6177   for (p = 0; p < numPoints; ++p) {
6178     const PetscInt     cp = cone[p];
6179     PetscInt           o  = coneO[p];
6180     PetscInt           dof, off, d;
6181     const PetscScalar *varr;
6182 
6183     if ((cp < pStart) || (cp >= pEnd)) continue;
6184     PetscCall(PetscSectionGetDof(section, cp, &dof));
6185     PetscCall(PetscSectionGetOffset(section, cp, &off));
6186     varr = PetscSafePointerPlusOffset(vArray, off);
6187     if (o >= 0) {
6188       for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
6189     } else {
6190       for (d = dof - 1; d >= 0; --d, ++offset) array[offset] = varr[d];
6191     }
6192     size += dof;
6193   }
6194   PetscCall(VecRestoreArrayRead(v, &vArray));
6195   if (!*values) {
6196     if (csize) *csize = size;
6197     *values = array;
6198   } else {
6199     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
6200     *csize = size;
6201   }
6202   PetscFunctionReturn(PETSC_SUCCESS);
6203 }
6204 
6205 /* Compress out points not in the section */
6206 static inline PetscErrorCode CompressPoints_Private(PetscSection section, PetscInt *numPoints, PetscInt points[])
6207 {
6208   const PetscInt np = *numPoints;
6209   PetscInt       pStart, pEnd, p, q;
6210 
6211   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6212   for (p = 0, q = 0; p < np; ++p) {
6213     const PetscInt r = points[p * 2];
6214     if ((r >= pStart) && (r < pEnd)) {
6215       points[q * 2]     = r;
6216       points[q * 2 + 1] = points[p * 2 + 1];
6217       ++q;
6218     }
6219   }
6220   *numPoints = q;
6221   return PETSC_SUCCESS;
6222 }
6223 
6224 /* Compressed closure does not apply closure permutation */
6225 PetscErrorCode DMPlexGetCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt ornt, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
6226 {
6227   const PetscInt *cla = NULL;
6228   PetscInt        np, *pts = NULL;
6229 
6230   PetscFunctionBeginHot;
6231   PetscCall(PetscSectionGetClosureIndex(section, (PetscObject)dm, clSec, clPoints));
6232   if (!ornt && *clPoints) {
6233     PetscInt dof, off;
6234 
6235     PetscCall(PetscSectionGetDof(*clSec, point, &dof));
6236     PetscCall(PetscSectionGetOffset(*clSec, point, &off));
6237     PetscCall(ISGetIndices(*clPoints, &cla));
6238     np  = dof / 2;
6239     pts = PetscSafePointerPlusOffset((PetscInt *)cla, off);
6240   } else {
6241     PetscCall(DMPlexGetTransitiveClosure_Internal(dm, point, ornt, PETSC_TRUE, &np, &pts));
6242     PetscCall(CompressPoints_Private(section, &np, pts));
6243   }
6244   *numPoints = np;
6245   *points    = pts;
6246   *clp       = cla;
6247   PetscFunctionReturn(PETSC_SUCCESS);
6248 }
6249 
6250 PetscErrorCode DMPlexRestoreCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
6251 {
6252   PetscFunctionBeginHot;
6253   if (!*clPoints) {
6254     PetscCall(DMPlexRestoreTransitiveClosure(dm, point, PETSC_TRUE, numPoints, points));
6255   } else {
6256     PetscCall(ISRestoreIndices(*clPoints, clp));
6257   }
6258   *numPoints = 0;
6259   *points    = NULL;
6260   *clSec     = NULL;
6261   *clPoints  = NULL;
6262   *clp       = NULL;
6263   PetscFunctionReturn(PETSC_SUCCESS);
6264 }
6265 
6266 static inline PetscErrorCode DMPlexVecGetClosure_Static(DM dm, PetscSection section, PetscInt numPoints, const PetscInt points[], const PetscInt clperm[], const PetscScalar vArray[], PetscInt *size, PetscScalar array[])
6267 {
6268   PetscInt            offset = 0, p;
6269   const PetscInt    **perms  = NULL;
6270   const PetscScalar **flips  = NULL;
6271 
6272   PetscFunctionBeginHot;
6273   *size = 0;
6274   PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
6275   for (p = 0; p < numPoints; p++) {
6276     const PetscInt     point = points[2 * p];
6277     const PetscInt    *perm  = perms ? perms[p] : NULL;
6278     const PetscScalar *flip  = flips ? flips[p] : NULL;
6279     PetscInt           dof, off, d;
6280     const PetscScalar *varr;
6281 
6282     PetscCall(PetscSectionGetDof(section, point, &dof));
6283     PetscCall(PetscSectionGetOffset(section, point, &off));
6284     varr = PetscSafePointerPlusOffset(vArray, off);
6285     if (clperm) {
6286       if (perm) {
6287         for (d = 0; d < dof; d++) array[clperm[offset + perm[d]]] = varr[d];
6288       } else {
6289         for (d = 0; d < dof; d++) array[clperm[offset + d]] = varr[d];
6290       }
6291       if (flip) {
6292         for (d = 0; d < dof; d++) array[clperm[offset + d]] *= flip[d];
6293       }
6294     } else {
6295       if (perm) {
6296         for (d = 0; d < dof; d++) array[offset + perm[d]] = varr[d];
6297       } else {
6298         for (d = 0; d < dof; d++) array[offset + d] = varr[d];
6299       }
6300       if (flip) {
6301         for (d = 0; d < dof; d++) array[offset + d] *= flip[d];
6302       }
6303     }
6304     offset += dof;
6305   }
6306   PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
6307   *size = offset;
6308   PetscFunctionReturn(PETSC_SUCCESS);
6309 }
6310 
6311 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[])
6312 {
6313   PetscInt offset = 0, f;
6314 
6315   PetscFunctionBeginHot;
6316   *size = 0;
6317   for (f = 0; f < numFields; ++f) {
6318     PetscInt            p;
6319     const PetscInt    **perms = NULL;
6320     const PetscScalar **flips = NULL;
6321 
6322     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6323     for (p = 0; p < numPoints; p++) {
6324       const PetscInt     point = points[2 * p];
6325       PetscInt           fdof, foff, b;
6326       const PetscScalar *varr;
6327       const PetscInt    *perm = perms ? perms[p] : NULL;
6328       const PetscScalar *flip = flips ? flips[p] : NULL;
6329 
6330       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6331       PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6332       varr = &vArray[foff];
6333       if (clperm) {
6334         if (perm) {
6335           for (b = 0; b < fdof; b++) array[clperm[offset + perm[b]]] = varr[b];
6336         } else {
6337           for (b = 0; b < fdof; b++) array[clperm[offset + b]] = varr[b];
6338         }
6339         if (flip) {
6340           for (b = 0; b < fdof; b++) array[clperm[offset + b]] *= flip[b];
6341         }
6342       } else {
6343         if (perm) {
6344           for (b = 0; b < fdof; b++) array[offset + perm[b]] = varr[b];
6345         } else {
6346           for (b = 0; b < fdof; b++) array[offset + b] = varr[b];
6347         }
6348         if (flip) {
6349           for (b = 0; b < fdof; b++) array[offset + b] *= flip[b];
6350         }
6351       }
6352       offset += fdof;
6353     }
6354     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6355   }
6356   *size = offset;
6357   PetscFunctionReturn(PETSC_SUCCESS);
6358 }
6359 
6360 PetscErrorCode DMPlexVecGetOrientedClosure_Internal(DM dm, PetscSection section, PetscBool useClPerm, Vec v, PetscInt point, PetscInt ornt, PetscInt *csize, PetscScalar *values[])
6361 {
6362   PetscSection    clSection;
6363   IS              clPoints;
6364   PetscInt       *points = NULL;
6365   const PetscInt *clp, *perm = NULL;
6366   PetscInt        depth, numFields, numPoints, asize;
6367 
6368   PetscFunctionBeginHot;
6369   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6370   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6371   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6372   PetscValidHeaderSpecific(v, VEC_CLASSID, 4);
6373   PetscCall(DMPlexGetDepth(dm, &depth));
6374   PetscCall(PetscSectionGetNumFields(section, &numFields));
6375   if (depth == 1 && numFields < 2) {
6376     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6377     PetscFunctionReturn(PETSC_SUCCESS);
6378   }
6379   /* Get points */
6380   PetscCall(DMPlexGetCompressedClosure(dm, section, point, ornt, &numPoints, &points, &clSection, &clPoints, &clp));
6381   /* Get sizes */
6382   asize = 0;
6383   for (PetscInt p = 0; p < numPoints * 2; p += 2) {
6384     PetscInt dof;
6385     PetscCall(PetscSectionGetDof(section, points[p], &dof));
6386     asize += dof;
6387   }
6388   if (values) {
6389     const PetscScalar *vArray;
6390     PetscInt           size;
6391 
6392     if (*values) {
6393       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);
6394     } else PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, values));
6395     if (useClPerm) PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, asize, &perm));
6396     PetscCall(VecGetArrayRead(v, &vArray));
6397     /* Get values */
6398     if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, numPoints, points, numFields, perm, vArray, &size, *values));
6399     else PetscCall(DMPlexVecGetClosure_Static(dm, section, numPoints, points, perm, vArray, &size, *values));
6400     PetscCheck(asize == size, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Section size %" PetscInt_FMT " does not match Vec closure size %" PetscInt_FMT, asize, size);
6401     /* Cleanup array */
6402     PetscCall(VecRestoreArrayRead(v, &vArray));
6403   }
6404   if (csize) *csize = asize;
6405   /* Cleanup points */
6406   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6407   PetscFunctionReturn(PETSC_SUCCESS);
6408 }
6409 
6410 /*@C
6411   DMPlexVecGetClosure - Get an array of the values on the closure of 'point'
6412 
6413   Not collective
6414 
6415   Input Parameters:
6416 + dm      - The `DM`
6417 . section - The section describing the layout in `v`, or `NULL` to use the default section
6418 . v       - The local vector
6419 - point   - The point in the `DM`
6420 
6421   Input/Output Parameters:
6422 + csize  - The size of the input values array, or `NULL`; on output the number of values in the closure
6423 - values - An array to use for the values, or `NULL` to have it allocated automatically;
6424            if the user provided `NULL`, it is a borrowed array and should not be freed
6425 
6426   Level: intermediate
6427 
6428   Notes:
6429   `DMPlexVecGetClosure()`/`DMPlexVecRestoreClosure()` only allocates the values array if it set to `NULL` in the
6430   calling function. This is because `DMPlexVecGetClosure()` is typically called in the inner loop of a `Vec` or `Mat`
6431   assembly function, and a user may already have allocated storage for this operation.
6432 
6433   A typical use could be
6434 .vb
6435    values = NULL;
6436    PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
6437    for (cl = 0; cl < clSize; ++cl) {
6438      <Compute on closure>
6439    }
6440    PetscCall(DMPlexVecRestoreClosure(dm, NULL, v, p, &clSize, &values));
6441 .ve
6442   or
6443 .vb
6444    PetscMalloc1(clMaxSize, &values);
6445    for (p = pStart; p < pEnd; ++p) {
6446      clSize = clMaxSize;
6447      PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
6448      for (cl = 0; cl < clSize; ++cl) {
6449        <Compute on closure>
6450      }
6451    }
6452    PetscFree(values);
6453 .ve
6454 
6455   Fortran Notes:
6456   The `csize` argument is not present in the Fortran binding since it is internal to the array.
6457 
6458 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecRestoreClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6459 @*/
6460 PetscErrorCode DMPlexVecGetClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6461 {
6462   PetscFunctionBeginHot;
6463   PetscCall(DMPlexVecGetOrientedClosure_Internal(dm, section, PETSC_TRUE, v, point, 0, csize, values));
6464   PetscFunctionReturn(PETSC_SUCCESS);
6465 }
6466 
6467 PetscErrorCode DMPlexVecGetClosureAtDepth_Internal(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt depth, PetscInt *csize, PetscScalar *values[])
6468 {
6469   DMLabel            depthLabel;
6470   PetscSection       clSection;
6471   IS                 clPoints;
6472   PetscScalar       *array;
6473   const PetscScalar *vArray;
6474   PetscInt          *points = NULL;
6475   const PetscInt    *clp, *perm = NULL;
6476   PetscInt           mdepth, numFields, numPoints, Np = 0, p, clsize, size;
6477 
6478   PetscFunctionBeginHot;
6479   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6480   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6481   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6482   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6483   PetscCall(DMPlexGetDepth(dm, &mdepth));
6484   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
6485   PetscCall(PetscSectionGetNumFields(section, &numFields));
6486   if (mdepth == 1 && numFields < 2) {
6487     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6488     PetscFunctionReturn(PETSC_SUCCESS);
6489   }
6490   /* Get points */
6491   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
6492   for (clsize = 0, p = 0; p < Np; p++) {
6493     PetscInt dof;
6494     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
6495     clsize += dof;
6496   }
6497   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &perm));
6498   /* Filter points */
6499   for (p = 0; p < numPoints * 2; p += 2) {
6500     PetscInt dep;
6501 
6502     PetscCall(DMLabelGetValue(depthLabel, points[p], &dep));
6503     if (dep != depth) continue;
6504     points[Np * 2 + 0] = points[p];
6505     points[Np * 2 + 1] = points[p + 1];
6506     ++Np;
6507   }
6508   /* Get array */
6509   if (!values || !*values) {
6510     PetscInt asize = 0, dof;
6511 
6512     for (p = 0; p < Np * 2; p += 2) {
6513       PetscCall(PetscSectionGetDof(section, points[p], &dof));
6514       asize += dof;
6515     }
6516     if (!values) {
6517       PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6518       if (csize) *csize = asize;
6519       PetscFunctionReturn(PETSC_SUCCESS);
6520     }
6521     PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, &array));
6522   } else {
6523     array = *values;
6524   }
6525   PetscCall(VecGetArrayRead(v, &vArray));
6526   /* Get values */
6527   if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, Np, points, numFields, perm, vArray, &size, array));
6528   else PetscCall(DMPlexVecGetClosure_Static(dm, section, Np, points, perm, vArray, &size, array));
6529   /* Cleanup points */
6530   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6531   /* Cleanup array */
6532   PetscCall(VecRestoreArrayRead(v, &vArray));
6533   if (!*values) {
6534     if (csize) *csize = size;
6535     *values = array;
6536   } else {
6537     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
6538     *csize = size;
6539   }
6540   PetscFunctionReturn(PETSC_SUCCESS);
6541 }
6542 
6543 /*@C
6544   DMPlexVecRestoreClosure - Restore the array of the values on the closure of 'point'
6545 
6546   Not collective
6547 
6548   Input Parameters:
6549 + dm      - The `DM`
6550 . section - The section describing the layout in `v`, or `NULL` to use the default section
6551 . v       - The local vector
6552 . point   - The point in the `DM`
6553 . csize   - The number of values in the closure, or `NULL`
6554 - values  - The array of values, which is a borrowed array and should not be freed
6555 
6556   Level: intermediate
6557 
6558   Note:
6559   The array values are discarded and not copied back into `v`. In order to copy values back to `v`, use `DMPlexVecSetClosure()`
6560 
6561   Fortran Notes:
6562   The `csize` argument is not present in the Fortran binding since it is internal to the array.
6563 
6564 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6565 @*/
6566 PetscErrorCode DMPlexVecRestoreClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6567 {
6568   PetscInt size = 0;
6569 
6570   PetscFunctionBegin;
6571   /* Should work without recalculating size */
6572   PetscCall(DMRestoreWorkArray(dm, size, MPIU_SCALAR, (void *)values));
6573   *values = NULL;
6574   PetscFunctionReturn(PETSC_SUCCESS);
6575 }
6576 
6577 static inline void add(PetscScalar *x, PetscScalar y)
6578 {
6579   *x += y;
6580 }
6581 static inline void insert(PetscScalar *x, PetscScalar y)
6582 {
6583   *x = y;
6584 }
6585 
6586 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[])
6587 {
6588   PetscInt        cdof;  /* The number of constraints on this point */
6589   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6590   PetscScalar    *a;
6591   PetscInt        off, cind = 0, k;
6592 
6593   PetscFunctionBegin;
6594   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6595   PetscCall(PetscSectionGetOffset(section, point, &off));
6596   a = &array[off];
6597   if (!cdof || setBC) {
6598     if (clperm) {
6599       if (perm) {
6600         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6601       } else {
6602         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6603       }
6604     } else {
6605       if (perm) {
6606         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6607       } else {
6608         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6609       }
6610     }
6611   } else {
6612     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6613     if (clperm) {
6614       if (perm) {
6615         for (k = 0; k < dof; ++k) {
6616           if ((cind < cdof) && (k == cdofs[cind])) {
6617             ++cind;
6618             continue;
6619           }
6620           fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6621         }
6622       } else {
6623         for (k = 0; k < dof; ++k) {
6624           if ((cind < cdof) && (k == cdofs[cind])) {
6625             ++cind;
6626             continue;
6627           }
6628           fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6629         }
6630       }
6631     } else {
6632       if (perm) {
6633         for (k = 0; k < dof; ++k) {
6634           if ((cind < cdof) && (k == cdofs[cind])) {
6635             ++cind;
6636             continue;
6637           }
6638           fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6639         }
6640       } else {
6641         for (k = 0; k < dof; ++k) {
6642           if ((cind < cdof) && (k == cdofs[cind])) {
6643             ++cind;
6644             continue;
6645           }
6646           fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6647         }
6648       }
6649     }
6650   }
6651   PetscFunctionReturn(PETSC_SUCCESS);
6652 }
6653 
6654 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[])
6655 {
6656   PetscInt        cdof;  /* The number of constraints on this point */
6657   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6658   PetscScalar    *a;
6659   PetscInt        off, cind = 0, k;
6660 
6661   PetscFunctionBegin;
6662   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6663   PetscCall(PetscSectionGetOffset(section, point, &off));
6664   a = &array[off];
6665   if (cdof) {
6666     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6667     if (clperm) {
6668       if (perm) {
6669         for (k = 0; k < dof; ++k) {
6670           if ((cind < cdof) && (k == cdofs[cind])) {
6671             fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6672             cind++;
6673           }
6674         }
6675       } else {
6676         for (k = 0; k < dof; ++k) {
6677           if ((cind < cdof) && (k == cdofs[cind])) {
6678             fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6679             cind++;
6680           }
6681         }
6682       }
6683     } else {
6684       if (perm) {
6685         for (k = 0; k < dof; ++k) {
6686           if ((cind < cdof) && (k == cdofs[cind])) {
6687             fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6688             cind++;
6689           }
6690         }
6691       } else {
6692         for (k = 0; k < dof; ++k) {
6693           if ((cind < cdof) && (k == cdofs[cind])) {
6694             fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6695             cind++;
6696           }
6697         }
6698       }
6699     }
6700   }
6701   PetscFunctionReturn(PETSC_SUCCESS);
6702 }
6703 
6704 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[])
6705 {
6706   PetscScalar    *a;
6707   PetscInt        fdof, foff, fcdof, foffset = *offset;
6708   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6709   PetscInt        cind = 0, b;
6710 
6711   PetscFunctionBegin;
6712   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6713   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6714   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6715   a = &array[foff];
6716   if (!fcdof || setBC) {
6717     if (clperm) {
6718       if (perm) {
6719         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6720       } else {
6721         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6722       }
6723     } else {
6724       if (perm) {
6725         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6726       } else {
6727         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6728       }
6729     }
6730   } else {
6731     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6732     if (clperm) {
6733       if (perm) {
6734         for (b = 0; b < fdof; b++) {
6735           if ((cind < fcdof) && (b == fcdofs[cind])) {
6736             ++cind;
6737             continue;
6738           }
6739           fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6740         }
6741       } else {
6742         for (b = 0; b < fdof; b++) {
6743           if ((cind < fcdof) && (b == fcdofs[cind])) {
6744             ++cind;
6745             continue;
6746           }
6747           fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6748         }
6749       }
6750     } else {
6751       if (perm) {
6752         for (b = 0; b < fdof; b++) {
6753           if ((cind < fcdof) && (b == fcdofs[cind])) {
6754             ++cind;
6755             continue;
6756           }
6757           fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6758         }
6759       } else {
6760         for (b = 0; b < fdof; b++) {
6761           if ((cind < fcdof) && (b == fcdofs[cind])) {
6762             ++cind;
6763             continue;
6764           }
6765           fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6766         }
6767       }
6768     }
6769   }
6770   *offset += fdof;
6771   PetscFunctionReturn(PETSC_SUCCESS);
6772 }
6773 
6774 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[])
6775 {
6776   PetscScalar    *a;
6777   PetscInt        fdof, foff, fcdof, foffset = *offset;
6778   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6779   PetscInt        Nc, cind = 0, ncind = 0, b;
6780   PetscBool       ncSet, fcSet;
6781 
6782   PetscFunctionBegin;
6783   PetscCall(PetscSectionGetFieldComponents(section, f, &Nc));
6784   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6785   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6786   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6787   a = &array[foff];
6788   if (fcdof) {
6789     /* We just override fcdof and fcdofs with Ncc and comps */
6790     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6791     if (clperm) {
6792       if (perm) {
6793         if (comps) {
6794           for (b = 0; b < fdof; b++) {
6795             ncSet = fcSet = PETSC_FALSE;
6796             if (b % Nc == comps[ncind]) {
6797               ncind = (ncind + 1) % Ncc;
6798               ncSet = PETSC_TRUE;
6799             }
6800             if ((cind < fcdof) && (b == fcdofs[cind])) {
6801               ++cind;
6802               fcSet = PETSC_TRUE;
6803             }
6804             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6805           }
6806         } else {
6807           for (b = 0; b < fdof; b++) {
6808             if ((cind < fcdof) && (b == fcdofs[cind])) {
6809               fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6810               ++cind;
6811             }
6812           }
6813         }
6814       } else {
6815         if (comps) {
6816           for (b = 0; b < fdof; b++) {
6817             ncSet = fcSet = PETSC_FALSE;
6818             if (b % Nc == comps[ncind]) {
6819               ncind = (ncind + 1) % Ncc;
6820               ncSet = PETSC_TRUE;
6821             }
6822             if ((cind < fcdof) && (b == fcdofs[cind])) {
6823               ++cind;
6824               fcSet = PETSC_TRUE;
6825             }
6826             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6827           }
6828         } else {
6829           for (b = 0; b < fdof; b++) {
6830             if ((cind < fcdof) && (b == fcdofs[cind])) {
6831               fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6832               ++cind;
6833             }
6834           }
6835         }
6836       }
6837     } else {
6838       if (perm) {
6839         if (comps) {
6840           for (b = 0; b < fdof; b++) {
6841             ncSet = fcSet = PETSC_FALSE;
6842             if (b % Nc == comps[ncind]) {
6843               ncind = (ncind + 1) % Ncc;
6844               ncSet = PETSC_TRUE;
6845             }
6846             if ((cind < fcdof) && (b == fcdofs[cind])) {
6847               ++cind;
6848               fcSet = PETSC_TRUE;
6849             }
6850             if (ncSet && fcSet) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6851           }
6852         } else {
6853           for (b = 0; b < fdof; b++) {
6854             if ((cind < fcdof) && (b == fcdofs[cind])) {
6855               fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6856               ++cind;
6857             }
6858           }
6859         }
6860       } else {
6861         if (comps) {
6862           for (b = 0; b < fdof; b++) {
6863             ncSet = fcSet = PETSC_FALSE;
6864             if (b % Nc == comps[ncind]) {
6865               ncind = (ncind + 1) % Ncc;
6866               ncSet = PETSC_TRUE;
6867             }
6868             if ((cind < fcdof) && (b == fcdofs[cind])) {
6869               ++cind;
6870               fcSet = PETSC_TRUE;
6871             }
6872             if (ncSet && fcSet) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6873           }
6874         } else {
6875           for (b = 0; b < fdof; b++) {
6876             if ((cind < fcdof) && (b == fcdofs[cind])) {
6877               fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6878               ++cind;
6879             }
6880           }
6881         }
6882       }
6883     }
6884   }
6885   *offset += fdof;
6886   PetscFunctionReturn(PETSC_SUCCESS);
6887 }
6888 
6889 static inline PetscErrorCode DMPlexVecSetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6890 {
6891   PetscScalar    *array;
6892   const PetscInt *cone, *coneO;
6893   PetscInt        pStart, pEnd, p, numPoints, off, dof;
6894 
6895   PetscFunctionBeginHot;
6896   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6897   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
6898   PetscCall(DMPlexGetCone(dm, point, &cone));
6899   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
6900   PetscCall(VecGetArray(v, &array));
6901   for (p = 0, off = 0; p <= numPoints; ++p, off += dof) {
6902     const PetscInt cp = !p ? point : cone[p - 1];
6903     const PetscInt o  = !p ? 0 : coneO[p - 1];
6904 
6905     if ((cp < pStart) || (cp >= pEnd)) {
6906       dof = 0;
6907       continue;
6908     }
6909     PetscCall(PetscSectionGetDof(section, cp, &dof));
6910     /* ADD_VALUES */
6911     {
6912       const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6913       PetscScalar    *a;
6914       PetscInt        cdof, coff, cind = 0, k;
6915 
6916       PetscCall(PetscSectionGetConstraintDof(section, cp, &cdof));
6917       PetscCall(PetscSectionGetOffset(section, cp, &coff));
6918       a = &array[coff];
6919       if (!cdof) {
6920         if (o >= 0) {
6921           for (k = 0; k < dof; ++k) a[k] += values[off + k];
6922         } else {
6923           for (k = 0; k < dof; ++k) a[k] += values[off + dof - k - 1];
6924         }
6925       } else {
6926         PetscCall(PetscSectionGetConstraintIndices(section, cp, &cdofs));
6927         if (o >= 0) {
6928           for (k = 0; k < dof; ++k) {
6929             if ((cind < cdof) && (k == cdofs[cind])) {
6930               ++cind;
6931               continue;
6932             }
6933             a[k] += values[off + k];
6934           }
6935         } else {
6936           for (k = 0; k < dof; ++k) {
6937             if ((cind < cdof) && (k == cdofs[cind])) {
6938               ++cind;
6939               continue;
6940             }
6941             a[k] += values[off + dof - k - 1];
6942           }
6943         }
6944       }
6945     }
6946   }
6947   PetscCall(VecRestoreArray(v, &array));
6948   PetscFunctionReturn(PETSC_SUCCESS);
6949 }
6950 
6951 /*@C
6952   DMPlexVecSetClosure - Set an array of the values on the closure of `point`
6953 
6954   Not collective
6955 
6956   Input Parameters:
6957 + dm      - The `DM`
6958 . section - The section describing the layout in `v`, or `NULL` to use the default section
6959 . v       - The local vector
6960 . point   - The point in the `DM`
6961 . values  - The array of values
6962 - mode    - The insert mode. One of `INSERT_ALL_VALUES`, `ADD_ALL_VALUES`, `INSERT_VALUES`, `ADD_VALUES`, `INSERT_BC_VALUES`, and `ADD_BC_VALUES`,
6963          where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions.
6964 
6965   Level: intermediate
6966 
6967 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`
6968 @*/
6969 PetscErrorCode DMPlexVecSetClosure(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6970 {
6971   PetscSection    clSection;
6972   IS              clPoints;
6973   PetscScalar    *array;
6974   PetscInt       *points = NULL;
6975   const PetscInt *clp, *clperm = NULL;
6976   PetscInt        depth, numFields, numPoints, p, clsize;
6977 
6978   PetscFunctionBeginHot;
6979   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6980   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6981   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6982   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6983   PetscCall(DMPlexGetDepth(dm, &depth));
6984   PetscCall(PetscSectionGetNumFields(section, &numFields));
6985   if (depth == 1 && numFields < 2 && mode == ADD_VALUES) {
6986     PetscCall(DMPlexVecSetClosure_Depth1_Static(dm, section, v, point, values, mode));
6987     PetscFunctionReturn(PETSC_SUCCESS);
6988   }
6989   /* Get points */
6990   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
6991   for (clsize = 0, p = 0; p < numPoints; p++) {
6992     PetscInt dof;
6993     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
6994     clsize += dof;
6995   }
6996   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
6997   /* Get array */
6998   PetscCall(VecGetArray(v, &array));
6999   /* Get values */
7000   if (numFields > 0) {
7001     PetscInt offset = 0, f;
7002     for (f = 0; f < numFields; ++f) {
7003       const PetscInt    **perms = NULL;
7004       const PetscScalar **flips = NULL;
7005 
7006       PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7007       switch (mode) {
7008       case INSERT_VALUES:
7009         for (p = 0; p < numPoints; p++) {
7010           const PetscInt     point = points[2 * p];
7011           const PetscInt    *perm  = perms ? perms[p] : NULL;
7012           const PetscScalar *flip  = flips ? flips[p] : NULL;
7013           PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, clperm, values, &offset, array));
7014         }
7015         break;
7016       case INSERT_ALL_VALUES:
7017         for (p = 0; p < numPoints; p++) {
7018           const PetscInt     point = points[2 * p];
7019           const PetscInt    *perm  = perms ? perms[p] : NULL;
7020           const PetscScalar *flip  = flips ? flips[p] : NULL;
7021           PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, clperm, values, &offset, array));
7022         }
7023         break;
7024       case INSERT_BC_VALUES:
7025         for (p = 0; p < numPoints; p++) {
7026           const PetscInt     point = points[2 * p];
7027           const PetscInt    *perm  = perms ? perms[p] : NULL;
7028           const PetscScalar *flip  = flips ? flips[p] : NULL;
7029           PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, insert, clperm, values, &offset, array));
7030         }
7031         break;
7032       case ADD_VALUES:
7033         for (p = 0; p < numPoints; p++) {
7034           const PetscInt     point = points[2 * p];
7035           const PetscInt    *perm  = perms ? perms[p] : NULL;
7036           const PetscScalar *flip  = flips ? flips[p] : NULL;
7037           PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, clperm, values, &offset, array));
7038         }
7039         break;
7040       case ADD_ALL_VALUES:
7041         for (p = 0; p < numPoints; p++) {
7042           const PetscInt     point = points[2 * p];
7043           const PetscInt    *perm  = perms ? perms[p] : NULL;
7044           const PetscScalar *flip  = flips ? flips[p] : NULL;
7045           PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, clperm, values, &offset, array));
7046         }
7047         break;
7048       case ADD_BC_VALUES:
7049         for (p = 0; p < numPoints; p++) {
7050           const PetscInt     point = points[2 * p];
7051           const PetscInt    *perm  = perms ? perms[p] : NULL;
7052           const PetscScalar *flip  = flips ? flips[p] : NULL;
7053           PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, add, clperm, values, &offset, array));
7054         }
7055         break;
7056       default:
7057         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
7058       }
7059       PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7060     }
7061   } else {
7062     PetscInt            dof, off;
7063     const PetscInt    **perms = NULL;
7064     const PetscScalar **flips = NULL;
7065 
7066     PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
7067     switch (mode) {
7068     case INSERT_VALUES:
7069       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7070         const PetscInt     point = points[2 * p];
7071         const PetscInt    *perm  = perms ? perms[p] : NULL;
7072         const PetscScalar *flip  = flips ? flips[p] : NULL;
7073         PetscCall(PetscSectionGetDof(section, point, &dof));
7074         PetscCall(updatePoint_private(section, point, dof, insert, PETSC_FALSE, perm, flip, clperm, values, off, array));
7075       }
7076       break;
7077     case INSERT_ALL_VALUES:
7078       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7079         const PetscInt     point = points[2 * p];
7080         const PetscInt    *perm  = perms ? perms[p] : NULL;
7081         const PetscScalar *flip  = flips ? flips[p] : NULL;
7082         PetscCall(PetscSectionGetDof(section, point, &dof));
7083         PetscCall(updatePoint_private(section, point, dof, insert, PETSC_TRUE, perm, flip, clperm, values, off, array));
7084       }
7085       break;
7086     case INSERT_BC_VALUES:
7087       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7088         const PetscInt     point = points[2 * p];
7089         const PetscInt    *perm  = perms ? perms[p] : NULL;
7090         const PetscScalar *flip  = flips ? flips[p] : NULL;
7091         PetscCall(PetscSectionGetDof(section, point, &dof));
7092         PetscCall(updatePointBC_private(section, point, dof, insert, perm, flip, clperm, values, off, array));
7093       }
7094       break;
7095     case ADD_VALUES:
7096       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7097         const PetscInt     point = points[2 * p];
7098         const PetscInt    *perm  = perms ? perms[p] : NULL;
7099         const PetscScalar *flip  = flips ? flips[p] : NULL;
7100         PetscCall(PetscSectionGetDof(section, point, &dof));
7101         PetscCall(updatePoint_private(section, point, dof, add, PETSC_FALSE, perm, flip, clperm, values, off, array));
7102       }
7103       break;
7104     case ADD_ALL_VALUES:
7105       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7106         const PetscInt     point = points[2 * p];
7107         const PetscInt    *perm  = perms ? perms[p] : NULL;
7108         const PetscScalar *flip  = flips ? flips[p] : NULL;
7109         PetscCall(PetscSectionGetDof(section, point, &dof));
7110         PetscCall(updatePoint_private(section, point, dof, add, PETSC_TRUE, perm, flip, clperm, values, off, array));
7111       }
7112       break;
7113     case ADD_BC_VALUES:
7114       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7115         const PetscInt     point = points[2 * p];
7116         const PetscInt    *perm  = perms ? perms[p] : NULL;
7117         const PetscScalar *flip  = flips ? flips[p] : NULL;
7118         PetscCall(PetscSectionGetDof(section, point, &dof));
7119         PetscCall(updatePointBC_private(section, point, dof, add, perm, flip, clperm, values, off, array));
7120       }
7121       break;
7122     default:
7123       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
7124     }
7125     PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
7126   }
7127   /* Cleanup points */
7128   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
7129   /* Cleanup array */
7130   PetscCall(VecRestoreArray(v, &array));
7131   PetscFunctionReturn(PETSC_SUCCESS);
7132 }
7133 
7134 /* Check whether the given point is in the label. If not, update the offset to skip this point */
7135 static inline PetscErrorCode CheckPoint_Private(DMLabel label, PetscInt labelId, PetscSection section, PetscInt point, PetscInt f, PetscInt *offset, PetscBool *contains)
7136 {
7137   PetscFunctionBegin;
7138   *contains = PETSC_TRUE;
7139   if (label) {
7140     PetscInt fdof;
7141 
7142     PetscCall(DMLabelStratumHasPoint(label, labelId, point, contains));
7143     if (!*contains) {
7144       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7145       *offset += fdof;
7146       PetscFunctionReturn(PETSC_SUCCESS);
7147     }
7148   }
7149   PetscFunctionReturn(PETSC_SUCCESS);
7150 }
7151 
7152 /* Unlike DMPlexVecSetClosure(), this uses plex-native closure permutation, not a user-specified permutation such as DMPlexSetClosurePermutationTensor(). */
7153 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)
7154 {
7155   PetscSection    clSection;
7156   IS              clPoints;
7157   PetscScalar    *array;
7158   PetscInt       *points = NULL;
7159   const PetscInt *clp;
7160   PetscInt        numFields, numPoints, p;
7161   PetscInt        offset = 0, f;
7162 
7163   PetscFunctionBeginHot;
7164   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7165   if (!section) PetscCall(DMGetLocalSection(dm, &section));
7166   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7167   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
7168   PetscCall(PetscSectionGetNumFields(section, &numFields));
7169   /* Get points */
7170   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
7171   /* Get array */
7172   PetscCall(VecGetArray(v, &array));
7173   /* Get values */
7174   for (f = 0; f < numFields; ++f) {
7175     const PetscInt    **perms = NULL;
7176     const PetscScalar **flips = NULL;
7177     PetscBool           contains;
7178 
7179     if (!fieldActive[f]) {
7180       for (p = 0; p < numPoints * 2; p += 2) {
7181         PetscInt fdof;
7182         PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
7183         offset += fdof;
7184       }
7185       continue;
7186     }
7187     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7188     switch (mode) {
7189     case INSERT_VALUES:
7190       for (p = 0; p < numPoints; p++) {
7191         const PetscInt     point = points[2 * p];
7192         const PetscInt    *perm  = perms ? perms[p] : NULL;
7193         const PetscScalar *flip  = flips ? flips[p] : NULL;
7194         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7195         if (!contains) continue;
7196         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, NULL, values, &offset, array));
7197       }
7198       break;
7199     case INSERT_ALL_VALUES:
7200       for (p = 0; p < numPoints; p++) {
7201         const PetscInt     point = points[2 * p];
7202         const PetscInt    *perm  = perms ? perms[p] : NULL;
7203         const PetscScalar *flip  = flips ? flips[p] : NULL;
7204         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7205         if (!contains) continue;
7206         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, NULL, values, &offset, array));
7207       }
7208       break;
7209     case INSERT_BC_VALUES:
7210       for (p = 0; p < numPoints; p++) {
7211         const PetscInt     point = points[2 * p];
7212         const PetscInt    *perm  = perms ? perms[p] : NULL;
7213         const PetscScalar *flip  = flips ? flips[p] : NULL;
7214         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7215         if (!contains) continue;
7216         PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, Ncc, comps, insert, NULL, values, &offset, array));
7217       }
7218       break;
7219     case ADD_VALUES:
7220       for (p = 0; p < numPoints; p++) {
7221         const PetscInt     point = points[2 * p];
7222         const PetscInt    *perm  = perms ? perms[p] : NULL;
7223         const PetscScalar *flip  = flips ? flips[p] : NULL;
7224         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7225         if (!contains) continue;
7226         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, NULL, values, &offset, array));
7227       }
7228       break;
7229     case ADD_ALL_VALUES:
7230       for (p = 0; p < numPoints; p++) {
7231         const PetscInt     point = points[2 * p];
7232         const PetscInt    *perm  = perms ? perms[p] : NULL;
7233         const PetscScalar *flip  = flips ? flips[p] : NULL;
7234         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7235         if (!contains) continue;
7236         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, NULL, values, &offset, array));
7237       }
7238       break;
7239     default:
7240       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
7241     }
7242     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7243   }
7244   /* Cleanup points */
7245   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
7246   /* Cleanup array */
7247   PetscCall(VecRestoreArray(v, &array));
7248   PetscFunctionReturn(PETSC_SUCCESS);
7249 }
7250 
7251 static PetscErrorCode DMPlexPrintMatSetValues(PetscViewer viewer, Mat A, PetscInt point, PetscInt numRIndices, const PetscInt rindices[], PetscInt numCIndices, const PetscInt cindices[], const PetscScalar values[])
7252 {
7253   PetscMPIInt rank;
7254   PetscInt    i, j;
7255 
7256   PetscFunctionBegin;
7257   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7258   PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat for point %" PetscInt_FMT "\n", rank, point));
7259   for (i = 0; i < numRIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat row indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, rindices[i]));
7260   for (i = 0; i < numCIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat col indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, cindices[i]));
7261   numCIndices = numCIndices ? numCIndices : numRIndices;
7262   if (!values) PetscFunctionReturn(PETSC_SUCCESS);
7263   for (i = 0; i < numRIndices; i++) {
7264     PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]", rank));
7265     for (j = 0; j < numCIndices; j++) {
7266 #if defined(PETSC_USE_COMPLEX)
7267       PetscCall(PetscViewerASCIIPrintf(viewer, " (%g,%g)", (double)PetscRealPart(values[i * numCIndices + j]), (double)PetscImaginaryPart(values[i * numCIndices + j])));
7268 #else
7269       PetscCall(PetscViewerASCIIPrintf(viewer, " %g", (double)values[i * numCIndices + j]));
7270 #endif
7271     }
7272     PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
7273   }
7274   PetscFunctionReturn(PETSC_SUCCESS);
7275 }
7276 
7277 /*
7278   DMPlexGetIndicesPoint_Internal - Add the indices for dofs on a point to an index array
7279 
7280   Input Parameters:
7281 + section - The section for this data layout
7282 . islocal - Is the section (and thus indices being requested) local or global?
7283 . point   - The point contributing dofs with these indices
7284 . off     - The global offset of this point
7285 . loff    - The local offset of each field
7286 . setBC   - The flag determining whether to include indices of boundary values
7287 . perm    - A permutation of the dofs on this point, or NULL
7288 - indperm - A permutation of the entire indices array, or NULL
7289 
7290   Output Parameter:
7291 . indices - Indices for dofs on this point
7292 
7293   Level: developer
7294 
7295   Note: The indices could be local or global, depending on the value of 'off'.
7296 */
7297 PetscErrorCode DMPlexGetIndicesPoint_Internal(PetscSection section, PetscBool islocal, PetscInt point, PetscInt off, PetscInt *loff, PetscBool setBC, const PetscInt perm[], const PetscInt indperm[], PetscInt indices[])
7298 {
7299   PetscInt        dof;   /* The number of unknowns on this point */
7300   PetscInt        cdof;  /* The number of constraints on this point */
7301   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
7302   PetscInt        cind = 0, k;
7303 
7304   PetscFunctionBegin;
7305   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
7306   PetscCall(PetscSectionGetDof(section, point, &dof));
7307   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
7308   if (!cdof || setBC) {
7309     for (k = 0; k < dof; ++k) {
7310       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
7311       const PetscInt ind    = indperm ? indperm[preind] : preind;
7312 
7313       indices[ind] = off + k;
7314     }
7315   } else {
7316     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
7317     for (k = 0; k < dof; ++k) {
7318       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
7319       const PetscInt ind    = indperm ? indperm[preind] : preind;
7320 
7321       if ((cind < cdof) && (k == cdofs[cind])) {
7322         /* Insert check for returning constrained indices */
7323         indices[ind] = -(off + k + 1);
7324         ++cind;
7325       } else {
7326         indices[ind] = off + k - (islocal ? 0 : cind);
7327       }
7328     }
7329   }
7330   *loff += dof;
7331   PetscFunctionReturn(PETSC_SUCCESS);
7332 }
7333 
7334 /*
7335  DMPlexGetIndicesPointFields_Internal - gets section indices for a point in its canonical ordering.
7336 
7337  Input Parameters:
7338 + section - a section (global or local)
7339 - islocal - `PETSC_TRUE` if requesting local indices (i.e., section is local); `PETSC_FALSE` for global
7340 . point - point within section
7341 . off - The offset of this point in the (local or global) indexed space - should match islocal and (usually) the section
7342 . foffs - array of length numFields containing the offset in canonical point ordering (the location in indices) of each field
7343 . setBC - identify constrained (boundary condition) points via involution.
7344 . perms - perms[f][permsoff][:] is a permutation of dofs within each field
7345 . permsoff - offset
7346 - indperm - index permutation
7347 
7348  Output Parameter:
7349 . foffs - each entry is incremented by the number of (unconstrained if setBC=FALSE) dofs in that field
7350 . indices - array to hold indices (as defined by section) of each dof associated with point
7351 
7352  Notes:
7353  If section is local and setBC=true, there is no distinction between constrained and unconstrained dofs.
7354  If section is local and setBC=false, the indices for constrained points are the involution -(i+1) of their position
7355  in the local vector.
7356 
7357  If section is global and setBC=false, the indices for constrained points are negative (and their value is not
7358  significant).  It is invalid to call with a global section and setBC=true.
7359 
7360  Developer Note:
7361  The section is only used for field layout, so islocal is technically a statement about the offset (off).  At some point
7362  in the future, global sections may have fields set, in which case we could pass the global section and obtain the
7363  offset could be obtained from the section instead of passing it explicitly as we do now.
7364 
7365  Example:
7366  Suppose a point contains one field with three components, and for which the unconstrained indices are {10, 11, 12}.
7367  When the middle component is constrained, we get the array {10, -12, 12} for (islocal=TRUE, setBC=FALSE).
7368  Note that -12 is the involution of 11, so the user can involute negative indices to recover local indices.
7369  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.
7370 
7371  Level: developer
7372 */
7373 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[])
7374 {
7375   PetscInt numFields, foff, f;
7376 
7377   PetscFunctionBegin;
7378   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
7379   PetscCall(PetscSectionGetNumFields(section, &numFields));
7380   for (f = 0, foff = 0; f < numFields; ++f) {
7381     PetscInt        fdof, cfdof;
7382     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7383     PetscInt        cind = 0, b;
7384     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7385 
7386     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7387     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7388     if (!cfdof || setBC) {
7389       for (b = 0; b < fdof; ++b) {
7390         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7391         const PetscInt ind    = indperm ? indperm[preind] : preind;
7392 
7393         indices[ind] = off + foff + b;
7394       }
7395     } else {
7396       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7397       for (b = 0; b < fdof; ++b) {
7398         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7399         const PetscInt ind    = indperm ? indperm[preind] : preind;
7400 
7401         if ((cind < cfdof) && (b == fcdofs[cind])) {
7402           indices[ind] = -(off + foff + b + 1);
7403           ++cind;
7404         } else {
7405           indices[ind] = off + foff + b - (islocal ? 0 : cind);
7406         }
7407       }
7408     }
7409     foff += (setBC || islocal ? fdof : (fdof - cfdof));
7410     foffs[f] += fdof;
7411   }
7412   PetscFunctionReturn(PETSC_SUCCESS);
7413 }
7414 
7415 /*
7416   This version believes the globalSection offsets for each field, rather than just the point offset
7417 
7418  . foffs - The offset into 'indices' for each field, since it is segregated by field
7419 
7420  Notes:
7421  The semantics of this function relate to that of setBC=FALSE in DMPlexGetIndicesPointFields_Internal.
7422  Since this function uses global indices, setBC=TRUE would be invalid, so no such argument exists.
7423 */
7424 static PetscErrorCode DMPlexGetIndicesPointFieldsSplit_Internal(PetscSection section, PetscSection globalSection, PetscInt point, PetscInt foffs[], const PetscInt ***perms, PetscInt permsoff, const PetscInt indperm[], PetscInt indices[])
7425 {
7426   PetscInt numFields, foff, f;
7427 
7428   PetscFunctionBegin;
7429   PetscCall(PetscSectionGetNumFields(section, &numFields));
7430   for (f = 0; f < numFields; ++f) {
7431     PetscInt        fdof, cfdof;
7432     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7433     PetscInt        cind = 0, b;
7434     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7435 
7436     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7437     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7438     PetscCall(PetscSectionGetFieldOffset(globalSection, point, f, &foff));
7439     if (!cfdof) {
7440       for (b = 0; b < fdof; ++b) {
7441         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7442         const PetscInt ind    = indperm ? indperm[preind] : preind;
7443 
7444         indices[ind] = foff + b;
7445       }
7446     } else {
7447       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7448       for (b = 0; b < fdof; ++b) {
7449         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7450         const PetscInt ind    = indperm ? indperm[preind] : preind;
7451 
7452         if ((cind < cfdof) && (b == fcdofs[cind])) {
7453           indices[ind] = -(foff + b + 1);
7454           ++cind;
7455         } else {
7456           indices[ind] = foff + b - cind;
7457         }
7458       }
7459     }
7460     foffs[f] += fdof;
7461   }
7462   PetscFunctionReturn(PETSC_SUCCESS);
7463 }
7464 
7465 static PetscErrorCode DMPlexAnchorsGetSubMatIndices(PetscInt nPoints, const PetscInt pnts[], PetscSection section, PetscSection cSec, PetscInt tmpIndices[], PetscInt fieldOffsets[], PetscInt indices[], const PetscInt ***perms)
7466 {
7467   PetscInt numFields, sStart, sEnd, cStart, cEnd;
7468 
7469   PetscFunctionBegin;
7470   PetscCall(PetscSectionGetNumFields(section, &numFields));
7471   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
7472   PetscCall(PetscSectionGetChart(cSec, &cStart, &cEnd));
7473   for (PetscInt p = 0; p < nPoints; p++) {
7474     PetscInt     b       = pnts[2 * p];
7475     PetscInt     bSecDof = 0, bOff;
7476     PetscInt     cSecDof = 0;
7477     PetscSection indices_section;
7478 
7479     if (b >= sStart && b < sEnd) PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7480     if (!bSecDof) continue;
7481     if (b >= cStart && b < cEnd) PetscCall(PetscSectionGetDof(cSec, b, &cSecDof));
7482     indices_section = cSecDof > 0 ? cSec : section;
7483     if (numFields) {
7484       PetscInt fStart[32], fEnd[32];
7485 
7486       fStart[0] = 0;
7487       fEnd[0]   = 0;
7488       for (PetscInt f = 0; f < numFields; f++) {
7489         PetscInt fDof = 0;
7490 
7491         PetscCall(PetscSectionGetFieldDof(indices_section, b, f, &fDof));
7492         fStart[f + 1] = fStart[f] + fDof;
7493         fEnd[f + 1]   = fStart[f + 1];
7494       }
7495       PetscCall(PetscSectionGetOffset(indices_section, b, &bOff));
7496       // only apply permutations on one side
7497       PetscCall(DMPlexGetIndicesPointFields_Internal(indices_section, PETSC_TRUE, b, bOff, fEnd, PETSC_TRUE, perms, perms ? p : -1, NULL, tmpIndices));
7498       for (PetscInt f = 0; f < numFields; f++) {
7499         for (PetscInt i = fStart[f]; i < fEnd[f]; i++) { indices[fieldOffsets[f]++] = (cSecDof > 0) ? tmpIndices[i] : -(tmpIndices[i] + 1); }
7500       }
7501     } else {
7502       PetscInt bEnd = 0;
7503 
7504       PetscCall(PetscSectionGetOffset(indices_section, b, &bOff));
7505       PetscCall(DMPlexGetIndicesPoint_Internal(indices_section, PETSC_TRUE, b, bOff, &bEnd, PETSC_TRUE, (perms && perms[0]) ? perms[0][p] : NULL, NULL, tmpIndices));
7506 
7507       for (PetscInt i = 0; i < bEnd; i++) indices[fieldOffsets[0]++] = (cSecDof > 0) ? tmpIndices[i] : -(tmpIndices[i] + 1);
7508     }
7509   }
7510   PetscFunctionReturn(PETSC_SUCCESS);
7511 }
7512 
7513 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[])
7514 {
7515   Mat             cMat;
7516   PetscSection    aSec, cSec;
7517   IS              aIS;
7518   PetscInt        aStart = -1, aEnd = -1;
7519   PetscInt        sStart = -1, sEnd = -1;
7520   PetscInt        cStart = -1, cEnd = -1;
7521   const PetscInt *anchors;
7522   PetscInt        numFields, f, p;
7523   PetscInt        newNumPoints = 0, newNumIndices = 0;
7524   PetscInt       *newPoints, *indices, *newIndices, *tmpIndices, *tmpNewIndices;
7525   PetscInt        oldOffsets[32];
7526   PetscInt        newOffsets[32];
7527   PetscInt        oldOffsetsCopy[32];
7528   PetscInt        newOffsetsCopy[32];
7529   PetscScalar    *modMat         = NULL;
7530   PetscBool       anyConstrained = PETSC_FALSE;
7531 
7532   PetscFunctionBegin;
7533   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7534   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7535   PetscCall(PetscSectionGetNumFields(section, &numFields));
7536 
7537   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
7538   /* if there are point-to-point constraints */
7539   if (aSec) {
7540     PetscCall(PetscArrayzero(newOffsets, 32));
7541     PetscCall(PetscArrayzero(oldOffsets, 32));
7542     PetscCall(ISGetIndices(aIS, &anchors));
7543     PetscCall(PetscSectionGetChart(aSec, &aStart, &aEnd));
7544     PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
7545     /* figure out how many points are going to be in the new element matrix
7546      * (we allow double counting, because it's all just going to be summed
7547      * into the global matrix anyway) */
7548     for (p = 0; p < 2 * numPoints; p += 2) {
7549       PetscInt b    = points[p];
7550       PetscInt bDof = 0, bSecDof = 0;
7551 
7552       if (b >= sStart && b < sEnd) PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7553       if (!bSecDof) continue;
7554 
7555       for (PetscInt f = 0; f < numFields; f++) {
7556         PetscInt fDof = 0;
7557 
7558         PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7559         oldOffsets[f + 1] += fDof;
7560       }
7561       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7562       if (bDof) {
7563         /* this point is constrained */
7564         /* it is going to be replaced by its anchors */
7565         PetscInt bOff, q;
7566 
7567         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7568         for (q = 0; q < bDof; q++) {
7569           PetscInt a    = anchors[bOff + q];
7570           PetscInt aDof = 0;
7571 
7572           if (a >= sStart && a < sEnd) PetscCall(PetscSectionGetDof(section, a, &aDof));
7573           if (aDof) {
7574             anyConstrained = PETSC_TRUE;
7575             newNumPoints += 1;
7576           }
7577           newNumIndices += aDof;
7578           for (f = 0; f < numFields; ++f) {
7579             PetscInt fDof = 0;
7580 
7581             if (a >= sStart && a < sEnd) PetscCall(PetscSectionGetFieldDof(section, a, f, &fDof));
7582             newOffsets[f + 1] += fDof;
7583           }
7584         }
7585       } else {
7586         /* this point is not constrained */
7587         newNumPoints++;
7588         newNumIndices += bSecDof;
7589         for (f = 0; f < numFields; ++f) {
7590           PetscInt fDof;
7591 
7592           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7593           newOffsets[f + 1] += fDof;
7594         }
7595       }
7596     }
7597   }
7598   if (!anyConstrained) {
7599     if (outNumPoints) *outNumPoints = 0;
7600     if (outNumIndices) *outNumIndices = 0;
7601     if (outPoints) *outPoints = NULL;
7602     if (outMat) *outMat = NULL;
7603     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7604     PetscFunctionReturn(PETSC_SUCCESS);
7605   }
7606 
7607   if (outNumPoints) *outNumPoints = newNumPoints;
7608   if (outNumIndices) *outNumIndices = newNumIndices;
7609 
7610   for (f = 0; f < numFields; ++f) newOffsets[f + 1] += newOffsets[f];
7611   for (f = 0; f < numFields; ++f) oldOffsets[f + 1] += oldOffsets[f];
7612 
7613   if (!outPoints && !outMat) {
7614     if (offsets) {
7615       for (f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7616     }
7617     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7618     PetscFunctionReturn(PETSC_SUCCESS);
7619   }
7620 
7621   PetscCheck(!numFields || newOffsets[numFields] == newNumIndices, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, newOffsets[numFields], newNumIndices);
7622   PetscCheck(!numFields || oldOffsets[numFields] == numIndices, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, oldOffsets[numFields], numIndices);
7623 
7624   PetscCall(DMGetDefaultConstraints(dm, &cSec, &cMat, NULL));
7625   PetscCall(PetscSectionGetChart(cSec, &cStart, &cEnd));
7626 
7627   /* output arrays */
7628   PetscCall(DMGetWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7629   PetscCall(PetscArrayzero(newPoints, 2 * newNumPoints));
7630 
7631   // get the new Points
7632   for (PetscInt p = 0, newP = 0; p < numPoints; p++) {
7633     PetscInt b    = points[2 * p];
7634     PetscInt bDof = 0, bSecDof = 0, bOff;
7635 
7636     if (b >= sStart && b < sEnd) PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7637     if (!bSecDof) continue;
7638     if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7639     if (bDof) {
7640       PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7641       for (PetscInt q = 0; q < bDof; q++) {
7642         PetscInt a = anchors[bOff + q], aDof = 0;
7643 
7644         if (a >= sStart && a < sEnd) PetscCall(PetscSectionGetDof(section, a, &aDof));
7645         if (aDof) {
7646           newPoints[2 * newP]     = a;
7647           newPoints[2 * newP + 1] = 0; // orientations are accounted for in constructing the matrix, newly added points are in default orientation
7648           newP++;
7649         }
7650       }
7651     } else {
7652       newPoints[2 * newP]     = b;
7653       newPoints[2 * newP + 1] = points[2 * p + 1];
7654       newP++;
7655     }
7656   }
7657 
7658   if (outMat) {
7659     PetscScalar *tmpMat;
7660     PetscCall(PetscArraycpy(oldOffsetsCopy, oldOffsets, 32));
7661     PetscCall(PetscArraycpy(newOffsetsCopy, newOffsets, 32));
7662 
7663     PetscCall(DMGetWorkArray(dm, numIndices, MPIU_INT, &indices));
7664     PetscCall(DMGetWorkArray(dm, numIndices, MPIU_INT, &tmpIndices));
7665     PetscCall(DMGetWorkArray(dm, newNumIndices, MPIU_INT, &newIndices));
7666     PetscCall(DMGetWorkArray(dm, newNumIndices, MPIU_INT, &tmpNewIndices));
7667 
7668     for (PetscInt i = 0; i < numIndices; i++) indices[i] = -1;
7669     for (PetscInt i = 0; i < newNumIndices; i++) newIndices[i] = -1;
7670 
7671     PetscCall(DMPlexAnchorsGetSubMatIndices(numPoints, points, section, cSec, tmpIndices, oldOffsetsCopy, indices, perms));
7672     PetscCall(DMPlexAnchorsGetSubMatIndices(newNumPoints, newPoints, section, section, tmpNewIndices, newOffsetsCopy, newIndices, NULL));
7673 
7674     PetscCall(DMGetWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &modMat));
7675     PetscCall(DMGetWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &tmpMat));
7676     PetscCall(PetscArrayzero(modMat, newNumIndices * numIndices));
7677     // for each field, insert the anchor modification into modMat
7678     for (PetscInt f = 0; f < PetscMax(1, numFields); f++) {
7679       PetscInt fStart    = oldOffsets[f];
7680       PetscInt fNewStart = newOffsets[f];
7681       for (PetscInt p = 0, newP = 0, o = fStart, oNew = fNewStart; p < numPoints; p++) {
7682         PetscInt b    = points[2 * p];
7683         PetscInt bDof = 0, bSecDof = 0, bOff;
7684 
7685         if (b >= sStart && b < sEnd) {
7686           if (numFields) {
7687             PetscCall(PetscSectionGetFieldDof(section, b, f, &bSecDof));
7688           } else {
7689             PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7690           }
7691         }
7692         if (!bSecDof) continue;
7693         if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7694         if (bDof) {
7695           PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7696           for (PetscInt q = 0; q < bDof; q++, newP++) {
7697             PetscInt a = anchors[bOff + q], aDof = 0;
7698 
7699             if (a >= sStart && a < sEnd) {
7700               if (numFields) {
7701                 PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
7702               } else {
7703                 PetscCall(PetscSectionGetDof(section, a, &aDof));
7704               }
7705             }
7706             if (aDof) {
7707               PetscCall(MatGetValues(cMat, bSecDof, &indices[o], aDof, &newIndices[oNew], tmpMat));
7708               for (PetscInt d = 0; d < bSecDof; d++) {
7709                 for (PetscInt e = 0; e < aDof; e++) modMat[(o + d) * newNumIndices + oNew + e] = tmpMat[d * aDof + e];
7710               }
7711             }
7712             oNew += aDof;
7713           }
7714         } else {
7715           // Insert the identity matrix in this block
7716           for (PetscInt d = 0; d < bSecDof; d++) modMat[(o + d) * newNumIndices + oNew + d] = 1;
7717           oNew += bSecDof;
7718           newP++;
7719         }
7720         o += bSecDof;
7721       }
7722     }
7723 
7724     *outMat = modMat;
7725 
7726     PetscCall(DMRestoreWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &tmpMat));
7727     PetscCall(DMRestoreWorkArray(dm, newNumIndices, MPIU_INT, &tmpNewIndices));
7728     PetscCall(DMRestoreWorkArray(dm, newNumIndices, MPIU_INT, &newIndices));
7729     PetscCall(DMRestoreWorkArray(dm, numIndices, MPIU_INT, &tmpIndices));
7730     PetscCall(DMRestoreWorkArray(dm, numIndices, MPIU_INT, &indices));
7731   }
7732   PetscCall(ISRestoreIndices(aIS, &anchors));
7733 
7734   /* output */
7735   if (outPoints) {
7736     *outPoints = newPoints;
7737   } else {
7738     PetscCall(DMRestoreWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7739   }
7740   for (f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7741   PetscFunctionReturn(PETSC_SUCCESS);
7742 }
7743 
7744 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)
7745 {
7746   PetscScalar *modMat        = NULL;
7747   PetscInt     newNumIndices = -1;
7748 
7749   PetscFunctionBegin;
7750   /* 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.
7751      modMat is that matrix C */
7752   PetscCall(DMPlexAnchorsGetSubMatModification(dm, section, numPoints, numIndices, points, perms, outNumPoints, &newNumIndices, outPoints, offsets, outValues ? &modMat : NULL));
7753   if (outNumIndices) *outNumIndices = newNumIndices;
7754   if (modMat) {
7755     const PetscScalar *newValues = values;
7756 
7757     if (multiplyRight) {
7758       PetscScalar *newNewValues = NULL;
7759       PetscBLASInt M            = newNumIndices;
7760       PetscBLASInt N            = numRows;
7761       PetscBLASInt K            = numIndices;
7762       PetscScalar  a = 1.0, b = 0.0;
7763 
7764       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);
7765 
7766       PetscCall(DMGetWorkArray(dm, numRows * newNumIndices, MPIU_SCALAR, &newNewValues));
7767       // row-major to column-major conversion, right multiplication becomes left multiplication
7768       PetscCallBLAS("BLASgemm", BLASgemm_("N", "N", &M, &N, &K, &a, modMat, &M, newValues, &K, &b, newNewValues, &M));
7769 
7770       numCols   = newNumIndices;
7771       newValues = newNewValues;
7772     }
7773 
7774     if (multiplyLeft) {
7775       PetscScalar *newNewValues = NULL;
7776       PetscBLASInt M            = numCols;
7777       PetscBLASInt N            = newNumIndices;
7778       PetscBLASInt K            = numIndices;
7779       PetscScalar  a = 1.0, b = 0.0;
7780 
7781       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);
7782 
7783       PetscCall(DMGetWorkArray(dm, newNumIndices * numCols, MPIU_SCALAR, &newNewValues));
7784       // row-major to column-major conversion, left multiplication becomes right multiplication
7785       PetscCallBLAS("BLASgemm", BLASgemm_("N", "T", &M, &N, &K, &a, newValues, &M, modMat, &N, &b, newNewValues, &M));
7786       if (newValues != values) PetscCall(DMRestoreWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &newValues));
7787       newValues = newNewValues;
7788     }
7789     *outValues = (PetscScalar *)newValues;
7790     PetscCall(DMRestoreWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &modMat));
7791   }
7792   PetscFunctionReturn(PETSC_SUCCESS);
7793 }
7794 
7795 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)
7796 {
7797   PetscFunctionBegin;
7798   PetscCall(DMPlexAnchorsModifyMat_Internal(dm, section, numPoints, numIndices, points, perms, numIndices, numIndices, values, outNumPoints, outNumIndices, outPoints, outValues, offsets, PETSC_TRUE, multiplyLeft));
7799   PetscFunctionReturn(PETSC_SUCCESS);
7800 }
7801 
7802 static PetscErrorCode DMPlexGetClosureIndicesSize_Internal(DM dm, PetscSection section, PetscInt point, PetscInt *closureSize)
7803 {
7804   /* Closure ordering */
7805   PetscSection    clSection;
7806   IS              clPoints;
7807   const PetscInt *clp;
7808   PetscInt       *points;
7809   PetscInt        Ncl, Ni = 0;
7810 
7811   PetscFunctionBeginHot;
7812   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &Ncl, &points, &clSection, &clPoints, &clp));
7813   for (PetscInt p = 0; p < Ncl * 2; p += 2) {
7814     PetscInt dof;
7815 
7816     PetscCall(PetscSectionGetDof(section, points[p], &dof));
7817     Ni += dof;
7818   }
7819   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7820   *closureSize = Ni;
7821   PetscFunctionReturn(PETSC_SUCCESS);
7822 }
7823 
7824 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)
7825 {
7826   /* Closure ordering */
7827   PetscSection    clSection;
7828   IS              clPoints;
7829   const PetscInt *clp;
7830   PetscInt       *points;
7831   const PetscInt *clperm = NULL;
7832   /* Dof permutation and sign flips */
7833   const PetscInt    **perms[32] = {NULL};
7834   const PetscScalar **flips[32] = {NULL};
7835   PetscScalar        *valCopy   = NULL;
7836   /* Hanging node constraints */
7837   PetscInt    *pointsC = NULL;
7838   PetscScalar *valuesC = NULL;
7839   PetscInt     NclC, NiC;
7840 
7841   PetscInt *idx;
7842   PetscInt  Nf, Ncl, Ni = 0, offsets[32], p, f;
7843   PetscBool isLocal = (section == idxSection) ? PETSC_TRUE : PETSC_FALSE;
7844   PetscInt  idxStart, idxEnd;
7845   PetscInt  nRows, nCols;
7846 
7847   PetscFunctionBeginHot;
7848   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7849   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7850   PetscValidHeaderSpecific(idxSection, PETSC_SECTION_CLASSID, 3);
7851   PetscAssertPointer(numRows, 6);
7852   PetscAssertPointer(numCols, 7);
7853   if (indices) PetscAssertPointer(indices, 8);
7854   if (outOffsets) PetscAssertPointer(outOffsets, 9);
7855   if (values) PetscAssertPointer(values, 10);
7856   PetscCall(PetscSectionGetNumFields(section, &Nf));
7857   PetscCheck(Nf <= 31, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", Nf);
7858   PetscCall(PetscArrayzero(offsets, 32));
7859   /* 1) Get points in closure */
7860   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &Ncl, &points, &clSection, &clPoints, &clp));
7861   if (useClPerm) {
7862     PetscInt depth, clsize;
7863     PetscCall(DMPlexGetPointDepth(dm, point, &depth));
7864     for (clsize = 0, p = 0; p < Ncl; p++) {
7865       PetscInt dof;
7866       PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
7867       clsize += dof;
7868     }
7869     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
7870   }
7871   /* 2) Get number of indices on these points and field offsets from section */
7872   for (p = 0; p < Ncl * 2; p += 2) {
7873     PetscInt dof, fdof;
7874 
7875     PetscCall(PetscSectionGetDof(section, points[p], &dof));
7876     for (f = 0; f < Nf; ++f) {
7877       PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
7878       offsets[f + 1] += fdof;
7879     }
7880     Ni += dof;
7881   }
7882   if (*numRows == -1) *numRows = Ni;
7883   if (*numCols == -1) *numCols = Ni;
7884   nRows = *numRows;
7885   nCols = *numCols;
7886   for (f = 1; f < Nf; ++f) offsets[f + 1] += offsets[f];
7887   PetscCheck(!Nf || offsets[Nf] == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, offsets[Nf], Ni);
7888   /* 3) Get symmetries and sign flips. Apply sign flips to values if passed in (only works for square values matrix) */
7889   if (multiplyRight) PetscCheck(nCols == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Expected %" PetscInt_FMT " columns, got %" PetscInt_FMT, Ni, nCols);
7890   if (multiplyLeft) PetscCheck(nRows == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Expected %" PetscInt_FMT " rows, got %" PetscInt_FMT, Ni, nRows);
7891   for (f = 0; f < PetscMax(1, Nf); ++f) {
7892     if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7893     else PetscCall(PetscSectionGetPointSyms(section, Ncl, points, &perms[f], &flips[f]));
7894     /* may need to apply sign changes to the element matrix */
7895     if (values && flips[f]) {
7896       PetscInt foffset = offsets[f];
7897 
7898       for (p = 0; p < Ncl; ++p) {
7899         PetscInt           pnt  = points[2 * p], fdof;
7900         const PetscScalar *flip = flips[f] ? flips[f][p] : NULL;
7901 
7902         if (!Nf) PetscCall(PetscSectionGetDof(section, pnt, &fdof));
7903         else PetscCall(PetscSectionGetFieldDof(section, pnt, f, &fdof));
7904         if (flip) {
7905           PetscInt i, j, k;
7906 
7907           if (!valCopy) {
7908             PetscCall(DMGetWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
7909             for (j = 0; j < Ni * Ni; ++j) valCopy[j] = (*values)[j];
7910             *values = valCopy;
7911           }
7912           for (i = 0; i < fdof; ++i) {
7913             PetscScalar fval = flip[i];
7914 
7915             if (multiplyRight) {
7916               for (k = 0; k < nRows; ++k) { valCopy[Ni * k + (foffset + i)] *= fval; }
7917             }
7918             if (multiplyLeft) {
7919               for (k = 0; k < nCols; ++k) { valCopy[nCols * (foffset + i) + k] *= fval; }
7920             }
7921           }
7922         }
7923         foffset += fdof;
7924       }
7925     }
7926   }
7927   /* 4) Apply hanging node constraints. Get new symmetries and replace all storage with constrained storage */
7928   PetscCall(DMPlexAnchorsModifyMat_Internal(dm, section, Ncl, Ni, points, perms, nRows, nCols, values ? *values : NULL, &NclC, &NiC, &pointsC, values ? &valuesC : NULL, offsets, multiplyRight, multiplyLeft));
7929   if (NclC) {
7930     if (multiplyRight) { *numCols = nCols = NiC; }
7931     if (multiplyLeft) { *numRows = nRows = NiC; }
7932     if (valCopy) PetscCall(DMRestoreWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
7933     for (f = 0; f < PetscMax(1, Nf); ++f) {
7934       if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7935       else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
7936     }
7937     for (f = 0; f < PetscMax(1, Nf); ++f) {
7938       if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, NclC, pointsC, &perms[f], &flips[f]));
7939       else PetscCall(PetscSectionGetPointSyms(section, NclC, pointsC, &perms[f], &flips[f]));
7940     }
7941     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7942     Ncl    = NclC;
7943     Ni     = NiC;
7944     points = pointsC;
7945     if (values) *values = valuesC;
7946   }
7947   /* 5) Calculate indices */
7948   PetscCall(DMGetWorkArray(dm, Ni, MPIU_INT, &idx));
7949   PetscCall(PetscSectionGetChart(idxSection, &idxStart, &idxEnd));
7950   if (Nf) {
7951     PetscInt  idxOff;
7952     PetscBool useFieldOffsets;
7953 
7954     if (outOffsets) {
7955       for (f = 0; f <= Nf; f++) outOffsets[f] = offsets[f];
7956     }
7957     PetscCall(PetscSectionGetUseFieldOffsets(idxSection, &useFieldOffsets));
7958     if (useFieldOffsets) {
7959       for (p = 0; p < Ncl; ++p) {
7960         const PetscInt pnt = points[p * 2];
7961 
7962         PetscCall(DMPlexGetIndicesPointFieldsSplit_Internal(section, idxSection, pnt, offsets, perms, p, clperm, idx));
7963       }
7964     } else {
7965       for (p = 0; p < Ncl; ++p) {
7966         const PetscInt pnt = points[p * 2];
7967 
7968         if (pnt < idxStart || pnt >= idxEnd) continue;
7969         PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
7970         /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
7971          * not (at the time of this writing) have fields set. They probably should, in which case we would pass the
7972          * global section. */
7973         PetscCall(DMPlexGetIndicesPointFields_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, offsets, PETSC_FALSE, perms, p, clperm, idx));
7974       }
7975     }
7976   } else {
7977     PetscInt off = 0, idxOff;
7978 
7979     for (p = 0; p < Ncl; ++p) {
7980       const PetscInt  pnt  = points[p * 2];
7981       const PetscInt *perm = perms[0] ? perms[0][p] : NULL;
7982 
7983       if (pnt < idxStart || pnt >= idxEnd) continue;
7984       PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
7985       /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
7986        * not (at the time of this writing) have fields set. They probably should, in which case we would pass the global section. */
7987       PetscCall(DMPlexGetIndicesPoint_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, &off, PETSC_FALSE, perm, clperm, idx));
7988     }
7989   }
7990   /* 6) Cleanup */
7991   for (f = 0; f < PetscMax(1, Nf); ++f) {
7992     if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7993     else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
7994   }
7995   if (NclC) {
7996     PetscCall(DMRestoreWorkArray(dm, NclC * 2, MPIU_INT, &pointsC));
7997   } else {
7998     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7999   }
8000 
8001   if (indices) *indices = idx;
8002   PetscFunctionReturn(PETSC_SUCCESS);
8003 }
8004 
8005 /*@C
8006   DMPlexGetClosureIndices - Gets the global dof indices associated with the closure of the given point within the provided sections.
8007 
8008   Not collective
8009 
8010   Input Parameters:
8011 + dm         - The `DM`
8012 . section    - The `PetscSection` describing the points (a local section)
8013 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
8014 . point      - The point defining the closure
8015 - useClPerm  - Use the closure point permutation if available
8016 
8017   Output Parameters:
8018 + numIndices - The number of dof indices in the closure of point with the input sections
8019 . indices    - The dof indices
8020 . outOffsets - Array to write the field offsets into, or `NULL`
8021 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or `NULL`
8022 
8023   Level: advanced
8024 
8025   Notes:
8026   Must call `DMPlexRestoreClosureIndices()` to free allocated memory
8027 
8028   If `idxSection` is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
8029   of those indices is not significant.  If `idxSection` is local, the constrained dofs will yield the involution -(idx+1)
8030   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
8031   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when `idxSection` == section, otherwise global
8032   indices (with the above semantics) are implied.
8033 
8034 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`,
8035           `PetscSection`, `DMGetGlobalSection()`
8036 @*/
8037 PetscErrorCode DMPlexGetClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
8038 {
8039   PetscInt numRows = -1, numCols = -1;
8040 
8041   PetscFunctionBeginHot;
8042   PetscCall(DMPlexGetClosureIndices_Internal(dm, section, idxSection, point, useClPerm, &numRows, &numCols, indices, outOffsets, values, PETSC_TRUE, PETSC_TRUE));
8043   PetscCheck(numRows == numCols, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Symmetric matrix transformation produces rectangular dimensions (%" PetscInt_FMT ", %" PetscInt_FMT ")", numRows, numCols);
8044   *numIndices = numRows;
8045   PetscFunctionReturn(PETSC_SUCCESS);
8046 }
8047 
8048 /*@C
8049   DMPlexRestoreClosureIndices - Restores the global dof indices associated with the closure of the given point within the provided sections.
8050 
8051   Not collective
8052 
8053   Input Parameters:
8054 + dm         - The `DM`
8055 . section    - The `PetscSection` describing the points (a local section)
8056 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
8057 . point      - The point defining the closure
8058 - useClPerm  - Use the closure point permutation if available
8059 
8060   Output Parameters:
8061 + numIndices - The number of dof indices in the closure of point with the input sections
8062 . indices    - The dof indices
8063 . outOffsets - Array to write the field offsets into, or `NULL`
8064 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or `NULL`
8065 
8066   Level: advanced
8067 
8068   Notes:
8069   If values were modified, the user is responsible for calling `DMRestoreWorkArray`(dm, 0, `MPIU_SCALAR`, &values).
8070 
8071   If idxSection is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
8072   of those indices is not significant.  If idxSection is local, the constrained dofs will yield the involution -(idx+1)
8073   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
8074   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when idxSection == section, otherwise global
8075   indices (with the above semantics) are implied.
8076 
8077 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`, `DMGetGlobalSection()`
8078 @*/
8079 PetscErrorCode DMPlexRestoreClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
8080 {
8081   PetscFunctionBegin;
8082   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8083   PetscAssertPointer(indices, 7);
8084   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, indices));
8085   PetscFunctionReturn(PETSC_SUCCESS);
8086 }
8087 
8088 PetscErrorCode DMPlexMatSetClosure_Internal(DM dm, PetscSection section, PetscSection globalSection, PetscBool useClPerm, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8089 {
8090   DM_Plex           *mesh = (DM_Plex *)dm->data;
8091   PetscInt          *indices;
8092   PetscInt           numIndices;
8093   const PetscScalar *valuesOrig = values;
8094   PetscErrorCode     ierr;
8095 
8096   PetscFunctionBegin;
8097   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8098   if (!section) PetscCall(DMGetLocalSection(dm, &section));
8099   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
8100   if (!globalSection) PetscCall(DMGetGlobalSection(dm, &globalSection));
8101   PetscValidHeaderSpecific(globalSection, PETSC_SECTION_CLASSID, 3);
8102   PetscValidHeaderSpecific(A, MAT_CLASSID, 5);
8103 
8104   PetscCall(DMPlexGetClosureIndices(dm, section, globalSection, point, useClPerm, &numIndices, &indices, NULL, (PetscScalar **)&values));
8105 
8106   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndices, indices, 0, NULL, values));
8107   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8108   ierr = MatSetValues(A, numIndices, indices, numIndices, indices, values, mode);
8109   if (ierr) {
8110     PetscMPIInt rank;
8111 
8112     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8113     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8114     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndices, indices, 0, NULL, values));
8115     PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
8116     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
8117     SETERRQ(PetscObjectComm((PetscObject)dm), ierr, "Not possible to set matrix values");
8118   }
8119   if (mesh->printFEM > 1) {
8120     PetscInt i;
8121     PetscCall(PetscPrintf(PETSC_COMM_SELF, "  Indices:"));
8122     for (i = 0; i < numIndices; ++i) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, indices[i]));
8123     PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8124   }
8125 
8126   PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
8127   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
8128   PetscFunctionReturn(PETSC_SUCCESS);
8129 }
8130 
8131 /*@C
8132   DMPlexMatSetClosure - Set an array of the values on the closure of 'point'
8133 
8134   Not collective
8135 
8136   Input Parameters:
8137 + dm            - The `DM`
8138 . section       - The section describing the layout in `v`, or `NULL` to use the default section
8139 . globalSection - The section describing the layout in `v`, or `NULL` to use the default global section
8140 . A             - The matrix
8141 . point         - The point in the `DM`
8142 . values        - The array of values
8143 - mode          - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
8144 
8145   Level: intermediate
8146 
8147 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosureGeneral()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
8148 @*/
8149 PetscErrorCode DMPlexMatSetClosure(DM dm, PetscSection section, PetscSection globalSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8150 {
8151   PetscFunctionBegin;
8152   PetscCall(DMPlexMatSetClosure_Internal(dm, section, globalSection, PETSC_TRUE, A, point, values, mode));
8153   PetscFunctionReturn(PETSC_SUCCESS);
8154 }
8155 
8156 /*@C
8157   DMPlexMatSetClosureGeneral - Set an array of the values on the closure of 'point' using a different row and column section
8158 
8159   Not collective
8160 
8161   Input Parameters:
8162 + dmRow            - The `DM` for the row fields
8163 . sectionRow       - The section describing the layout, or `NULL` to use the default section in `dmRow`
8164 . useRowPerm       - The flag to use the closure permutation of the `dmRow` if available
8165 . globalSectionRow - The section describing the layout, or `NULL` to use the default global section in `dmRow`
8166 . dmCol            - The `DM` for the column fields
8167 . sectionCol       - The section describing the layout, or `NULL` to use the default section in `dmCol`
8168 . useColPerm       - The flag to use the closure permutation of the `dmCol` if available
8169 . globalSectionCol - The section describing the layout, or `NULL` to use the default global section in `dmCol`
8170 . A                - The matrix
8171 . point            - The point in the `DM`
8172 . values           - The array of values
8173 - mode             - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
8174 
8175   Level: intermediate
8176 
8177 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosure()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
8178 @*/
8179 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)
8180 {
8181   DM_Plex           *mesh = (DM_Plex *)dmRow->data;
8182   PetscInt          *indicesRow, *indicesCol;
8183   PetscInt           numIndicesRow = -1, numIndicesCol = -1;
8184   const PetscScalar *valuesV0 = values, *valuesV1, *valuesV2;
8185 
8186   PetscErrorCode ierr;
8187 
8188   PetscFunctionBegin;
8189   PetscValidHeaderSpecific(dmRow, DM_CLASSID, 1);
8190   if (!sectionRow) PetscCall(DMGetLocalSection(dmRow, &sectionRow));
8191   PetscValidHeaderSpecific(sectionRow, PETSC_SECTION_CLASSID, 2);
8192   if (!globalSectionRow) PetscCall(DMGetGlobalSection(dmRow, &globalSectionRow));
8193   PetscValidHeaderSpecific(globalSectionRow, PETSC_SECTION_CLASSID, 3);
8194   PetscValidHeaderSpecific(dmCol, DM_CLASSID, 5);
8195   if (!sectionCol) PetscCall(DMGetLocalSection(dmCol, &sectionCol));
8196   PetscValidHeaderSpecific(sectionCol, PETSC_SECTION_CLASSID, 6);
8197   if (!globalSectionCol) PetscCall(DMGetGlobalSection(dmCol, &globalSectionCol));
8198   PetscValidHeaderSpecific(globalSectionCol, PETSC_SECTION_CLASSID, 7);
8199   PetscValidHeaderSpecific(A, MAT_CLASSID, 9);
8200 
8201   PetscCall(DMPlexGetClosureIndicesSize_Internal(dmRow, sectionRow, point, &numIndicesRow));
8202   PetscCall(DMPlexGetClosureIndicesSize_Internal(dmCol, sectionCol, point, &numIndicesCol));
8203   valuesV1 = valuesV0;
8204   PetscCall(DMPlexGetClosureIndices_Internal(dmRow, sectionRow, globalSectionRow, point, useRowPerm, &numIndicesRow, &numIndicesCol, &indicesRow, NULL, (PetscScalar **)&valuesV1, PETSC_FALSE, PETSC_TRUE));
8205   valuesV2 = valuesV1;
8206   PetscCall(DMPlexGetClosureIndices_Internal(dmCol, sectionCol, globalSectionCol, point, useColPerm, &numIndicesRow, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&valuesV2, PETSC_TRUE, PETSC_FALSE));
8207 
8208   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, valuesV2));
8209   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8210   ierr = MatSetValues(A, numIndicesRow, indicesRow, numIndicesCol, indicesCol, valuesV2, mode);
8211   if (ierr) {
8212     PetscMPIInt rank;
8213 
8214     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8215     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8216     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
8217     PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesRow, NULL, (PetscScalar **)&valuesV2));
8218     PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&valuesV1));
8219     if (valuesV2 != valuesV1) PetscCall(DMRestoreWorkArray(dmCol, 0, MPIU_SCALAR, &valuesV2));
8220     if (valuesV1 != valuesV0) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &valuesV1));
8221   }
8222 
8223   PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, useColPerm, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&valuesV2));
8224   PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, useRowPerm, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&valuesV1));
8225   if (valuesV2 != valuesV1) PetscCall(DMRestoreWorkArray(dmCol, 0, MPIU_SCALAR, &valuesV2));
8226   if (valuesV1 != valuesV0) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &valuesV1));
8227   PetscFunctionReturn(PETSC_SUCCESS);
8228 }
8229 
8230 PetscErrorCode DMPlexMatSetClosureRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8231 {
8232   DM_Plex        *mesh    = (DM_Plex *)dmf->data;
8233   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
8234   PetscInt       *cpoints = NULL;
8235   PetscInt       *findices, *cindices;
8236   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
8237   PetscInt        foffsets[32], coffsets[32];
8238   DMPolytopeType  ct;
8239   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
8240   PetscErrorCode  ierr;
8241 
8242   PetscFunctionBegin;
8243   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
8244   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
8245   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
8246   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
8247   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
8248   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
8249   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
8250   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
8251   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
8252   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
8253   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
8254   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
8255   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
8256   PetscCall(PetscArrayzero(foffsets, 32));
8257   PetscCall(PetscArrayzero(coffsets, 32));
8258   /* Column indices */
8259   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8260   maxFPoints = numCPoints;
8261   /* Compress out points not in the section */
8262   /*   TODO: Squeeze out points with 0 dof as well */
8263   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
8264   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
8265     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
8266       cpoints[q * 2]     = cpoints[p];
8267       cpoints[q * 2 + 1] = cpoints[p + 1];
8268       ++q;
8269     }
8270   }
8271   numCPoints = q;
8272   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
8273     PetscInt fdof;
8274 
8275     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
8276     if (!dof) continue;
8277     for (f = 0; f < numFields; ++f) {
8278       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
8279       coffsets[f + 1] += fdof;
8280     }
8281     numCIndices += dof;
8282   }
8283   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
8284   /* Row indices */
8285   PetscCall(DMPlexGetCellType(dmc, point, &ct));
8286   {
8287     DMPlexTransform tr;
8288     DMPolytopeType *rct;
8289     PetscInt       *rsize, *rcone, *rornt, Nt;
8290 
8291     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8292     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8293     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8294     numSubcells = rsize[Nt - 1];
8295     PetscCall(DMPlexTransformDestroy(&tr));
8296   }
8297   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8298   for (r = 0, q = 0; r < numSubcells; ++r) {
8299     /* TODO Map from coarse to fine cells */
8300     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8301     /* Compress out points not in the section */
8302     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8303     for (p = 0; p < numFPoints * 2; p += 2) {
8304       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8305         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8306         if (!dof) continue;
8307         for (s = 0; s < q; ++s)
8308           if (fpoints[p] == ftotpoints[s * 2]) break;
8309         if (s < q) continue;
8310         ftotpoints[q * 2]     = fpoints[p];
8311         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8312         ++q;
8313       }
8314     }
8315     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8316   }
8317   numFPoints = q;
8318   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8319     PetscInt fdof;
8320 
8321     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8322     if (!dof) continue;
8323     for (f = 0; f < numFields; ++f) {
8324       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8325       foffsets[f + 1] += fdof;
8326     }
8327     numFIndices += dof;
8328   }
8329   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8330 
8331   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8332   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8333   PetscCall(DMGetWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8334   PetscCall(DMGetWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8335   if (numFields) {
8336     const PetscInt **permsF[32] = {NULL};
8337     const PetscInt **permsC[32] = {NULL};
8338 
8339     for (f = 0; f < numFields; f++) {
8340       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8341       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8342     }
8343     for (p = 0; p < numFPoints; p++) {
8344       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8345       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8346     }
8347     for (p = 0; p < numCPoints; p++) {
8348       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8349       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8350     }
8351     for (f = 0; f < numFields; f++) {
8352       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8353       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8354     }
8355   } else {
8356     const PetscInt **permsF = NULL;
8357     const PetscInt **permsC = NULL;
8358 
8359     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8360     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8361     for (p = 0, off = 0; p < numFPoints; p++) {
8362       const PetscInt *perm = permsF ? permsF[p] : NULL;
8363 
8364       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8365       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8366     }
8367     for (p = 0, off = 0; p < numCPoints; p++) {
8368       const PetscInt *perm = permsC ? permsC[p] : NULL;
8369 
8370       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8371       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8372     }
8373     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8374     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8375   }
8376   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8377   /* TODO: flips */
8378   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8379   ierr = MatSetValues(A, numFIndices, findices, numCIndices, cindices, values, mode);
8380   if (ierr) {
8381     PetscMPIInt rank;
8382 
8383     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8384     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8385     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8386     PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8387     PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8388   }
8389   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8390   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8391   PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8392   PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8393   PetscFunctionReturn(PETSC_SUCCESS);
8394 }
8395 
8396 PetscErrorCode DMPlexMatGetClosureIndicesRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, PetscInt point, PetscInt cindices[], PetscInt findices[])
8397 {
8398   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
8399   PetscInt       *cpoints      = NULL;
8400   PetscInt        foffsets[32] = {0}, coffsets[32] = {0};
8401   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
8402   DMPolytopeType  ct;
8403   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
8404 
8405   PetscFunctionBegin;
8406   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
8407   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
8408   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
8409   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
8410   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
8411   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
8412   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
8413   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
8414   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
8415   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
8416   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
8417   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
8418   /* Column indices */
8419   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8420   maxFPoints = numCPoints;
8421   /* Compress out points not in the section */
8422   /*   TODO: Squeeze out points with 0 dof as well */
8423   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
8424   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
8425     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
8426       cpoints[q * 2]     = cpoints[p];
8427       cpoints[q * 2 + 1] = cpoints[p + 1];
8428       ++q;
8429     }
8430   }
8431   numCPoints = q;
8432   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
8433     PetscInt fdof;
8434 
8435     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
8436     if (!dof) continue;
8437     for (f = 0; f < numFields; ++f) {
8438       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
8439       coffsets[f + 1] += fdof;
8440     }
8441     numCIndices += dof;
8442   }
8443   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
8444   /* Row indices */
8445   PetscCall(DMPlexGetCellType(dmc, point, &ct));
8446   {
8447     DMPlexTransform tr;
8448     DMPolytopeType *rct;
8449     PetscInt       *rsize, *rcone, *rornt, Nt;
8450 
8451     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8452     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8453     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8454     numSubcells = rsize[Nt - 1];
8455     PetscCall(DMPlexTransformDestroy(&tr));
8456   }
8457   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8458   for (r = 0, q = 0; r < numSubcells; ++r) {
8459     /* TODO Map from coarse to fine cells */
8460     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8461     /* Compress out points not in the section */
8462     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8463     for (p = 0; p < numFPoints * 2; p += 2) {
8464       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8465         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8466         if (!dof) continue;
8467         for (s = 0; s < q; ++s)
8468           if (fpoints[p] == ftotpoints[s * 2]) break;
8469         if (s < q) continue;
8470         ftotpoints[q * 2]     = fpoints[p];
8471         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8472         ++q;
8473       }
8474     }
8475     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8476   }
8477   numFPoints = q;
8478   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8479     PetscInt fdof;
8480 
8481     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8482     if (!dof) continue;
8483     for (f = 0; f < numFields; ++f) {
8484       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8485       foffsets[f + 1] += fdof;
8486     }
8487     numFIndices += dof;
8488   }
8489   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8490 
8491   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8492   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8493   if (numFields) {
8494     const PetscInt **permsF[32] = {NULL};
8495     const PetscInt **permsC[32] = {NULL};
8496 
8497     for (f = 0; f < numFields; f++) {
8498       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8499       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8500     }
8501     for (p = 0; p < numFPoints; p++) {
8502       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8503       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8504     }
8505     for (p = 0; p < numCPoints; p++) {
8506       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8507       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8508     }
8509     for (f = 0; f < numFields; f++) {
8510       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8511       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8512     }
8513   } else {
8514     const PetscInt **permsF = NULL;
8515     const PetscInt **permsC = NULL;
8516 
8517     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8518     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8519     for (p = 0, off = 0; p < numFPoints; p++) {
8520       const PetscInt *perm = permsF ? permsF[p] : NULL;
8521 
8522       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8523       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8524     }
8525     for (p = 0, off = 0; p < numCPoints; p++) {
8526       const PetscInt *perm = permsC ? permsC[p] : NULL;
8527 
8528       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8529       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8530     }
8531     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8532     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8533   }
8534   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8535   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8536   PetscFunctionReturn(PETSC_SUCCESS);
8537 }
8538 
8539 /*@C
8540   DMPlexGetVTKCellHeight - Returns the height in the DAG used to determine which points are cells (normally 0)
8541 
8542   Input Parameter:
8543 . dm - The `DMPLEX` object
8544 
8545   Output Parameter:
8546 . cellHeight - The height of a cell
8547 
8548   Level: developer
8549 
8550 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetVTKCellHeight()`
8551 @*/
8552 PetscErrorCode DMPlexGetVTKCellHeight(DM dm, PetscInt *cellHeight)
8553 {
8554   DM_Plex *mesh = (DM_Plex *)dm->data;
8555 
8556   PetscFunctionBegin;
8557   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8558   PetscAssertPointer(cellHeight, 2);
8559   *cellHeight = mesh->vtkCellHeight;
8560   PetscFunctionReturn(PETSC_SUCCESS);
8561 }
8562 
8563 /*@C
8564   DMPlexSetVTKCellHeight - Sets the height in the DAG used to determine which points are cells (normally 0)
8565 
8566   Input Parameters:
8567 + dm         - The `DMPLEX` object
8568 - cellHeight - The height of a cell
8569 
8570   Level: developer
8571 
8572 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetVTKCellHeight()`
8573 @*/
8574 PetscErrorCode DMPlexSetVTKCellHeight(DM dm, PetscInt cellHeight)
8575 {
8576   DM_Plex *mesh = (DM_Plex *)dm->data;
8577 
8578   PetscFunctionBegin;
8579   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8580   mesh->vtkCellHeight = cellHeight;
8581   PetscFunctionReturn(PETSC_SUCCESS);
8582 }
8583 
8584 /*@
8585   DMPlexGetCellTypeStratum - Get the range of cells of a given celltype
8586 
8587   Input Parameters:
8588 + dm - The `DMPLEX` object
8589 - ct - The `DMPolytopeType` of the cell
8590 
8591   Output Parameters:
8592 + start - The first cell of this type, or `NULL`
8593 - end   - The upper bound on this celltype, or `NULL`
8594 
8595   Level: advanced
8596 
8597 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexConstructGhostCells()`, `DMPlexGetDepthStratum()`, `DMPlexGetHeightStratum()`
8598 @*/
8599 PetscErrorCode DMPlexGetCellTypeStratum(DM dm, DMPolytopeType ct, PetscInt *start, PetscInt *end)
8600 {
8601   DM_Plex *mesh = (DM_Plex *)dm->data;
8602   DMLabel  label;
8603   PetscInt pStart, pEnd;
8604 
8605   PetscFunctionBegin;
8606   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8607   if (start) {
8608     PetscAssertPointer(start, 3);
8609     *start = 0;
8610   }
8611   if (end) {
8612     PetscAssertPointer(end, 4);
8613     *end = 0;
8614   }
8615   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8616   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
8617   if (mesh->tr) {
8618     PetscCall(DMPlexTransformGetCellTypeStratum(mesh->tr, ct, start, end));
8619   } else {
8620     PetscCall(DMPlexGetCellTypeLabel(dm, &label));
8621     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named celltype was found");
8622     PetscCall(DMLabelGetStratumBounds(label, ct, start, end));
8623   }
8624   PetscFunctionReturn(PETSC_SUCCESS);
8625 }
8626 
8627 PetscErrorCode DMPlexCreateNumbering_Plex(DM dm, PetscInt pStart, PetscInt pEnd, PetscInt shift, PetscInt *globalSize, PetscSF sf, IS *numbering)
8628 {
8629   PetscSection section, globalSection;
8630   PetscInt    *numbers, p;
8631 
8632   PetscFunctionBegin;
8633   if (PetscDefined(USE_DEBUG)) PetscCall(DMPlexCheckPointSF(dm, sf, PETSC_TRUE));
8634   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
8635   PetscCall(PetscSectionSetChart(section, pStart, pEnd));
8636   for (p = pStart; p < pEnd; ++p) PetscCall(PetscSectionSetDof(section, p, 1));
8637   PetscCall(PetscSectionSetUp(section));
8638   PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &globalSection));
8639   PetscCall(PetscMalloc1(pEnd - pStart, &numbers));
8640   for (p = pStart; p < pEnd; ++p) {
8641     PetscCall(PetscSectionGetOffset(globalSection, p, &numbers[p - pStart]));
8642     if (numbers[p - pStart] < 0) numbers[p - pStart] -= shift;
8643     else numbers[p - pStart] += shift;
8644   }
8645   PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), pEnd - pStart, numbers, PETSC_OWN_POINTER, numbering));
8646   if (globalSize) {
8647     PetscLayout layout;
8648     PetscCall(PetscSectionGetPointLayout(PetscObjectComm((PetscObject)dm), globalSection, &layout));
8649     PetscCall(PetscLayoutGetSize(layout, globalSize));
8650     PetscCall(PetscLayoutDestroy(&layout));
8651   }
8652   PetscCall(PetscSectionDestroy(&section));
8653   PetscCall(PetscSectionDestroy(&globalSection));
8654   PetscFunctionReturn(PETSC_SUCCESS);
8655 }
8656 
8657 PetscErrorCode DMPlexCreateCellNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalCellNumbers)
8658 {
8659   PetscInt cellHeight, cStart, cEnd;
8660 
8661   PetscFunctionBegin;
8662   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8663   if (includeHybrid) PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8664   else PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd));
8665   PetscCall(DMPlexCreateNumbering_Plex(dm, cStart, cEnd, 0, NULL, dm->sf, globalCellNumbers));
8666   PetscFunctionReturn(PETSC_SUCCESS);
8667 }
8668 
8669 /*@
8670   DMPlexGetCellNumbering - Get a global cell numbering for all cells on this process
8671 
8672   Input Parameter:
8673 . dm - The `DMPLEX` object
8674 
8675   Output Parameter:
8676 . globalCellNumbers - Global cell numbers for all cells on this process
8677 
8678   Level: developer
8679 
8680 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetVertexNumbering()`
8681 @*/
8682 PetscErrorCode DMPlexGetCellNumbering(DM dm, IS *globalCellNumbers)
8683 {
8684   DM_Plex *mesh = (DM_Plex *)dm->data;
8685 
8686   PetscFunctionBegin;
8687   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8688   if (!mesh->globalCellNumbers) PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_FALSE, &mesh->globalCellNumbers));
8689   *globalCellNumbers = mesh->globalCellNumbers;
8690   PetscFunctionReturn(PETSC_SUCCESS);
8691 }
8692 
8693 PetscErrorCode DMPlexCreateVertexNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalVertexNumbers)
8694 {
8695   PetscInt vStart, vEnd;
8696 
8697   PetscFunctionBegin;
8698   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8699   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8700   PetscCall(DMPlexCreateNumbering_Plex(dm, vStart, vEnd, 0, NULL, dm->sf, globalVertexNumbers));
8701   PetscFunctionReturn(PETSC_SUCCESS);
8702 }
8703 
8704 /*@
8705   DMPlexGetVertexNumbering - Get a global vertex numbering for all vertices on this process
8706 
8707   Input Parameter:
8708 . dm - The `DMPLEX` object
8709 
8710   Output Parameter:
8711 . globalVertexNumbers - Global vertex numbers for all vertices on this process
8712 
8713   Level: developer
8714 
8715 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
8716 @*/
8717 PetscErrorCode DMPlexGetVertexNumbering(DM dm, IS *globalVertexNumbers)
8718 {
8719   DM_Plex *mesh = (DM_Plex *)dm->data;
8720 
8721   PetscFunctionBegin;
8722   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8723   if (!mesh->globalVertexNumbers) PetscCall(DMPlexCreateVertexNumbering_Internal(dm, PETSC_FALSE, &mesh->globalVertexNumbers));
8724   *globalVertexNumbers = mesh->globalVertexNumbers;
8725   PetscFunctionReturn(PETSC_SUCCESS);
8726 }
8727 
8728 /*@
8729   DMPlexCreatePointNumbering - Create a global numbering for all points.
8730 
8731   Collective
8732 
8733   Input Parameter:
8734 . dm - The `DMPLEX` object
8735 
8736   Output Parameter:
8737 . globalPointNumbers - Global numbers for all points on this process
8738 
8739   Level: developer
8740 
8741   Notes:
8742   The point numbering `IS` is parallel, with local portion indexed by local points (see `DMGetLocalSection()`). The global
8743   points are taken as stratified, with each MPI rank owning a contiguous subset of each stratum. In the IS, owned points
8744   will have their non-negative value while points owned by different ranks will be involuted -(idx+1). As an example,
8745   consider a parallel mesh in which the first two elements and first two vertices are owned by rank 0.
8746 
8747   The partitioned mesh is
8748   ```
8749   (2)--0--(3)--1--(4)    (1)--0--(2)
8750   ```
8751   and its global numbering is
8752   ```
8753   (3)--0--(4)--1--(5)--2--(6)
8754   ```
8755   Then the global numbering is provided as
8756   ```
8757   [0] Number of indices in set 5
8758   [0] 0 0
8759   [0] 1 1
8760   [0] 2 3
8761   [0] 3 4
8762   [0] 4 -6
8763   [1] Number of indices in set 3
8764   [1] 0 2
8765   [1] 1 5
8766   [1] 2 6
8767   ```
8768 
8769 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
8770 @*/
8771 PetscErrorCode DMPlexCreatePointNumbering(DM dm, IS *globalPointNumbers)
8772 {
8773   IS        nums[4];
8774   PetscInt  depths[4], gdepths[4], starts[4];
8775   PetscInt  depth, d, shift = 0;
8776   PetscBool empty = PETSC_FALSE;
8777 
8778   PetscFunctionBegin;
8779   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8780   PetscCall(DMPlexGetDepth(dm, &depth));
8781   // For unstratified meshes use dim instead of depth
8782   if (depth < 0) PetscCall(DMGetDimension(dm, &depth));
8783   // If any stratum is empty, we must mark all empty
8784   for (d = 0; d <= depth; ++d) {
8785     PetscInt end;
8786 
8787     depths[d] = depth - d;
8788     PetscCall(DMPlexGetDepthStratum(dm, depths[d], &starts[d], &end));
8789     if (!(starts[d] - end)) empty = PETSC_TRUE;
8790   }
8791   if (empty)
8792     for (d = 0; d <= depth; ++d) {
8793       depths[d] = -1;
8794       starts[d] = -1;
8795     }
8796   else PetscCall(PetscSortIntWithArray(depth + 1, starts, depths));
8797   PetscCall(MPIU_Allreduce(depths, gdepths, depth + 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
8798   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]);
8799   // Note here that 'shift' is collective, so that the numbering is stratified by depth
8800   for (d = 0; d <= depth; ++d) {
8801     PetscInt pStart, pEnd, gsize;
8802 
8803     PetscCall(DMPlexGetDepthStratum(dm, gdepths[d], &pStart, &pEnd));
8804     PetscCall(DMPlexCreateNumbering_Plex(dm, pStart, pEnd, shift, &gsize, dm->sf, &nums[d]));
8805     shift += gsize;
8806   }
8807   PetscCall(ISConcatenate(PETSC_COMM_SELF, depth + 1, nums, globalPointNumbers));
8808   for (d = 0; d <= depth; ++d) PetscCall(ISDestroy(&nums[d]));
8809   PetscFunctionReturn(PETSC_SUCCESS);
8810 }
8811 
8812 /*@
8813   DMPlexCreateRankField - Create a cell field whose value is the rank of the owner
8814 
8815   Input Parameter:
8816 . dm - The `DMPLEX` object
8817 
8818   Output Parameter:
8819 . ranks - The rank field
8820 
8821   Options Database Key:
8822 . -dm_partition_view - Adds the rank field into the `DM` output from `-dm_view` using the same viewer
8823 
8824   Level: intermediate
8825 
8826 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`
8827 @*/
8828 PetscErrorCode DMPlexCreateRankField(DM dm, Vec *ranks)
8829 {
8830   DM             rdm;
8831   PetscFE        fe;
8832   PetscScalar   *r;
8833   PetscMPIInt    rank;
8834   DMPolytopeType ct;
8835   PetscInt       dim, cStart, cEnd, c;
8836   PetscBool      simplex;
8837 
8838   PetscFunctionBeginUser;
8839   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8840   PetscAssertPointer(ranks, 2);
8841   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
8842   PetscCall(DMClone(dm, &rdm));
8843   PetscCall(DMGetDimension(rdm, &dim));
8844   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
8845   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
8846   simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct) + 1 ? PETSC_TRUE : PETSC_FALSE;
8847   PetscCall(PetscFECreateDefault(PETSC_COMM_SELF, dim, 1, simplex, "PETSc___rank_", -1, &fe));
8848   PetscCall(PetscObjectSetName((PetscObject)fe, "rank"));
8849   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
8850   PetscCall(PetscFEDestroy(&fe));
8851   PetscCall(DMCreateDS(rdm));
8852   PetscCall(DMCreateGlobalVector(rdm, ranks));
8853   PetscCall(PetscObjectSetName((PetscObject)*ranks, "partition"));
8854   PetscCall(VecGetArray(*ranks, &r));
8855   for (c = cStart; c < cEnd; ++c) {
8856     PetscScalar *lr;
8857 
8858     PetscCall(DMPlexPointGlobalRef(rdm, c, r, &lr));
8859     if (lr) *lr = rank;
8860   }
8861   PetscCall(VecRestoreArray(*ranks, &r));
8862   PetscCall(DMDestroy(&rdm));
8863   PetscFunctionReturn(PETSC_SUCCESS);
8864 }
8865 
8866 /*@
8867   DMPlexCreateLabelField - Create a field whose value is the label value for that point
8868 
8869   Input Parameters:
8870 + dm    - The `DMPLEX`
8871 - label - The `DMLabel`
8872 
8873   Output Parameter:
8874 . val - The label value field
8875 
8876   Options Database Key:
8877 . -dm_label_view - Adds the label value field into the `DM` output from `-dm_view` using the same viewer
8878 
8879   Level: intermediate
8880 
8881 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`
8882 @*/
8883 PetscErrorCode DMPlexCreateLabelField(DM dm, DMLabel label, Vec *val)
8884 {
8885   DM             rdm, plex;
8886   Vec            lval;
8887   PetscSection   section;
8888   PetscFE        fe;
8889   PetscScalar   *v;
8890   PetscInt       dim, pStart, pEnd, p, cStart;
8891   DMPolytopeType ct;
8892   char           name[PETSC_MAX_PATH_LEN];
8893   const char    *lname, *prefix;
8894 
8895   PetscFunctionBeginUser;
8896   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8897   PetscAssertPointer(label, 2);
8898   PetscAssertPointer(val, 3);
8899   PetscCall(DMClone(dm, &rdm));
8900   PetscCall(DMConvert(rdm, DMPLEX, &plex));
8901   PetscCall(DMPlexGetHeightStratum(plex, 0, &cStart, NULL));
8902   PetscCall(DMPlexGetCellType(plex, cStart, &ct));
8903   PetscCall(DMDestroy(&plex));
8904   PetscCall(DMGetDimension(rdm, &dim));
8905   PetscCall(DMGetOptionsPrefix(dm, &prefix));
8906   PetscCall(PetscObjectGetName((PetscObject)label, &lname));
8907   PetscCall(PetscSNPrintf(name, sizeof(name), "%s%s_", prefix ? prefix : "", lname));
8908   PetscCall(PetscFECreateByCell(PETSC_COMM_SELF, dim, 1, ct, name, -1, &fe));
8909   PetscCall(PetscObjectSetName((PetscObject)fe, ""));
8910   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
8911   PetscCall(PetscFEDestroy(&fe));
8912   PetscCall(DMCreateDS(rdm));
8913   PetscCall(DMCreateGlobalVector(rdm, val));
8914   PetscCall(DMCreateLocalVector(rdm, &lval));
8915   PetscCall(PetscObjectSetName((PetscObject)*val, lname));
8916   PetscCall(DMGetLocalSection(rdm, &section));
8917   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
8918   PetscCall(VecGetArray(lval, &v));
8919   for (p = pStart; p < pEnd; ++p) {
8920     PetscInt cval, dof, off;
8921 
8922     PetscCall(PetscSectionGetDof(section, p, &dof));
8923     if (!dof) continue;
8924     PetscCall(DMLabelGetValue(label, p, &cval));
8925     PetscCall(PetscSectionGetOffset(section, p, &off));
8926     for (PetscInt d = 0; d < dof; d++) v[off + d] = cval;
8927   }
8928   PetscCall(VecRestoreArray(lval, &v));
8929   PetscCall(DMLocalToGlobal(rdm, lval, INSERT_VALUES, *val));
8930   PetscCall(VecDestroy(&lval));
8931   PetscCall(DMDestroy(&rdm));
8932   PetscFunctionReturn(PETSC_SUCCESS);
8933 }
8934 
8935 /*@
8936   DMPlexCheckSymmetry - Check that the adjacency information in the mesh is symmetric.
8937 
8938   Input Parameter:
8939 . dm - The `DMPLEX` object
8940 
8941   Level: developer
8942 
8943   Notes:
8944   This is a useful diagnostic when creating meshes programmatically.
8945 
8946   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
8947 
8948 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
8949 @*/
8950 PetscErrorCode DMPlexCheckSymmetry(DM dm)
8951 {
8952   PetscSection    coneSection, supportSection;
8953   const PetscInt *cone, *support;
8954   PetscInt        coneSize, c, supportSize, s;
8955   PetscInt        pStart, pEnd, p, pp, csize, ssize;
8956   PetscBool       storagecheck = PETSC_TRUE;
8957 
8958   PetscFunctionBegin;
8959   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8960   PetscCall(DMViewFromOptions(dm, NULL, "-sym_dm_view"));
8961   PetscCall(DMPlexGetConeSection(dm, &coneSection));
8962   PetscCall(DMPlexGetSupportSection(dm, &supportSection));
8963   /* Check that point p is found in the support of its cone points, and vice versa */
8964   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8965   for (p = pStart; p < pEnd; ++p) {
8966     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
8967     PetscCall(DMPlexGetCone(dm, p, &cone));
8968     for (c = 0; c < coneSize; ++c) {
8969       PetscBool dup = PETSC_FALSE;
8970       PetscInt  d;
8971       for (d = c - 1; d >= 0; --d) {
8972         if (cone[c] == cone[d]) {
8973           dup = PETSC_TRUE;
8974           break;
8975         }
8976       }
8977       PetscCall(DMPlexGetSupportSize(dm, cone[c], &supportSize));
8978       PetscCall(DMPlexGetSupport(dm, cone[c], &support));
8979       for (s = 0; s < supportSize; ++s) {
8980         if (support[s] == p) break;
8981       }
8982       if ((s >= supportSize) || (dup && (support[s + 1] != p))) {
8983         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", p));
8984         for (s = 0; s < coneSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[s]));
8985         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8986         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", cone[c]));
8987         for (s = 0; s < supportSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[s]));
8988         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8989         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]);
8990         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in support of cone point %" PetscInt_FMT, p, cone[c]);
8991       }
8992     }
8993     PetscCall(DMPlexGetTreeParent(dm, p, &pp, NULL));
8994     if (p != pp) {
8995       storagecheck = PETSC_FALSE;
8996       continue;
8997     }
8998     PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
8999     PetscCall(DMPlexGetSupport(dm, p, &support));
9000     for (s = 0; s < supportSize; ++s) {
9001       PetscCall(DMPlexGetConeSize(dm, support[s], &coneSize));
9002       PetscCall(DMPlexGetCone(dm, support[s], &cone));
9003       for (c = 0; c < coneSize; ++c) {
9004         PetscCall(DMPlexGetTreeParent(dm, cone[c], &pp, NULL));
9005         if (cone[c] != pp) {
9006           c = 0;
9007           break;
9008         }
9009         if (cone[c] == p) break;
9010       }
9011       if (c >= coneSize) {
9012         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", p));
9013         for (c = 0; c < supportSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[c]));
9014         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9015         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", support[s]));
9016         for (c = 0; c < coneSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[c]));
9017         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9018         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in cone of support point %" PetscInt_FMT, p, support[s]);
9019       }
9020     }
9021   }
9022   if (storagecheck) {
9023     PetscCall(PetscSectionGetStorageSize(coneSection, &csize));
9024     PetscCall(PetscSectionGetStorageSize(supportSection, &ssize));
9025     PetscCheck(csize == ssize, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Total cone size %" PetscInt_FMT " != Total support size %" PetscInt_FMT, csize, ssize);
9026   }
9027   PetscFunctionReturn(PETSC_SUCCESS);
9028 }
9029 
9030 /*
9031   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.
9032 */
9033 static PetscErrorCode DMPlexCellUnsplitVertices_Private(DM dm, PetscInt c, DMPolytopeType ct, PetscInt *unsplit)
9034 {
9035   DMPolytopeType  cct;
9036   PetscInt        ptpoints[4];
9037   const PetscInt *cone, *ccone, *ptcone;
9038   PetscInt        coneSize, cp, cconeSize, ccp, npt = 0, pt;
9039 
9040   PetscFunctionBegin;
9041   *unsplit = 0;
9042   switch (ct) {
9043   case DM_POLYTOPE_POINT_PRISM_TENSOR:
9044     ptpoints[npt++] = c;
9045     break;
9046   case DM_POLYTOPE_SEG_PRISM_TENSOR:
9047     PetscCall(DMPlexGetCone(dm, c, &cone));
9048     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9049     for (cp = 0; cp < coneSize; ++cp) {
9050       PetscCall(DMPlexGetCellType(dm, cone[cp], &cct));
9051       if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) ptpoints[npt++] = cone[cp];
9052     }
9053     break;
9054   case DM_POLYTOPE_TRI_PRISM_TENSOR:
9055   case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9056     PetscCall(DMPlexGetCone(dm, c, &cone));
9057     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9058     for (cp = 0; cp < coneSize; ++cp) {
9059       PetscCall(DMPlexGetCone(dm, cone[cp], &ccone));
9060       PetscCall(DMPlexGetConeSize(dm, cone[cp], &cconeSize));
9061       for (ccp = 0; ccp < cconeSize; ++ccp) {
9062         PetscCall(DMPlexGetCellType(dm, ccone[ccp], &cct));
9063         if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) {
9064           PetscInt p;
9065           for (p = 0; p < npt; ++p)
9066             if (ptpoints[p] == ccone[ccp]) break;
9067           if (p == npt) ptpoints[npt++] = ccone[ccp];
9068         }
9069       }
9070     }
9071     break;
9072   default:
9073     break;
9074   }
9075   for (pt = 0; pt < npt; ++pt) {
9076     PetscCall(DMPlexGetCone(dm, ptpoints[pt], &ptcone));
9077     if (ptcone[0] == ptcone[1]) ++(*unsplit);
9078   }
9079   PetscFunctionReturn(PETSC_SUCCESS);
9080 }
9081 
9082 /*@
9083   DMPlexCheckSkeleton - Check that each cell has the correct number of vertices
9084 
9085   Input Parameters:
9086 + dm         - The `DMPLEX` object
9087 - cellHeight - Normally 0
9088 
9089   Level: developer
9090 
9091   Notes:
9092   This is a useful diagnostic when creating meshes programmatically.
9093   Currently applicable only to homogeneous simplex or tensor meshes.
9094 
9095   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9096 
9097 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9098 @*/
9099 PetscErrorCode DMPlexCheckSkeleton(DM dm, PetscInt cellHeight)
9100 {
9101   DMPlexInterpolatedFlag interp;
9102   DMPolytopeType         ct;
9103   PetscInt               vStart, vEnd, cStart, cEnd, c;
9104 
9105   PetscFunctionBegin;
9106   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9107   PetscCall(DMPlexIsInterpolated(dm, &interp));
9108   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9109   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
9110   for (c = cStart; c < cEnd; ++c) {
9111     PetscInt *closure = NULL;
9112     PetscInt  coneSize, closureSize, cl, Nv = 0;
9113 
9114     PetscCall(DMPlexGetCellType(dm, c, &ct));
9115     PetscCheck((PetscInt)ct >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell %" PetscInt_FMT " has no cell type", c);
9116     if (ct == DM_POLYTOPE_UNKNOWN) continue;
9117     if (interp == DMPLEX_INTERPOLATED_FULL) {
9118       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9119       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));
9120     }
9121     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9122     for (cl = 0; cl < closureSize * 2; cl += 2) {
9123       const PetscInt p = closure[cl];
9124       if ((p >= vStart) && (p < vEnd)) ++Nv;
9125     }
9126     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9127     /* Special Case: Tensor faces with identified vertices */
9128     if (Nv < DMPolytopeTypeGetNumVertices(ct)) {
9129       PetscInt unsplit;
9130 
9131       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9132       if (Nv + unsplit == DMPolytopeTypeGetNumVertices(ct)) continue;
9133     }
9134     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));
9135   }
9136   PetscFunctionReturn(PETSC_SUCCESS);
9137 }
9138 
9139 /*@
9140   DMPlexCheckFaces - Check that the faces of each cell give a vertex order this is consistent with what we expect from the cell type
9141 
9142   Collective
9143 
9144   Input Parameters:
9145 + dm         - The `DMPLEX` object
9146 - cellHeight - Normally 0
9147 
9148   Level: developer
9149 
9150   Notes:
9151   This is a useful diagnostic when creating meshes programmatically.
9152   This routine is only relevant for meshes that are fully interpolated across all ranks.
9153   It will error out if a partially interpolated mesh is given on some rank.
9154   It will do nothing for locally uninterpolated mesh (as there is nothing to check).
9155 
9156   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9157 
9158 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMPlexGetVTKCellHeight()`, `DMSetFromOptions()`
9159 @*/
9160 PetscErrorCode DMPlexCheckFaces(DM dm, PetscInt cellHeight)
9161 {
9162   PetscInt               dim, depth, vStart, vEnd, cStart, cEnd, c, h;
9163   DMPlexInterpolatedFlag interpEnum;
9164 
9165   PetscFunctionBegin;
9166   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9167   PetscCall(DMPlexIsInterpolatedCollective(dm, &interpEnum));
9168   if (interpEnum == DMPLEX_INTERPOLATED_NONE) PetscFunctionReturn(PETSC_SUCCESS);
9169   if (interpEnum != DMPLEX_INTERPOLATED_FULL) {
9170     PetscCall(PetscPrintf(PetscObjectComm((PetscObject)dm), "DMPlexCheckFaces() warning: Mesh is only partially interpolated, this is currently not supported"));
9171     PetscFunctionReturn(PETSC_SUCCESS);
9172   }
9173 
9174   PetscCall(DMGetDimension(dm, &dim));
9175   PetscCall(DMPlexGetDepth(dm, &depth));
9176   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
9177   for (h = cellHeight; h < PetscMin(depth, dim); ++h) {
9178     PetscCall(DMPlexGetHeightStratum(dm, h, &cStart, &cEnd));
9179     for (c = cStart; c < cEnd; ++c) {
9180       const PetscInt       *cone, *ornt, *faceSizes, *faces;
9181       const DMPolytopeType *faceTypes;
9182       DMPolytopeType        ct;
9183       PetscInt              numFaces, coneSize, f;
9184       PetscInt             *closure = NULL, closureSize, cl, numCorners = 0, fOff = 0, unsplit;
9185 
9186       PetscCall(DMPlexGetCellType(dm, c, &ct));
9187       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9188       if (unsplit) continue;
9189       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9190       PetscCall(DMPlexGetCone(dm, c, &cone));
9191       PetscCall(DMPlexGetConeOrientation(dm, c, &ornt));
9192       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9193       for (cl = 0; cl < closureSize * 2; cl += 2) {
9194         const PetscInt p = closure[cl];
9195         if ((p >= vStart) && (p < vEnd)) closure[numCorners++] = p;
9196       }
9197       PetscCall(DMPlexGetRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
9198       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);
9199       for (f = 0; f < numFaces; ++f) {
9200         DMPolytopeType fct;
9201         PetscInt      *fclosure = NULL, fclosureSize, cl, fnumCorners = 0, v;
9202 
9203         PetscCall(DMPlexGetCellType(dm, cone[f], &fct));
9204         PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[f], ornt[f], PETSC_TRUE, &fclosureSize, &fclosure));
9205         for (cl = 0; cl < fclosureSize * 2; cl += 2) {
9206           const PetscInt p = fclosure[cl];
9207           if ((p >= vStart) && (p < vEnd)) fclosure[fnumCorners++] = p;
9208         }
9209         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]);
9210         for (v = 0; v < fnumCorners; ++v) {
9211           if (fclosure[v] != faces[fOff + v]) {
9212             PetscInt v1;
9213 
9214             PetscCall(PetscPrintf(PETSC_COMM_SELF, "face closure:"));
9215             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, fclosure[v1]));
9216             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\ncell face:"));
9217             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, faces[fOff + v1]));
9218             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9219             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]);
9220           }
9221         }
9222         PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[f], PETSC_TRUE, &fclosureSize, &fclosure));
9223         fOff += faceSizes[f];
9224       }
9225       PetscCall(DMPlexRestoreRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
9226       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9227     }
9228   }
9229   PetscFunctionReturn(PETSC_SUCCESS);
9230 }
9231 
9232 /*@
9233   DMPlexCheckGeometry - Check the geometry of mesh cells
9234 
9235   Input Parameter:
9236 . dm - The `DMPLEX` object
9237 
9238   Level: developer
9239 
9240   Notes:
9241   This is a useful diagnostic when creating meshes programmatically.
9242 
9243   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9244 
9245 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9246 @*/
9247 PetscErrorCode DMPlexCheckGeometry(DM dm)
9248 {
9249   Vec       coordinates;
9250   PetscReal detJ, J[9], refVol = 1.0;
9251   PetscReal vol;
9252   PetscInt  dim, depth, dE, d, cStart, cEnd, c;
9253 
9254   PetscFunctionBegin;
9255   PetscCall(DMGetDimension(dm, &dim));
9256   PetscCall(DMGetCoordinateDim(dm, &dE));
9257   if (dim != dE) PetscFunctionReturn(PETSC_SUCCESS);
9258   PetscCall(DMPlexGetDepth(dm, &depth));
9259   for (d = 0; d < dim; ++d) refVol *= 2.0;
9260   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
9261   /* Make sure local coordinates are created, because that step is collective */
9262   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
9263   if (!coordinates) PetscFunctionReturn(PETSC_SUCCESS);
9264   for (c = cStart; c < cEnd; ++c) {
9265     DMPolytopeType ct;
9266     PetscInt       unsplit;
9267     PetscBool      ignoreZeroVol = PETSC_FALSE;
9268 
9269     PetscCall(DMPlexGetCellType(dm, c, &ct));
9270     switch (ct) {
9271     case DM_POLYTOPE_SEG_PRISM_TENSOR:
9272     case DM_POLYTOPE_TRI_PRISM_TENSOR:
9273     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9274       ignoreZeroVol = PETSC_TRUE;
9275       break;
9276     default:
9277       break;
9278     }
9279     switch (ct) {
9280     case DM_POLYTOPE_TRI_PRISM:
9281     case DM_POLYTOPE_TRI_PRISM_TENSOR:
9282     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9283     case DM_POLYTOPE_PYRAMID:
9284       continue;
9285     default:
9286       break;
9287     }
9288     PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9289     if (unsplit) continue;
9290     PetscCall(DMPlexComputeCellGeometryFEM(dm, c, NULL, NULL, J, NULL, &detJ));
9291     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);
9292     PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FEM Volume %g\n", c, (double)(detJ * refVol)));
9293     /* This should work with periodicity since DG coordinates should be used */
9294     if (depth > 1) {
9295       PetscCall(DMPlexComputeCellGeometryFVM(dm, c, &vol, NULL, NULL));
9296       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);
9297       PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FVM Volume %g\n", c, (double)vol));
9298     }
9299   }
9300   PetscFunctionReturn(PETSC_SUCCESS);
9301 }
9302 
9303 /*@
9304   DMPlexCheckPointSF - Check that several necessary conditions are met for the point `PetscSF` of this plex.
9305 
9306   Collective
9307 
9308   Input Parameters:
9309 + dm              - The `DMPLEX` object
9310 . pointSF         - The `PetscSF`, or `NULL` for `PointSF` attached to `DM`
9311 - allowExtraRoots - Flag to allow extra points not present in the `DM`
9312 
9313   Level: developer
9314 
9315   Notes:
9316   This is mainly intended for debugging/testing purposes.
9317 
9318   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9319 
9320   Extra roots can come from periodic cuts, where additional points appear on the boundary
9321 
9322 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMGetPointSF()`, `DMSetFromOptions()`
9323 @*/
9324 PetscErrorCode DMPlexCheckPointSF(DM dm, PetscSF pointSF, PetscBool allowExtraRoots)
9325 {
9326   PetscInt           l, nleaves, nroots, overlap;
9327   const PetscInt    *locals;
9328   const PetscSFNode *remotes;
9329   PetscBool          distributed;
9330   MPI_Comm           comm;
9331   PetscMPIInt        rank;
9332 
9333   PetscFunctionBegin;
9334   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9335   if (pointSF) PetscValidHeaderSpecific(pointSF, PETSCSF_CLASSID, 2);
9336   else pointSF = dm->sf;
9337   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9338   PetscCheck(pointSF, comm, PETSC_ERR_ARG_WRONGSTATE, "DMPlex must have Point SF attached");
9339   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9340   {
9341     PetscMPIInt mpiFlag;
9342 
9343     PetscCallMPI(MPI_Comm_compare(comm, PetscObjectComm((PetscObject)pointSF), &mpiFlag));
9344     PetscCheck(mpiFlag == MPI_CONGRUENT || mpiFlag == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "DM and Point SF have different communicators (flag %d)", mpiFlag);
9345   }
9346   PetscCall(PetscSFGetGraph(pointSF, &nroots, &nleaves, &locals, &remotes));
9347   PetscCall(DMPlexIsDistributed(dm, &distributed));
9348   if (!distributed) {
9349     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);
9350     PetscFunctionReturn(PETSC_SUCCESS);
9351   }
9352   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);
9353   PetscCall(DMPlexGetOverlap(dm, &overlap));
9354 
9355   /* Check SF graph is compatible with DMPlex chart */
9356   {
9357     PetscInt pStart, pEnd, maxLeaf;
9358 
9359     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
9360     PetscCall(PetscSFGetLeafRange(pointSF, NULL, &maxLeaf));
9361     PetscCheck(allowExtraRoots || pEnd - pStart == nroots, PETSC_COMM_SELF, PETSC_ERR_PLIB, "pEnd - pStart = %" PetscInt_FMT " != nroots = %" PetscInt_FMT, pEnd - pStart, nroots);
9362     PetscCheck(maxLeaf < pEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "maxLeaf = %" PetscInt_FMT " >= pEnd = %" PetscInt_FMT, maxLeaf, pEnd);
9363   }
9364 
9365   /* Check Point SF has no local points referenced */
9366   for (l = 0; l < nleaves; l++) {
9367     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);
9368   }
9369 
9370   /* Check there are no cells in interface */
9371   if (!overlap) {
9372     PetscInt cellHeight, cStart, cEnd;
9373 
9374     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9375     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9376     for (l = 0; l < nleaves; ++l) {
9377       const PetscInt point = locals ? locals[l] : l;
9378 
9379       PetscCheck(point < cStart || point >= cEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " which is a cell", point);
9380     }
9381   }
9382 
9383   /* If some point is in interface, then all its cone points must be also in interface (either as leaves or roots) */
9384   {
9385     const PetscInt *rootdegree;
9386 
9387     PetscCall(PetscSFComputeDegreeBegin(pointSF, &rootdegree));
9388     PetscCall(PetscSFComputeDegreeEnd(pointSF, &rootdegree));
9389     for (l = 0; l < nleaves; ++l) {
9390       const PetscInt  point = locals ? locals[l] : l;
9391       const PetscInt *cone;
9392       PetscInt        coneSize, c, idx;
9393 
9394       PetscCall(DMPlexGetConeSize(dm, point, &coneSize));
9395       PetscCall(DMPlexGetCone(dm, point, &cone));
9396       for (c = 0; c < coneSize; ++c) {
9397         if (!rootdegree[cone[c]]) {
9398           if (locals) {
9399             PetscCall(PetscFindInt(cone[c], nleaves, locals, &idx));
9400           } else {
9401             idx = (cone[c] < nleaves) ? cone[c] : -1;
9402           }
9403           PetscCheck(idx >= 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " but not %" PetscInt_FMT " from its cone", point, cone[c]);
9404         }
9405       }
9406     }
9407   }
9408   PetscFunctionReturn(PETSC_SUCCESS);
9409 }
9410 
9411 /*@
9412   DMPlexCheck - Perform various checks of `DMPLEX` sanity
9413 
9414   Input Parameter:
9415 . dm - The `DMPLEX` object
9416 
9417   Level: developer
9418 
9419   Notes:
9420   This is a useful diagnostic when creating meshes programmatically.
9421 
9422   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9423 
9424   Currently does not include `DMPlexCheckCellShape()`.
9425 
9426 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9427 @*/
9428 PetscErrorCode DMPlexCheck(DM dm)
9429 {
9430   PetscInt cellHeight;
9431 
9432   PetscFunctionBegin;
9433   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9434   PetscCall(DMPlexCheckSymmetry(dm));
9435   PetscCall(DMPlexCheckSkeleton(dm, cellHeight));
9436   PetscCall(DMPlexCheckFaces(dm, cellHeight));
9437   PetscCall(DMPlexCheckGeometry(dm));
9438   PetscCall(DMPlexCheckPointSF(dm, NULL, PETSC_FALSE));
9439   PetscCall(DMPlexCheckInterfaceCones(dm));
9440   PetscFunctionReturn(PETSC_SUCCESS);
9441 }
9442 
9443 typedef struct cell_stats {
9444   PetscReal min, max, sum, squaresum;
9445   PetscInt  count;
9446 } cell_stats_t;
9447 
9448 static void MPIAPI cell_stats_reduce(void *a, void *b, int *len, MPI_Datatype *datatype)
9449 {
9450   PetscInt i, N = *len;
9451 
9452   for (i = 0; i < N; i++) {
9453     cell_stats_t *A = (cell_stats_t *)a;
9454     cell_stats_t *B = (cell_stats_t *)b;
9455 
9456     B->min = PetscMin(A->min, B->min);
9457     B->max = PetscMax(A->max, B->max);
9458     B->sum += A->sum;
9459     B->squaresum += A->squaresum;
9460     B->count += A->count;
9461   }
9462 }
9463 
9464 /*@
9465   DMPlexCheckCellShape - Checks the Jacobian of the mapping from reference to real cells and computes some minimal statistics.
9466 
9467   Collective
9468 
9469   Input Parameters:
9470 + dm        - The `DMPLEX` object
9471 . output    - If true, statistics will be displayed on `stdout`
9472 - condLimit - Display all cells above this condition number, or `PETSC_DETERMINE` for no cell output
9473 
9474   Level: developer
9475 
9476   Notes:
9477   This is mainly intended for debugging/testing purposes.
9478 
9479   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9480 
9481 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexComputeOrthogonalQuality()`
9482 @*/
9483 PetscErrorCode DMPlexCheckCellShape(DM dm, PetscBool output, PetscReal condLimit)
9484 {
9485   DM           dmCoarse;
9486   cell_stats_t stats, globalStats;
9487   MPI_Comm     comm = PetscObjectComm((PetscObject)dm);
9488   PetscReal   *J, *invJ, min = 0, max = 0, mean = 0, stdev = 0;
9489   PetscReal    limit = condLimit > 0 ? condLimit : PETSC_MAX_REAL;
9490   PetscInt     cdim, cStart, cEnd, c, eStart, eEnd, count = 0;
9491   PetscMPIInt  rank, size;
9492 
9493   PetscFunctionBegin;
9494   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9495   stats.min = PETSC_MAX_REAL;
9496   stats.max = PETSC_MIN_REAL;
9497   stats.sum = stats.squaresum = 0.;
9498   stats.count                 = 0;
9499 
9500   PetscCallMPI(MPI_Comm_size(comm, &size));
9501   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9502   PetscCall(DMGetCoordinateDim(dm, &cdim));
9503   PetscCall(PetscMalloc2(PetscSqr(cdim), &J, PetscSqr(cdim), &invJ));
9504   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
9505   PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
9506   for (c = cStart; c < cEnd; c++) {
9507     PetscInt  i;
9508     PetscReal frobJ = 0., frobInvJ = 0., cond2, cond, detJ;
9509 
9510     PetscCall(DMPlexComputeCellGeometryAffineFEM(dm, c, NULL, J, invJ, &detJ));
9511     PetscCheck(detJ >= 0.0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Mesh cell %" PetscInt_FMT " is inverted", c);
9512     for (i = 0; i < PetscSqr(cdim); ++i) {
9513       frobJ += J[i] * J[i];
9514       frobInvJ += invJ[i] * invJ[i];
9515     }
9516     cond2 = frobJ * frobInvJ;
9517     cond  = PetscSqrtReal(cond2);
9518 
9519     stats.min = PetscMin(stats.min, cond);
9520     stats.max = PetscMax(stats.max, cond);
9521     stats.sum += cond;
9522     stats.squaresum += cond2;
9523     stats.count++;
9524     if (output && cond > limit) {
9525       PetscSection coordSection;
9526       Vec          coordsLocal;
9527       PetscScalar *coords = NULL;
9528       PetscInt     Nv, d, clSize, cl, *closure = NULL;
9529 
9530       PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
9531       PetscCall(DMGetCoordinateSection(dm, &coordSection));
9532       PetscCall(DMPlexVecGetClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9533       PetscCall(PetscSynchronizedPrintf(comm, "[%d] Cell %" PetscInt_FMT " cond %g\n", rank, c, (double)cond));
9534       for (i = 0; i < Nv / cdim; ++i) {
9535         PetscCall(PetscSynchronizedPrintf(comm, "  Vertex %" PetscInt_FMT ": (", i));
9536         for (d = 0; d < cdim; ++d) {
9537           if (d > 0) PetscCall(PetscSynchronizedPrintf(comm, ", "));
9538           PetscCall(PetscSynchronizedPrintf(comm, "%g", (double)PetscRealPart(coords[i * cdim + d])));
9539         }
9540         PetscCall(PetscSynchronizedPrintf(comm, ")\n"));
9541       }
9542       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9543       for (cl = 0; cl < clSize * 2; cl += 2) {
9544         const PetscInt edge = closure[cl];
9545 
9546         if ((edge >= eStart) && (edge < eEnd)) {
9547           PetscReal len;
9548 
9549           PetscCall(DMPlexComputeCellGeometryFVM(dm, edge, &len, NULL, NULL));
9550           PetscCall(PetscSynchronizedPrintf(comm, "  Edge %" PetscInt_FMT ": length %g\n", edge, (double)len));
9551         }
9552       }
9553       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9554       PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9555     }
9556   }
9557   if (output) PetscCall(PetscSynchronizedFlush(comm, NULL));
9558 
9559   if (size > 1) {
9560     PetscMPIInt  blockLengths[2] = {4, 1};
9561     MPI_Aint     blockOffsets[2] = {offsetof(cell_stats_t, min), offsetof(cell_stats_t, count)};
9562     MPI_Datatype blockTypes[2]   = {MPIU_REAL, MPIU_INT}, statType;
9563     MPI_Op       statReduce;
9564 
9565     PetscCallMPI(MPI_Type_create_struct(2, blockLengths, blockOffsets, blockTypes, &statType));
9566     PetscCallMPI(MPI_Type_commit(&statType));
9567     PetscCallMPI(MPI_Op_create(cell_stats_reduce, PETSC_TRUE, &statReduce));
9568     PetscCallMPI(MPI_Reduce(&stats, &globalStats, 1, statType, statReduce, 0, comm));
9569     PetscCallMPI(MPI_Op_free(&statReduce));
9570     PetscCallMPI(MPI_Type_free(&statType));
9571   } else {
9572     PetscCall(PetscArraycpy(&globalStats, &stats, 1));
9573   }
9574   if (rank == 0) {
9575     count = globalStats.count;
9576     min   = globalStats.min;
9577     max   = globalStats.max;
9578     mean  = globalStats.sum / globalStats.count;
9579     stdev = globalStats.count > 1 ? PetscSqrtReal(PetscMax((globalStats.squaresum - globalStats.count * mean * mean) / (globalStats.count - 1), 0)) : 0.0;
9580   }
9581 
9582   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));
9583   PetscCall(PetscFree2(J, invJ));
9584 
9585   PetscCall(DMGetCoarseDM(dm, &dmCoarse));
9586   if (dmCoarse) {
9587     PetscBool isplex;
9588 
9589     PetscCall(PetscObjectTypeCompare((PetscObject)dmCoarse, DMPLEX, &isplex));
9590     if (isplex) PetscCall(DMPlexCheckCellShape(dmCoarse, output, condLimit));
9591   }
9592   PetscFunctionReturn(PETSC_SUCCESS);
9593 }
9594 
9595 /*@
9596   DMPlexComputeOrthogonalQuality - Compute cell-wise orthogonal quality mesh statistic. Optionally tags all cells with
9597   orthogonal quality below given tolerance.
9598 
9599   Collective
9600 
9601   Input Parameters:
9602 + dm   - The `DMPLEX` object
9603 . fv   - Optional `PetscFV` object for pre-computed cell/face centroid information
9604 - atol - [0, 1] Absolute tolerance for tagging cells.
9605 
9606   Output Parameters:
9607 + OrthQual      - `Vec` containing orthogonal quality per cell
9608 - OrthQualLabel - `DMLabel` tagging cells below atol with `DM_ADAPT_REFINE`
9609 
9610   Options Database Keys:
9611 + -dm_plex_orthogonal_quality_label_view - view OrthQualLabel if label is requested. Currently only `PETSCVIEWERASCII` is supported.
9612 - -dm_plex_orthogonal_quality_vec_view   - view OrthQual vector.
9613 
9614   Level: intermediate
9615 
9616   Notes:
9617   Orthogonal quality is given by the following formula\:
9618 
9619   $ \min \left[ \frac{A_i \cdot f_i}{\|A_i\| \|f_i\|} , \frac{A_i \cdot c_i}{\|A_i\| \|c_i\|} \right]$
9620 
9621   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
9622   is the vector from the current cells centroid to the centroid of its i'th neighbor (which shares a face with the
9623   current cell). This computes the vector similarity between each cell face and its corresponding neighbor centroid by
9624   calculating the cosine of the angle between these vectors.
9625 
9626   Orthogonal quality ranges from 1 (best) to 0 (worst).
9627 
9628   This routine is mainly useful for FVM, however is not restricted to only FVM. The `PetscFV` object is optionally used to check for
9629   pre-computed FVM cell data, but if it is not passed in then this data will be computed.
9630 
9631   Cells are tagged if they have an orthogonal quality less than or equal to the absolute tolerance.
9632 
9633 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCheckCellShape()`, `DMCreateLabel()`, `PetscFV`, `DMLabel`, `Vec`
9634 @*/
9635 PetscErrorCode DMPlexComputeOrthogonalQuality(DM dm, PetscFV fv, PetscReal atol, Vec *OrthQual, DMLabel *OrthQualLabel)
9636 {
9637   PetscInt               nc, cellHeight, cStart, cEnd, cell, cellIter = 0;
9638   PetscInt              *idx;
9639   PetscScalar           *oqVals;
9640   const PetscScalar     *cellGeomArr, *faceGeomArr;
9641   PetscReal             *ci, *fi, *Ai;
9642   MPI_Comm               comm;
9643   Vec                    cellgeom, facegeom;
9644   DM                     dmFace, dmCell;
9645   IS                     glob;
9646   ISLocalToGlobalMapping ltog;
9647   PetscViewer            vwr;
9648 
9649   PetscFunctionBegin;
9650   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9651   if (fv) PetscValidHeaderSpecific(fv, PETSCFV_CLASSID, 2);
9652   PetscAssertPointer(OrthQual, 4);
9653   PetscCheck(atol >= 0.0 && atol <= 1.0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Absolute tolerance %g not in [0,1]", (double)atol);
9654   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9655   PetscCall(DMGetDimension(dm, &nc));
9656   PetscCheck(nc >= 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must have dimension >= 2 (current %" PetscInt_FMT ")", nc);
9657   {
9658     DMPlexInterpolatedFlag interpFlag;
9659 
9660     PetscCall(DMPlexIsInterpolated(dm, &interpFlag));
9661     if (interpFlag != DMPLEX_INTERPOLATED_FULL) {
9662       PetscMPIInt rank;
9663 
9664       PetscCallMPI(MPI_Comm_rank(comm, &rank));
9665       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must be fully interpolated, DM on rank %d is not fully interpolated", rank);
9666     }
9667   }
9668   if (OrthQualLabel) {
9669     PetscAssertPointer(OrthQualLabel, 5);
9670     PetscCall(DMCreateLabel(dm, "Orthogonal_Quality"));
9671     PetscCall(DMGetLabel(dm, "Orthogonal_Quality", OrthQualLabel));
9672   } else {
9673     *OrthQualLabel = NULL;
9674   }
9675   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9676   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9677   PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_TRUE, &glob));
9678   PetscCall(ISLocalToGlobalMappingCreateIS(glob, &ltog));
9679   PetscCall(ISLocalToGlobalMappingSetType(ltog, ISLOCALTOGLOBALMAPPINGHASH));
9680   PetscCall(VecCreate(comm, OrthQual));
9681   PetscCall(VecSetType(*OrthQual, VECSTANDARD));
9682   PetscCall(VecSetSizes(*OrthQual, cEnd - cStart, PETSC_DETERMINE));
9683   PetscCall(VecSetLocalToGlobalMapping(*OrthQual, ltog));
9684   PetscCall(VecSetUp(*OrthQual));
9685   PetscCall(ISDestroy(&glob));
9686   PetscCall(ISLocalToGlobalMappingDestroy(&ltog));
9687   PetscCall(DMPlexGetDataFVM(dm, fv, &cellgeom, &facegeom, NULL));
9688   PetscCall(VecGetArrayRead(cellgeom, &cellGeomArr));
9689   PetscCall(VecGetArrayRead(facegeom, &faceGeomArr));
9690   PetscCall(VecGetDM(cellgeom, &dmCell));
9691   PetscCall(VecGetDM(facegeom, &dmFace));
9692   PetscCall(PetscMalloc5(cEnd - cStart, &idx, cEnd - cStart, &oqVals, nc, &ci, nc, &fi, nc, &Ai));
9693   for (cell = cStart; cell < cEnd; cellIter++, cell++) {
9694     PetscInt         cellneigh, cellneighiter = 0, adjSize = PETSC_DETERMINE;
9695     PetscInt         cellarr[2], *adj = NULL;
9696     PetscScalar     *cArr, *fArr;
9697     PetscReal        minvalc = 1.0, minvalf = 1.0;
9698     PetscFVCellGeom *cg;
9699 
9700     idx[cellIter] = cell - cStart;
9701     cellarr[0]    = cell;
9702     /* Make indexing into cellGeom easier */
9703     PetscCall(DMPlexPointLocalRead(dmCell, cell, cellGeomArr, &cg));
9704     PetscCall(DMPlexGetAdjacency_Internal(dm, cell, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &adjSize, &adj));
9705     /* Technically 1 too big, but easier than fiddling with empty adjacency array */
9706     PetscCall(PetscCalloc2(adjSize, &cArr, adjSize, &fArr));
9707     for (cellneigh = 0; cellneigh < adjSize; cellneighiter++, cellneigh++) {
9708       PetscInt         i;
9709       const PetscInt   neigh  = adj[cellneigh];
9710       PetscReal        normci = 0, normfi = 0, normai = 0;
9711       PetscFVCellGeom *cgneigh;
9712       PetscFVFaceGeom *fg;
9713 
9714       /* Don't count ourselves in the neighbor list */
9715       if (neigh == cell) continue;
9716       PetscCall(DMPlexPointLocalRead(dmCell, neigh, cellGeomArr, &cgneigh));
9717       cellarr[1] = neigh;
9718       {
9719         PetscInt        numcovpts;
9720         const PetscInt *covpts;
9721 
9722         PetscCall(DMPlexGetMeet(dm, 2, cellarr, &numcovpts, &covpts));
9723         PetscCall(DMPlexPointLocalRead(dmFace, covpts[0], faceGeomArr, &fg));
9724         PetscCall(DMPlexRestoreMeet(dm, 2, cellarr, &numcovpts, &covpts));
9725       }
9726 
9727       /* Compute c_i, f_i and their norms */
9728       for (i = 0; i < nc; i++) {
9729         ci[i] = cgneigh->centroid[i] - cg->centroid[i];
9730         fi[i] = fg->centroid[i] - cg->centroid[i];
9731         Ai[i] = fg->normal[i];
9732         normci += PetscPowReal(ci[i], 2);
9733         normfi += PetscPowReal(fi[i], 2);
9734         normai += PetscPowReal(Ai[i], 2);
9735       }
9736       normci = PetscSqrtReal(normci);
9737       normfi = PetscSqrtReal(normfi);
9738       normai = PetscSqrtReal(normai);
9739 
9740       /* Normalize and compute for each face-cell-normal pair */
9741       for (i = 0; i < nc; i++) {
9742         ci[i] = ci[i] / normci;
9743         fi[i] = fi[i] / normfi;
9744         Ai[i] = Ai[i] / normai;
9745         /* PetscAbs because I don't know if normals are guaranteed to point out */
9746         cArr[cellneighiter] += PetscAbs(Ai[i] * ci[i]);
9747         fArr[cellneighiter] += PetscAbs(Ai[i] * fi[i]);
9748       }
9749       if (PetscRealPart(cArr[cellneighiter]) < minvalc) minvalc = PetscRealPart(cArr[cellneighiter]);
9750       if (PetscRealPart(fArr[cellneighiter]) < minvalf) minvalf = PetscRealPart(fArr[cellneighiter]);
9751     }
9752     PetscCall(PetscFree(adj));
9753     PetscCall(PetscFree2(cArr, fArr));
9754     /* Defer to cell if they're equal */
9755     oqVals[cellIter] = PetscMin(minvalf, minvalc);
9756     if (OrthQualLabel) {
9757       if (PetscRealPart(oqVals[cellIter]) <= atol) PetscCall(DMLabelSetValue(*OrthQualLabel, cell, DM_ADAPT_REFINE));
9758     }
9759   }
9760   PetscCall(VecSetValuesLocal(*OrthQual, cEnd - cStart, idx, oqVals, INSERT_VALUES));
9761   PetscCall(VecAssemblyBegin(*OrthQual));
9762   PetscCall(VecAssemblyEnd(*OrthQual));
9763   PetscCall(VecRestoreArrayRead(cellgeom, &cellGeomArr));
9764   PetscCall(VecRestoreArrayRead(facegeom, &faceGeomArr));
9765   PetscCall(PetscOptionsGetViewer(comm, NULL, NULL, "-dm_plex_orthogonal_quality_label_view", &vwr, NULL, NULL));
9766   if (OrthQualLabel) {
9767     if (vwr) PetscCall(DMLabelView(*OrthQualLabel, vwr));
9768   }
9769   PetscCall(PetscFree5(idx, oqVals, ci, fi, Ai));
9770   PetscCall(PetscOptionsRestoreViewer(&vwr));
9771   PetscCall(VecViewFromOptions(*OrthQual, NULL, "-dm_plex_orthogonal_quality_vec_view"));
9772   PetscFunctionReturn(PETSC_SUCCESS);
9773 }
9774 
9775 /* this is here instead of DMGetOutputDM because output DM still has constraints in the local indices that affect
9776  * interpolator construction */
9777 static PetscErrorCode DMGetFullDM(DM dm, DM *odm)
9778 {
9779   PetscSection section, newSection, gsection;
9780   PetscSF      sf;
9781   PetscBool    hasConstraints, ghasConstraints;
9782 
9783   PetscFunctionBegin;
9784   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9785   PetscAssertPointer(odm, 2);
9786   PetscCall(DMGetLocalSection(dm, &section));
9787   PetscCall(PetscSectionHasConstraints(section, &hasConstraints));
9788   PetscCall(MPIU_Allreduce(&hasConstraints, &ghasConstraints, 1, MPIU_BOOL, MPI_LOR, PetscObjectComm((PetscObject)dm)));
9789   if (!ghasConstraints) {
9790     PetscCall(PetscObjectReference((PetscObject)dm));
9791     *odm = dm;
9792     PetscFunctionReturn(PETSC_SUCCESS);
9793   }
9794   PetscCall(DMClone(dm, odm));
9795   PetscCall(DMCopyFields(dm, *odm));
9796   PetscCall(DMGetLocalSection(*odm, &newSection));
9797   PetscCall(DMGetPointSF(*odm, &sf));
9798   PetscCall(PetscSectionCreateGlobalSection(newSection, sf, PETSC_TRUE, PETSC_TRUE, PETSC_FALSE, &gsection));
9799   PetscCall(DMSetGlobalSection(*odm, gsection));
9800   PetscCall(PetscSectionDestroy(&gsection));
9801   PetscFunctionReturn(PETSC_SUCCESS);
9802 }
9803 
9804 static PetscErrorCode DMCreateAffineInterpolationCorrection_Plex(DM dmc, DM dmf, Vec *shift)
9805 {
9806   DM        dmco, dmfo;
9807   Mat       interpo;
9808   Vec       rscale;
9809   Vec       cglobalo, clocal;
9810   Vec       fglobal, fglobalo, flocal;
9811   PetscBool regular;
9812 
9813   PetscFunctionBegin;
9814   PetscCall(DMGetFullDM(dmc, &dmco));
9815   PetscCall(DMGetFullDM(dmf, &dmfo));
9816   PetscCall(DMSetCoarseDM(dmfo, dmco));
9817   PetscCall(DMPlexGetRegularRefinement(dmf, &regular));
9818   PetscCall(DMPlexSetRegularRefinement(dmfo, regular));
9819   PetscCall(DMCreateInterpolation(dmco, dmfo, &interpo, &rscale));
9820   PetscCall(DMCreateGlobalVector(dmco, &cglobalo));
9821   PetscCall(DMCreateLocalVector(dmc, &clocal));
9822   PetscCall(VecSet(cglobalo, 0.));
9823   PetscCall(VecSet(clocal, 0.));
9824   PetscCall(DMCreateGlobalVector(dmf, &fglobal));
9825   PetscCall(DMCreateGlobalVector(dmfo, &fglobalo));
9826   PetscCall(DMCreateLocalVector(dmf, &flocal));
9827   PetscCall(VecSet(fglobal, 0.));
9828   PetscCall(VecSet(fglobalo, 0.));
9829   PetscCall(VecSet(flocal, 0.));
9830   PetscCall(DMPlexInsertBoundaryValues(dmc, PETSC_TRUE, clocal, 0., NULL, NULL, NULL));
9831   PetscCall(DMLocalToGlobalBegin(dmco, clocal, INSERT_VALUES, cglobalo));
9832   PetscCall(DMLocalToGlobalEnd(dmco, clocal, INSERT_VALUES, cglobalo));
9833   PetscCall(MatMult(interpo, cglobalo, fglobalo));
9834   PetscCall(DMGlobalToLocalBegin(dmfo, fglobalo, INSERT_VALUES, flocal));
9835   PetscCall(DMGlobalToLocalEnd(dmfo, fglobalo, INSERT_VALUES, flocal));
9836   PetscCall(DMLocalToGlobalBegin(dmf, flocal, INSERT_VALUES, fglobal));
9837   PetscCall(DMLocalToGlobalEnd(dmf, flocal, INSERT_VALUES, fglobal));
9838   *shift = fglobal;
9839   PetscCall(VecDestroy(&flocal));
9840   PetscCall(VecDestroy(&fglobalo));
9841   PetscCall(VecDestroy(&clocal));
9842   PetscCall(VecDestroy(&cglobalo));
9843   PetscCall(VecDestroy(&rscale));
9844   PetscCall(MatDestroy(&interpo));
9845   PetscCall(DMDestroy(&dmfo));
9846   PetscCall(DMDestroy(&dmco));
9847   PetscFunctionReturn(PETSC_SUCCESS);
9848 }
9849 
9850 PETSC_INTERN PetscErrorCode DMInterpolateSolution_Plex(DM coarse, DM fine, Mat interp, Vec coarseSol, Vec fineSol)
9851 {
9852   PetscObject shifto;
9853   Vec         shift;
9854 
9855   PetscFunctionBegin;
9856   if (!interp) {
9857     Vec rscale;
9858 
9859     PetscCall(DMCreateInterpolation(coarse, fine, &interp, &rscale));
9860     PetscCall(VecDestroy(&rscale));
9861   } else {
9862     PetscCall(PetscObjectReference((PetscObject)interp));
9863   }
9864   PetscCall(PetscObjectQuery((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", &shifto));
9865   if (!shifto) {
9866     PetscCall(DMCreateAffineInterpolationCorrection_Plex(coarse, fine, &shift));
9867     PetscCall(PetscObjectCompose((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", (PetscObject)shift));
9868     shifto = (PetscObject)shift;
9869     PetscCall(VecDestroy(&shift));
9870   }
9871   shift = (Vec)shifto;
9872   PetscCall(MatInterpolate(interp, coarseSol, fineSol));
9873   PetscCall(VecAXPY(fineSol, 1.0, shift));
9874   PetscCall(MatDestroy(&interp));
9875   PetscFunctionReturn(PETSC_SUCCESS);
9876 }
9877 
9878 /* Pointwise interpolation
9879      Just code FEM for now
9880      u^f = I u^c
9881      sum_k u^f_k phi^f_k = I sum_j u^c_j phi^c_j
9882      u^f_i = sum_j psi^f_i I phi^c_j u^c_j
9883      I_{ij} = psi^f_i phi^c_j
9884 */
9885 PetscErrorCode DMCreateInterpolation_Plex(DM dmCoarse, DM dmFine, Mat *interpolation, Vec *scaling)
9886 {
9887   PetscSection gsc, gsf;
9888   PetscInt     m, n;
9889   void        *ctx;
9890   DM           cdm;
9891   PetscBool    regular, ismatis, isRefined = dmCoarse->data == dmFine->data ? PETSC_FALSE : PETSC_TRUE;
9892 
9893   PetscFunctionBegin;
9894   PetscCall(DMGetGlobalSection(dmFine, &gsf));
9895   PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
9896   PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
9897   PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
9898 
9899   PetscCall(PetscStrcmp(dmCoarse->mattype, MATIS, &ismatis));
9900   PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), interpolation));
9901   PetscCall(MatSetSizes(*interpolation, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
9902   PetscCall(MatSetType(*interpolation, ismatis ? MATAIJ : dmCoarse->mattype));
9903   PetscCall(DMGetApplicationContext(dmFine, &ctx));
9904 
9905   PetscCall(DMGetCoarseDM(dmFine, &cdm));
9906   PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
9907   if (!isRefined || (regular && cdm == dmCoarse)) PetscCall(DMPlexComputeInterpolatorNested(dmCoarse, dmFine, isRefined, *interpolation, ctx));
9908   else PetscCall(DMPlexComputeInterpolatorGeneral(dmCoarse, dmFine, *interpolation, ctx));
9909   PetscCall(MatViewFromOptions(*interpolation, NULL, "-interp_mat_view"));
9910   if (scaling) {
9911     /* Use naive scaling */
9912     PetscCall(DMCreateInterpolationScale(dmCoarse, dmFine, *interpolation, scaling));
9913   }
9914   PetscFunctionReturn(PETSC_SUCCESS);
9915 }
9916 
9917 PetscErrorCode DMCreateInjection_Plex(DM dmCoarse, DM dmFine, Mat *mat)
9918 {
9919   VecScatter ctx;
9920 
9921   PetscFunctionBegin;
9922   PetscCall(DMPlexComputeInjectorFEM(dmCoarse, dmFine, &ctx, NULL));
9923   PetscCall(MatCreateScatter(PetscObjectComm((PetscObject)ctx), ctx, mat));
9924   PetscCall(VecScatterDestroy(&ctx));
9925   PetscFunctionReturn(PETSC_SUCCESS);
9926 }
9927 
9928 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[])
9929 {
9930   const PetscInt Nc = uOff[1] - uOff[0];
9931   PetscInt       c;
9932   for (c = 0; c < Nc; ++c) g0[c * Nc + c] = 1.0;
9933 }
9934 
9935 PetscErrorCode DMCreateMassMatrixLumped_Plex(DM dm, Vec *mass)
9936 {
9937   DM           dmc;
9938   PetscDS      ds;
9939   Vec          ones, locmass;
9940   IS           cellIS;
9941   PetscFormKey key;
9942   PetscInt     depth;
9943 
9944   PetscFunctionBegin;
9945   PetscCall(DMClone(dm, &dmc));
9946   PetscCall(DMCopyDisc(dm, dmc));
9947   PetscCall(DMGetDS(dmc, &ds));
9948   PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
9949   PetscCall(DMCreateGlobalVector(dmc, mass));
9950   PetscCall(DMGetLocalVector(dmc, &ones));
9951   PetscCall(DMGetLocalVector(dmc, &locmass));
9952   PetscCall(DMPlexGetDepth(dmc, &depth));
9953   PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
9954   PetscCall(VecSet(locmass, 0.0));
9955   PetscCall(VecSet(ones, 1.0));
9956   key.label = NULL;
9957   key.value = 0;
9958   key.field = 0;
9959   key.part  = 0;
9960   PetscCall(DMPlexComputeJacobian_Action_Internal(dmc, key, cellIS, 0.0, 0.0, ones, NULL, ones, locmass, NULL));
9961   PetscCall(ISDestroy(&cellIS));
9962   PetscCall(VecSet(*mass, 0.0));
9963   PetscCall(DMLocalToGlobalBegin(dmc, locmass, ADD_VALUES, *mass));
9964   PetscCall(DMLocalToGlobalEnd(dmc, locmass, ADD_VALUES, *mass));
9965   PetscCall(DMRestoreLocalVector(dmc, &ones));
9966   PetscCall(DMRestoreLocalVector(dmc, &locmass));
9967   PetscCall(DMDestroy(&dmc));
9968   PetscFunctionReturn(PETSC_SUCCESS);
9969 }
9970 
9971 PetscErrorCode DMCreateMassMatrix_Plex(DM dmCoarse, DM dmFine, Mat *mass)
9972 {
9973   PetscSection gsc, gsf;
9974   PetscInt     m, n;
9975   void        *ctx;
9976   DM           cdm;
9977   PetscBool    regular;
9978 
9979   PetscFunctionBegin;
9980   if (dmFine == dmCoarse) {
9981     DM            dmc;
9982     PetscDS       ds;
9983     PetscWeakForm wf;
9984     Vec           u;
9985     IS            cellIS;
9986     PetscFormKey  key;
9987     PetscInt      depth;
9988 
9989     PetscCall(DMClone(dmFine, &dmc));
9990     PetscCall(DMCopyDisc(dmFine, dmc));
9991     PetscCall(DMGetDS(dmc, &ds));
9992     PetscCall(PetscDSGetWeakForm(ds, &wf));
9993     PetscCall(PetscWeakFormClear(wf));
9994     PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
9995     PetscCall(DMCreateMatrix(dmc, mass));
9996     PetscCall(DMGetLocalVector(dmc, &u));
9997     PetscCall(DMPlexGetDepth(dmc, &depth));
9998     PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
9999     PetscCall(MatZeroEntries(*mass));
10000     key.label = NULL;
10001     key.value = 0;
10002     key.field = 0;
10003     key.part  = 0;
10004     PetscCall(DMPlexComputeJacobian_Internal(dmc, key, cellIS, 0.0, 0.0, u, NULL, *mass, *mass, NULL));
10005     PetscCall(ISDestroy(&cellIS));
10006     PetscCall(DMRestoreLocalVector(dmc, &u));
10007     PetscCall(DMDestroy(&dmc));
10008   } else {
10009     PetscCall(DMGetGlobalSection(dmFine, &gsf));
10010     PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
10011     PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
10012     PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
10013 
10014     PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), mass));
10015     PetscCall(MatSetSizes(*mass, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
10016     PetscCall(MatSetType(*mass, dmCoarse->mattype));
10017     PetscCall(DMGetApplicationContext(dmFine, &ctx));
10018 
10019     PetscCall(DMGetCoarseDM(dmFine, &cdm));
10020     PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
10021     if (regular && cdm == dmCoarse) PetscCall(DMPlexComputeMassMatrixNested(dmCoarse, dmFine, *mass, ctx));
10022     else PetscCall(DMPlexComputeMassMatrixGeneral(dmCoarse, dmFine, *mass, ctx));
10023   }
10024   PetscCall(MatViewFromOptions(*mass, NULL, "-mass_mat_view"));
10025   PetscFunctionReturn(PETSC_SUCCESS);
10026 }
10027 
10028 /*@
10029   DMPlexGetRegularRefinement - Get the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
10030 
10031   Input Parameter:
10032 . dm - The `DMPLEX` object
10033 
10034   Output Parameter:
10035 . regular - The flag
10036 
10037   Level: intermediate
10038 
10039 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetRegularRefinement()`
10040 @*/
10041 PetscErrorCode DMPlexGetRegularRefinement(DM dm, PetscBool *regular)
10042 {
10043   PetscFunctionBegin;
10044   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10045   PetscAssertPointer(regular, 2);
10046   *regular = ((DM_Plex *)dm->data)->regularRefinement;
10047   PetscFunctionReturn(PETSC_SUCCESS);
10048 }
10049 
10050 /*@
10051   DMPlexSetRegularRefinement - Set the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
10052 
10053   Input Parameters:
10054 + dm      - The `DMPLEX` object
10055 - regular - The flag
10056 
10057   Level: intermediate
10058 
10059 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetRegularRefinement()`
10060 @*/
10061 PetscErrorCode DMPlexSetRegularRefinement(DM dm, PetscBool regular)
10062 {
10063   PetscFunctionBegin;
10064   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10065   ((DM_Plex *)dm->data)->regularRefinement = regular;
10066   PetscFunctionReturn(PETSC_SUCCESS);
10067 }
10068 
10069 /*@
10070   DMPlexGetAnchors - Get the layout of the anchor (point-to-point) constraints.  Typically, the user will not have to
10071   call DMPlexGetAnchors() directly: if there are anchors, then `DMPlexGetAnchors()` is called during `DMGetDefaultConstraints()`.
10072 
10073   Not Collective
10074 
10075   Input Parameter:
10076 . dm - The `DMPLEX` object
10077 
10078   Output Parameters:
10079 + anchorSection - If not `NULL`, set to the section describing which points anchor the constrained points.
10080 - anchorIS      - If not `NULL`, set to the list of anchors indexed by `anchorSection`
10081 
10082   Level: intermediate
10083 
10084 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`, `IS`, `PetscSection`
10085 @*/
10086 PetscErrorCode DMPlexGetAnchors(DM dm, PetscSection *anchorSection, IS *anchorIS)
10087 {
10088   DM_Plex *plex = (DM_Plex *)dm->data;
10089 
10090   PetscFunctionBegin;
10091   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10092   if (!plex->anchorSection && !plex->anchorIS && plex->createanchors) PetscCall((*plex->createanchors)(dm));
10093   if (anchorSection) *anchorSection = plex->anchorSection;
10094   if (anchorIS) *anchorIS = plex->anchorIS;
10095   PetscFunctionReturn(PETSC_SUCCESS);
10096 }
10097 
10098 /*@
10099   DMPlexSetAnchors - Set the layout of the local anchor (point-to-point) constraints.
10100 
10101   Collective
10102 
10103   Input Parameters:
10104 + dm            - The `DMPLEX` object
10105 . anchorSection - The section that describes the mapping from constrained points to the anchor points listed in anchorIS.
10106                   Must have a local communicator (`PETSC_COMM_SELF` or derivative).
10107 - anchorIS      - The list of all anchor points.  Must have a local communicator (`PETSC_COMM_SELF` or derivative).
10108 
10109   Level: intermediate
10110 
10111   Notes:
10112   Unlike boundary conditions, when a point's degrees of freedom in a section are constrained to
10113   an outside value, the anchor constraints set a point's degrees of freedom to be a linear
10114   combination of other points' degrees of freedom.
10115 
10116   After specifying the layout of constraints with `DMPlexSetAnchors()`, one specifies the constraints by calling
10117   `DMGetDefaultConstraints()` and filling in the entries in the constraint matrix.
10118 
10119   The reference counts of `anchorSection` and `anchorIS` are incremented.
10120 
10121 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`
10122 @*/
10123 PetscErrorCode DMPlexSetAnchors(DM dm, PetscSection anchorSection, IS anchorIS)
10124 {
10125   DM_Plex    *plex = (DM_Plex *)dm->data;
10126   PetscMPIInt result;
10127 
10128   PetscFunctionBegin;
10129   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10130   if (anchorSection) {
10131     PetscValidHeaderSpecific(anchorSection, PETSC_SECTION_CLASSID, 2);
10132     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorSection), &result));
10133     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor section must have local communicator");
10134   }
10135   if (anchorIS) {
10136     PetscValidHeaderSpecific(anchorIS, IS_CLASSID, 3);
10137     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorIS), &result));
10138     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor IS must have local communicator");
10139   }
10140 
10141   PetscCall(PetscObjectReference((PetscObject)anchorSection));
10142   PetscCall(PetscSectionDestroy(&plex->anchorSection));
10143   plex->anchorSection = anchorSection;
10144 
10145   PetscCall(PetscObjectReference((PetscObject)anchorIS));
10146   PetscCall(ISDestroy(&plex->anchorIS));
10147   plex->anchorIS = anchorIS;
10148 
10149   if (PetscUnlikelyDebug(anchorIS && anchorSection)) {
10150     PetscInt        size, a, pStart, pEnd;
10151     const PetscInt *anchors;
10152 
10153     PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
10154     PetscCall(ISGetLocalSize(anchorIS, &size));
10155     PetscCall(ISGetIndices(anchorIS, &anchors));
10156     for (a = 0; a < size; a++) {
10157       PetscInt p;
10158 
10159       p = anchors[a];
10160       if (p >= pStart && p < pEnd) {
10161         PetscInt dof;
10162 
10163         PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
10164         if (dof) {
10165           PetscCall(ISRestoreIndices(anchorIS, &anchors));
10166           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Point %" PetscInt_FMT " cannot be constrained and an anchor", p);
10167         }
10168       }
10169     }
10170     PetscCall(ISRestoreIndices(anchorIS, &anchors));
10171   }
10172   /* reset the generic constraints */
10173   PetscCall(DMSetDefaultConstraints(dm, NULL, NULL, NULL));
10174   PetscFunctionReturn(PETSC_SUCCESS);
10175 }
10176 
10177 static PetscErrorCode DMPlexCreateConstraintSection_Anchors(DM dm, PetscSection section, PetscSection *cSec)
10178 {
10179   PetscSection anchorSection;
10180   PetscInt     pStart, pEnd, sStart, sEnd, p, dof, numFields, f;
10181 
10182   PetscFunctionBegin;
10183   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10184   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
10185   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, cSec));
10186   PetscCall(PetscSectionGetNumFields(section, &numFields));
10187   if (numFields) {
10188     PetscInt f;
10189     PetscCall(PetscSectionSetNumFields(*cSec, numFields));
10190 
10191     for (f = 0; f < numFields; f++) {
10192       PetscInt numComp;
10193 
10194       PetscCall(PetscSectionGetFieldComponents(section, f, &numComp));
10195       PetscCall(PetscSectionSetFieldComponents(*cSec, f, numComp));
10196     }
10197   }
10198   PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
10199   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
10200   pStart = PetscMax(pStart, sStart);
10201   pEnd   = PetscMin(pEnd, sEnd);
10202   pEnd   = PetscMax(pStart, pEnd);
10203   PetscCall(PetscSectionSetChart(*cSec, pStart, pEnd));
10204   for (p = pStart; p < pEnd; p++) {
10205     PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
10206     if (dof) {
10207       PetscCall(PetscSectionGetDof(section, p, &dof));
10208       PetscCall(PetscSectionSetDof(*cSec, p, dof));
10209       for (f = 0; f < numFields; f++) {
10210         PetscCall(PetscSectionGetFieldDof(section, p, f, &dof));
10211         PetscCall(PetscSectionSetFieldDof(*cSec, p, f, dof));
10212       }
10213     }
10214   }
10215   PetscCall(PetscSectionSetUp(*cSec));
10216   PetscCall(PetscObjectSetName((PetscObject)*cSec, "Constraint Section"));
10217   PetscFunctionReturn(PETSC_SUCCESS);
10218 }
10219 
10220 static PetscErrorCode DMPlexCreateConstraintMatrix_Anchors(DM dm, PetscSection section, PetscSection cSec, Mat *cMat)
10221 {
10222   PetscSection    aSec;
10223   PetscInt        pStart, pEnd, p, sStart, sEnd, dof, aDof, aOff, off, nnz, annz, m, n, q, a, offset, *i, *j;
10224   const PetscInt *anchors;
10225   PetscInt        numFields, f;
10226   IS              aIS;
10227   MatType         mtype;
10228   PetscBool       iscuda, iskokkos;
10229 
10230   PetscFunctionBegin;
10231   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10232   PetscCall(PetscSectionGetStorageSize(cSec, &m));
10233   PetscCall(PetscSectionGetStorageSize(section, &n));
10234   PetscCall(MatCreate(PETSC_COMM_SELF, cMat));
10235   PetscCall(MatSetSizes(*cMat, m, n, m, n));
10236   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJCUSPARSE, &iscuda));
10237   if (!iscuda) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJCUSPARSE, &iscuda));
10238   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJKOKKOS, &iskokkos));
10239   if (!iskokkos) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJKOKKOS, &iskokkos));
10240   if (iscuda) mtype = MATSEQAIJCUSPARSE;
10241   else if (iskokkos) mtype = MATSEQAIJKOKKOS;
10242   else mtype = MATSEQAIJ;
10243   PetscCall(MatSetType(*cMat, mtype));
10244   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
10245   PetscCall(ISGetIndices(aIS, &anchors));
10246   /* cSec will be a subset of aSec and section */
10247   PetscCall(PetscSectionGetChart(cSec, &pStart, &pEnd));
10248   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
10249   PetscCall(PetscMalloc1(m + 1, &i));
10250   i[0] = 0;
10251   PetscCall(PetscSectionGetNumFields(section, &numFields));
10252   for (p = pStart; p < pEnd; p++) {
10253     PetscInt rDof, rOff, r;
10254 
10255     PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10256     if (!rDof) continue;
10257     PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10258     if (numFields) {
10259       for (f = 0; f < numFields; f++) {
10260         annz = 0;
10261         for (r = 0; r < rDof; r++) {
10262           a = anchors[rOff + r];
10263           if (a < sStart || a >= sEnd) continue;
10264           PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
10265           annz += aDof;
10266         }
10267         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
10268         PetscCall(PetscSectionGetFieldOffset(cSec, p, f, &off));
10269         for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
10270       }
10271     } else {
10272       annz = 0;
10273       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10274       for (q = 0; q < dof; q++) {
10275         a = anchors[rOff + q];
10276         if (a < sStart || a >= sEnd) continue;
10277         PetscCall(PetscSectionGetDof(section, a, &aDof));
10278         annz += aDof;
10279       }
10280       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10281       PetscCall(PetscSectionGetOffset(cSec, p, &off));
10282       for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
10283     }
10284   }
10285   nnz = i[m];
10286   PetscCall(PetscMalloc1(nnz, &j));
10287   offset = 0;
10288   for (p = pStart; p < pEnd; p++) {
10289     if (numFields) {
10290       for (f = 0; f < numFields; f++) {
10291         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
10292         for (q = 0; q < dof; q++) {
10293           PetscInt rDof, rOff, r;
10294           PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10295           PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10296           for (r = 0; r < rDof; r++) {
10297             PetscInt s;
10298 
10299             a = anchors[rOff + r];
10300             if (a < sStart || a >= sEnd) continue;
10301             PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
10302             PetscCall(PetscSectionGetFieldOffset(section, a, f, &aOff));
10303             for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
10304           }
10305         }
10306       }
10307     } else {
10308       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10309       for (q = 0; q < dof; q++) {
10310         PetscInt rDof, rOff, r;
10311         PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10312         PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10313         for (r = 0; r < rDof; r++) {
10314           PetscInt s;
10315 
10316           a = anchors[rOff + r];
10317           if (a < sStart || a >= sEnd) continue;
10318           PetscCall(PetscSectionGetDof(section, a, &aDof));
10319           PetscCall(PetscSectionGetOffset(section, a, &aOff));
10320           for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
10321         }
10322       }
10323     }
10324   }
10325   PetscCall(MatSeqAIJSetPreallocationCSR(*cMat, i, j, NULL));
10326   PetscCall(PetscFree(i));
10327   PetscCall(PetscFree(j));
10328   PetscCall(ISRestoreIndices(aIS, &anchors));
10329   PetscFunctionReturn(PETSC_SUCCESS);
10330 }
10331 
10332 PetscErrorCode DMCreateDefaultConstraints_Plex(DM dm)
10333 {
10334   DM_Plex     *plex = (DM_Plex *)dm->data;
10335   PetscSection anchorSection, section, cSec;
10336   Mat          cMat;
10337 
10338   PetscFunctionBegin;
10339   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10340   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
10341   if (anchorSection) {
10342     PetscInt Nf;
10343 
10344     PetscCall(DMGetLocalSection(dm, &section));
10345     PetscCall(DMPlexCreateConstraintSection_Anchors(dm, section, &cSec));
10346     PetscCall(DMPlexCreateConstraintMatrix_Anchors(dm, section, cSec, &cMat));
10347     PetscCall(DMGetNumFields(dm, &Nf));
10348     if (Nf && plex->computeanchormatrix) PetscCall((*plex->computeanchormatrix)(dm, section, cSec, cMat));
10349     PetscCall(DMSetDefaultConstraints(dm, cSec, cMat, NULL));
10350     PetscCall(PetscSectionDestroy(&cSec));
10351     PetscCall(MatDestroy(&cMat));
10352   }
10353   PetscFunctionReturn(PETSC_SUCCESS);
10354 }
10355 
10356 PetscErrorCode DMCreateSubDomainDM_Plex(DM dm, DMLabel label, PetscInt value, IS *is, DM *subdm)
10357 {
10358   IS           subis;
10359   PetscSection section, subsection;
10360 
10361   PetscFunctionBegin;
10362   PetscCall(DMGetLocalSection(dm, &section));
10363   PetscCheck(section, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set default section for DM before splitting subdomain");
10364   PetscCheck(subdm, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set output subDM for splitting subdomain");
10365   /* Create subdomain */
10366   PetscCall(DMPlexFilter(dm, label, value, PETSC_FALSE, PETSC_FALSE, NULL, subdm));
10367   /* Create submodel */
10368   PetscCall(DMPlexGetSubpointIS(*subdm, &subis));
10369   PetscCall(PetscSectionCreateSubmeshSection(section, subis, &subsection));
10370   PetscCall(DMSetLocalSection(*subdm, subsection));
10371   PetscCall(PetscSectionDestroy(&subsection));
10372   PetscCall(DMCopyDisc(dm, *subdm));
10373   /* Create map from submodel to global model */
10374   if (is) {
10375     PetscSection    sectionGlobal, subsectionGlobal;
10376     IS              spIS;
10377     const PetscInt *spmap;
10378     PetscInt       *subIndices;
10379     PetscInt        subSize = 0, subOff = 0, pStart, pEnd, p;
10380     PetscInt        Nf, f, bs = -1, bsLocal[2], bsMinMax[2];
10381 
10382     PetscCall(DMPlexGetSubpointIS(*subdm, &spIS));
10383     PetscCall(ISGetIndices(spIS, &spmap));
10384     PetscCall(PetscSectionGetNumFields(section, &Nf));
10385     PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
10386     PetscCall(DMGetGlobalSection(*subdm, &subsectionGlobal));
10387     PetscCall(PetscSectionGetChart(subsection, &pStart, &pEnd));
10388     for (p = pStart; p < pEnd; ++p) {
10389       PetscInt gdof, pSubSize = 0;
10390 
10391       PetscCall(PetscSectionGetDof(sectionGlobal, p, &gdof));
10392       if (gdof > 0) {
10393         for (f = 0; f < Nf; ++f) {
10394           PetscInt fdof, fcdof;
10395 
10396           PetscCall(PetscSectionGetFieldDof(subsection, p, f, &fdof));
10397           PetscCall(PetscSectionGetFieldConstraintDof(subsection, p, f, &fcdof));
10398           pSubSize += fdof - fcdof;
10399         }
10400         subSize += pSubSize;
10401         if (pSubSize) {
10402           if (bs < 0) {
10403             bs = pSubSize;
10404           } else if (bs != pSubSize) {
10405             /* Layout does not admit a pointwise block size */
10406             bs = 1;
10407           }
10408         }
10409       }
10410     }
10411     /* Must have same blocksize on all procs (some might have no points) */
10412     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs;
10413     bsLocal[1] = bs;
10414     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
10415     if (bsMinMax[0] != bsMinMax[1]) {
10416       bs = 1;
10417     } else {
10418       bs = bsMinMax[0];
10419     }
10420     PetscCall(PetscMalloc1(subSize, &subIndices));
10421     for (p = pStart; p < pEnd; ++p) {
10422       PetscInt gdof, goff;
10423 
10424       PetscCall(PetscSectionGetDof(subsectionGlobal, p, &gdof));
10425       if (gdof > 0) {
10426         const PetscInt point = spmap[p];
10427 
10428         PetscCall(PetscSectionGetOffset(sectionGlobal, point, &goff));
10429         for (f = 0; f < Nf; ++f) {
10430           PetscInt fdof, fcdof, fc, f2, poff = 0;
10431 
10432           /* Can get rid of this loop by storing field information in the global section */
10433           for (f2 = 0; f2 < f; ++f2) {
10434             PetscCall(PetscSectionGetFieldDof(section, p, f2, &fdof));
10435             PetscCall(PetscSectionGetFieldConstraintDof(section, p, f2, &fcdof));
10436             poff += fdof - fcdof;
10437           }
10438           PetscCall(PetscSectionGetFieldDof(section, p, f, &fdof));
10439           PetscCall(PetscSectionGetFieldConstraintDof(section, p, f, &fcdof));
10440           for (fc = 0; fc < fdof - fcdof; ++fc, ++subOff) subIndices[subOff] = goff + poff + fc;
10441         }
10442       }
10443     }
10444     PetscCall(ISRestoreIndices(spIS, &spmap));
10445     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), subSize, subIndices, PETSC_OWN_POINTER, is));
10446     if (bs > 1) {
10447       /* We need to check that the block size does not come from non-contiguous fields */
10448       PetscInt i, j, set = 1;
10449       for (i = 0; i < subSize; i += bs) {
10450         for (j = 0; j < bs; ++j) {
10451           if (subIndices[i + j] != subIndices[i] + j) {
10452             set = 0;
10453             break;
10454           }
10455         }
10456       }
10457       if (set) PetscCall(ISSetBlockSize(*is, bs));
10458     }
10459     /* Attach nullspace */
10460     for (f = 0; f < Nf; ++f) {
10461       (*subdm)->nullspaceConstructors[f] = dm->nullspaceConstructors[f];
10462       if ((*subdm)->nullspaceConstructors[f]) break;
10463     }
10464     if (f < Nf) {
10465       MatNullSpace nullSpace;
10466       PetscCall((*(*subdm)->nullspaceConstructors[f])(*subdm, f, f, &nullSpace));
10467 
10468       PetscCall(PetscObjectCompose((PetscObject)*is, "nullspace", (PetscObject)nullSpace));
10469       PetscCall(MatNullSpaceDestroy(&nullSpace));
10470     }
10471   }
10472   PetscFunctionReturn(PETSC_SUCCESS);
10473 }
10474 
10475 /*@
10476   DMPlexMonitorThroughput - Report the cell throughput of FE integration
10477 
10478   Input Parameters:
10479 + dm    - The `DM`
10480 - dummy - unused argument
10481 
10482   Options Database Key:
10483 . -dm_plex_monitor_throughput - Activate the monitor
10484 
10485   Level: developer
10486 
10487 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexCreate()`
10488 @*/
10489 PetscErrorCode DMPlexMonitorThroughput(DM dm, void *dummy)
10490 {
10491   PetscLogHandler default_handler;
10492 
10493   PetscFunctionBegin;
10494   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10495   PetscCall(PetscLogGetDefaultHandler(&default_handler));
10496   if (default_handler) {
10497     PetscLogEvent      event;
10498     PetscEventPerfInfo eventInfo;
10499     PetscReal          cellRate, flopRate;
10500     PetscInt           cStart, cEnd, Nf, N;
10501     const char        *name;
10502 
10503     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
10504     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
10505     PetscCall(DMGetNumFields(dm, &Nf));
10506     PetscCall(PetscLogEventGetId("DMPlexResidualFE", &event));
10507     PetscCall(PetscLogEventGetPerfInfo(PETSC_DEFAULT, event, &eventInfo));
10508     N        = (cEnd - cStart) * Nf * eventInfo.count;
10509     flopRate = eventInfo.flops / eventInfo.time;
10510     cellRate = N / eventInfo.time;
10511     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)));
10512   } else {
10513     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.");
10514   }
10515   PetscFunctionReturn(PETSC_SUCCESS);
10516 }
10517