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