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