xref: /petsc/src/dm/impls/plex/plex.c (revision eb9d3e4d1b0d07ce7eca2be9429d4698ffa7ae7f)
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 
12 /* Logging support */
13 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;
14 PetscLogEvent DMPLEX_RebalBuildGraph, DMPLEX_RebalRewriteSF, DMPLEX_RebalGatherGraph, DMPLEX_RebalPartition, DMPLEX_RebalScatterPart, DMPLEX_Generate, DMPLEX_Transform, DMPLEX_GetLocalOffsets, DMPLEX_Uninterpolate;
15 
16 PetscBool  Plexcite       = PETSC_FALSE;
17 const char PlexCitation[] = "@article{LangeMitchellKnepleyGorman2015,\n"
18                             "title     = {Efficient mesh management in {Firedrake} using {PETSc-DMPlex}},\n"
19                             "author    = {Michael Lange and Lawrence Mitchell and Matthew G. Knepley and Gerard J. Gorman},\n"
20                             "journal   = {SIAM Journal on Scientific Computing},\n"
21                             "volume    = {38},\n"
22                             "number    = {5},\n"
23                             "pages     = {S143--S155},\n"
24                             "eprint    = {http://arxiv.org/abs/1506.07749},\n"
25                             "doi       = {10.1137/15M1026092},\n"
26                             "year      = {2016},\n"
27                             "petsc_uses={DMPlex},\n}\n";
28 
29 PETSC_EXTERN PetscErrorCode VecView_MPI(Vec, PetscViewer);
30 
31 /*@
32   DMPlexIsSimplex - Is the first cell in this mesh a simplex?
33 
34   Input Parameter:
35 . dm - The `DMPLEX` object
36 
37   Output Parameter:
38 . simplex - Flag checking for a simplex
39 
40   Level: intermediate
41 
42   Note:
43   This just gives the first range of cells found. If the mesh has several cell types, it will only give the first.
44   If the mesh has no cells, this returns `PETSC_FALSE`.
45 
46 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSimplexOrBoxCells()`, `DMPlexGetCellType()`, `DMPlexGetHeightStratum()`, `DMPolytopeTypeGetNumVertices()`
47 @*/
48 PetscErrorCode DMPlexIsSimplex(DM dm, PetscBool *simplex)
49 {
50   DMPolytopeType ct;
51   PetscInt       cStart, cEnd;
52 
53   PetscFunctionBegin;
54   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
55   if (cEnd <= cStart) {
56     *simplex = PETSC_FALSE;
57     PetscFunctionReturn(PETSC_SUCCESS);
58   }
59   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
60   *simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct) + 1 ? PETSC_TRUE : PETSC_FALSE;
61   PetscFunctionReturn(PETSC_SUCCESS);
62 }
63 
64 /*@
65   DMPlexGetSimplexOrBoxCells - Get the range of cells which are neither prisms nor ghost FV cells
66 
67   Input Parameters:
68 + dm     - The `DMPLEX` object
69 - height - The cell height in the Plex, 0 is the default
70 
71   Output Parameters:
72 + cStart - The first "normal" cell
73 - cEnd   - The upper bound on "normal" cells
74 
75   Level: developer
76 
77   Note:
78   This function requires that tensor cells are ordered last.
79 
80 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexConstructGhostCells()`, `DMPlexGetCellTypeStratum()`
81 @*/
82 PetscErrorCode DMPlexGetSimplexOrBoxCells(DM dm, PetscInt height, PetscInt *cStart, PetscInt *cEnd)
83 {
84   DMLabel         ctLabel;
85   IS              valueIS;
86   const PetscInt *ctypes;
87   PetscInt        Nct, cS = PETSC_MAX_INT, cE = 0;
88 
89   PetscFunctionBegin;
90   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
91   PetscCall(DMLabelGetValueIS(ctLabel, &valueIS));
92   PetscCall(ISGetLocalSize(valueIS, &Nct));
93   PetscCall(ISGetIndices(valueIS, &ctypes));
94   if (!Nct) cS = cE = 0;
95   for (PetscInt t = 0; t < Nct; ++t) {
96     const DMPolytopeType ct = (DMPolytopeType)ctypes[t];
97     PetscInt             ctS, ctE, ht;
98 
99     if (ct == DM_POLYTOPE_UNKNOWN) {
100       // If any cells are not typed, just use all cells
101       PetscCall(DMPlexGetHeightStratum(dm, PetscMax(height, 0), cStart, cEnd));
102       break;
103     }
104     if (DMPolytopeTypeIsHybrid(ct) || ct == DM_POLYTOPE_FV_GHOST) continue;
105     PetscCall(DMLabelGetStratumBounds(ctLabel, ct, &ctS, &ctE));
106     if (ctS >= ctE) continue;
107     // Check that a point has the right height
108     PetscCall(DMPlexGetPointHeight(dm, ctS, &ht));
109     if (ht != height) continue;
110     cS = PetscMin(cS, ctS);
111     cE = PetscMax(cE, ctE);
112   }
113   PetscCall(ISDestroy(&valueIS));
114   // Reset label for fast lookup
115   PetscCall(DMLabelMakeAllInvalid_Internal(ctLabel));
116   if (cStart) *cStart = cS;
117   if (cEnd) *cEnd = cE;
118   PetscFunctionReturn(PETSC_SUCCESS);
119 }
120 
121 PetscErrorCode DMPlexGetFieldTypes_Internal(DM dm, PetscSection section, PetscInt field, PetscInt *types, PetscInt **ssStart, PetscInt **ssEnd, PetscViewerVTKFieldType **sft)
122 {
123   PetscInt                 cdim, pStart, pEnd, vStart, vEnd, cStart, cEnd, c, depth, cellHeight, t;
124   PetscInt                *sStart, *sEnd;
125   PetscViewerVTKFieldType *ft;
126   PetscInt                 vcdof[DM_NUM_POLYTOPES + 1], globalvcdof[DM_NUM_POLYTOPES + 1];
127   DMLabel                  depthLabel, ctLabel;
128 
129   PetscFunctionBegin;
130 
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     PetscCall(MPIU_Allreduce(vcdof, globalvcdof, 2, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
162   }
163 
164   PetscCall(MPIU_Allreduce(vcdof, globalvcdof, DM_NUM_POLYTOPES + 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
165   *types = 0;
166 
167   for (c = 0; c < DM_NUM_POLYTOPES + 1; ++c) {
168     if (globalvcdof[c]) ++(*types);
169   }
170 
171   PetscCall(PetscMalloc3(*types, &sStart, *types, &sEnd, *types, &ft));
172   t = 0;
173   if (globalvcdof[DM_NUM_POLYTOPES]) {
174     sStart[t] = vStart;
175     sEnd[t]   = vEnd;
176     ft[t]     = (globalvcdof[t] == cdim) ? PETSC_VTK_POINT_VECTOR_FIELD : PETSC_VTK_POINT_FIELD;
177     ++t;
178   }
179 
180   for (c = 0; c < DM_NUM_POLYTOPES; ++c) {
181     if (globalvcdof[c]) {
182       const DMPolytopeType ict = (DMPolytopeType)c;
183 
184       PetscCall(DMLabelGetStratumBounds(ctLabel, ict, &cStart, &cEnd));
185       sStart[t] = cStart;
186       sEnd[t]   = cEnd;
187       ft[t]     = (globalvcdof[c] == cdim) ? PETSC_VTK_CELL_VECTOR_FIELD : PETSC_VTK_CELL_FIELD;
188       ++t;
189     }
190   }
191 
192   if (!(*types)) {
193     if (field >= 0) {
194       const char *fieldname;
195 
196       PetscCall(PetscSectionGetFieldName(section, field, &fieldname));
197       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section field %" PetscInt_FMT " \"%s\"\n", field, fieldname));
198     } else {
199       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section\n"));
200     }
201   }
202 
203   *ssStart = sStart;
204   *ssEnd   = sEnd;
205   *sft     = ft;
206   PetscFunctionReturn(PETSC_SUCCESS);
207 }
208 
209 PetscErrorCode DMPlexRestoreFieldTypes_Internal(DM dm, PetscSection section, PetscInt field, PetscInt *types, PetscInt **sStart, PetscInt **sEnd, PetscViewerVTKFieldType **ft)
210 {
211   PetscFunctionBegin;
212   PetscCall(PetscFree3(*sStart, *sEnd, *ft));
213   PetscFunctionReturn(PETSC_SUCCESS);
214 }
215 
216 PetscErrorCode DMPlexGetFieldType_Internal(DM dm, PetscSection section, PetscInt field, PetscInt *sStart, PetscInt *sEnd, PetscViewerVTKFieldType *ft)
217 {
218   PetscInt cdim, pStart, pEnd, vStart, vEnd, cStart, cEnd;
219   PetscInt vcdof[2] = {0, 0}, globalvcdof[2];
220 
221   PetscFunctionBegin;
222   *ft = PETSC_VTK_INVALID;
223   PetscCall(DMGetCoordinateDim(dm, &cdim));
224   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
225   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
226   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
227   if (field >= 0) {
228     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, vStart, field, &vcdof[0]));
229     if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, cStart, field, &vcdof[1]));
230   } else {
231     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetDof(section, vStart, &vcdof[0]));
232     if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetDof(section, cStart, &vcdof[1]));
233   }
234   PetscCall(MPIU_Allreduce(vcdof, globalvcdof, 2, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
235   if (globalvcdof[0]) {
236     *sStart = vStart;
237     *sEnd   = vEnd;
238     if (globalvcdof[0] == cdim) *ft = PETSC_VTK_POINT_VECTOR_FIELD;
239     else *ft = PETSC_VTK_POINT_FIELD;
240   } else if (globalvcdof[1]) {
241     *sStart = cStart;
242     *sEnd   = cEnd;
243     if (globalvcdof[1] == cdim) *ft = PETSC_VTK_CELL_VECTOR_FIELD;
244     else *ft = PETSC_VTK_CELL_FIELD;
245   } else {
246     if (field >= 0) {
247       const char *fieldname;
248 
249       PetscCall(PetscSectionGetFieldName(section, field, &fieldname));
250       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section field %" PetscInt_FMT " \"%s\"\n", field, fieldname));
251     } else {
252       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section\n"));
253     }
254   }
255   PetscFunctionReturn(PETSC_SUCCESS);
256 }
257 
258 /*@
259   DMPlexVecView1D - Plot many 1D solutions on the same line graph
260 
261   Collective
262 
263   Input Parameters:
264 + dm     - The `DMPLEX` object
265 . n      - The number of vectors
266 . u      - The array of local vectors
267 - viewer - The `PetscViewer`
268 
269   Level: advanced
270 
271 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `VecViewFromOptions()`, `VecView()`
272 @*/
273 PetscErrorCode DMPlexVecView1D(DM dm, PetscInt n, Vec u[], PetscViewer viewer)
274 {
275   PetscDS            ds;
276   PetscDraw          draw = NULL;
277   PetscDrawLG        lg;
278   Vec                coordinates;
279   const PetscScalar *coords, **sol;
280   PetscReal         *vals;
281   PetscInt          *Nc;
282   PetscInt           Nf, f, c, Nl, l, i, vStart, vEnd, v;
283   char             **names;
284 
285   PetscFunctionBegin;
286   PetscCall(DMGetDS(dm, &ds));
287   PetscCall(PetscDSGetNumFields(ds, &Nf));
288   PetscCall(PetscDSGetTotalComponents(ds, &Nl));
289   PetscCall(PetscDSGetComponents(ds, &Nc));
290 
291   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
292   if (!draw) PetscFunctionReturn(PETSC_SUCCESS);
293   PetscCall(PetscDrawLGCreate(draw, n * Nl, &lg));
294 
295   PetscCall(PetscMalloc3(n, &sol, n * Nl, &names, n * Nl, &vals));
296   for (i = 0, l = 0; i < n; ++i) {
297     const char *vname;
298 
299     PetscCall(PetscObjectGetName((PetscObject)u[i], &vname));
300     for (f = 0; f < Nf; ++f) {
301       PetscObject disc;
302       const char *fname;
303       char        tmpname[PETSC_MAX_PATH_LEN];
304 
305       PetscCall(PetscDSGetDiscretization(ds, f, &disc));
306       /* TODO Create names for components */
307       for (c = 0; c < Nc[f]; ++c, ++l) {
308         PetscCall(PetscObjectGetName(disc, &fname));
309         PetscCall(PetscStrncpy(tmpname, vname, sizeof(tmpname)));
310         PetscCall(PetscStrlcat(tmpname, ":", sizeof(tmpname)));
311         PetscCall(PetscStrlcat(tmpname, fname, sizeof(tmpname)));
312         PetscCall(PetscStrallocpy(tmpname, &names[l]));
313       }
314     }
315   }
316   PetscCall(PetscDrawLGSetLegend(lg, (const char *const *)names));
317   /* Just add P_1 support for now */
318   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
319   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
320   PetscCall(VecGetArrayRead(coordinates, &coords));
321   for (i = 0; i < n; ++i) PetscCall(VecGetArrayRead(u[i], &sol[i]));
322   for (v = vStart; v < vEnd; ++v) {
323     PetscScalar *x, *svals;
324 
325     PetscCall(DMPlexPointLocalRead(dm, v, coords, &x));
326     for (i = 0; i < n; ++i) {
327       PetscCall(DMPlexPointLocalRead(dm, v, sol[i], &svals));
328       for (l = 0; l < Nl; ++l) vals[i * Nl + l] = PetscRealPart(svals[l]);
329     }
330     PetscCall(PetscDrawLGAddCommonPoint(lg, PetscRealPart(x[0]), vals));
331   }
332   PetscCall(VecRestoreArrayRead(coordinates, &coords));
333   for (i = 0; i < n; ++i) PetscCall(VecRestoreArrayRead(u[i], &sol[i]));
334   for (l = 0; l < n * Nl; ++l) PetscCall(PetscFree(names[l]));
335   PetscCall(PetscFree3(sol, names, vals));
336 
337   PetscCall(PetscDrawLGDraw(lg));
338   PetscCall(PetscDrawLGDestroy(&lg));
339   PetscFunctionReturn(PETSC_SUCCESS);
340 }
341 
342 static PetscErrorCode VecView_Plex_Local_Draw_1D(Vec u, PetscViewer viewer)
343 {
344   DM dm;
345 
346   PetscFunctionBegin;
347   PetscCall(VecGetDM(u, &dm));
348   PetscCall(DMPlexVecView1D(dm, 1, &u, viewer));
349   PetscFunctionReturn(PETSC_SUCCESS);
350 }
351 
352 static PetscErrorCode VecView_Plex_Local_Draw_2D(Vec v, PetscViewer viewer)
353 {
354   DM                 dm;
355   PetscSection       s;
356   PetscDraw          draw, popup;
357   DM                 cdm;
358   PetscSection       coordSection;
359   Vec                coordinates;
360   const PetscScalar *array;
361   PetscReal          lbound[3], ubound[3];
362   PetscReal          vbound[2], time;
363   PetscBool          flg;
364   PetscInt           dim, Nf, f, Nc, comp, vStart, vEnd, cStart, cEnd, c, N, level, step, w = 0;
365   const char        *name;
366   char               title[PETSC_MAX_PATH_LEN];
367 
368   PetscFunctionBegin;
369   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
370   PetscCall(VecGetDM(v, &dm));
371   PetscCall(DMGetCoordinateDim(dm, &dim));
372   PetscCall(DMGetLocalSection(dm, &s));
373   PetscCall(PetscSectionGetNumFields(s, &Nf));
374   PetscCall(DMGetCoarsenLevel(dm, &level));
375   PetscCall(DMGetCoordinateDM(dm, &cdm));
376   PetscCall(DMGetLocalSection(cdm, &coordSection));
377   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
378   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
379   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
380 
381   PetscCall(PetscObjectGetName((PetscObject)v, &name));
382   PetscCall(DMGetOutputSequenceNumber(dm, &step, &time));
383 
384   PetscCall(VecGetLocalSize(coordinates, &N));
385   PetscCall(DMGetBoundingBox(dm, lbound, ubound));
386   PetscCall(PetscDrawClear(draw));
387 
388   /* Could implement something like DMDASelectFields() */
389   for (f = 0; f < Nf; ++f) {
390     DM          fdm = dm;
391     Vec         fv  = v;
392     IS          fis;
393     char        prefix[PETSC_MAX_PATH_LEN];
394     const char *fname;
395 
396     PetscCall(PetscSectionGetFieldComponents(s, f, &Nc));
397     PetscCall(PetscSectionGetFieldName(s, f, &fname));
398 
399     if (v->hdr.prefix) PetscCall(PetscStrncpy(prefix, v->hdr.prefix, sizeof(prefix)));
400     else prefix[0] = '\0';
401     if (Nf > 1) {
402       PetscCall(DMCreateSubDM(dm, 1, &f, &fis, &fdm));
403       PetscCall(VecGetSubVector(v, fis, &fv));
404       PetscCall(PetscStrlcat(prefix, fname, sizeof(prefix)));
405       PetscCall(PetscStrlcat(prefix, "_", sizeof(prefix)));
406     }
407     for (comp = 0; comp < Nc; ++comp, ++w) {
408       PetscInt nmax = 2;
409 
410       PetscCall(PetscViewerDrawGetDraw(viewer, w, &draw));
411       if (Nc > 1) PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s_%" PetscInt_FMT " Step: %" PetscInt_FMT " Time: %.4g", name, fname, comp, step, (double)time));
412       else PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s Step: %" PetscInt_FMT " Time: %.4g", name, fname, step, (double)time));
413       PetscCall(PetscDrawSetTitle(draw, title));
414 
415       /* TODO Get max and min only for this component */
416       PetscCall(PetscOptionsGetRealArray(NULL, prefix, "-vec_view_bounds", vbound, &nmax, &flg));
417       if (!flg) {
418         PetscCall(VecMin(fv, NULL, &vbound[0]));
419         PetscCall(VecMax(fv, NULL, &vbound[1]));
420         if (vbound[1] <= vbound[0]) vbound[1] = vbound[0] + 1.0;
421       }
422 
423       PetscCall(PetscDrawGetPopup(draw, &popup));
424       PetscCall(PetscDrawScalePopup(popup, vbound[0], vbound[1]));
425       PetscCall(PetscDrawSetCoordinates(draw, lbound[0], lbound[1], ubound[0], ubound[1]));
426       PetscCall(VecGetArrayRead(fv, &array));
427       for (c = cStart; c < cEnd; ++c) {
428         PetscScalar       *coords = NULL, *a = NULL;
429         const PetscScalar *coords_arr;
430         PetscBool          isDG;
431         PetscInt           numCoords, color[4] = {-1, -1, -1, -1};
432 
433         PetscCall(DMPlexPointLocalRead(fdm, c, array, &a));
434         if (a) {
435           color[0] = PetscDrawRealToColor(PetscRealPart(a[comp]), vbound[0], vbound[1]);
436           color[1] = color[2] = color[3] = color[0];
437         } else {
438           PetscScalar *vals = NULL;
439           PetscInt     numVals, va;
440 
441           PetscCall(DMPlexVecGetClosure(fdm, NULL, fv, c, &numVals, &vals));
442           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);
443           switch (numVals / Nc) {
444           case 3: /* P1 Triangle */
445           case 4: /* P1 Quadrangle */
446             for (va = 0; va < numVals / Nc; ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp]), vbound[0], vbound[1]);
447             break;
448           case 6: /* P2 Triangle */
449           case 8: /* P2 Quadrangle */
450             for (va = 0; va < numVals / (Nc * 2); ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp + numVals / (Nc * 2)]), vbound[0], vbound[1]);
451             break;
452           default:
453             SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of values for cell closure %" PetscInt_FMT " cannot be handled", numVals / Nc);
454           }
455           PetscCall(DMPlexVecRestoreClosure(fdm, NULL, fv, c, &numVals, &vals));
456         }
457         PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
458         switch (numCoords) {
459         case 6:
460         case 12: /* Localized triangle */
461           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]));
462           break;
463         case 8:
464         case 16: /* Localized quadrilateral */
465           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]));
466           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]));
467           break;
468         default:
469           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells with %" PetscInt_FMT " coordinates", numCoords);
470         }
471         PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
472       }
473       PetscCall(VecRestoreArrayRead(fv, &array));
474       PetscCall(PetscDrawFlush(draw));
475       PetscCall(PetscDrawPause(draw));
476       PetscCall(PetscDrawSave(draw));
477     }
478     if (Nf > 1) {
479       PetscCall(VecRestoreSubVector(v, fis, &fv));
480       PetscCall(ISDestroy(&fis));
481       PetscCall(DMDestroy(&fdm));
482     }
483   }
484   PetscFunctionReturn(PETSC_SUCCESS);
485 }
486 
487 static PetscErrorCode VecView_Plex_Local_Draw(Vec v, PetscViewer viewer)
488 {
489   DM        dm;
490   PetscDraw draw;
491   PetscInt  dim;
492   PetscBool isnull;
493 
494   PetscFunctionBegin;
495   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
496   PetscCall(PetscDrawIsNull(draw, &isnull));
497   if (isnull) PetscFunctionReturn(PETSC_SUCCESS);
498 
499   PetscCall(VecGetDM(v, &dm));
500   PetscCall(DMGetCoordinateDim(dm, &dim));
501   switch (dim) {
502   case 1:
503     PetscCall(VecView_Plex_Local_Draw_1D(v, viewer));
504     break;
505   case 2:
506     PetscCall(VecView_Plex_Local_Draw_2D(v, viewer));
507     break;
508   default:
509     SETERRQ(PetscObjectComm((PetscObject)v), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT ". Try PETSCVIEWERGLVIS", dim);
510   }
511   PetscFunctionReturn(PETSC_SUCCESS);
512 }
513 
514 static PetscErrorCode VecView_Plex_Local_VTK(Vec v, PetscViewer viewer)
515 {
516   DM                      dm;
517   Vec                     locv;
518   const char             *name;
519   PetscSection            section;
520   PetscInt                pStart, pEnd;
521   PetscInt                numFields;
522   PetscViewerVTKFieldType ft;
523 
524   PetscFunctionBegin;
525   PetscCall(VecGetDM(v, &dm));
526   PetscCall(DMCreateLocalVector(dm, &locv)); /* VTK viewer requires exclusive ownership of the vector */
527   PetscCall(PetscObjectGetName((PetscObject)v, &name));
528   PetscCall(PetscObjectSetName((PetscObject)locv, name));
529   PetscCall(VecCopy(v, locv));
530   PetscCall(DMGetLocalSection(dm, &section));
531   PetscCall(PetscSectionGetNumFields(section, &numFields));
532   if (!numFields) {
533     PetscCall(DMPlexGetFieldType_Internal(dm, section, PETSC_DETERMINE, &pStart, &pEnd, &ft));
534     PetscCall(PetscViewerVTKAddField(viewer, (PetscObject)dm, DMPlexVTKWriteAll, PETSC_DEFAULT, ft, PETSC_TRUE, (PetscObject)locv));
535   } else {
536     PetscInt f;
537 
538     for (f = 0; f < numFields; f++) {
539       PetscCall(DMPlexGetFieldType_Internal(dm, section, f, &pStart, &pEnd, &ft));
540       if (ft == PETSC_VTK_INVALID) continue;
541       PetscCall(PetscObjectReference((PetscObject)locv));
542       PetscCall(PetscViewerVTKAddField(viewer, (PetscObject)dm, DMPlexVTKWriteAll, f, ft, PETSC_TRUE, (PetscObject)locv));
543     }
544     PetscCall(VecDestroy(&locv));
545   }
546   PetscFunctionReturn(PETSC_SUCCESS);
547 }
548 
549 PetscErrorCode VecView_Plex_Local(Vec v, PetscViewer viewer)
550 {
551   DM        dm;
552   PetscBool isvtk, ishdf5, isdraw, isglvis, iscgns;
553 
554   PetscFunctionBegin;
555   PetscCall(VecGetDM(v, &dm));
556   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
557   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
558   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
559   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
560   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
561   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
562   if (isvtk || ishdf5 || isdraw || isglvis || iscgns) {
563     PetscInt    i, numFields;
564     PetscObject fe;
565     PetscBool   fem  = PETSC_FALSE;
566     Vec         locv = v;
567     const char *name;
568     PetscInt    step;
569     PetscReal   time;
570 
571     PetscCall(DMGetNumFields(dm, &numFields));
572     for (i = 0; i < numFields; i++) {
573       PetscCall(DMGetField(dm, i, NULL, &fe));
574       if (fe->classid == PETSCFE_CLASSID) {
575         fem = PETSC_TRUE;
576         break;
577       }
578     }
579     if (fem) {
580       PetscObject isZero;
581 
582       PetscCall(DMGetLocalVector(dm, &locv));
583       PetscCall(PetscObjectGetName((PetscObject)v, &name));
584       PetscCall(PetscObjectSetName((PetscObject)locv, name));
585       PetscCall(PetscObjectQuery((PetscObject)v, "__Vec_bc_zero__", &isZero));
586       PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", isZero));
587       PetscCall(VecCopy(v, locv));
588       PetscCall(DMGetOutputSequenceNumber(dm, NULL, &time));
589       PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_TRUE, locv, time, NULL, NULL, NULL));
590     }
591     if (isvtk) {
592       PetscCall(VecView_Plex_Local_VTK(locv, viewer));
593     } else if (ishdf5) {
594 #if defined(PETSC_HAVE_HDF5)
595       PetscCall(VecView_Plex_Local_HDF5_Internal(locv, viewer));
596 #else
597       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
598 #endif
599     } else if (isdraw) {
600       PetscCall(VecView_Plex_Local_Draw(locv, viewer));
601     } else if (isglvis) {
602       PetscCall(DMGetOutputSequenceNumber(dm, &step, NULL));
603       PetscCall(PetscViewerGLVisSetSnapId(viewer, step));
604       PetscCall(VecView_GLVis(locv, viewer));
605     } else if (iscgns) {
606 #if defined(PETSC_HAVE_CGNS)
607       PetscCall(VecView_Plex_Local_CGNS(locv, viewer));
608 #else
609       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "CGNS not supported in this build.\nPlease reconfigure using --download-cgns");
610 #endif
611     }
612     if (fem) {
613       PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", NULL));
614       PetscCall(DMRestoreLocalVector(dm, &locv));
615     }
616   } else {
617     PetscBool isseq;
618 
619     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
620     if (isseq) PetscCall(VecView_Seq(v, viewer));
621     else PetscCall(VecView_MPI(v, viewer));
622   }
623   PetscFunctionReturn(PETSC_SUCCESS);
624 }
625 
626 PetscErrorCode VecView_Plex(Vec v, PetscViewer viewer)
627 {
628   DM        dm;
629   PetscBool isvtk, ishdf5, isdraw, isglvis, isexodusii, iscgns;
630 
631   PetscFunctionBegin;
632   PetscCall(VecGetDM(v, &dm));
633   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
634   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
635   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
636   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
637   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
638   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
639   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodusii));
640   if (isvtk || isdraw || isglvis || iscgns) {
641     Vec         locv;
642     PetscObject isZero;
643     const char *name;
644 
645     PetscCall(DMGetLocalVector(dm, &locv));
646     PetscCall(PetscObjectGetName((PetscObject)v, &name));
647     PetscCall(PetscObjectSetName((PetscObject)locv, name));
648     PetscCall(DMGlobalToLocalBegin(dm, v, INSERT_VALUES, locv));
649     PetscCall(DMGlobalToLocalEnd(dm, v, INSERT_VALUES, locv));
650     PetscCall(PetscObjectQuery((PetscObject)v, "__Vec_bc_zero__", &isZero));
651     PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", isZero));
652     PetscCall(VecView_Plex_Local(locv, viewer));
653     PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", NULL));
654     PetscCall(DMRestoreLocalVector(dm, &locv));
655   } else if (ishdf5) {
656 #if defined(PETSC_HAVE_HDF5)
657     PetscCall(VecView_Plex_HDF5_Internal(v, viewer));
658 #else
659     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
660 #endif
661   } else if (isexodusii) {
662 #if defined(PETSC_HAVE_EXODUSII)
663     PetscCall(VecView_PlexExodusII_Internal(v, viewer));
664 #else
665     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
666 #endif
667   } else {
668     PetscBool isseq;
669 
670     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
671     if (isseq) PetscCall(VecView_Seq(v, viewer));
672     else PetscCall(VecView_MPI(v, viewer));
673   }
674   PetscFunctionReturn(PETSC_SUCCESS);
675 }
676 
677 PetscErrorCode VecView_Plex_Native(Vec originalv, PetscViewer viewer)
678 {
679   DM                dm;
680   MPI_Comm          comm;
681   PetscViewerFormat format;
682   Vec               v;
683   PetscBool         isvtk, ishdf5;
684 
685   PetscFunctionBegin;
686   PetscCall(VecGetDM(originalv, &dm));
687   PetscCall(PetscObjectGetComm((PetscObject)originalv, &comm));
688   PetscCheck(dm, comm, PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
689   PetscCall(PetscViewerGetFormat(viewer, &format));
690   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
691   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
692   if (format == PETSC_VIEWER_NATIVE) {
693     /* Natural ordering is the common case for DMDA, NATIVE means plain vector, for PLEX is the opposite */
694     /* this need a better fix */
695     if (dm->useNatural) {
696       if (dm->sfNatural) {
697         const char *vecname;
698         PetscInt    n, nroots;
699 
700         PetscCall(VecGetLocalSize(originalv, &n));
701         PetscCall(PetscSFGetGraph(dm->sfNatural, &nroots, NULL, NULL, NULL));
702         if (n == nroots) {
703           PetscCall(DMPlexCreateNaturalVector(dm, &v));
704           PetscCall(DMPlexGlobalToNaturalBegin(dm, originalv, v));
705           PetscCall(DMPlexGlobalToNaturalEnd(dm, originalv, v));
706           PetscCall(PetscObjectGetName((PetscObject)originalv, &vecname));
707           PetscCall(PetscObjectSetName((PetscObject)v, vecname));
708         } else SETERRQ(comm, PETSC_ERR_ARG_WRONG, "DM global to natural SF only handles global vectors");
709       } else SETERRQ(comm, PETSC_ERR_ARG_WRONGSTATE, "DM global to natural SF was not created");
710     } else v = originalv;
711   } else v = originalv;
712 
713   if (ishdf5) {
714 #if defined(PETSC_HAVE_HDF5)
715     PetscCall(VecView_Plex_HDF5_Native_Internal(v, viewer));
716 #else
717     SETERRQ(comm, PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
718 #endif
719   } else if (isvtk) {
720     SETERRQ(comm, PETSC_ERR_SUP, "VTK format does not support viewing in natural order. Please switch to HDF5.");
721   } else {
722     PetscBool isseq;
723 
724     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
725     if (isseq) PetscCall(VecView_Seq(v, viewer));
726     else PetscCall(VecView_MPI(v, viewer));
727   }
728   if (v != originalv) PetscCall(VecDestroy(&v));
729   PetscFunctionReturn(PETSC_SUCCESS);
730 }
731 
732 PetscErrorCode VecLoad_Plex_Local(Vec v, PetscViewer viewer)
733 {
734   DM        dm;
735   PetscBool ishdf5;
736 
737   PetscFunctionBegin;
738   PetscCall(VecGetDM(v, &dm));
739   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
740   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
741   if (ishdf5) {
742     DM          dmBC;
743     Vec         gv;
744     const char *name;
745 
746     PetscCall(DMGetOutputDM(dm, &dmBC));
747     PetscCall(DMGetGlobalVector(dmBC, &gv));
748     PetscCall(PetscObjectGetName((PetscObject)v, &name));
749     PetscCall(PetscObjectSetName((PetscObject)gv, name));
750     PetscCall(VecLoad_Default(gv, viewer));
751     PetscCall(DMGlobalToLocalBegin(dmBC, gv, INSERT_VALUES, v));
752     PetscCall(DMGlobalToLocalEnd(dmBC, gv, INSERT_VALUES, v));
753     PetscCall(DMRestoreGlobalVector(dmBC, &gv));
754   } else PetscCall(VecLoad_Default(v, viewer));
755   PetscFunctionReturn(PETSC_SUCCESS);
756 }
757 
758 PetscErrorCode VecLoad_Plex(Vec v, PetscViewer viewer)
759 {
760   DM        dm;
761   PetscBool ishdf5, isexodusii;
762 
763   PetscFunctionBegin;
764   PetscCall(VecGetDM(v, &dm));
765   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
766   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
767   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodusii));
768   if (ishdf5) {
769 #if defined(PETSC_HAVE_HDF5)
770     PetscCall(VecLoad_Plex_HDF5_Internal(v, viewer));
771 #else
772     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
773 #endif
774   } else if (isexodusii) {
775 #if defined(PETSC_HAVE_EXODUSII)
776     PetscCall(VecLoad_PlexExodusII_Internal(v, viewer));
777 #else
778     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
779 #endif
780   } else PetscCall(VecLoad_Default(v, viewer));
781   PetscFunctionReturn(PETSC_SUCCESS);
782 }
783 
784 PetscErrorCode VecLoad_Plex_Native(Vec originalv, PetscViewer viewer)
785 {
786   DM                dm;
787   PetscViewerFormat format;
788   PetscBool         ishdf5;
789 
790   PetscFunctionBegin;
791   PetscCall(VecGetDM(originalv, &dm));
792   PetscCheck(dm, PetscObjectComm((PetscObject)originalv), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
793   PetscCall(PetscViewerGetFormat(viewer, &format));
794   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
795   if (format == PETSC_VIEWER_NATIVE) {
796     if (dm->useNatural) {
797       if (dm->sfNatural) {
798         if (ishdf5) {
799 #if defined(PETSC_HAVE_HDF5)
800           Vec         v;
801           const char *vecname;
802 
803           PetscCall(DMPlexCreateNaturalVector(dm, &v));
804           PetscCall(PetscObjectGetName((PetscObject)originalv, &vecname));
805           PetscCall(PetscObjectSetName((PetscObject)v, vecname));
806           PetscCall(VecLoad_Plex_HDF5_Native_Internal(v, viewer));
807           PetscCall(DMPlexNaturalToGlobalBegin(dm, v, originalv));
808           PetscCall(DMPlexNaturalToGlobalEnd(dm, v, originalv));
809           PetscCall(VecDestroy(&v));
810 #else
811           SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
812 #endif
813         } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Reading in natural order is not supported for anything but HDF5.");
814       }
815     } else PetscCall(VecLoad_Default(originalv, viewer));
816   }
817   PetscFunctionReturn(PETSC_SUCCESS);
818 }
819 
820 PETSC_UNUSED static PetscErrorCode DMPlexView_Ascii_Geometry(DM dm, PetscViewer viewer)
821 {
822   PetscSection       coordSection;
823   Vec                coordinates;
824   DMLabel            depthLabel, celltypeLabel;
825   const char        *name[4];
826   const PetscScalar *a;
827   PetscInt           dim, pStart, pEnd, cStart, cEnd, c;
828 
829   PetscFunctionBegin;
830   PetscCall(DMGetDimension(dm, &dim));
831   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
832   PetscCall(DMGetCoordinateSection(dm, &coordSection));
833   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
834   PetscCall(DMPlexGetCellTypeLabel(dm, &celltypeLabel));
835   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
836   PetscCall(PetscSectionGetChart(coordSection, &pStart, &pEnd));
837   PetscCall(VecGetArrayRead(coordinates, &a));
838   name[0]       = "vertex";
839   name[1]       = "edge";
840   name[dim - 1] = "face";
841   name[dim]     = "cell";
842   for (c = cStart; c < cEnd; ++c) {
843     PetscInt *closure = NULL;
844     PetscInt  closureSize, cl, ct;
845 
846     PetscCall(DMLabelGetValue(celltypeLabel, c, &ct));
847     PetscCall(PetscViewerASCIIPrintf(viewer, "Geometry for cell %" PetscInt_FMT " polytope type %s:\n", c, DMPolytopeTypes[ct]));
848     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
849     PetscCall(PetscViewerASCIIPushTab(viewer));
850     for (cl = 0; cl < closureSize * 2; cl += 2) {
851       PetscInt point = closure[cl], depth, dof, off, d, p;
852 
853       if ((point < pStart) || (point >= pEnd)) continue;
854       PetscCall(PetscSectionGetDof(coordSection, point, &dof));
855       if (!dof) continue;
856       PetscCall(DMLabelGetValue(depthLabel, point, &depth));
857       PetscCall(PetscSectionGetOffset(coordSection, point, &off));
858       PetscCall(PetscViewerASCIIPrintf(viewer, "%s %" PetscInt_FMT " coords:", name[depth], point));
859       for (p = 0; p < dof / dim; ++p) {
860         PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
861         for (d = 0; d < dim; ++d) {
862           if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
863           PetscCall(PetscViewerASCIIPrintf(viewer, "%g", (double)PetscRealPart(a[off + p * dim + d])));
864         }
865         PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
866       }
867       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
868     }
869     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
870     PetscCall(PetscViewerASCIIPopTab(viewer));
871   }
872   PetscCall(VecRestoreArrayRead(coordinates, &a));
873   PetscFunctionReturn(PETSC_SUCCESS);
874 }
875 
876 typedef enum {
877   CS_CARTESIAN,
878   CS_POLAR,
879   CS_CYLINDRICAL,
880   CS_SPHERICAL
881 } CoordSystem;
882 const char *CoordSystems[] = {"cartesian", "polar", "cylindrical", "spherical", "CoordSystem", "CS_", NULL};
883 
884 static PetscErrorCode DMPlexView_Ascii_Coordinates(PetscViewer viewer, CoordSystem cs, PetscInt dim, const PetscScalar x[])
885 {
886   PetscInt i;
887 
888   PetscFunctionBegin;
889   if (dim > 3) {
890     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double)PetscRealPart(x[i])));
891   } else {
892     PetscReal coords[3], trcoords[3] = {0., 0., 0.};
893 
894     for (i = 0; i < dim; ++i) coords[i] = PetscRealPart(x[i]);
895     switch (cs) {
896     case CS_CARTESIAN:
897       for (i = 0; i < dim; ++i) trcoords[i] = coords[i];
898       break;
899     case CS_POLAR:
900       PetscCheck(dim == 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Polar coordinates are for 2 dimension, not %" PetscInt_FMT, dim);
901       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
902       trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
903       break;
904     case CS_CYLINDRICAL:
905       PetscCheck(dim == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cylindrical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
906       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
907       trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
908       trcoords[2] = coords[2];
909       break;
910     case CS_SPHERICAL:
911       PetscCheck(dim == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Spherical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
912       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]) + PetscSqr(coords[2]));
913       trcoords[1] = PetscAtan2Real(PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1])), coords[2]);
914       trcoords[2] = PetscAtan2Real(coords[1], coords[0]);
915       break;
916     }
917     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double)trcoords[i]));
918   }
919   PetscFunctionReturn(PETSC_SUCCESS);
920 }
921 
922 static PetscErrorCode DMPlexView_Ascii(DM dm, PetscViewer viewer)
923 {
924   DM_Plex          *mesh = (DM_Plex *)dm->data;
925   DM                cdm, cdmCell;
926   PetscSection      coordSection, coordSectionCell;
927   Vec               coordinates, coordinatesCell;
928   PetscViewerFormat format;
929 
930   PetscFunctionBegin;
931   PetscCall(PetscViewerGetFormat(viewer, &format));
932   if (format == PETSC_VIEWER_ASCII_INFO_DETAIL) {
933     const char *name;
934     PetscInt    dim, cellHeight, maxConeSize, maxSupportSize;
935     PetscInt    pStart, pEnd, p, numLabels, l;
936     PetscMPIInt rank, size;
937 
938     PetscCall(DMGetCoordinateDM(dm, &cdm));
939     PetscCall(DMGetCoordinateSection(dm, &coordSection));
940     PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
941     PetscCall(DMGetCellCoordinateDM(dm, &cdmCell));
942     PetscCall(DMGetCellCoordinateSection(dm, &coordSectionCell));
943     PetscCall(DMGetCellCoordinatesLocal(dm, &coordinatesCell));
944     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
945     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
946     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
947     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
948     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
949     PetscCall(DMGetDimension(dm, &dim));
950     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
951     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
952     else PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
953     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
954     PetscCall(PetscViewerASCIIPrintf(viewer, "Supports:\n"));
955     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
956     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max support size: %" PetscInt_FMT "\n", rank, maxSupportSize));
957     for (p = pStart; p < pEnd; ++p) {
958       PetscInt dof, off, s;
959 
960       PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
961       PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
962       for (s = off; s < off + dof; ++s) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d]: %" PetscInt_FMT " ----> %" PetscInt_FMT "\n", rank, p, mesh->supports[s]));
963     }
964     PetscCall(PetscViewerFlush(viewer));
965     PetscCall(PetscViewerASCIIPrintf(viewer, "Cones:\n"));
966     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max cone size: %" PetscInt_FMT "\n", rank, maxConeSize));
967     for (p = pStart; p < pEnd; ++p) {
968       PetscInt dof, off, c;
969 
970       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
971       PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
972       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]));
973     }
974     PetscCall(PetscViewerFlush(viewer));
975     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
976     if (coordSection && coordinates) {
977       CoordSystem        cs = CS_CARTESIAN;
978       const PetscScalar *array, *arrayCell = NULL;
979       PetscInt           Nf, Nc, pvStart, pvEnd, pcStart = PETSC_MAX_INT, pcEnd = PETSC_MIN_INT, pStart, pEnd, p;
980       PetscMPIInt        rank;
981       const char        *name;
982 
983       PetscCall(PetscOptionsGetEnum(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_coord_system", CoordSystems, (PetscEnum *)&cs, NULL));
984       PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)viewer), &rank));
985       PetscCall(PetscSectionGetNumFields(coordSection, &Nf));
986       PetscCheck(Nf == 1, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Coordinate section should have 1 field, not %" PetscInt_FMT, Nf);
987       PetscCall(PetscSectionGetFieldComponents(coordSection, 0, &Nc));
988       PetscCall(PetscSectionGetChart(coordSection, &pvStart, &pvEnd));
989       if (coordSectionCell) PetscCall(PetscSectionGetChart(coordSectionCell, &pcStart, &pcEnd));
990       pStart = PetscMin(pvStart, pcStart);
991       pEnd   = PetscMax(pvEnd, pcEnd);
992       PetscCall(PetscObjectGetName((PetscObject)coordinates, &name));
993       PetscCall(PetscViewerASCIIPrintf(viewer, "%s with %" PetscInt_FMT " fields\n", name, Nf));
994       PetscCall(PetscViewerASCIIPrintf(viewer, "  field 0 with %" PetscInt_FMT " components\n", Nc));
995       if (cs != CS_CARTESIAN) PetscCall(PetscViewerASCIIPrintf(viewer, "  output coordinate system: %s\n", CoordSystems[cs]));
996 
997       PetscCall(VecGetArrayRead(coordinates, &array));
998       if (coordinatesCell) PetscCall(VecGetArrayRead(coordinatesCell, &arrayCell));
999       PetscCall(PetscViewerASCIIPushSynchronized(viewer));
1000       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "Process %d:\n", rank));
1001       for (p = pStart; p < pEnd; ++p) {
1002         PetscInt dof, off;
1003 
1004         if (p >= pvStart && p < pvEnd) {
1005           PetscCall(PetscSectionGetDof(coordSection, p, &dof));
1006           PetscCall(PetscSectionGetOffset(coordSection, p, &off));
1007           if (dof) {
1008             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dim %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
1009             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &array[off]));
1010             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
1011           }
1012         }
1013         if (cdmCell && p >= pcStart && p < pcEnd) {
1014           PetscCall(PetscSectionGetDof(coordSectionCell, p, &dof));
1015           PetscCall(PetscSectionGetOffset(coordSectionCell, p, &off));
1016           if (dof) {
1017             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dim %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
1018             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &arrayCell[off]));
1019             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
1020           }
1021         }
1022       }
1023       PetscCall(PetscViewerFlush(viewer));
1024       PetscCall(PetscViewerASCIIPopSynchronized(viewer));
1025       PetscCall(VecRestoreArrayRead(coordinates, &array));
1026       if (coordinatesCell) PetscCall(VecRestoreArrayRead(coordinatesCell, &arrayCell));
1027     }
1028     PetscCall(DMGetNumLabels(dm, &numLabels));
1029     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
1030     for (l = 0; l < numLabels; ++l) {
1031       DMLabel     label;
1032       PetscBool   isdepth;
1033       const char *name;
1034 
1035       PetscCall(DMGetLabelName(dm, l, &name));
1036       PetscCall(PetscStrcmp(name, "depth", &isdepth));
1037       if (isdepth) continue;
1038       PetscCall(DMGetLabel(dm, name, &label));
1039       PetscCall(DMLabelView(label, viewer));
1040     }
1041     if (size > 1) {
1042       PetscSF sf;
1043 
1044       PetscCall(DMGetPointSF(dm, &sf));
1045       PetscCall(PetscSFView(sf, viewer));
1046     }
1047     if (mesh->periodic.face_sf) PetscCall(PetscSFView(mesh->periodic.face_sf, viewer));
1048     PetscCall(PetscViewerFlush(viewer));
1049   } else if (format == PETSC_VIEWER_ASCII_LATEX) {
1050     const char  *name, *color;
1051     const char  *defcolors[3]  = {"gray", "orange", "green"};
1052     const char  *deflcolors[4] = {"blue", "cyan", "red", "magenta"};
1053     char         lname[PETSC_MAX_PATH_LEN];
1054     PetscReal    scale      = 2.0;
1055     PetscReal    tikzscale  = 1.0;
1056     PetscBool    useNumbers = PETSC_TRUE, drawNumbers[4], drawColors[4], useLabels, useColors, plotEdges, drawHasse = PETSC_FALSE;
1057     double       tcoords[3];
1058     PetscScalar *coords;
1059     PetscInt     numLabels, l, numColors, numLColors, dim, d, depth, cStart, cEnd, c, vStart, vEnd, v, eStart = 0, eEnd = 0, e, p, n;
1060     PetscMPIInt  rank, size;
1061     char       **names, **colors, **lcolors;
1062     PetscBool    flg, lflg;
1063     PetscBT      wp = NULL;
1064     PetscInt     pEnd, pStart;
1065 
1066     PetscCall(DMGetCoordinateDM(dm, &cdm));
1067     PetscCall(DMGetCoordinateSection(dm, &coordSection));
1068     PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1069     PetscCall(DMGetCellCoordinateDM(dm, &cdmCell));
1070     PetscCall(DMGetCellCoordinateSection(dm, &coordSectionCell));
1071     PetscCall(DMGetCellCoordinatesLocal(dm, &coordinatesCell));
1072     PetscCall(DMGetDimension(dm, &dim));
1073     PetscCall(DMPlexGetDepth(dm, &depth));
1074     PetscCall(DMGetNumLabels(dm, &numLabels));
1075     numLabels  = PetscMax(numLabels, 10);
1076     numColors  = 10;
1077     numLColors = 10;
1078     PetscCall(PetscCalloc3(numLabels, &names, numColors, &colors, numLColors, &lcolors));
1079     PetscCall(PetscOptionsGetReal(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_scale", &scale, NULL));
1080     PetscCall(PetscOptionsGetReal(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_tikzscale", &tikzscale, NULL));
1081     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_numbers", &useNumbers, NULL));
1082     for (d = 0; d < 4; ++d) drawNumbers[d] = useNumbers;
1083     for (d = 0; d < 4; ++d) drawColors[d] = PETSC_TRUE;
1084     n = 4;
1085     PetscCall(PetscOptionsGetBoolArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_numbers_depth", drawNumbers, &n, &flg));
1086     PetscCheck(!flg || n == dim + 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim + 1);
1087     n = 4;
1088     PetscCall(PetscOptionsGetBoolArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_colors_depth", drawColors, &n, &flg));
1089     PetscCheck(!flg || n == dim + 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim + 1);
1090     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_labels", names, &numLabels, &useLabels));
1091     if (!useLabels) numLabels = 0;
1092     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_colors", colors, &numColors, &useColors));
1093     if (!useColors) {
1094       numColors = 3;
1095       for (c = 0; c < numColors; ++c) PetscCall(PetscStrallocpy(defcolors[c], &colors[c]));
1096     }
1097     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_lcolors", lcolors, &numLColors, &useColors));
1098     if (!useColors) {
1099       numLColors = 4;
1100       for (c = 0; c < numLColors; ++c) PetscCall(PetscStrallocpy(deflcolors[c], &lcolors[c]));
1101     }
1102     PetscCall(PetscOptionsGetString(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_label_filter", lname, sizeof(lname), &lflg));
1103     plotEdges = (PetscBool)(depth > 1 && drawNumbers[1] && dim < 3);
1104     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_edges", &plotEdges, &flg));
1105     PetscCheck(!flg || !plotEdges || depth >= dim, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Mesh must be interpolated");
1106     if (depth < dim) plotEdges = PETSC_FALSE;
1107     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_hasse", &drawHasse, NULL));
1108 
1109     /* filter points with labelvalue != labeldefaultvalue */
1110     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
1111     PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1112     PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
1113     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1114     if (lflg) {
1115       DMLabel lbl;
1116 
1117       PetscCall(DMGetLabel(dm, lname, &lbl));
1118       if (lbl) {
1119         PetscInt val, defval;
1120 
1121         PetscCall(DMLabelGetDefaultValue(lbl, &defval));
1122         PetscCall(PetscBTCreate(pEnd - pStart, &wp));
1123         for (c = pStart; c < pEnd; c++) {
1124           PetscInt *closure = NULL;
1125           PetscInt  closureSize;
1126 
1127           PetscCall(DMLabelGetValue(lbl, c, &val));
1128           if (val == defval) continue;
1129 
1130           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1131           for (p = 0; p < closureSize * 2; p += 2) PetscCall(PetscBTSet(wp, closure[p] - pStart));
1132           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1133         }
1134       }
1135     }
1136 
1137     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1138     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
1139     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1140     PetscCall(PetscViewerASCIIPrintf(viewer, "\
1141 \\documentclass[tikz]{standalone}\n\n\
1142 \\usepackage{pgflibraryshapes}\n\
1143 \\usetikzlibrary{backgrounds}\n\
1144 \\usetikzlibrary{arrows}\n\
1145 \\begin{document}\n"));
1146     if (size > 1) {
1147       PetscCall(PetscViewerASCIIPrintf(viewer, "%s for process ", name));
1148       for (p = 0; p < size; ++p) {
1149         if (p) PetscCall(PetscViewerASCIIPrintf(viewer, (p == size - 1) ? ", and " : ", "));
1150         PetscCall(PetscViewerASCIIPrintf(viewer, "{\\textcolor{%s}%" PetscInt_FMT "}", colors[p % numColors], p));
1151       }
1152       PetscCall(PetscViewerASCIIPrintf(viewer, ".\n\n\n"));
1153     }
1154     if (drawHasse) {
1155       PetscInt maxStratum = PetscMax(vEnd - vStart, PetscMax(eEnd - eStart, cEnd - cStart));
1156 
1157       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vStart}{%" PetscInt_FMT "}\n", vStart));
1158       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vEnd}{%" PetscInt_FMT "}\n", vEnd - 1));
1159       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numVertices}{%" PetscInt_FMT "}\n", vEnd - vStart));
1160       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vShift}{%.2f}\n", 3 + (maxStratum - (vEnd - vStart)) / 2.));
1161       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eStart}{%" PetscInt_FMT "}\n", eStart));
1162       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eEnd}{%" PetscInt_FMT "}\n", eEnd - 1));
1163       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eShift}{%.2f}\n", 3 + (maxStratum - (eEnd - eStart)) / 2.));
1164       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numEdges}{%" PetscInt_FMT "}\n", eEnd - eStart));
1165       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cStart}{%" PetscInt_FMT "}\n", cStart));
1166       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cEnd}{%" PetscInt_FMT "}\n", cEnd - 1));
1167       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numCells}{%" PetscInt_FMT "}\n", cEnd - cStart));
1168       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cShift}{%.2f}\n", 3 + (maxStratum - (cEnd - cStart)) / 2.));
1169     }
1170     PetscCall(PetscViewerASCIIPrintf(viewer, "\\begin{tikzpicture}[scale = %g,font=\\fontsize{8}{8}\\selectfont]\n", (double)tikzscale));
1171 
1172     /* Plot vertices */
1173     PetscCall(VecGetArray(coordinates, &coords));
1174     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
1175     for (v = vStart; v < vEnd; ++v) {
1176       PetscInt  off, dof, d;
1177       PetscBool isLabeled = PETSC_FALSE;
1178 
1179       if (wp && !PetscBTLookup(wp, v - pStart)) continue;
1180       PetscCall(PetscSectionGetDof(coordSection, v, &dof));
1181       PetscCall(PetscSectionGetOffset(coordSection, v, &off));
1182       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1183       PetscCheck(dof <= 3, PETSC_COMM_SELF, PETSC_ERR_PLIB, "coordSection vertex %" PetscInt_FMT " has dof %" PetscInt_FMT " > 3", v, dof);
1184       for (d = 0; d < dof; ++d) {
1185         tcoords[d] = (double)(scale * PetscRealPart(coords[off + d]));
1186         tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1187       }
1188       /* Rotate coordinates since PGF makes z point out of the page instead of up */
1189       if (dim == 3) {
1190         PetscReal tmp = tcoords[1];
1191         tcoords[1]    = tcoords[2];
1192         tcoords[2]    = -tmp;
1193       }
1194       for (d = 0; d < dof; ++d) {
1195         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1196         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)tcoords[d]));
1197       }
1198       if (drawHasse) color = colors[0 % numColors];
1199       else color = colors[rank % numColors];
1200       for (l = 0; l < numLabels; ++l) {
1201         PetscInt val;
1202         PetscCall(DMGetLabelValue(dm, names[l], v, &val));
1203         if (val >= 0) {
1204           color     = lcolors[l % numLColors];
1205           isLabeled = PETSC_TRUE;
1206           break;
1207         }
1208       }
1209       if (drawNumbers[0]) {
1210         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", v, rank, color, v));
1211       } else if (drawColors[0]) {
1212         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", v, rank, !isLabeled ? 1 : 2, color));
1213       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", v, rank));
1214     }
1215     PetscCall(VecRestoreArray(coordinates, &coords));
1216     PetscCall(PetscViewerFlush(viewer));
1217     /* Plot edges */
1218     if (plotEdges) {
1219       PetscCall(VecGetArray(coordinates, &coords));
1220       PetscCall(PetscViewerASCIIPrintf(viewer, "\\path\n"));
1221       for (e = eStart; e < eEnd; ++e) {
1222         const PetscInt *cone;
1223         PetscInt        coneSize, offA, offB, dof, d;
1224 
1225         if (wp && !PetscBTLookup(wp, e - pStart)) continue;
1226         PetscCall(DMPlexGetConeSize(dm, e, &coneSize));
1227         PetscCheck(coneSize == 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Edge %" PetscInt_FMT " cone should have two vertices, not %" PetscInt_FMT, e, coneSize);
1228         PetscCall(DMPlexGetCone(dm, e, &cone));
1229         PetscCall(PetscSectionGetDof(coordSection, cone[0], &dof));
1230         PetscCall(PetscSectionGetOffset(coordSection, cone[0], &offA));
1231         PetscCall(PetscSectionGetOffset(coordSection, cone[1], &offB));
1232         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "("));
1233         for (d = 0; d < dof; ++d) {
1234           tcoords[d] = (double)(0.5 * scale * PetscRealPart(coords[offA + d] + coords[offB + d]));
1235           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1236         }
1237         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1238         if (dim == 3) {
1239           PetscReal tmp = tcoords[1];
1240           tcoords[1]    = tcoords[2];
1241           tcoords[2]    = -tmp;
1242         }
1243         for (d = 0; d < dof; ++d) {
1244           if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1245           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)tcoords[d]));
1246         }
1247         if (drawHasse) color = colors[1 % numColors];
1248         else color = colors[rank % numColors];
1249         for (l = 0; l < numLabels; ++l) {
1250           PetscInt val;
1251           PetscCall(DMGetLabelValue(dm, names[l], e, &val));
1252           if (val >= 0) {
1253             color = lcolors[l % numLColors];
1254             break;
1255           }
1256         }
1257         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "} --\n", e, rank, color, e));
1258       }
1259       PetscCall(VecRestoreArray(coordinates, &coords));
1260       PetscCall(PetscViewerFlush(viewer));
1261       PetscCall(PetscViewerASCIIPrintf(viewer, "(0,0);\n"));
1262     }
1263     /* Plot cells */
1264     if (dim == 3 || !drawNumbers[1]) {
1265       for (e = eStart; e < eEnd; ++e) {
1266         const PetscInt *cone;
1267 
1268         if (wp && !PetscBTLookup(wp, e - pStart)) continue;
1269         color = colors[rank % numColors];
1270         for (l = 0; l < numLabels; ++l) {
1271           PetscInt val;
1272           PetscCall(DMGetLabelValue(dm, names[l], e, &val));
1273           if (val >= 0) {
1274             color = lcolors[l % numLColors];
1275             break;
1276           }
1277         }
1278         PetscCall(DMPlexGetCone(dm, e, &cone));
1279         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", color, cone[0], rank, cone[1], rank));
1280       }
1281     } else {
1282       DMPolytopeType ct;
1283 
1284       /* Drawing a 2D polygon */
1285       for (c = cStart; c < cEnd; ++c) {
1286         if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1287         PetscCall(DMPlexGetCellType(dm, c, &ct));
1288         if (DMPolytopeTypeIsHybrid(ct)) {
1289           const PetscInt *cone;
1290           PetscInt        coneSize, e;
1291 
1292           PetscCall(DMPlexGetCone(dm, c, &cone));
1293           PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
1294           for (e = 0; e < coneSize; ++e) {
1295             const PetscInt *econe;
1296 
1297             PetscCall(DMPlexGetCone(dm, cone[e], &econe));
1298             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", colors[rank % numColors], econe[0], rank, cone[e], rank, econe[1], rank));
1299           }
1300         } else {
1301           PetscInt *closure = NULL;
1302           PetscInt  closureSize, Nv = 0, v;
1303 
1304           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1305           for (p = 0; p < closureSize * 2; p += 2) {
1306             const PetscInt point = closure[p];
1307 
1308             if ((point >= vStart) && (point < vEnd)) closure[Nv++] = point;
1309           }
1310           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] ", colors[rank % numColors]));
1311           for (v = 0; v <= Nv; ++v) {
1312             const PetscInt vertex = closure[v % Nv];
1313 
1314             if (v > 0) {
1315               if (plotEdges) {
1316                 const PetscInt *edge;
1317                 PetscInt        endpoints[2], ne;
1318 
1319                 endpoints[0] = closure[v - 1];
1320                 endpoints[1] = vertex;
1321                 PetscCall(DMPlexGetJoin(dm, 2, endpoints, &ne, &edge));
1322                 PetscCheck(ne == 1, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Could not find edge for vertices %" PetscInt_FMT ", %" PetscInt_FMT, endpoints[0], endpoints[1]);
1323                 PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- (%" PetscInt_FMT "_%d) -- ", edge[0], rank));
1324                 PetscCall(DMPlexRestoreJoin(dm, 2, endpoints, &ne, &edge));
1325               } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- "));
1326             }
1327             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "(%" PetscInt_FMT "_%d)", vertex, rank));
1328           }
1329           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ";\n"));
1330           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1331         }
1332       }
1333     }
1334     for (c = cStart; c < cEnd; ++c) {
1335       double             ccoords[3] = {0.0, 0.0, 0.0};
1336       PetscBool          isLabeled  = PETSC_FALSE;
1337       PetscScalar       *cellCoords = NULL;
1338       const PetscScalar *array;
1339       PetscInt           numCoords, cdim, d;
1340       PetscBool          isDG;
1341 
1342       if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1343       PetscCall(DMGetCoordinateDim(dm, &cdim));
1344       PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1345       PetscCheck(!(numCoords % cdim), PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "coordinate dim %" PetscInt_FMT " does not divide numCoords %" PetscInt_FMT, cdim, numCoords);
1346       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1347       for (p = 0; p < numCoords / cdim; ++p) {
1348         for (d = 0; d < cdim; ++d) {
1349           tcoords[d] = (double)(scale * PetscRealPart(cellCoords[p * cdim + d]));
1350           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1351         }
1352         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1353         if (cdim == 3) {
1354           PetscReal tmp = tcoords[1];
1355           tcoords[1]    = tcoords[2];
1356           tcoords[2]    = -tmp;
1357         }
1358         for (d = 0; d < dim; ++d) ccoords[d] += tcoords[d];
1359       }
1360       for (d = 0; d < cdim; ++d) ccoords[d] /= (numCoords / cdim);
1361       PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1362       for (d = 0; d < cdim; ++d) {
1363         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1364         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)ccoords[d]));
1365       }
1366       if (drawHasse) color = colors[depth % numColors];
1367       else color = colors[rank % numColors];
1368       for (l = 0; l < numLabels; ++l) {
1369         PetscInt val;
1370         PetscCall(DMGetLabelValue(dm, names[l], c, &val));
1371         if (val >= 0) {
1372           color     = lcolors[l % numLColors];
1373           isLabeled = PETSC_TRUE;
1374           break;
1375         }
1376       }
1377       if (drawNumbers[dim]) {
1378         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", c, rank, color, c));
1379       } else if (drawColors[dim]) {
1380         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", c, rank, !isLabeled ? 1 : 2, color));
1381       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", c, rank));
1382     }
1383     if (drawHasse) {
1384       color = colors[depth % numColors];
1385       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Cells\n"));
1386       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\c in {\\cStart,...,\\cEnd}\n"));
1387       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1388       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\c_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\cShift+\\c-\\cStart,0) {\\c};\n", rank, color));
1389       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1390 
1391       color = colors[1 % numColors];
1392       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Edges\n"));
1393       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\e in {\\eStart,...,\\eEnd}\n"));
1394       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1395       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\e_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\eShift+\\e-\\eStart,1) {\\e};\n", rank, color));
1396       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1397 
1398       color = colors[0 % numColors];
1399       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Vertices\n"));
1400       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\v in {\\vStart,...,\\vEnd}\n"));
1401       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1402       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\v_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\vShift+\\v-\\vStart,2) {\\v};\n", rank, color));
1403       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1404 
1405       for (p = pStart; p < pEnd; ++p) {
1406         const PetscInt *cone;
1407         PetscInt        coneSize, cp;
1408 
1409         PetscCall(DMPlexGetCone(dm, p, &cone));
1410         PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
1411         for (cp = 0; cp < coneSize; ++cp) PetscCall(PetscViewerASCIIPrintf(viewer, "\\draw[->, shorten >=1pt] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", cone[cp], rank, p, rank));
1412       }
1413     }
1414     PetscCall(PetscViewerFlush(viewer));
1415     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
1416     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{tikzpicture}\n"));
1417     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{document}\n"));
1418     for (l = 0; l < numLabels; ++l) PetscCall(PetscFree(names[l]));
1419     for (c = 0; c < numColors; ++c) PetscCall(PetscFree(colors[c]));
1420     for (c = 0; c < numLColors; ++c) PetscCall(PetscFree(lcolors[c]));
1421     PetscCall(PetscFree3(names, colors, lcolors));
1422     PetscCall(PetscBTDestroy(&wp));
1423   } else if (format == PETSC_VIEWER_LOAD_BALANCE) {
1424     Vec                    cown, acown;
1425     VecScatter             sct;
1426     ISLocalToGlobalMapping g2l;
1427     IS                     gid, acis;
1428     MPI_Comm               comm, ncomm = MPI_COMM_NULL;
1429     MPI_Group              ggroup, ngroup;
1430     PetscScalar           *array, nid;
1431     const PetscInt        *idxs;
1432     PetscInt              *idxs2, *start, *adjacency, *work;
1433     PetscInt64             lm[3], gm[3];
1434     PetscInt               i, c, cStart, cEnd, cum, numVertices, ect, ectn, cellHeight;
1435     PetscMPIInt            d1, d2, rank;
1436 
1437     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1438     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1439 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1440     PetscCallMPI(MPI_Comm_split_type(comm, MPI_COMM_TYPE_SHARED, rank, MPI_INFO_NULL, &ncomm));
1441 #endif
1442     if (ncomm != MPI_COMM_NULL) {
1443       PetscCallMPI(MPI_Comm_group(comm, &ggroup));
1444       PetscCallMPI(MPI_Comm_group(ncomm, &ngroup));
1445       d1 = 0;
1446       PetscCallMPI(MPI_Group_translate_ranks(ngroup, 1, &d1, ggroup, &d2));
1447       nid = d2;
1448       PetscCallMPI(MPI_Group_free(&ggroup));
1449       PetscCallMPI(MPI_Group_free(&ngroup));
1450       PetscCallMPI(MPI_Comm_free(&ncomm));
1451     } else nid = 0.0;
1452 
1453     /* Get connectivity */
1454     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1455     PetscCall(DMPlexCreatePartitionerGraph(dm, cellHeight, &numVertices, &start, &adjacency, &gid));
1456 
1457     /* filter overlapped local cells */
1458     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
1459     PetscCall(ISGetIndices(gid, &idxs));
1460     PetscCall(ISGetLocalSize(gid, &cum));
1461     PetscCall(PetscMalloc1(cum, &idxs2));
1462     for (c = cStart, cum = 0; c < cEnd; c++) {
1463       if (idxs[c - cStart] < 0) continue;
1464       idxs2[cum++] = idxs[c - cStart];
1465     }
1466     PetscCall(ISRestoreIndices(gid, &idxs));
1467     PetscCheck(numVertices == cum, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Unexpected %" PetscInt_FMT " != %" PetscInt_FMT, numVertices, cum);
1468     PetscCall(ISDestroy(&gid));
1469     PetscCall(ISCreateGeneral(comm, numVertices, idxs2, PETSC_OWN_POINTER, &gid));
1470 
1471     /* support for node-aware cell locality */
1472     PetscCall(ISCreateGeneral(comm, start[numVertices], adjacency, PETSC_USE_POINTER, &acis));
1473     PetscCall(VecCreateSeq(PETSC_COMM_SELF, start[numVertices], &acown));
1474     PetscCall(VecCreateMPI(comm, numVertices, PETSC_DECIDE, &cown));
1475     PetscCall(VecGetArray(cown, &array));
1476     for (c = 0; c < numVertices; c++) array[c] = nid;
1477     PetscCall(VecRestoreArray(cown, &array));
1478     PetscCall(VecScatterCreate(cown, acis, acown, NULL, &sct));
1479     PetscCall(VecScatterBegin(sct, cown, acown, INSERT_VALUES, SCATTER_FORWARD));
1480     PetscCall(VecScatterEnd(sct, cown, acown, INSERT_VALUES, SCATTER_FORWARD));
1481     PetscCall(ISDestroy(&acis));
1482     PetscCall(VecScatterDestroy(&sct));
1483     PetscCall(VecDestroy(&cown));
1484 
1485     /* compute edgeCut */
1486     for (c = 0, cum = 0; c < numVertices; c++) cum = PetscMax(cum, start[c + 1] - start[c]);
1487     PetscCall(PetscMalloc1(cum, &work));
1488     PetscCall(ISLocalToGlobalMappingCreateIS(gid, &g2l));
1489     PetscCall(ISLocalToGlobalMappingSetType(g2l, ISLOCALTOGLOBALMAPPINGHASH));
1490     PetscCall(ISDestroy(&gid));
1491     PetscCall(VecGetArray(acown, &array));
1492     for (c = 0, ect = 0, ectn = 0; c < numVertices; c++) {
1493       PetscInt totl;
1494 
1495       totl = start[c + 1] - start[c];
1496       PetscCall(ISGlobalToLocalMappingApply(g2l, IS_GTOLM_MASK, totl, adjacency + start[c], NULL, work));
1497       for (i = 0; i < totl; i++) {
1498         if (work[i] < 0) {
1499           ect += 1;
1500           ectn += (array[i + start[c]] != nid) ? 0 : 1;
1501         }
1502       }
1503     }
1504     PetscCall(PetscFree(work));
1505     PetscCall(VecRestoreArray(acown, &array));
1506     lm[0] = numVertices > 0 ? numVertices : PETSC_MAX_INT;
1507     lm[1] = -numVertices;
1508     PetscCall(MPIU_Allreduce(lm, gm, 2, MPIU_INT64, MPI_MIN, comm));
1509     PetscCall(PetscViewerASCIIPrintf(viewer, "  Cell balance: %.2f (max %" PetscInt_FMT ", min %" PetscInt_FMT, -((double)gm[1]) / ((double)gm[0]), -(PetscInt)gm[1], (PetscInt)gm[0]));
1510     lm[0] = ect;                     /* edgeCut */
1511     lm[1] = ectn;                    /* node-aware edgeCut */
1512     lm[2] = numVertices > 0 ? 0 : 1; /* empty processes */
1513     PetscCall(MPIU_Allreduce(lm, gm, 3, MPIU_INT64, MPI_SUM, comm));
1514     PetscCall(PetscViewerASCIIPrintf(viewer, ", empty %" PetscInt_FMT ")\n", (PetscInt)gm[2]));
1515 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1516     PetscCall(PetscViewerASCIIPrintf(viewer, "  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n", (PetscInt)(gm[0] / 2), gm[0] ? ((double)(gm[1])) / ((double)gm[0]) : 1.));
1517 #else
1518     PetscCall(PetscViewerASCIIPrintf(viewer, "  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n", (PetscInt)(gm[0] / 2), 0.0));
1519 #endif
1520     PetscCall(ISLocalToGlobalMappingDestroy(&g2l));
1521     PetscCall(PetscFree(start));
1522     PetscCall(PetscFree(adjacency));
1523     PetscCall(VecDestroy(&acown));
1524   } else {
1525     const char    *name;
1526     PetscInt      *sizes, *hybsizes, *ghostsizes;
1527     PetscInt       locDepth, depth, cellHeight, dim, d;
1528     PetscInt       pStart, pEnd, p, gcStart, gcEnd, gcNum;
1529     PetscInt       numLabels, l, maxSize = 17;
1530     DMPolytopeType ct0 = DM_POLYTOPE_UNKNOWN;
1531     MPI_Comm       comm;
1532     PetscMPIInt    size, rank;
1533 
1534     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1535     PetscCallMPI(MPI_Comm_size(comm, &size));
1536     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1537     PetscCall(DMGetDimension(dm, &dim));
1538     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1539     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1540     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
1541     else PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
1542     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
1543     PetscCall(DMPlexGetDepth(dm, &locDepth));
1544     PetscCall(MPIU_Allreduce(&locDepth, &depth, 1, MPIU_INT, MPI_MAX, comm));
1545     PetscCall(DMPlexGetCellTypeStratum(dm, DM_POLYTOPE_FV_GHOST, &gcStart, &gcEnd));
1546     gcNum = gcEnd - gcStart;
1547     if (size < maxSize) PetscCall(PetscCalloc3(size, &sizes, size, &hybsizes, size, &ghostsizes));
1548     else PetscCall(PetscCalloc3(3, &sizes, 3, &hybsizes, 3, &ghostsizes));
1549     for (d = 0; d <= depth; d++) {
1550       PetscInt Nc[2] = {0, 0}, ict;
1551 
1552       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
1553       if (pStart < pEnd) PetscCall(DMPlexGetCellType(dm, pStart, &ct0));
1554       ict = ct0;
1555       PetscCallMPI(MPI_Bcast(&ict, 1, MPIU_INT, 0, comm));
1556       ct0 = (DMPolytopeType)ict;
1557       for (p = pStart; p < pEnd; ++p) {
1558         DMPolytopeType ct;
1559 
1560         PetscCall(DMPlexGetCellType(dm, p, &ct));
1561         if (ct == ct0) ++Nc[0];
1562         else ++Nc[1];
1563       }
1564       if (size < maxSize) {
1565         PetscCallMPI(MPI_Gather(&Nc[0], 1, MPIU_INT, sizes, 1, MPIU_INT, 0, comm));
1566         PetscCallMPI(MPI_Gather(&Nc[1], 1, MPIU_INT, hybsizes, 1, MPIU_INT, 0, comm));
1567         if (d == depth) PetscCallMPI(MPI_Gather(&gcNum, 1, MPIU_INT, ghostsizes, 1, MPIU_INT, 0, comm));
1568         PetscCall(PetscViewerASCIIPrintf(viewer, "  Number of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1569         for (p = 0; p < size; ++p) {
1570           if (rank == 0) {
1571             PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT, sizes[p] + hybsizes[p]));
1572             if (hybsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT ")", hybsizes[p]));
1573             if (ghostsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "]", ghostsizes[p]));
1574           }
1575         }
1576       } else {
1577         PetscInt locMinMax[2];
1578 
1579         locMinMax[0] = Nc[0] + Nc[1];
1580         locMinMax[1] = Nc[0] + Nc[1];
1581         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, sizes));
1582         locMinMax[0] = Nc[1];
1583         locMinMax[1] = Nc[1];
1584         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, hybsizes));
1585         if (d == depth) {
1586           locMinMax[0] = gcNum;
1587           locMinMax[1] = gcNum;
1588           PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, ghostsizes));
1589         }
1590         PetscCall(PetscViewerASCIIPrintf(viewer, "  Min/Max of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1591         PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT "/%" PetscInt_FMT, sizes[0], sizes[1]));
1592         if (hybsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT "/%" PetscInt_FMT ")", hybsizes[0], hybsizes[1]));
1593         if (ghostsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "/%" PetscInt_FMT "]", ghostsizes[0], ghostsizes[1]));
1594       }
1595       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
1596     }
1597     PetscCall(PetscFree3(sizes, hybsizes, ghostsizes));
1598     {
1599       const PetscReal *maxCell;
1600       const PetscReal *L;
1601       PetscBool        localized;
1602 
1603       PetscCall(DMGetPeriodicity(dm, &maxCell, NULL, &L));
1604       PetscCall(DMGetCoordinatesLocalized(dm, &localized));
1605       if (L || localized) {
1606         PetscCall(PetscViewerASCIIPrintf(viewer, "Periodic mesh"));
1607         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1608         if (L) {
1609           PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
1610           for (d = 0; d < dim; ++d) {
1611             if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1612             PetscCall(PetscViewerASCIIPrintf(viewer, "%s", L[d] > 0.0 ? "PERIODIC" : "NONE"));
1613           }
1614           PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
1615         }
1616         PetscCall(PetscViewerASCIIPrintf(viewer, " coordinates %s\n", localized ? "localized" : "not localized"));
1617         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1618       }
1619     }
1620     PetscCall(DMGetNumLabels(dm, &numLabels));
1621     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
1622     for (l = 0; l < numLabels; ++l) {
1623       DMLabel         label;
1624       const char     *name;
1625       IS              valueIS;
1626       const PetscInt *values;
1627       PetscInt        numValues, v;
1628 
1629       PetscCall(DMGetLabelName(dm, l, &name));
1630       PetscCall(DMGetLabel(dm, name, &label));
1631       PetscCall(DMLabelGetNumValues(label, &numValues));
1632       PetscCall(PetscViewerASCIIPrintf(viewer, "  %s: %" PetscInt_FMT " strata with value/size (", name, numValues));
1633       PetscCall(DMLabelGetValueIS(label, &valueIS));
1634       PetscCall(ISGetIndices(valueIS, &values));
1635       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1636       for (v = 0; v < numValues; ++v) {
1637         PetscInt size;
1638 
1639         PetscCall(DMLabelGetStratumSize(label, values[v], &size));
1640         if (v > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1641         PetscCall(PetscViewerASCIIPrintf(viewer, "%" PetscInt_FMT " (%" PetscInt_FMT ")", values[v], size));
1642       }
1643       PetscCall(PetscViewerASCIIPrintf(viewer, ")\n"));
1644       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1645       PetscCall(ISRestoreIndices(valueIS, &values));
1646       PetscCall(ISDestroy(&valueIS));
1647     }
1648     {
1649       char    **labelNames;
1650       PetscInt  Nl = numLabels;
1651       PetscBool flg;
1652 
1653       PetscCall(PetscMalloc1(Nl, &labelNames));
1654       PetscCall(PetscOptionsGetStringArray(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_plex_view_labels", labelNames, &Nl, &flg));
1655       for (l = 0; l < Nl; ++l) {
1656         DMLabel label;
1657 
1658         PetscCall(DMHasLabel(dm, labelNames[l], &flg));
1659         if (flg) {
1660           PetscCall(DMGetLabel(dm, labelNames[l], &label));
1661           PetscCall(DMLabelView(label, viewer));
1662         }
1663         PetscCall(PetscFree(labelNames[l]));
1664       }
1665       PetscCall(PetscFree(labelNames));
1666     }
1667     /* If no fields are specified, people do not want to see adjacency */
1668     if (dm->Nf) {
1669       PetscInt f;
1670 
1671       for (f = 0; f < dm->Nf; ++f) {
1672         const char *name;
1673 
1674         PetscCall(PetscObjectGetName(dm->fields[f].disc, &name));
1675         if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Field %s:\n", name));
1676         PetscCall(PetscViewerASCIIPushTab(viewer));
1677         if (dm->fields[f].label) PetscCall(DMLabelView(dm->fields[f].label, viewer));
1678         if (dm->fields[f].adjacency[0]) {
1679           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM++\n"));
1680           else PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM\n"));
1681         } else {
1682           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FEM\n"));
1683           else PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FUNKY\n"));
1684         }
1685         PetscCall(PetscViewerASCIIPopTab(viewer));
1686       }
1687     }
1688     PetscCall(DMGetCoarseDM(dm, &cdm));
1689     if (cdm) {
1690       PetscCall(PetscViewerASCIIPushTab(viewer));
1691       PetscCall(PetscViewerASCIIPrintf(viewer, "Defined by transform from:\n"));
1692       PetscCall(DMPlexView_Ascii(cdm, viewer));
1693       PetscCall(PetscViewerASCIIPopTab(viewer));
1694     }
1695   }
1696   PetscFunctionReturn(PETSC_SUCCESS);
1697 }
1698 
1699 static PetscErrorCode DMPlexDrawCell(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[])
1700 {
1701   DMPolytopeType ct;
1702   PetscMPIInt    rank;
1703   PetscInt       cdim;
1704 
1705   PetscFunctionBegin;
1706   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1707   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1708   PetscCall(DMGetCoordinateDim(dm, &cdim));
1709   switch (ct) {
1710   case DM_POLYTOPE_SEGMENT:
1711   case DM_POLYTOPE_POINT_PRISM_TENSOR:
1712     switch (cdim) {
1713     case 1: {
1714       const PetscReal y  = 0.5;  /* TODO Put it in the middle of the viewport */
1715       const PetscReal dy = 0.05; /* TODO Make it a fraction of the total length */
1716 
1717       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y, PetscRealPart(coords[1]), y, PETSC_DRAW_BLACK));
1718       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y + dy, PetscRealPart(coords[0]), y - dy, PETSC_DRAW_BLACK));
1719       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[1]), y + dy, PetscRealPart(coords[1]), y - dy, PETSC_DRAW_BLACK));
1720     } break;
1721     case 2: {
1722       const PetscReal dx = (PetscRealPart(coords[3]) - PetscRealPart(coords[1]));
1723       const PetscReal dy = (PetscRealPart(coords[2]) - PetscRealPart(coords[0]));
1724       const PetscReal l  = 0.1 / PetscSqrtReal(dx * dx + dy * dy);
1725 
1726       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1727       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]) + l * dx, PetscRealPart(coords[1]) + l * dy, PetscRealPart(coords[0]) - l * dx, PetscRealPart(coords[1]) - l * dy, PETSC_DRAW_BLACK));
1728       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]) + l * dx, PetscRealPart(coords[3]) + l * dy, PetscRealPart(coords[2]) - l * dx, PetscRealPart(coords[3]) - l * dy, PETSC_DRAW_BLACK));
1729     } break;
1730     default:
1731       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of dimension %" PetscInt_FMT, cdim);
1732     }
1733     break;
1734   case DM_POLYTOPE_TRIANGLE:
1735     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2));
1736     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1737     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1738     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1739     break;
1740   case DM_POLYTOPE_QUADRILATERAL:
1741     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2));
1742     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2));
1743     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1744     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1745     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_BLACK));
1746     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1747     break;
1748   case DM_POLYTOPE_SEG_PRISM_TENSOR:
1749     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2));
1750     PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2, PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2));
1751     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1752     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_BLACK));
1753     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1754     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1755     break;
1756   case DM_POLYTOPE_FV_GHOST:
1757     break;
1758   default:
1759     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1760   }
1761   PetscFunctionReturn(PETSC_SUCCESS);
1762 }
1763 
1764 static PetscErrorCode DrawPolygon_Private(DM dm, PetscDraw draw, PetscInt cell, PetscInt Nv, const PetscReal refVertices[], const PetscScalar coords[], PetscInt edgeDiv, PetscReal refCoords[], PetscReal edgeCoords[])
1765 {
1766   PetscReal   centroid[2] = {0., 0.};
1767   PetscMPIInt rank;
1768   PetscInt    fillColor;
1769 
1770   PetscFunctionBegin;
1771   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1772   fillColor = PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2;
1773   for (PetscInt v = 0; v < Nv; ++v) {
1774     centroid[0] += PetscRealPart(coords[v * 2 + 0]) / Nv;
1775     centroid[1] += PetscRealPart(coords[v * 2 + 1]) / Nv;
1776   }
1777   for (PetscInt e = 0; e < Nv; ++e) {
1778     refCoords[0] = refVertices[e * 2 + 0];
1779     refCoords[1] = refVertices[e * 2 + 1];
1780     for (PetscInt d = 1; d <= edgeDiv; ++d) {
1781       refCoords[d * 2 + 0] = refCoords[0] + (refVertices[(e + 1) % Nv * 2 + 0] - refCoords[0]) * d / edgeDiv;
1782       refCoords[d * 2 + 1] = refCoords[1] + (refVertices[(e + 1) % Nv * 2 + 1] - refCoords[1]) * d / edgeDiv;
1783     }
1784     PetscCall(DMPlexReferenceToCoordinates(dm, cell, edgeDiv + 1, refCoords, edgeCoords));
1785     for (PetscInt d = 0; d < edgeDiv; ++d) {
1786       PetscCall(PetscDrawTriangle(draw, centroid[0], centroid[1], edgeCoords[d * 2 + 0], edgeCoords[d * 2 + 1], edgeCoords[(d + 1) * 2 + 0], edgeCoords[(d + 1) * 2 + 1], fillColor, fillColor, fillColor));
1787       PetscCall(PetscDrawLine(draw, edgeCoords[d * 2 + 0], edgeCoords[d * 2 + 1], edgeCoords[(d + 1) * 2 + 0], edgeCoords[(d + 1) * 2 + 1], PETSC_DRAW_BLACK));
1788     }
1789   }
1790   PetscFunctionReturn(PETSC_SUCCESS);
1791 }
1792 
1793 static PetscErrorCode DMPlexDrawCellHighOrder(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[], PetscInt edgeDiv, PetscReal refCoords[], PetscReal edgeCoords[])
1794 {
1795   DMPolytopeType ct;
1796 
1797   PetscFunctionBegin;
1798   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1799   switch (ct) {
1800   case DM_POLYTOPE_TRIANGLE: {
1801     PetscReal refVertices[6] = {-1., -1., 1., -1., -1., 1.};
1802 
1803     PetscCall(DrawPolygon_Private(dm, draw, cell, 3, refVertices, coords, edgeDiv, refCoords, edgeCoords));
1804   } break;
1805   case DM_POLYTOPE_QUADRILATERAL: {
1806     PetscReal refVertices[8] = {-1., -1., 1., -1., 1., 1., -1., 1.};
1807 
1808     PetscCall(DrawPolygon_Private(dm, draw, cell, 4, refVertices, coords, edgeDiv, refCoords, edgeCoords));
1809   } break;
1810   default:
1811     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1812   }
1813   PetscFunctionReturn(PETSC_SUCCESS);
1814 }
1815 
1816 static PetscErrorCode DMPlexView_Draw(DM dm, PetscViewer viewer)
1817 {
1818   PetscDraw    draw;
1819   DM           cdm;
1820   PetscSection coordSection;
1821   Vec          coordinates;
1822   PetscReal    xyl[3], xyr[3];
1823   PetscReal   *refCoords, *edgeCoords;
1824   PetscBool    isnull, drawAffine;
1825   PetscInt     dim, vStart, vEnd, cStart, cEnd, c, cDegree, edgeDiv;
1826 
1827   PetscFunctionBegin;
1828   PetscCall(DMGetCoordinateDim(dm, &dim));
1829   PetscCheck(dim <= 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT, dim);
1830   PetscCall(DMGetCoordinateDegree_Internal(dm, &cDegree));
1831   drawAffine = cDegree > 1 ? PETSC_FALSE : PETSC_TRUE;
1832   edgeDiv    = cDegree + 1;
1833   PetscCall(PetscOptionsGetBool(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_view_draw_affine", &drawAffine, NULL));
1834   if (!drawAffine) PetscCall(PetscMalloc2((edgeDiv + 1) * dim, &refCoords, (edgeDiv + 1) * dim, &edgeCoords));
1835   PetscCall(DMGetCoordinateDM(dm, &cdm));
1836   PetscCall(DMGetLocalSection(cdm, &coordSection));
1837   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1838   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1839   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1840 
1841   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
1842   PetscCall(PetscDrawIsNull(draw, &isnull));
1843   if (isnull) PetscFunctionReturn(PETSC_SUCCESS);
1844   PetscCall(PetscDrawSetTitle(draw, "Mesh"));
1845 
1846   PetscCall(DMGetBoundingBox(dm, xyl, xyr));
1847   PetscCall(PetscDrawSetCoordinates(draw, xyl[0], xyl[1], xyr[0], xyr[1]));
1848   PetscCall(PetscDrawClear(draw));
1849 
1850   for (c = cStart; c < cEnd; ++c) {
1851     PetscScalar       *coords = NULL;
1852     const PetscScalar *coords_arr;
1853     PetscInt           numCoords;
1854     PetscBool          isDG;
1855 
1856     PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
1857     if (drawAffine) PetscCall(DMPlexDrawCell(dm, draw, c, coords));
1858     else PetscCall(DMPlexDrawCellHighOrder(dm, draw, c, coords, edgeDiv, refCoords, edgeCoords));
1859     PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
1860   }
1861   if (!drawAffine) PetscCall(PetscFree2(refCoords, edgeCoords));
1862   PetscCall(PetscDrawFlush(draw));
1863   PetscCall(PetscDrawPause(draw));
1864   PetscCall(PetscDrawSave(draw));
1865   PetscFunctionReturn(PETSC_SUCCESS);
1866 }
1867 
1868 static PetscErrorCode DMPlexCreateHighOrderSurrogate_Internal(DM dm, DM *hdm)
1869 {
1870   DM           odm = dm, rdm = dm, cdm;
1871   PetscFE      fe;
1872   PetscSpace   sp;
1873   PetscClassId id;
1874   PetscInt     degree;
1875   PetscBool    hoView = PETSC_TRUE;
1876 
1877   PetscFunctionBegin;
1878   PetscObjectOptionsBegin((PetscObject)dm);
1879   PetscCall(PetscOptionsBool("-dm_plex_high_order_view", "Subsample to view meshes with high order coordinates", "DMPlexCreateHighOrderSurrogate_Internal", hoView, &hoView, NULL));
1880   PetscOptionsEnd();
1881   PetscCall(PetscObjectReference((PetscObject)dm));
1882   *hdm = dm;
1883   if (!hoView) PetscFunctionReturn(PETSC_SUCCESS);
1884   PetscCall(DMGetCoordinateDM(dm, &cdm));
1885   PetscCall(DMGetField(cdm, 0, NULL, (PetscObject *)&fe));
1886   PetscCall(PetscObjectGetClassId((PetscObject)fe, &id));
1887   if (id != PETSCFE_CLASSID) PetscFunctionReturn(PETSC_SUCCESS);
1888   PetscCall(PetscFEGetBasisSpace(fe, &sp));
1889   PetscCall(PetscSpaceGetDegree(sp, &degree, NULL));
1890   for (PetscInt r = 0, rd = PetscCeilReal(((PetscReal)degree) / 2.); r < (PetscInt)PetscCeilReal(PetscLog2Real(degree)); ++r, rd = PetscCeilReal(((PetscReal)rd) / 2.)) {
1891     DM  cdm, rcdm;
1892     Mat In;
1893     Vec cl, rcl;
1894 
1895     PetscCall(DMRefine(odm, PetscObjectComm((PetscObject)odm), &rdm));
1896     PetscCall(DMPlexCreateCoordinateSpace(rdm, rd, PETSC_FALSE, NULL));
1897     PetscCall(PetscObjectSetName((PetscObject)rdm, "Refined Mesh with Linear Coordinates"));
1898     PetscCall(DMGetCoordinateDM(odm, &cdm));
1899     PetscCall(DMGetCoordinateDM(rdm, &rcdm));
1900     PetscCall(DMGetCoordinatesLocal(odm, &cl));
1901     PetscCall(DMGetCoordinatesLocal(rdm, &rcl));
1902     PetscCall(DMSetCoarseDM(rcdm, cdm));
1903     PetscCall(DMCreateInterpolation(cdm, rcdm, &In, NULL));
1904     PetscCall(MatMult(In, cl, rcl));
1905     PetscCall(MatDestroy(&In));
1906     PetscCall(DMSetCoordinatesLocal(rdm, rcl));
1907     PetscCall(DMDestroy(&odm));
1908     odm = rdm;
1909   }
1910   *hdm = rdm;
1911   PetscFunctionReturn(PETSC_SUCCESS);
1912 }
1913 
1914 #if defined(PETSC_HAVE_EXODUSII)
1915   #include <exodusII.h>
1916   #include <petscviewerexodusii.h>
1917 #endif
1918 
1919 PetscErrorCode DMView_Plex(DM dm, PetscViewer viewer)
1920 {
1921   PetscBool iascii, ishdf5, isvtk, isdraw, flg, isglvis, isexodus, iscgns;
1922   char      name[PETSC_MAX_PATH_LEN];
1923 
1924   PetscFunctionBegin;
1925   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1926   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1927   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &iascii));
1928   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
1929   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1930   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
1931   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
1932   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodus));
1933   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
1934   if (iascii) {
1935     PetscViewerFormat format;
1936     PetscCall(PetscViewerGetFormat(viewer, &format));
1937     if (format == PETSC_VIEWER_ASCII_GLVIS) PetscCall(DMPlexView_GLVis(dm, viewer));
1938     else PetscCall(DMPlexView_Ascii(dm, viewer));
1939   } else if (ishdf5) {
1940 #if defined(PETSC_HAVE_HDF5)
1941     PetscCall(DMPlexView_HDF5_Internal(dm, viewer));
1942 #else
1943     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1944 #endif
1945   } else if (isvtk) {
1946     PetscCall(DMPlexVTKWriteAll((PetscObject)dm, viewer));
1947   } else if (isdraw) {
1948     DM hdm;
1949 
1950     PetscCall(DMPlexCreateHighOrderSurrogate_Internal(dm, &hdm));
1951     PetscCall(DMPlexView_Draw(hdm, viewer));
1952     PetscCall(DMDestroy(&hdm));
1953   } else if (isglvis) {
1954     PetscCall(DMPlexView_GLVis(dm, viewer));
1955 #if defined(PETSC_HAVE_EXODUSII)
1956   } else if (isexodus) {
1957     /*
1958       exodusII requires that all sets be part of exactly one cell set.
1959       If the dm does not have a "Cell Sets" label defined, we create one
1960       with ID 1, containing all cells.
1961       Note that if the Cell Sets label is defined but does not cover all cells,
1962       we may still have a problem. This should probably be checked here or in the viewer;
1963     */
1964     PetscInt numCS;
1965     PetscCall(DMGetLabelSize(dm, "Cell Sets", &numCS));
1966     if (!numCS) {
1967       PetscInt cStart, cEnd, c;
1968       PetscCall(DMCreateLabel(dm, "Cell Sets"));
1969       PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1970       for (c = cStart; c < cEnd; ++c) PetscCall(DMSetLabelValue(dm, "Cell Sets", c, 1));
1971     }
1972     PetscCall(DMView_PlexExodusII(dm, viewer));
1973 #endif
1974 #if defined(PETSC_HAVE_CGNS)
1975   } else if (iscgns) {
1976     PetscCall(DMView_PlexCGNS(dm, viewer));
1977 #endif
1978   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex writing", ((PetscObject)viewer)->type_name);
1979   /* Optionally view the partition */
1980   PetscCall(PetscOptionsHasName(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_partition_view", &flg));
1981   if (flg) {
1982     Vec ranks;
1983     PetscCall(DMPlexCreateRankField(dm, &ranks));
1984     PetscCall(VecView(ranks, viewer));
1985     PetscCall(VecDestroy(&ranks));
1986   }
1987   /* Optionally view a label */
1988   PetscCall(PetscOptionsGetString(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_label_view", name, sizeof(name), &flg));
1989   if (flg) {
1990     DMLabel label;
1991     Vec     val;
1992 
1993     PetscCall(DMGetLabel(dm, name, &label));
1994     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Label %s provided to -dm_label_view does not exist in this DM", name);
1995     PetscCall(DMPlexCreateLabelField(dm, label, &val));
1996     PetscCall(VecView(val, viewer));
1997     PetscCall(VecDestroy(&val));
1998   }
1999   PetscFunctionReturn(PETSC_SUCCESS);
2000 }
2001 
2002 /*@
2003   DMPlexTopologyView - Saves a `DMPLEX` topology into a file
2004 
2005   Collective
2006 
2007   Input Parameters:
2008 + dm     - The `DM` whose topology is to be saved
2009 - viewer - The `PetscViewer` to save it in
2010 
2011   Level: advanced
2012 
2013 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexTopologyLoad()`, `PetscViewer`
2014 @*/
2015 PetscErrorCode DMPlexTopologyView(DM dm, PetscViewer viewer)
2016 {
2017   PetscBool ishdf5;
2018 
2019   PetscFunctionBegin;
2020   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2021   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2022   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2023   PetscCall(PetscLogEventBegin(DMPLEX_TopologyView, viewer, 0, 0, 0));
2024   if (ishdf5) {
2025 #if defined(PETSC_HAVE_HDF5)
2026     PetscViewerFormat format;
2027     PetscCall(PetscViewerGetFormat(viewer, &format));
2028     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2029       IS globalPointNumbering;
2030 
2031       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
2032       PetscCall(DMPlexTopologyView_HDF5_Internal(dm, globalPointNumbering, viewer));
2033       PetscCall(ISDestroy(&globalPointNumbering));
2034     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
2035 #else
2036     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2037 #endif
2038   }
2039   PetscCall(PetscLogEventEnd(DMPLEX_TopologyView, viewer, 0, 0, 0));
2040   PetscFunctionReturn(PETSC_SUCCESS);
2041 }
2042 
2043 /*@
2044   DMPlexCoordinatesView - Saves `DMPLEX` coordinates into a file
2045 
2046   Collective
2047 
2048   Input Parameters:
2049 + dm     - The `DM` whose coordinates are to be saved
2050 - viewer - The `PetscViewer` for saving
2051 
2052   Level: advanced
2053 
2054 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexLabelsView()`, `DMPlexCoordinatesLoad()`, `PetscViewer`
2055 @*/
2056 PetscErrorCode DMPlexCoordinatesView(DM dm, PetscViewer viewer)
2057 {
2058   PetscBool ishdf5;
2059 
2060   PetscFunctionBegin;
2061   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2062   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2063   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2064   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
2065   if (ishdf5) {
2066 #if defined(PETSC_HAVE_HDF5)
2067     PetscViewerFormat format;
2068     PetscCall(PetscViewerGetFormat(viewer, &format));
2069     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2070       PetscCall(DMPlexCoordinatesView_HDF5_Internal(dm, viewer));
2071     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
2072 #else
2073     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2074 #endif
2075   }
2076   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
2077   PetscFunctionReturn(PETSC_SUCCESS);
2078 }
2079 
2080 /*@
2081   DMPlexLabelsView - Saves `DMPLEX` labels into a file
2082 
2083   Collective
2084 
2085   Input Parameters:
2086 + dm     - The `DM` whose labels are to be saved
2087 - viewer - The `PetscViewer` for saving
2088 
2089   Level: advanced
2090 
2091 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsLoad()`, `PetscViewer`
2092 @*/
2093 PetscErrorCode DMPlexLabelsView(DM dm, PetscViewer viewer)
2094 {
2095   PetscBool ishdf5;
2096 
2097   PetscFunctionBegin;
2098   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2099   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2100   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2101   PetscCall(PetscLogEventBegin(DMPLEX_LabelsView, viewer, 0, 0, 0));
2102   if (ishdf5) {
2103 #if defined(PETSC_HAVE_HDF5)
2104     IS                globalPointNumbering;
2105     PetscViewerFormat format;
2106 
2107     PetscCall(PetscViewerGetFormat(viewer, &format));
2108     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2109       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
2110       PetscCall(DMPlexLabelsView_HDF5_Internal(dm, globalPointNumbering, viewer));
2111       PetscCall(ISDestroy(&globalPointNumbering));
2112     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2113 #else
2114     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2115 #endif
2116   }
2117   PetscCall(PetscLogEventEnd(DMPLEX_LabelsView, viewer, 0, 0, 0));
2118   PetscFunctionReturn(PETSC_SUCCESS);
2119 }
2120 
2121 /*@
2122   DMPlexSectionView - Saves a section associated with a `DMPLEX`
2123 
2124   Collective
2125 
2126   Input Parameters:
2127 + dm        - The `DM` that contains the topology on which the section to be saved is defined
2128 . viewer    - The `PetscViewer` for saving
2129 - sectiondm - The `DM` that contains the section to be saved, can be `NULL`
2130 
2131   Level: advanced
2132 
2133   Notes:
2134   This function is a wrapper around `PetscSectionView()`; in addition to the raw section, it saves information that associates the section points to the topology (`dm`) points. When the topology (`dm`) and the section are later loaded with `DMPlexTopologyLoad()` and `DMPlexSectionLoad()`, respectively, this information is used to match section points with topology points.
2135 
2136   In general `dm` and `sectiondm` are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object (or in case `sectiondm` is `NULL`) if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2137 
2138 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`, `PetscSectionView()`, `DMPlexSectionLoad()`, `PetscViewer`
2139 @*/
2140 PetscErrorCode DMPlexSectionView(DM dm, PetscViewer viewer, DM sectiondm)
2141 {
2142   PetscBool ishdf5;
2143 
2144   PetscFunctionBegin;
2145   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2146   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2147   if (!sectiondm) sectiondm = dm;
2148   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2149   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2150   PetscCall(PetscLogEventBegin(DMPLEX_SectionView, viewer, 0, 0, 0));
2151   if (ishdf5) {
2152 #if defined(PETSC_HAVE_HDF5)
2153     PetscCall(DMPlexSectionView_HDF5_Internal(dm, viewer, sectiondm));
2154 #else
2155     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2156 #endif
2157   }
2158   PetscCall(PetscLogEventEnd(DMPLEX_SectionView, viewer, 0, 0, 0));
2159   PetscFunctionReturn(PETSC_SUCCESS);
2160 }
2161 
2162 /*@
2163   DMPlexGlobalVectorView - Saves a global vector
2164 
2165   Collective
2166 
2167   Input Parameters:
2168 + dm        - The `DM` that represents the topology
2169 . viewer    - The `PetscViewer` to save data with
2170 . sectiondm - The `DM` that contains the global section on which vec is defined, can be `NULL`
2171 - vec       - The global vector to be saved
2172 
2173   Level: advanced
2174 
2175   Notes:
2176   In general `dm` and `sectiondm` are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object (or in case `sectiondm` is `NULL`) if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2177 
2178   Calling sequence:
2179 .vb
2180        DMCreate(PETSC_COMM_WORLD, &dm);
2181        DMSetType(dm, DMPLEX);
2182        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2183        DMClone(dm, &sectiondm);
2184        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2185        PetscSectionCreate(PETSC_COMM_WORLD, &section);
2186        DMPlexGetChart(sectiondm, &pStart, &pEnd);
2187        PetscSectionSetChart(section, pStart, pEnd);
2188        PetscSectionSetUp(section);
2189        DMSetLocalSection(sectiondm, section);
2190        PetscSectionDestroy(&section);
2191        DMGetGlobalVector(sectiondm, &vec);
2192        PetscObjectSetName((PetscObject)vec, "vec_name");
2193        DMPlexTopologyView(dm, viewer);
2194        DMPlexSectionView(dm, viewer, sectiondm);
2195        DMPlexGlobalVectorView(dm, viewer, sectiondm, vec);
2196        DMRestoreGlobalVector(sectiondm, &vec);
2197        DMDestroy(&sectiondm);
2198        DMDestroy(&dm);
2199 .ve
2200 
2201 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexLocalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2202 @*/
2203 PetscErrorCode DMPlexGlobalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2204 {
2205   PetscBool ishdf5;
2206 
2207   PetscFunctionBegin;
2208   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2209   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2210   if (!sectiondm) sectiondm = dm;
2211   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2212   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2213   /* Check consistency */
2214   {
2215     PetscSection section;
2216     PetscBool    includesConstraints;
2217     PetscInt     m, m1;
2218 
2219     PetscCall(VecGetLocalSize(vec, &m1));
2220     PetscCall(DMGetGlobalSection(sectiondm, &section));
2221     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2222     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2223     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2224     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2225   }
2226   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2227   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorView, viewer, 0, 0, 0));
2228   if (ishdf5) {
2229 #if defined(PETSC_HAVE_HDF5)
2230     PetscCall(DMPlexGlobalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2231 #else
2232     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2233 #endif
2234   }
2235   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorView, viewer, 0, 0, 0));
2236   PetscFunctionReturn(PETSC_SUCCESS);
2237 }
2238 
2239 /*@
2240   DMPlexLocalVectorView - Saves a local vector
2241 
2242   Collective
2243 
2244   Input Parameters:
2245 + dm        - The `DM` that represents the topology
2246 . viewer    - The `PetscViewer` to save data with
2247 . sectiondm - The `DM` that contains the local section on which `vec` is defined, can be `NULL`
2248 - vec       - The local vector to be saved
2249 
2250   Level: advanced
2251 
2252   Note:
2253   In general `dm` and `sectiondm` are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object (or in case `sectiondm` is `NULL`) if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2254 
2255   Calling sequence:
2256 .vb
2257        DMCreate(PETSC_COMM_WORLD, &dm);
2258        DMSetType(dm, DMPLEX);
2259        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2260        DMClone(dm, &sectiondm);
2261        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2262        PetscSectionCreate(PETSC_COMM_WORLD, &section);
2263        DMPlexGetChart(sectiondm, &pStart, &pEnd);
2264        PetscSectionSetChart(section, pStart, pEnd);
2265        PetscSectionSetUp(section);
2266        DMSetLocalSection(sectiondm, section);
2267        DMGetLocalVector(sectiondm, &vec);
2268        PetscObjectSetName((PetscObject)vec, "vec_name");
2269        DMPlexTopologyView(dm, viewer);
2270        DMPlexSectionView(dm, viewer, sectiondm);
2271        DMPlexLocalVectorView(dm, viewer, sectiondm, vec);
2272        DMRestoreLocalVector(sectiondm, &vec);
2273        DMDestroy(&sectiondm);
2274        DMDestroy(&dm);
2275 .ve
2276 
2277 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexGlobalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2278 @*/
2279 PetscErrorCode DMPlexLocalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2280 {
2281   PetscBool ishdf5;
2282 
2283   PetscFunctionBegin;
2284   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2285   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2286   if (!sectiondm) sectiondm = dm;
2287   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2288   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2289   /* Check consistency */
2290   {
2291     PetscSection section;
2292     PetscBool    includesConstraints;
2293     PetscInt     m, m1;
2294 
2295     PetscCall(VecGetLocalSize(vec, &m1));
2296     PetscCall(DMGetLocalSection(sectiondm, &section));
2297     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2298     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2299     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2300     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2301   }
2302   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2303   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorView, viewer, 0, 0, 0));
2304   if (ishdf5) {
2305 #if defined(PETSC_HAVE_HDF5)
2306     PetscCall(DMPlexLocalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2307 #else
2308     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2309 #endif
2310   }
2311   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorView, viewer, 0, 0, 0));
2312   PetscFunctionReturn(PETSC_SUCCESS);
2313 }
2314 
2315 PetscErrorCode DMLoad_Plex(DM dm, PetscViewer viewer)
2316 {
2317   PetscBool ishdf5;
2318 
2319   PetscFunctionBegin;
2320   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2321   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2322   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2323   if (ishdf5) {
2324 #if defined(PETSC_HAVE_HDF5)
2325     PetscViewerFormat format;
2326     PetscCall(PetscViewerGetFormat(viewer, &format));
2327     if (format == PETSC_VIEWER_HDF5_XDMF || format == PETSC_VIEWER_HDF5_VIZ) {
2328       PetscCall(DMPlexLoad_HDF5_Xdmf_Internal(dm, viewer));
2329     } else if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2330       PetscCall(DMPlexLoad_HDF5_Internal(dm, viewer));
2331     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2332     PetscFunctionReturn(PETSC_SUCCESS);
2333 #else
2334     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2335 #endif
2336   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex loading", ((PetscObject)viewer)->type_name);
2337 }
2338 
2339 /*@
2340   DMPlexTopologyLoad - Loads a topology into a `DMPLEX`
2341 
2342   Collective
2343 
2344   Input Parameters:
2345 + dm     - The `DM` into which the topology is loaded
2346 - viewer - The `PetscViewer` for the saved topology
2347 
2348   Output Parameter:
2349 . globalToLocalPointSF - The `PetscSF` that pushes points in [0, N) to the associated points in the loaded `DMPLEX`, where N is the global number of points; `NULL` if unneeded
2350 
2351   Level: advanced
2352 
2353 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2354           `PetscViewer`, `PetscSF`
2355 @*/
2356 PetscErrorCode DMPlexTopologyLoad(DM dm, PetscViewer viewer, PetscSF *globalToLocalPointSF)
2357 {
2358   PetscBool ishdf5;
2359 
2360   PetscFunctionBegin;
2361   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2362   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2363   if (globalToLocalPointSF) PetscAssertPointer(globalToLocalPointSF, 3);
2364   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2365   PetscCall(PetscLogEventBegin(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2366   if (ishdf5) {
2367 #if defined(PETSC_HAVE_HDF5)
2368     PetscViewerFormat format;
2369     PetscCall(PetscViewerGetFormat(viewer, &format));
2370     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2371       PetscCall(DMPlexTopologyLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2372     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2373 #else
2374     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2375 #endif
2376   }
2377   PetscCall(PetscLogEventEnd(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2378   PetscFunctionReturn(PETSC_SUCCESS);
2379 }
2380 
2381 /*@
2382   DMPlexCoordinatesLoad - Loads coordinates into a `DMPLEX`
2383 
2384   Collective
2385 
2386   Input Parameters:
2387 + dm                   - The `DM` into which the coordinates are loaded
2388 . viewer               - The `PetscViewer` for the saved coordinates
2389 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad()` when loading dm from viewer
2390 
2391   Level: advanced
2392 
2393 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2394           `PetscSF`, `PetscViewer`
2395 @*/
2396 PetscErrorCode DMPlexCoordinatesLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2397 {
2398   PetscBool ishdf5;
2399 
2400   PetscFunctionBegin;
2401   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2402   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2403   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2404   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2405   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesLoad, viewer, 0, 0, 0));
2406   if (ishdf5) {
2407 #if defined(PETSC_HAVE_HDF5)
2408     PetscViewerFormat format;
2409     PetscCall(PetscViewerGetFormat(viewer, &format));
2410     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2411       PetscCall(DMPlexCoordinatesLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2412     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2413 #else
2414     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2415 #endif
2416   }
2417   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesLoad, viewer, 0, 0, 0));
2418   PetscFunctionReturn(PETSC_SUCCESS);
2419 }
2420 
2421 /*@
2422   DMPlexLabelsLoad - Loads labels into a `DMPLEX`
2423 
2424   Collective
2425 
2426   Input Parameters:
2427 + dm                   - The `DM` into which the labels are loaded
2428 . viewer               - The `PetscViewer` for the saved labels
2429 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad()` when loading `dm` from viewer
2430 
2431   Level: advanced
2432 
2433   Note:
2434   The `PetscSF` argument must not be NULL if the `DM` is distributed, otherwise an error occurs.
2435 
2436 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2437           `PetscSF`, `PetscViewer`
2438 @*/
2439 PetscErrorCode DMPlexLabelsLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2440 {
2441   PetscBool ishdf5;
2442 
2443   PetscFunctionBegin;
2444   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2445   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2446   if (globalToLocalPointSF) PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2447   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2448   PetscCall(PetscLogEventBegin(DMPLEX_LabelsLoad, viewer, 0, 0, 0));
2449   if (ishdf5) {
2450 #if defined(PETSC_HAVE_HDF5)
2451     PetscViewerFormat format;
2452 
2453     PetscCall(PetscViewerGetFormat(viewer, &format));
2454     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2455       PetscCall(DMPlexLabelsLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2456     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2457 #else
2458     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2459 #endif
2460   }
2461   PetscCall(PetscLogEventEnd(DMPLEX_LabelsLoad, viewer, 0, 0, 0));
2462   PetscFunctionReturn(PETSC_SUCCESS);
2463 }
2464 
2465 /*@
2466   DMPlexSectionLoad - Loads section into a `DMPLEX`
2467 
2468   Collective
2469 
2470   Input Parameters:
2471 + dm                   - The `DM` that represents the topology
2472 . viewer               - The `PetscViewer` that represents the on-disk section (sectionA)
2473 . sectiondm            - The `DM` into which the on-disk section (sectionA) is migrated, can be `NULL`
2474 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad(`) when loading dm from viewer
2475 
2476   Output Parameters:
2477 + globalDofSF - The `PetscSF` that migrates any on-disk `Vec` data associated with sectionA into a global `Vec` associated with the `sectiondm`'s global section (`NULL` if not needed)
2478 - localDofSF  - The `PetscSF` that migrates any on-disk `Vec` data associated with sectionA into a local `Vec` associated with the `sectiondm`'s local section (`NULL` if not needed)
2479 
2480   Level: advanced
2481 
2482   Notes:
2483   This function is a wrapper around `PetscSectionLoad()`; it loads, in addition to the raw section, a list of global point numbers that associates each on-disk section point with a global point number in [0, NX), where NX is the number of topology points in `dm`. Noting that globalToLocalPointSF associates each topology point in dm with a global number in [0, NX), one can readily establish an association of the on-disk section points with the topology points.
2484 
2485   In general `dm` and `sectiondm` are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object (or in case `sectiondm` is `NULL`) if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2486 
2487   The output parameter, `globalDofSF` (`localDofSF`), can later be used with `DMPlexGlobalVectorLoad()` (`DMPlexLocalVectorLoad()`) to load on-disk vectors into global (local) vectors associated with sectiondm's global (local) section.
2488 
2489   Example using 2 processes:
2490 .vb
2491   NX (number of points on dm): 4
2492   sectionA                   : the on-disk section
2493   vecA                       : a vector associated with sectionA
2494   sectionB                   : sectiondm's local section constructed in this function
2495   vecB (local)               : a vector associated with sectiondm's local section
2496   vecB (global)              : a vector associated with sectiondm's global section
2497 
2498                                      rank 0    rank 1
2499   vecA (global)                  : [.0 .4 .1 | .2 .3]        <- to be loaded in DMPlexGlobalVectorLoad() or DMPlexLocalVectorLoad()
2500   sectionA->atlasOff             :       0 2 | 1             <- loaded in PetscSectionLoad()
2501   sectionA->atlasDof             :       1 3 | 1             <- loaded in PetscSectionLoad()
2502   sectionA's global point numbers:       0 2 | 3             <- loaded in DMPlexSectionLoad()
2503   [0, NX)                        :       0 1 | 2 3           <- conceptual partition used in globalToLocalPointSF
2504   sectionB's global point numbers:     0 1 3 | 3 2           <- associated with [0, NX) by globalToLocalPointSF
2505   sectionB->atlasDof             :     1 0 1 | 1 3
2506   sectionB->atlasOff (no perm)   :     0 1 1 | 0 1
2507   vecB (local)                   :   [.0 .4] | [.4 .1 .2 .3] <- to be constructed by calling DMPlexLocalVectorLoad() with localDofSF
2508   vecB (global)                  :    [.0 .4 | .1 .2 .3]     <- to be constructed by calling DMPlexGlobalVectorLoad() with globalDofSF
2509 .ve
2510   where "|" represents a partition of loaded data, and global point 3 is assumed to be owned by rank 0.
2511 
2512 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`, `PetscSectionLoad()`, `DMPlexSectionView()`, `PetscSF`, `PetscViewer`
2513 @*/
2514 PetscErrorCode DMPlexSectionLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF globalToLocalPointSF, PetscSF *globalDofSF, PetscSF *localDofSF)
2515 {
2516   PetscBool ishdf5;
2517 
2518   PetscFunctionBegin;
2519   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2520   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2521   if (!sectiondm) sectiondm = dm;
2522   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2523   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 4);
2524   if (globalDofSF) PetscAssertPointer(globalDofSF, 5);
2525   if (localDofSF) PetscAssertPointer(localDofSF, 6);
2526   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2527   PetscCall(PetscLogEventBegin(DMPLEX_SectionLoad, viewer, 0, 0, 0));
2528   if (ishdf5) {
2529 #if defined(PETSC_HAVE_HDF5)
2530     PetscCall(DMPlexSectionLoad_HDF5_Internal(dm, viewer, sectiondm, globalToLocalPointSF, globalDofSF, localDofSF));
2531 #else
2532     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2533 #endif
2534   }
2535   PetscCall(PetscLogEventEnd(DMPLEX_SectionLoad, viewer, 0, 0, 0));
2536   PetscFunctionReturn(PETSC_SUCCESS);
2537 }
2538 
2539 /*@
2540   DMPlexGlobalVectorLoad - Loads on-disk vector data into a global vector
2541 
2542   Collective
2543 
2544   Input Parameters:
2545 + dm        - The `DM` that represents the topology
2546 . viewer    - The `PetscViewer` that represents the on-disk vector data
2547 . sectiondm - The `DM` that contains the global section on which vec is defined, can be `NULL`
2548 . sf        - The `PetscSF` that migrates the on-disk vector data into vec
2549 - vec       - The global vector to set values of
2550 
2551   Level: advanced
2552 
2553   Notes:
2554   In general dm and sectiondm are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object (or in case `sectiondm` is `NULL`) if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2555 
2556   Calling sequence:
2557 .vb
2558        DMCreate(PETSC_COMM_WORLD, &dm);
2559        DMSetType(dm, DMPLEX);
2560        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2561        DMPlexTopologyLoad(dm, viewer, &sfX);
2562        DMClone(dm, &sectiondm);
2563        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2564        DMPlexSectionLoad(dm, viewer, sectiondm, sfX, &gsf, NULL);
2565        DMGetGlobalVector(sectiondm, &vec);
2566        PetscObjectSetName((PetscObject)vec, "vec_name");
2567        DMPlexGlobalVectorLoad(dm, viewer, sectiondm, gsf, vec);
2568        DMRestoreGlobalVector(sectiondm, &vec);
2569        PetscSFDestroy(&gsf);
2570        PetscSFDestroy(&sfX);
2571        DMDestroy(&sectiondm);
2572        DMDestroy(&dm);
2573 .ve
2574 
2575 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexLocalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`,
2576           `PetscSF`, `PetscViewer`
2577 @*/
2578 PetscErrorCode DMPlexGlobalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2579 {
2580   PetscBool ishdf5;
2581 
2582   PetscFunctionBegin;
2583   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2584   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2585   if (!sectiondm) sectiondm = dm;
2586   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2587   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2588   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2589   /* Check consistency */
2590   {
2591     PetscSection section;
2592     PetscBool    includesConstraints;
2593     PetscInt     m, m1;
2594 
2595     PetscCall(VecGetLocalSize(vec, &m1));
2596     PetscCall(DMGetGlobalSection(sectiondm, &section));
2597     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2598     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2599     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2600     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2601   }
2602   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2603   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorLoad, viewer, 0, 0, 0));
2604   if (ishdf5) {
2605 #if defined(PETSC_HAVE_HDF5)
2606     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2607 #else
2608     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2609 #endif
2610   }
2611   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorLoad, viewer, 0, 0, 0));
2612   PetscFunctionReturn(PETSC_SUCCESS);
2613 }
2614 
2615 /*@
2616   DMPlexLocalVectorLoad - Loads on-disk vector data into a local vector
2617 
2618   Collective
2619 
2620   Input Parameters:
2621 + dm        - The `DM` that represents the topology
2622 . viewer    - The `PetscViewer` that represents the on-disk vector data
2623 . sectiondm - The `DM` that contains the local section on which vec is defined, can be `NULL`
2624 . sf        - The `PetscSF` that migrates the on-disk vector data into vec
2625 - vec       - The local vector to set values of
2626 
2627   Level: advanced
2628 
2629   Notes:
2630   In general `dm` and `sectiondm` are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object (or in case `sectiondm` is `NULL`) if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2631 
2632   Calling sequence:
2633 .vb
2634        DMCreate(PETSC_COMM_WORLD, &dm);
2635        DMSetType(dm, DMPLEX);
2636        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2637        DMPlexTopologyLoad(dm, viewer, &sfX);
2638        DMClone(dm, &sectiondm);
2639        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2640        DMPlexSectionLoad(dm, viewer, sectiondm, sfX, NULL, &lsf);
2641        DMGetLocalVector(sectiondm, &vec);
2642        PetscObjectSetName((PetscObject)vec, "vec_name");
2643        DMPlexLocalVectorLoad(dm, viewer, sectiondm, lsf, vec);
2644        DMRestoreLocalVector(sectiondm, &vec);
2645        PetscSFDestroy(&lsf);
2646        PetscSFDestroy(&sfX);
2647        DMDestroy(&sectiondm);
2648        DMDestroy(&dm);
2649 .ve
2650 
2651 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`,
2652           `PetscSF`, `PetscViewer`
2653 @*/
2654 PetscErrorCode DMPlexLocalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2655 {
2656   PetscBool ishdf5;
2657 
2658   PetscFunctionBegin;
2659   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2660   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2661   if (!sectiondm) sectiondm = dm;
2662   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2663   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2664   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2665   /* Check consistency */
2666   {
2667     PetscSection section;
2668     PetscBool    includesConstraints;
2669     PetscInt     m, m1;
2670 
2671     PetscCall(VecGetLocalSize(vec, &m1));
2672     PetscCall(DMGetLocalSection(sectiondm, &section));
2673     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2674     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2675     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2676     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2677   }
2678   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2679   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorLoad, viewer, 0, 0, 0));
2680   if (ishdf5) {
2681 #if defined(PETSC_HAVE_HDF5)
2682     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2683 #else
2684     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2685 #endif
2686   }
2687   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorLoad, viewer, 0, 0, 0));
2688   PetscFunctionReturn(PETSC_SUCCESS);
2689 }
2690 
2691 PetscErrorCode DMDestroy_Plex(DM dm)
2692 {
2693   DM_Plex *mesh = (DM_Plex *)dm->data;
2694 
2695   PetscFunctionBegin;
2696   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMSetUpGLVisViewer_C", NULL));
2697   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertBoundaryValues_C", NULL));
2698   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMCreateNeumannOverlap_C", NULL));
2699   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMInterpolateSolution_C", NULL));
2700   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertTimeDerivativeBoundaryValues_C", NULL));
2701   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", NULL));
2702   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeGetDefault_C", NULL));
2703   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeSetDefault_C", NULL));
2704   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "MatComputeNeumannOverlap_C", NULL));
2705   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderGetDefault_C", NULL));
2706   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderSetDefault_C", NULL));
2707   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionGetDefault_C", NULL));
2708   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionSetDefault_C", NULL));
2709   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionGetType_C", NULL));
2710   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionSetType_C", NULL));
2711   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", NULL));
2712   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexSetOverlap_C", NULL));
2713   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetUseCeed_C", NULL));
2714   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexSetUseCeed_C", NULL));
2715   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMGetIsoperiodicPointSF_C", NULL));
2716   if (--mesh->refct > 0) PetscFunctionReturn(PETSC_SUCCESS);
2717   PetscCall(PetscSectionDestroy(&mesh->coneSection));
2718   PetscCall(PetscFree(mesh->cones));
2719   PetscCall(PetscFree(mesh->coneOrientations));
2720   PetscCall(PetscSectionDestroy(&mesh->supportSection));
2721   PetscCall(PetscSectionDestroy(&mesh->subdomainSection));
2722   PetscCall(PetscFree(mesh->supports));
2723   PetscCall(PetscFree(mesh->cellTypes));
2724   PetscCall(DMPlexTransformDestroy(&mesh->tr));
2725   PetscCall(PetscFree(mesh->tetgenOpts));
2726   PetscCall(PetscFree(mesh->triangleOpts));
2727   PetscCall(PetscFree(mesh->transformType));
2728   PetscCall(PetscFree(mesh->distributionName));
2729   PetscCall(PetscPartitionerDestroy(&mesh->partitioner));
2730   PetscCall(DMLabelDestroy(&mesh->subpointMap));
2731   PetscCall(ISDestroy(&mesh->subpointIS));
2732   PetscCall(ISDestroy(&mesh->globalVertexNumbers));
2733   PetscCall(ISDestroy(&mesh->globalCellNumbers));
2734   PetscCall(PetscSFDestroy(&mesh->periodic.face_sf));
2735   PetscCall(PetscSFDestroy(&mesh->periodic.composed_sf));
2736   PetscCall(ISDestroy(&mesh->periodic.periodic_points));
2737   PetscCall(PetscSectionDestroy(&mesh->anchorSection));
2738   PetscCall(ISDestroy(&mesh->anchorIS));
2739   PetscCall(PetscSectionDestroy(&mesh->parentSection));
2740   PetscCall(PetscFree(mesh->parents));
2741   PetscCall(PetscFree(mesh->childIDs));
2742   PetscCall(PetscSectionDestroy(&mesh->childSection));
2743   PetscCall(PetscFree(mesh->children));
2744   PetscCall(DMDestroy(&mesh->referenceTree));
2745   PetscCall(PetscGridHashDestroy(&mesh->lbox));
2746   PetscCall(PetscFree(mesh->neighbors));
2747   if (mesh->metricCtx) PetscCall(PetscFree(mesh->metricCtx));
2748   /* This was originally freed in DMDestroy(), but that prevents reference counting of backend objects */
2749   PetscCall(PetscFree(mesh));
2750   PetscFunctionReturn(PETSC_SUCCESS);
2751 }
2752 
2753 PetscErrorCode DMCreateMatrix_Plex(DM dm, Mat *J)
2754 {
2755   PetscSection           sectionGlobal, sectionLocal;
2756   PetscInt               bs = -1, mbs;
2757   PetscInt               localSize, localStart = 0;
2758   PetscBool              isShell, isBlock, isSeqBlock, isMPIBlock, isSymBlock, isSymSeqBlock, isSymMPIBlock, isMatIS;
2759   MatType                mtype;
2760   ISLocalToGlobalMapping ltog;
2761 
2762   PetscFunctionBegin;
2763   PetscCall(MatInitializePackage());
2764   mtype = dm->mattype;
2765   PetscCall(DMGetLocalSection(dm, &sectionLocal));
2766   PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
2767   /* PetscCall(PetscSectionGetStorageSize(sectionGlobal, &localSize)); */
2768   PetscCall(PetscSectionGetConstrainedStorageSize(sectionGlobal, &localSize));
2769   PetscCallMPI(MPI_Exscan(&localSize, &localStart, 1, MPIU_INT, MPI_SUM, PetscObjectComm((PetscObject)dm)));
2770   PetscCall(MatCreate(PetscObjectComm((PetscObject)dm), J));
2771   PetscCall(MatSetSizes(*J, localSize, localSize, PETSC_DETERMINE, PETSC_DETERMINE));
2772   PetscCall(MatSetType(*J, mtype));
2773   PetscCall(MatSetFromOptions(*J));
2774   PetscCall(MatGetBlockSize(*J, &mbs));
2775   if (mbs > 1) bs = mbs;
2776   PetscCall(PetscStrcmp(mtype, MATSHELL, &isShell));
2777   PetscCall(PetscStrcmp(mtype, MATBAIJ, &isBlock));
2778   PetscCall(PetscStrcmp(mtype, MATSEQBAIJ, &isSeqBlock));
2779   PetscCall(PetscStrcmp(mtype, MATMPIBAIJ, &isMPIBlock));
2780   PetscCall(PetscStrcmp(mtype, MATSBAIJ, &isSymBlock));
2781   PetscCall(PetscStrcmp(mtype, MATSEQSBAIJ, &isSymSeqBlock));
2782   PetscCall(PetscStrcmp(mtype, MATMPISBAIJ, &isSymMPIBlock));
2783   PetscCall(PetscStrcmp(mtype, MATIS, &isMatIS));
2784   if (!isShell) {
2785     PetscBool fillMatrix = (PetscBool)(!dm->prealloc_only && !isMatIS);
2786     PetscInt *dnz, *onz, *dnzu, *onzu, bsLocal[2], bsMinMax[2], *pblocks;
2787     PetscInt  pStart, pEnd, p, dof, cdof, num_fields;
2788 
2789     PetscCall(DMGetLocalToGlobalMapping(dm, &ltog));
2790 
2791     PetscCall(PetscCalloc1(localSize, &pblocks));
2792     PetscCall(PetscSectionGetChart(sectionGlobal, &pStart, &pEnd));
2793     PetscCall(PetscSectionGetNumFields(sectionGlobal, &num_fields));
2794     for (p = pStart; p < pEnd; ++p) {
2795       switch (dm->blocking_type) {
2796       case DM_BLOCKING_TOPOLOGICAL_POINT: { // One block per topological point
2797         PetscInt bdof, offset;
2798 
2799         PetscCall(PetscSectionGetDof(sectionGlobal, p, &dof));
2800         PetscCall(PetscSectionGetOffset(sectionGlobal, p, &offset));
2801         PetscCall(PetscSectionGetConstraintDof(sectionGlobal, p, &cdof));
2802         for (PetscInt i = 0; i < dof - cdof; ++i) pblocks[offset - localStart + i] = dof - cdof;
2803         // Signal block concatenation
2804         if (dof - cdof && sectionLocal->blockStarts && !PetscBTLookup(sectionLocal->blockStarts, p)) pblocks[offset - localStart] = -(dof - cdof);
2805         dof  = dof < 0 ? -(dof + 1) : dof;
2806         bdof = cdof && (dof - cdof) ? 1 : dof;
2807         if (dof) {
2808           if (bs < 0) {
2809             bs = bdof;
2810           } else if (bs != bdof) {
2811             bs = 1;
2812           }
2813         }
2814       } break;
2815       case DM_BLOCKING_FIELD_NODE: {
2816         for (PetscInt field = 0; field < num_fields; field++) {
2817           PetscInt num_comp, bdof, offset;
2818           PetscCall(PetscSectionGetFieldComponents(sectionGlobal, field, &num_comp));
2819           PetscCall(PetscSectionGetFieldDof(sectionGlobal, p, field, &dof));
2820           if (dof < 0) continue;
2821           PetscCall(PetscSectionGetFieldOffset(sectionGlobal, p, field, &offset));
2822           PetscCall(PetscSectionGetFieldConstraintDof(sectionGlobal, p, field, &cdof));
2823           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);
2824           PetscInt num_nodes = dof / num_comp;
2825           for (PetscInt i = 0; i < dof - cdof; i++) pblocks[offset - localStart + i] = (dof - cdof) / num_nodes;
2826           // Handle possibly constant block size (unlikely)
2827           bdof = cdof && (dof - cdof) ? 1 : dof;
2828           if (dof) {
2829             if (bs < 0) {
2830               bs = bdof;
2831             } else if (bs != bdof) {
2832               bs = 1;
2833             }
2834           }
2835         }
2836       } break;
2837       }
2838     }
2839     /* Must have same blocksize on all procs (some might have no points) */
2840     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs;
2841     bsLocal[1] = bs;
2842     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
2843     if (bsMinMax[0] != bsMinMax[1]) bs = 1;
2844     else bs = bsMinMax[0];
2845     bs = PetscMax(1, bs);
2846     PetscCall(MatSetLocalToGlobalMapping(*J, ltog, ltog));
2847     if (dm->prealloc_skip) { // User will likely use MatSetPreallocationCOO(), but still set structural parameters
2848       PetscCall(MatSetBlockSize(*J, bs));
2849       PetscCall(MatSetUp(*J));
2850     } else {
2851       PetscCall(PetscCalloc4(localSize / bs, &dnz, localSize / bs, &onz, localSize / bs, &dnzu, localSize / bs, &onzu));
2852       PetscCall(DMPlexPreallocateOperator(dm, bs, dnz, onz, dnzu, onzu, *J, fillMatrix));
2853       PetscCall(PetscFree4(dnz, onz, dnzu, onzu));
2854     }
2855     { // Consolidate blocks
2856       PetscInt nblocks = 0;
2857       for (PetscInt i = 0; i < localSize; i += PetscMax(1, pblocks[i])) {
2858         if (pblocks[i] == 0) continue;
2859         // Negative block size indicates the blocks should be concatenated
2860         if (pblocks[i] < 0) {
2861           pblocks[i] = -pblocks[i];
2862           pblocks[nblocks - 1] += pblocks[i];
2863         } else {
2864           pblocks[nblocks++] = pblocks[i]; // nblocks always <= i
2865         }
2866         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]);
2867       }
2868       PetscCall(MatSetVariableBlockSizes(*J, nblocks, pblocks));
2869     }
2870     PetscCall(PetscFree(pblocks));
2871   }
2872   PetscCall(MatSetDM(*J, dm));
2873   PetscFunctionReturn(PETSC_SUCCESS);
2874 }
2875 
2876 /*@
2877   DMPlexGetSubdomainSection - Returns the section associated with the subdomain
2878 
2879   Not Collective
2880 
2881   Input Parameter:
2882 . dm - The `DMPLEX`
2883 
2884   Output Parameter:
2885 . subsection - The subdomain section
2886 
2887   Level: developer
2888 
2889 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `PetscSection`
2890 @*/
2891 PetscErrorCode DMPlexGetSubdomainSection(DM dm, PetscSection *subsection)
2892 {
2893   DM_Plex *mesh = (DM_Plex *)dm->data;
2894 
2895   PetscFunctionBegin;
2896   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2897   if (!mesh->subdomainSection) {
2898     PetscSection section;
2899     PetscSF      sf;
2900 
2901     PetscCall(PetscSFCreate(PETSC_COMM_SELF, &sf));
2902     PetscCall(DMGetLocalSection(dm, &section));
2903     PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_TRUE, PETSC_FALSE, PETSC_TRUE, &mesh->subdomainSection));
2904     PetscCall(PetscSFDestroy(&sf));
2905   }
2906   *subsection = mesh->subdomainSection;
2907   PetscFunctionReturn(PETSC_SUCCESS);
2908 }
2909 
2910 /*@
2911   DMPlexGetChart - Return the interval for all mesh points [`pStart`, `pEnd`)
2912 
2913   Not Collective
2914 
2915   Input Parameter:
2916 . dm - The `DMPLEX`
2917 
2918   Output Parameters:
2919 + pStart - The first mesh point
2920 - pEnd   - The upper bound for mesh points
2921 
2922   Level: beginner
2923 
2924 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetChart()`
2925 @*/
2926 PetscErrorCode DMPlexGetChart(DM dm, PetscInt *pStart, PetscInt *pEnd)
2927 {
2928   DM_Plex *mesh = (DM_Plex *)dm->data;
2929 
2930   PetscFunctionBegin;
2931   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2932   if (mesh->tr) PetscCall(DMPlexTransformGetChart(mesh->tr, pStart, pEnd));
2933   else PetscCall(PetscSectionGetChart(mesh->coneSection, pStart, pEnd));
2934   PetscFunctionReturn(PETSC_SUCCESS);
2935 }
2936 
2937 /*@
2938   DMPlexSetChart - Set the interval for all mesh points [`pStart`, `pEnd`)
2939 
2940   Not Collective
2941 
2942   Input Parameters:
2943 + dm     - The `DMPLEX`
2944 . pStart - The first mesh point
2945 - pEnd   - The upper bound for mesh points
2946 
2947   Level: beginner
2948 
2949 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetChart()`
2950 @*/
2951 PetscErrorCode DMPlexSetChart(DM dm, PetscInt pStart, PetscInt pEnd)
2952 {
2953   DM_Plex *mesh = (DM_Plex *)dm->data;
2954 
2955   PetscFunctionBegin;
2956   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2957   PetscCall(PetscSectionSetChart(mesh->coneSection, pStart, pEnd));
2958   PetscCall(PetscSectionSetChart(mesh->supportSection, pStart, pEnd));
2959   PetscCall(PetscFree(mesh->cellTypes));
2960   PetscFunctionReturn(PETSC_SUCCESS);
2961 }
2962 
2963 /*@
2964   DMPlexGetConeSize - Return the number of in-edges for this point in the DAG
2965 
2966   Not Collective
2967 
2968   Input Parameters:
2969 + dm - The `DMPLEX`
2970 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
2971 
2972   Output Parameter:
2973 . size - The cone size for point `p`
2974 
2975   Level: beginner
2976 
2977 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
2978 @*/
2979 PetscErrorCode DMPlexGetConeSize(DM dm, PetscInt p, PetscInt *size)
2980 {
2981   DM_Plex *mesh = (DM_Plex *)dm->data;
2982 
2983   PetscFunctionBegin;
2984   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2985   PetscAssertPointer(size, 3);
2986   if (mesh->tr) PetscCall(DMPlexTransformGetConeSize(mesh->tr, p, size));
2987   else PetscCall(PetscSectionGetDof(mesh->coneSection, p, size));
2988   PetscFunctionReturn(PETSC_SUCCESS);
2989 }
2990 
2991 /*@
2992   DMPlexSetConeSize - Set the number of in-edges for this point in the DAG
2993 
2994   Not Collective
2995 
2996   Input Parameters:
2997 + dm   - The `DMPLEX`
2998 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
2999 - size - The cone size for point `p`
3000 
3001   Level: beginner
3002 
3003   Note:
3004   This should be called after `DMPlexSetChart()`.
3005 
3006 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetConeSize()`, `DMPlexSetChart()`
3007 @*/
3008 PetscErrorCode DMPlexSetConeSize(DM dm, PetscInt p, PetscInt size)
3009 {
3010   DM_Plex *mesh = (DM_Plex *)dm->data;
3011 
3012   PetscFunctionBegin;
3013   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3014   PetscCheck(!mesh->tr, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Cannot call DMPlexSetConeSize() on a mesh with a transform defined.");
3015   PetscCall(PetscSectionSetDof(mesh->coneSection, p, size));
3016   PetscFunctionReturn(PETSC_SUCCESS);
3017 }
3018 
3019 /*@C
3020   DMPlexGetCone - Return the points on the in-edges for this point in the DAG
3021 
3022   Not Collective
3023 
3024   Input Parameters:
3025 + dm - The `DMPLEX`
3026 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3027 
3028   Output Parameter:
3029 . cone - An array of points which are on the in-edges for point `p`
3030 
3031   Level: beginner
3032 
3033   Fortran Notes:
3034   You must also call `DMPlexRestoreCone()` after you finish using the returned array.
3035   `DMPlexRestoreCone()` is not needed/available in C.
3036 
3037 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSize()`, `DMPlexSetCone()`, `DMPlexGetConeTuple()`, `DMPlexSetChart()`, `DMPlexRestoreCone()`
3038 @*/
3039 PetscErrorCode DMPlexGetCone(DM dm, PetscInt p, const PetscInt *cone[])
3040 {
3041   DM_Plex *mesh = (DM_Plex *)dm->data;
3042   PetscInt off;
3043 
3044   PetscFunctionBegin;
3045   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3046   PetscAssertPointer(cone, 3);
3047   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3048   *cone = PetscSafePointerPlusOffset(mesh->cones, off);
3049   PetscFunctionReturn(PETSC_SUCCESS);
3050 }
3051 
3052 /*@C
3053   DMPlexGetConeTuple - Return the points on the in-edges of several points in the DAG
3054 
3055   Not Collective
3056 
3057   Input Parameters:
3058 + dm - The `DMPLEX`
3059 - p  - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3060 
3061   Output Parameters:
3062 + pConesSection - `PetscSection` describing the layout of `pCones`
3063 - pCones        - An array of points which are on the in-edges for the point set `p`
3064 
3065   Level: intermediate
3066 
3067 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeRecursive()`, `DMPlexSetChart()`, `PetscSection`, `IS`
3068 @*/
3069 PetscErrorCode DMPlexGetConeTuple(DM dm, IS p, PetscSection *pConesSection, IS *pCones)
3070 {
3071   PetscSection cs, newcs;
3072   PetscInt    *cones;
3073   PetscInt    *newarr = NULL;
3074   PetscInt     n;
3075 
3076   PetscFunctionBegin;
3077   PetscCall(DMPlexGetCones(dm, &cones));
3078   PetscCall(DMPlexGetConeSection(dm, &cs));
3079   PetscCall(PetscSectionExtractDofsFromArray(cs, MPIU_INT, cones, p, &newcs, pCones ? ((void **)&newarr) : NULL));
3080   if (pConesSection) *pConesSection = newcs;
3081   if (pCones) {
3082     PetscCall(PetscSectionGetStorageSize(newcs, &n));
3083     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)p), n, newarr, PETSC_OWN_POINTER, pCones));
3084   }
3085   PetscFunctionReturn(PETSC_SUCCESS);
3086 }
3087 
3088 /*@
3089   DMPlexGetConeRecursiveVertices - Expand each given point into its cone points and do that recursively until we end up just with vertices.
3090 
3091   Not Collective
3092 
3093   Input Parameters:
3094 + dm     - The `DMPLEX`
3095 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3096 
3097   Output Parameter:
3098 . expandedPoints - An array of vertices recursively expanded from input points
3099 
3100   Level: advanced
3101 
3102   Notes:
3103   Like `DMPlexGetConeRecursive()` but returns only the 0-depth `IS` (i.e. vertices only) and no sections.
3104 
3105   There is no corresponding Restore function, just call `ISDestroy()` on the returned `IS` to deallocate.
3106 
3107 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexRestoreConeRecursive()`,
3108           `DMPlexGetDepth()`, `IS`
3109 @*/
3110 PetscErrorCode DMPlexGetConeRecursiveVertices(DM dm, IS points, IS *expandedPoints)
3111 {
3112   IS      *expandedPointsAll;
3113   PetscInt depth;
3114 
3115   PetscFunctionBegin;
3116   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3117   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
3118   PetscAssertPointer(expandedPoints, 3);
3119   PetscCall(DMPlexGetConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
3120   *expandedPoints = expandedPointsAll[0];
3121   PetscCall(PetscObjectReference((PetscObject)expandedPointsAll[0]));
3122   PetscCall(DMPlexRestoreConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
3123   PetscFunctionReturn(PETSC_SUCCESS);
3124 }
3125 
3126 /*@
3127   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).
3128 
3129   Not Collective
3130 
3131   Input Parameters:
3132 + dm     - The `DMPLEX`
3133 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3134 
3135   Output Parameters:
3136 + depth          - (optional) Size of the output arrays, equal to `DMPLEX` depth, returned by `DMPlexGetDepth()`
3137 . expandedPoints - (optional) An array of index sets with recursively expanded cones
3138 - sections       - (optional) An array of sections which describe mappings from points to their cone points
3139 
3140   Level: advanced
3141 
3142   Notes:
3143   Like `DMPlexGetConeTuple()` but recursive.
3144 
3145   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.
3146   For example, for d=0 it contains only vertices, for d=1 it can contain vertices and edges, etc.
3147 
3148   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\:
3149   (1) DAG points in `expandedPoints`[d+1] with `depth` d+1 to their cone points in `expandedPoints`[d];
3150   (2) DAG points in `expandedPoints`[d+1] with `depth` in [0,d] to the same points in `expandedPoints`[d].
3151 
3152 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexRestoreConeRecursive()`, `DMPlexGetConeRecursiveVertices()`,
3153           `DMPlexGetDepth()`, `PetscSection`, `IS`
3154 @*/
3155 PetscErrorCode DMPlexGetConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
3156 {
3157   const PetscInt *arr0 = NULL, *cone = NULL;
3158   PetscInt       *arr = NULL, *newarr = NULL;
3159   PetscInt        d, depth_, i, n, newn, cn, co, start, end;
3160   IS             *expandedPoints_;
3161   PetscSection   *sections_;
3162 
3163   PetscFunctionBegin;
3164   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3165   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
3166   if (depth) PetscAssertPointer(depth, 3);
3167   if (expandedPoints) PetscAssertPointer(expandedPoints, 4);
3168   if (sections) PetscAssertPointer(sections, 5);
3169   PetscCall(ISGetLocalSize(points, &n));
3170   PetscCall(ISGetIndices(points, &arr0));
3171   PetscCall(DMPlexGetDepth(dm, &depth_));
3172   PetscCall(PetscCalloc1(depth_, &expandedPoints_));
3173   PetscCall(PetscCalloc1(depth_, &sections_));
3174   arr = (PetscInt *)arr0; /* this is ok because first generation of arr is not modified */
3175   for (d = depth_ - 1; d >= 0; d--) {
3176     PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &sections_[d]));
3177     PetscCall(PetscSectionSetChart(sections_[d], 0, n));
3178     for (i = 0; i < n; i++) {
3179       PetscCall(DMPlexGetDepthStratum(dm, d + 1, &start, &end));
3180       if (arr[i] >= start && arr[i] < end) {
3181         PetscCall(DMPlexGetConeSize(dm, arr[i], &cn));
3182         PetscCall(PetscSectionSetDof(sections_[d], i, cn));
3183       } else {
3184         PetscCall(PetscSectionSetDof(sections_[d], i, 1));
3185       }
3186     }
3187     PetscCall(PetscSectionSetUp(sections_[d]));
3188     PetscCall(PetscSectionGetStorageSize(sections_[d], &newn));
3189     PetscCall(PetscMalloc1(newn, &newarr));
3190     for (i = 0; i < n; i++) {
3191       PetscCall(PetscSectionGetDof(sections_[d], i, &cn));
3192       PetscCall(PetscSectionGetOffset(sections_[d], i, &co));
3193       if (cn > 1) {
3194         PetscCall(DMPlexGetCone(dm, arr[i], &cone));
3195         PetscCall(PetscMemcpy(&newarr[co], cone, cn * sizeof(PetscInt)));
3196       } else {
3197         newarr[co] = arr[i];
3198       }
3199     }
3200     PetscCall(ISCreateGeneral(PETSC_COMM_SELF, newn, newarr, PETSC_OWN_POINTER, &expandedPoints_[d]));
3201     arr = newarr;
3202     n   = newn;
3203   }
3204   PetscCall(ISRestoreIndices(points, &arr0));
3205   *depth = depth_;
3206   if (expandedPoints) *expandedPoints = expandedPoints_;
3207   else {
3208     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&expandedPoints_[d]));
3209     PetscCall(PetscFree(expandedPoints_));
3210   }
3211   if (sections) *sections = sections_;
3212   else {
3213     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&sections_[d]));
3214     PetscCall(PetscFree(sections_));
3215   }
3216   PetscFunctionReturn(PETSC_SUCCESS);
3217 }
3218 
3219 /*@
3220   DMPlexRestoreConeRecursive - Deallocates arrays created by `DMPlexGetConeRecursive()`
3221 
3222   Not Collective
3223 
3224   Input Parameters:
3225 + dm     - The `DMPLEX`
3226 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3227 
3228   Output Parameters:
3229 + depth          - (optional) Size of the output arrays, equal to `DMPLEX` depth, returned by `DMPlexGetDepth()`
3230 . expandedPoints - (optional) An array of recursively expanded cones
3231 - sections       - (optional) An array of sections which describe mappings from points to their cone points
3232 
3233   Level: advanced
3234 
3235   Note:
3236   See `DMPlexGetConeRecursive()`
3237 
3238 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexGetConeRecursiveVertices()`,
3239           `DMPlexGetDepth()`, `IS`, `PetscSection`
3240 @*/
3241 PetscErrorCode DMPlexRestoreConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
3242 {
3243   PetscInt d, depth_;
3244 
3245   PetscFunctionBegin;
3246   PetscCall(DMPlexGetDepth(dm, &depth_));
3247   PetscCheck(!depth || *depth == depth_, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "depth changed since last call to DMPlexGetConeRecursive");
3248   if (depth) *depth = 0;
3249   if (expandedPoints) {
3250     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&((*expandedPoints)[d])));
3251     PetscCall(PetscFree(*expandedPoints));
3252   }
3253   if (sections) {
3254     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&((*sections)[d])));
3255     PetscCall(PetscFree(*sections));
3256   }
3257   PetscFunctionReturn(PETSC_SUCCESS);
3258 }
3259 
3260 /*@
3261   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
3262 
3263   Not Collective
3264 
3265   Input Parameters:
3266 + dm   - The `DMPLEX`
3267 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3268 - cone - An array of points which are on the in-edges for point `p`
3269 
3270   Level: beginner
3271 
3272   Note:
3273   This should be called after all calls to `DMPlexSetConeSize()` and `DMSetUp()`.
3274 
3275 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`, `DMPlexSetSupport()`, `DMPlexSetSupportSize()`
3276 @*/
3277 PetscErrorCode DMPlexSetCone(DM dm, PetscInt p, const PetscInt cone[])
3278 {
3279   DM_Plex *mesh = (DM_Plex *)dm->data;
3280   PetscInt dof, off, c;
3281 
3282   PetscFunctionBegin;
3283   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3284   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3285   if (dof) PetscAssertPointer(cone, 3);
3286   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3287   if (PetscDefined(USE_DEBUG)) {
3288     PetscInt pStart, pEnd;
3289     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3290     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);
3291     for (c = 0; c < dof; ++c) {
3292       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);
3293       mesh->cones[off + c] = cone[c];
3294     }
3295   } else {
3296     for (c = 0; c < dof; ++c) mesh->cones[off + c] = cone[c];
3297   }
3298   PetscFunctionReturn(PETSC_SUCCESS);
3299 }
3300 
3301 /*@C
3302   DMPlexGetConeOrientation - Return the orientations on the in-edges for this point in the DAG
3303 
3304   Not Collective
3305 
3306   Input Parameters:
3307 + dm - The `DMPLEX`
3308 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3309 
3310   Output Parameter:
3311 . coneOrientation - An array of orientations which are on the in-edges for point `p`. An orientation is an
3312                     integer giving the prescription for cone traversal.
3313 
3314   Level: beginner
3315 
3316   Note:
3317   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3318   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3319   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3320   with the identity.
3321 
3322   Fortran Notes:
3323   You must also call `DMPlexRestoreConeOrientation()` after you finish using the returned array.
3324   `DMPlexRestoreConeOrientation()` is not needed/available in C.
3325 
3326 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPolytopeTypeComposeOrientation()`, `DMPolytopeTypeComposeOrientationInv()`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetCone()`, `DMPlexSetChart()`
3327 @*/
3328 PetscErrorCode DMPlexGetConeOrientation(DM dm, PetscInt p, const PetscInt *coneOrientation[])
3329 {
3330   DM_Plex *mesh = (DM_Plex *)dm->data;
3331   PetscInt off;
3332 
3333   PetscFunctionBegin;
3334   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3335   if (PetscDefined(USE_DEBUG)) {
3336     PetscInt dof;
3337     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3338     if (dof) PetscAssertPointer(coneOrientation, 3);
3339   }
3340   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3341 
3342   *coneOrientation = &mesh->coneOrientations[off];
3343   PetscFunctionReturn(PETSC_SUCCESS);
3344 }
3345 
3346 /*@
3347   DMPlexSetConeOrientation - Set the orientations on the in-edges for this point in the DAG
3348 
3349   Not Collective
3350 
3351   Input Parameters:
3352 + dm              - The `DMPLEX`
3353 . p               - The point, which must lie in the chart set with `DMPlexSetChart()`
3354 - coneOrientation - An array of orientations
3355 
3356   Level: beginner
3357 
3358   Notes:
3359   This should be called after all calls to `DMPlexSetConeSize()` and `DMSetUp()`.
3360 
3361   The meaning of coneOrientation is detailed in `DMPlexGetConeOrientation()`.
3362 
3363 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetConeOrientation()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3364 @*/
3365 PetscErrorCode DMPlexSetConeOrientation(DM dm, PetscInt p, const PetscInt coneOrientation[])
3366 {
3367   DM_Plex *mesh = (DM_Plex *)dm->data;
3368   PetscInt pStart, pEnd;
3369   PetscInt dof, off, c;
3370 
3371   PetscFunctionBegin;
3372   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3373   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3374   if (dof) PetscAssertPointer(coneOrientation, 3);
3375   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3376   if (PetscDefined(USE_DEBUG)) {
3377     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3378     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);
3379     for (c = 0; c < dof; ++c) {
3380       PetscInt cdof, o = coneOrientation[c];
3381 
3382       PetscCall(PetscSectionGetDof(mesh->coneSection, mesh->cones[off + c], &cdof));
3383       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);
3384       mesh->coneOrientations[off + c] = o;
3385     }
3386   } else {
3387     for (c = 0; c < dof; ++c) mesh->coneOrientations[off + c] = coneOrientation[c];
3388   }
3389   PetscFunctionReturn(PETSC_SUCCESS);
3390 }
3391 
3392 /*@
3393   DMPlexInsertCone - Insert a point into the in-edges for the point p in the DAG
3394 
3395   Not Collective
3396 
3397   Input Parameters:
3398 + dm        - The `DMPLEX`
3399 . p         - The point, which must lie in the chart set with `DMPlexSetChart()`
3400 . conePos   - The local index in the cone where the point should be put
3401 - conePoint - The mesh point to insert
3402 
3403   Level: beginner
3404 
3405 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3406 @*/
3407 PetscErrorCode DMPlexInsertCone(DM dm, PetscInt p, PetscInt conePos, PetscInt conePoint)
3408 {
3409   DM_Plex *mesh = (DM_Plex *)dm->data;
3410   PetscInt pStart, pEnd;
3411   PetscInt dof, off;
3412 
3413   PetscFunctionBegin;
3414   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3415   if (PetscDefined(USE_DEBUG)) {
3416     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3417     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);
3418     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);
3419     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3420     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);
3421   }
3422   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3423   mesh->cones[off + conePos] = conePoint;
3424   PetscFunctionReturn(PETSC_SUCCESS);
3425 }
3426 
3427 /*@
3428   DMPlexInsertConeOrientation - Insert a point orientation for the in-edge for the point p in the DAG
3429 
3430   Not Collective
3431 
3432   Input Parameters:
3433 + dm              - The `DMPLEX`
3434 . p               - The point, which must lie in the chart set with `DMPlexSetChart()`
3435 . conePos         - The local index in the cone where the point should be put
3436 - coneOrientation - The point orientation to insert
3437 
3438   Level: beginner
3439 
3440   Note:
3441   The meaning of coneOrientation values is detailed in `DMPlexGetConeOrientation()`.
3442 
3443 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3444 @*/
3445 PetscErrorCode DMPlexInsertConeOrientation(DM dm, PetscInt p, PetscInt conePos, PetscInt coneOrientation)
3446 {
3447   DM_Plex *mesh = (DM_Plex *)dm->data;
3448   PetscInt pStart, pEnd;
3449   PetscInt dof, off;
3450 
3451   PetscFunctionBegin;
3452   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3453   if (PetscDefined(USE_DEBUG)) {
3454     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3455     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);
3456     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3457     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);
3458   }
3459   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3460   mesh->coneOrientations[off + conePos] = coneOrientation;
3461   PetscFunctionReturn(PETSC_SUCCESS);
3462 }
3463 
3464 /*@C
3465   DMPlexGetOrientedCone - Return the points and orientations on the in-edges for this point in the DAG
3466 
3467   Not collective
3468 
3469   Input Parameters:
3470 + dm - The DMPlex
3471 - p  - The point, which must lie in the chart set with DMPlexSetChart()
3472 
3473   Output Parameters:
3474 + cone - An array of points which are on the in-edges for point `p`
3475 - ornt - An array of orientations which are on the in-edges for point `p`. An orientation is an
3476         integer giving the prescription for cone traversal.
3477 
3478   Level: beginner
3479 
3480   Notes:
3481   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3482   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3483   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3484   with the identity.
3485 
3486   Fortran Notes:
3487   You must also call `DMPlexRestoreCone()` after you finish using the returned array.
3488   `DMPlexRestoreCone()` is not needed/available in C.
3489 
3490 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreOrientedCone()`, `DMPlexGetConeSize()`, `DMPlexGetCone()`, `DMPlexGetChart()`
3491 @*/
3492 PetscErrorCode DMPlexGetOrientedCone(DM dm, PetscInt p, const PetscInt *cone[], const PetscInt *ornt[])
3493 {
3494   DM_Plex *mesh = (DM_Plex *)dm->data;
3495 
3496   PetscFunctionBegin;
3497   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3498   if (mesh->tr) {
3499     PetscCall(DMPlexTransformGetCone(mesh->tr, p, cone, ornt));
3500   } else {
3501     PetscInt off;
3502     if (PetscDefined(USE_DEBUG)) {
3503       PetscInt dof;
3504       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3505       if (dof) {
3506         if (cone) PetscAssertPointer(cone, 3);
3507         if (ornt) PetscAssertPointer(ornt, 4);
3508       }
3509     }
3510     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3511     if (cone) *cone = PetscSafePointerPlusOffset(mesh->cones, off);
3512     if (ornt) *ornt = PetscSafePointerPlusOffset(mesh->coneOrientations, off);
3513   }
3514   PetscFunctionReturn(PETSC_SUCCESS);
3515 }
3516 
3517 /*@C
3518   DMPlexRestoreOrientedCone - Restore the points and orientations on the in-edges for this point in the DAG
3519 
3520   Not Collective
3521 
3522   Input Parameters:
3523 + dm   - The DMPlex
3524 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3525 . cone - An array of points which are on the in-edges for point p
3526 - ornt - An array of orientations which are on the in-edges for point `p`. An orientation is an
3527         integer giving the prescription for cone traversal.
3528 
3529   Level: beginner
3530 
3531   Notes:
3532   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3533   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3534   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3535   with the identity.
3536 
3537   Fortran Notes:
3538   You must also call `DMPlexRestoreCone()` after you finish using the returned array.
3539   `DMPlexRestoreCone()` is not needed/available in C.
3540 
3541 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetOrientedCone()`, `DMPlexGetConeSize()`, `DMPlexGetCone()`, `DMPlexGetChart()`
3542 @*/
3543 PetscErrorCode DMPlexRestoreOrientedCone(DM dm, PetscInt p, const PetscInt *cone[], const PetscInt *ornt[])
3544 {
3545   DM_Plex *mesh = (DM_Plex *)dm->data;
3546 
3547   PetscFunctionBegin;
3548   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3549   if (mesh->tr) PetscCall(DMPlexTransformRestoreCone(mesh->tr, p, cone, ornt));
3550   PetscFunctionReturn(PETSC_SUCCESS);
3551 }
3552 
3553 /*@
3554   DMPlexGetSupportSize - Return the number of out-edges for this point in the DAG
3555 
3556   Not Collective
3557 
3558   Input Parameters:
3559 + dm - The `DMPLEX`
3560 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3561 
3562   Output Parameter:
3563 . size - The support size for point `p`
3564 
3565   Level: beginner
3566 
3567 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`, `DMPlexGetConeSize()`
3568 @*/
3569 PetscErrorCode DMPlexGetSupportSize(DM dm, PetscInt p, PetscInt *size)
3570 {
3571   DM_Plex *mesh = (DM_Plex *)dm->data;
3572 
3573   PetscFunctionBegin;
3574   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3575   PetscAssertPointer(size, 3);
3576   PetscCall(PetscSectionGetDof(mesh->supportSection, p, size));
3577   PetscFunctionReturn(PETSC_SUCCESS);
3578 }
3579 
3580 /*@
3581   DMPlexSetSupportSize - Set the number of out-edges for this point in the DAG
3582 
3583   Not Collective
3584 
3585   Input Parameters:
3586 + dm   - The `DMPLEX`
3587 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3588 - size - The support size for point `p`
3589 
3590   Level: beginner
3591 
3592   Note:
3593   This should be called after `DMPlexSetChart()`.
3594 
3595 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetSupportSize()`, `DMPlexSetChart()`
3596 @*/
3597 PetscErrorCode DMPlexSetSupportSize(DM dm, PetscInt p, PetscInt size)
3598 {
3599   DM_Plex *mesh = (DM_Plex *)dm->data;
3600 
3601   PetscFunctionBegin;
3602   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3603   PetscCall(PetscSectionSetDof(mesh->supportSection, p, size));
3604   PetscFunctionReturn(PETSC_SUCCESS);
3605 }
3606 
3607 /*@C
3608   DMPlexGetSupport - Return the points on the out-edges for this point in the DAG
3609 
3610   Not Collective
3611 
3612   Input Parameters:
3613 + dm - The `DMPLEX`
3614 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3615 
3616   Output Parameter:
3617 . support - An array of points which are on the out-edges for point `p`
3618 
3619   Level: beginner
3620 
3621   Fortran Notes:
3622   You must also call `DMPlexRestoreSupport()` after you finish using the returned array.
3623   `DMPlexRestoreSupport()` is not needed/available in C.
3624 
3625 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSupportSize()`, `DMPlexSetSupport()`, `DMPlexGetCone()`, `DMPlexSetChart()`
3626 @*/
3627 PetscErrorCode DMPlexGetSupport(DM dm, PetscInt p, const PetscInt *support[])
3628 {
3629   DM_Plex *mesh = (DM_Plex *)dm->data;
3630   PetscInt off;
3631 
3632   PetscFunctionBegin;
3633   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3634   PetscAssertPointer(support, 3);
3635   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3636   *support = PetscSafePointerPlusOffset(mesh->supports, off);
3637   PetscFunctionReturn(PETSC_SUCCESS);
3638 }
3639 
3640 /*@
3641   DMPlexSetSupport - Set the points on the out-edges for this point in the DAG, that is the list of points that this point covers
3642 
3643   Not Collective
3644 
3645   Input Parameters:
3646 + dm      - The `DMPLEX`
3647 . p       - The point, which must lie in the chart set with `DMPlexSetChart()`
3648 - support - An array of points which are on the out-edges for point `p`
3649 
3650   Level: beginner
3651 
3652   Note:
3653   This should be called after all calls to `DMPlexSetSupportSize()` and `DMSetUp()`.
3654 
3655 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetCone()`, `DMPlexSetConeSize()`, `DMPlexCreate()`, `DMPlexGetSupport()`, `DMPlexSetChart()`, `DMPlexSetSupportSize()`, `DMSetUp()`
3656 @*/
3657 PetscErrorCode DMPlexSetSupport(DM dm, PetscInt p, const PetscInt support[])
3658 {
3659   DM_Plex *mesh = (DM_Plex *)dm->data;
3660   PetscInt pStart, pEnd;
3661   PetscInt dof, off, c;
3662 
3663   PetscFunctionBegin;
3664   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3665   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3666   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3667   if (dof) PetscAssertPointer(support, 3);
3668   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3669   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);
3670   for (c = 0; c < dof; ++c) {
3671     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);
3672     mesh->supports[off + c] = support[c];
3673   }
3674   PetscFunctionReturn(PETSC_SUCCESS);
3675 }
3676 
3677 /*@
3678   DMPlexInsertSupport - Insert a point into the out-edges for the point p in the DAG
3679 
3680   Not Collective
3681 
3682   Input Parameters:
3683 + dm           - The `DMPLEX`
3684 . p            - The point, which must lie in the chart set with `DMPlexSetChart()`
3685 . supportPos   - The local index in the cone where the point should be put
3686 - supportPoint - The mesh point to insert
3687 
3688   Level: beginner
3689 
3690 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3691 @*/
3692 PetscErrorCode DMPlexInsertSupport(DM dm, PetscInt p, PetscInt supportPos, PetscInt supportPoint)
3693 {
3694   DM_Plex *mesh = (DM_Plex *)dm->data;
3695   PetscInt pStart, pEnd;
3696   PetscInt dof, off;
3697 
3698   PetscFunctionBegin;
3699   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3700   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3701   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3702   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3703   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);
3704   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);
3705   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);
3706   mesh->supports[off + supportPos] = supportPoint;
3707   PetscFunctionReturn(PETSC_SUCCESS);
3708 }
3709 
3710 /* Converts an orientation o in the current numbering to the previous scheme used in Plex */
3711 PetscInt DMPolytopeConvertNewOrientation_Internal(DMPolytopeType ct, PetscInt o)
3712 {
3713   switch (ct) {
3714   case DM_POLYTOPE_SEGMENT:
3715     if (o == -1) return -2;
3716     break;
3717   case DM_POLYTOPE_TRIANGLE:
3718     if (o == -3) return -1;
3719     if (o == -2) return -3;
3720     if (o == -1) return -2;
3721     break;
3722   case DM_POLYTOPE_QUADRILATERAL:
3723     if (o == -4) return -2;
3724     if (o == -3) return -1;
3725     if (o == -2) return -4;
3726     if (o == -1) return -3;
3727     break;
3728   default:
3729     return o;
3730   }
3731   return o;
3732 }
3733 
3734 /* Converts an orientation o in the previous scheme used in Plex to the current numbering */
3735 PetscInt DMPolytopeConvertOldOrientation_Internal(DMPolytopeType ct, PetscInt o)
3736 {
3737   switch (ct) {
3738   case DM_POLYTOPE_SEGMENT:
3739     if ((o == -2) || (o == 1)) return -1;
3740     if (o == -1) return 0;
3741     break;
3742   case DM_POLYTOPE_TRIANGLE:
3743     if (o == -3) return -2;
3744     if (o == -2) return -1;
3745     if (o == -1) return -3;
3746     break;
3747   case DM_POLYTOPE_QUADRILATERAL:
3748     if (o == -4) return -2;
3749     if (o == -3) return -1;
3750     if (o == -2) return -4;
3751     if (o == -1) return -3;
3752     break;
3753   default:
3754     return o;
3755   }
3756   return o;
3757 }
3758 
3759 /* Takes in a mesh whose orientations are in the previous scheme and converts them all to the current numbering */
3760 PetscErrorCode DMPlexConvertOldOrientations_Internal(DM dm)
3761 {
3762   PetscInt pStart, pEnd, p;
3763 
3764   PetscFunctionBegin;
3765   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3766   for (p = pStart; p < pEnd; ++p) {
3767     const PetscInt *cone, *ornt;
3768     PetscInt        coneSize, c;
3769 
3770     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
3771     PetscCall(DMPlexGetCone(dm, p, &cone));
3772     PetscCall(DMPlexGetConeOrientation(dm, p, &ornt));
3773     for (c = 0; c < coneSize; ++c) {
3774       DMPolytopeType ct;
3775       const PetscInt o = ornt[c];
3776 
3777       PetscCall(DMPlexGetCellType(dm, cone[c], &ct));
3778       switch (ct) {
3779       case DM_POLYTOPE_SEGMENT:
3780         if ((o == -2) || (o == 1)) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3781         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, 0));
3782         break;
3783       case DM_POLYTOPE_TRIANGLE:
3784         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3785         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3786         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3787         break;
3788       case DM_POLYTOPE_QUADRILATERAL:
3789         if (o == -4) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3790         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3791         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -4));
3792         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3793         break;
3794       default:
3795         break;
3796       }
3797     }
3798   }
3799   PetscFunctionReturn(PETSC_SUCCESS);
3800 }
3801 
3802 static inline PetscErrorCode DMPlexGetTransitiveClosure_Hot_Private(DM dm, PetscInt p, PetscBool useCone, PetscInt *size, const PetscInt *arr[], const PetscInt *ornt[])
3803 {
3804   DM_Plex *mesh = (DM_Plex *)dm->data;
3805 
3806   PetscFunctionBeginHot;
3807   if (PetscDefined(USE_DEBUG) || mesh->tr) {
3808     if (useCone) {
3809       PetscCall(DMPlexGetConeSize(dm, p, size));
3810       PetscCall(DMPlexGetOrientedCone(dm, p, arr, ornt));
3811     } else {
3812       PetscCall(DMPlexGetSupportSize(dm, p, size));
3813       PetscCall(DMPlexGetSupport(dm, p, arr));
3814     }
3815   } else {
3816     if (useCone) {
3817       const PetscSection s   = mesh->coneSection;
3818       const PetscInt     ps  = p - s->pStart;
3819       const PetscInt     off = s->atlasOff[ps];
3820 
3821       *size = s->atlasDof[ps];
3822       *arr  = mesh->cones + off;
3823       *ornt = mesh->coneOrientations + off;
3824     } else {
3825       const PetscSection s   = mesh->supportSection;
3826       const PetscInt     ps  = p - s->pStart;
3827       const PetscInt     off = s->atlasOff[ps];
3828 
3829       *size = s->atlasDof[ps];
3830       *arr  = mesh->supports + off;
3831     }
3832   }
3833   PetscFunctionReturn(PETSC_SUCCESS);
3834 }
3835 
3836 static inline PetscErrorCode DMPlexRestoreTransitiveClosure_Hot_Private(DM dm, PetscInt p, PetscBool useCone, PetscInt *size, const PetscInt *arr[], const PetscInt *ornt[])
3837 {
3838   DM_Plex *mesh = (DM_Plex *)dm->data;
3839 
3840   PetscFunctionBeginHot;
3841   if (PetscDefined(USE_DEBUG) || mesh->tr) {
3842     if (useCone) PetscCall(DMPlexRestoreOrientedCone(dm, p, arr, ornt));
3843   }
3844   PetscFunctionReturn(PETSC_SUCCESS);
3845 }
3846 
3847 static PetscErrorCode DMPlexGetTransitiveClosure_Depth1_Private(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3848 {
3849   DMPolytopeType  ct = DM_POLYTOPE_UNKNOWN;
3850   PetscInt       *closure;
3851   const PetscInt *tmp = NULL, *tmpO = NULL;
3852   PetscInt        off = 0, tmpSize, t;
3853 
3854   PetscFunctionBeginHot;
3855   if (ornt) {
3856     PetscCall(DMPlexGetCellType(dm, p, &ct));
3857     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;
3858   }
3859   if (*points) {
3860     closure = *points;
3861   } else {
3862     PetscInt maxConeSize, maxSupportSize;
3863     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3864     PetscCall(DMGetWorkArray(dm, 2 * (PetscMax(maxConeSize, maxSupportSize) + 1), MPIU_INT, &closure));
3865   }
3866   PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, p, useCone, &tmpSize, &tmp, &tmpO));
3867   if (ct == DM_POLYTOPE_UNKNOWN) {
3868     closure[off++] = p;
3869     closure[off++] = 0;
3870     for (t = 0; t < tmpSize; ++t) {
3871       closure[off++] = tmp[t];
3872       closure[off++] = tmpO ? tmpO[t] : 0;
3873     }
3874   } else {
3875     const PetscInt *arr = DMPolytopeTypeGetArrangement(ct, ornt);
3876 
3877     /* We assume that cells with a valid type have faces with a valid type */
3878     closure[off++] = p;
3879     closure[off++] = ornt;
3880     for (t = 0; t < tmpSize; ++t) {
3881       DMPolytopeType ft;
3882 
3883       PetscCall(DMPlexGetCellType(dm, tmp[t], &ft));
3884       closure[off++] = tmp[arr[t]];
3885       closure[off++] = tmpO ? DMPolytopeTypeComposeOrientation(ft, ornt, tmpO[t]) : 0;
3886     }
3887   }
3888   PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, p, useCone, &tmpSize, &tmp, &tmpO));
3889   if (numPoints) *numPoints = tmpSize + 1;
3890   if (points) *points = closure;
3891   PetscFunctionReturn(PETSC_SUCCESS);
3892 }
3893 
3894 /* We need a special tensor version because we want to allow duplicate points in the endcaps for hybrid cells */
3895 static PetscErrorCode DMPlexTransitiveClosure_Tensor_Internal(DM dm, PetscInt point, DMPolytopeType ct, PetscInt o, PetscBool useCone, PetscInt *numPoints, PetscInt **points)
3896 {
3897   const PetscInt *arr = DMPolytopeTypeGetArrangement(ct, o);
3898   const PetscInt *cone, *ornt;
3899   PetscInt       *pts, *closure = NULL;
3900   DMPolytopeType  ft;
3901   PetscInt        maxConeSize, maxSupportSize, coneSeries, supportSeries, maxSize;
3902   PetscInt        dim, coneSize, c, d, clSize, cl;
3903 
3904   PetscFunctionBeginHot;
3905   PetscCall(DMGetDimension(dm, &dim));
3906   PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, point, PETSC_TRUE, &coneSize, &cone, &ornt));
3907   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3908   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, dim + 1) - 1) / (maxConeSize - 1)) : dim + 1;
3909   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, dim + 1) - 1) / (maxSupportSize - 1)) : dim + 1;
3910   maxSize       = PetscMax(coneSeries, supportSeries);
3911   if (*points) {
3912     pts = *points;
3913   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &pts));
3914   c        = 0;
3915   pts[c++] = point;
3916   pts[c++] = o;
3917   PetscCall(DMPlexGetCellType(dm, cone[arr[0 * 2 + 0]], &ft));
3918   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[0 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[0 * 2 + 1], ornt[0]), useCone, &clSize, &closure));
3919   for (cl = 0; cl < clSize * 2; cl += 2) {
3920     pts[c++] = closure[cl];
3921     pts[c++] = closure[cl + 1];
3922   }
3923   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[1 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[1 * 2 + 1], ornt[1]), useCone, &clSize, &closure));
3924   for (cl = 0; cl < clSize * 2; cl += 2) {
3925     pts[c++] = closure[cl];
3926     pts[c++] = closure[cl + 1];
3927   }
3928   PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[0], useCone, &clSize, &closure));
3929   for (d = 2; d < coneSize; ++d) {
3930     PetscCall(DMPlexGetCellType(dm, cone[arr[d * 2 + 0]], &ft));
3931     pts[c++] = cone[arr[d * 2 + 0]];
3932     pts[c++] = DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]);
3933   }
3934   PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, point, PETSC_TRUE, &coneSize, &cone, &ornt));
3935   if (dim >= 3) {
3936     for (d = 2; d < coneSize; ++d) {
3937       const PetscInt  fpoint = cone[arr[d * 2 + 0]];
3938       const PetscInt *fcone, *fornt;
3939       PetscInt        fconeSize, fc, i;
3940 
3941       PetscCall(DMPlexGetCellType(dm, fpoint, &ft));
3942       const PetscInt *farr = DMPolytopeTypeGetArrangement(ft, DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]));
3943       PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, fpoint, PETSC_TRUE, &fconeSize, &fcone, &fornt));
3944       for (fc = 0; fc < fconeSize; ++fc) {
3945         const PetscInt cp = fcone[farr[fc * 2 + 0]];
3946         const PetscInt co = farr[fc * 2 + 1];
3947 
3948         for (i = 0; i < c; i += 2)
3949           if (pts[i] == cp) break;
3950         if (i == c) {
3951           PetscCall(DMPlexGetCellType(dm, cp, &ft));
3952           pts[c++] = cp;
3953           pts[c++] = DMPolytopeTypeComposeOrientation(ft, co, fornt[farr[fc * 2 + 0]]);
3954         }
3955       }
3956       PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, fpoint, PETSC_TRUE, &fconeSize, &fcone, &fornt));
3957     }
3958   }
3959   *numPoints = c / 2;
3960   *points    = pts;
3961   PetscFunctionReturn(PETSC_SUCCESS);
3962 }
3963 
3964 PetscErrorCode DMPlexGetTransitiveClosure_Internal(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3965 {
3966   DMPolytopeType ct;
3967   PetscInt      *closure, *fifo;
3968   PetscInt       closureSize = 0, fifoStart = 0, fifoSize = 0;
3969   PetscInt       maxConeSize, maxSupportSize, coneSeries, supportSeries;
3970   PetscInt       depth, maxSize;
3971 
3972   PetscFunctionBeginHot;
3973   PetscCall(DMPlexGetDepth(dm, &depth));
3974   if (depth == 1) {
3975     PetscCall(DMPlexGetTransitiveClosure_Depth1_Private(dm, p, ornt, useCone, numPoints, points));
3976     PetscFunctionReturn(PETSC_SUCCESS);
3977   }
3978   PetscCall(DMPlexGetCellType(dm, p, &ct));
3979   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;
3980   if (DMPolytopeTypeIsHybrid(ct) && ct != DM_POLYTOPE_POINT_PRISM_TENSOR) {
3981     PetscCall(DMPlexTransitiveClosure_Tensor_Internal(dm, p, ct, ornt, useCone, numPoints, points));
3982     PetscFunctionReturn(PETSC_SUCCESS);
3983   }
3984   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3985   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, depth + 1) - 1) / (maxConeSize - 1)) : depth + 1;
3986   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, depth + 1) - 1) / (maxSupportSize - 1)) : depth + 1;
3987   maxSize       = PetscMax(coneSeries, supportSeries);
3988   PetscCall(DMGetWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
3989   if (*points) {
3990     closure = *points;
3991   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &closure));
3992   closure[closureSize++] = p;
3993   closure[closureSize++] = ornt;
3994   fifo[fifoSize++]       = p;
3995   fifo[fifoSize++]       = ornt;
3996   fifo[fifoSize++]       = ct;
3997   /* Should kick out early when depth is reached, rather than checking all vertices for empty cones */
3998   while (fifoSize - fifoStart) {
3999     const PetscInt       q    = fifo[fifoStart++];
4000     const PetscInt       o    = fifo[fifoStart++];
4001     const DMPolytopeType qt   = (DMPolytopeType)fifo[fifoStart++];
4002     const PetscInt      *qarr = DMPolytopeTypeGetArrangement(qt, o);
4003     const PetscInt      *tmp, *tmpO = NULL;
4004     PetscInt             tmpSize, t;
4005 
4006     if (PetscDefined(USE_DEBUG)) {
4007       PetscInt nO = DMPolytopeTypeGetNumArrangements(qt) / 2;
4008       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);
4009     }
4010     PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, q, useCone, &tmpSize, &tmp, &tmpO));
4011     for (t = 0; t < tmpSize; ++t) {
4012       const PetscInt ip = useCone && qarr ? qarr[t * 2] : t;
4013       const PetscInt io = useCone && qarr ? qarr[t * 2 + 1] : 0;
4014       const PetscInt cp = tmp[ip];
4015       PetscCall(DMPlexGetCellType(dm, cp, &ct));
4016       const PetscInt co = tmpO ? DMPolytopeTypeComposeOrientation(ct, io, tmpO[ip]) : 0;
4017       PetscInt       c;
4018 
4019       /* Check for duplicate */
4020       for (c = 0; c < closureSize; c += 2) {
4021         if (closure[c] == cp) break;
4022       }
4023       if (c == closureSize) {
4024         closure[closureSize++] = cp;
4025         closure[closureSize++] = co;
4026         fifo[fifoSize++]       = cp;
4027         fifo[fifoSize++]       = co;
4028         fifo[fifoSize++]       = ct;
4029       }
4030     }
4031     PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, q, useCone, &tmpSize, &tmp, &tmpO));
4032   }
4033   PetscCall(DMRestoreWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
4034   if (numPoints) *numPoints = closureSize / 2;
4035   if (points) *points = closure;
4036   PetscFunctionReturn(PETSC_SUCCESS);
4037 }
4038 
4039 /*@C
4040   DMPlexGetTransitiveClosure - Return the points on the transitive closure of the in-edges or out-edges for this point in the DAG
4041 
4042   Not Collective
4043 
4044   Input Parameters:
4045 + dm      - The `DMPLEX`
4046 . p       - The mesh point
4047 - useCone - `PETSC_TRUE` for the closure, otherwise return the star
4048 
4049   Input/Output Parameter:
4050 . points - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...];
4051            if `NULL` on input, internal storage will be returned, otherwise the provided array is used
4052 
4053   Output Parameter:
4054 . numPoints - The number of points in the closure, so points[] is of size 2*`numPoints`
4055 
4056   Level: beginner
4057 
4058   Note:
4059   If using internal storage (points is `NULL` on input), each call overwrites the last output.
4060 
4061   Fortran Notes:
4062   The `numPoints` argument is not present in the Fortran binding since it is internal to the array.
4063 
4064 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
4065 @*/
4066 PetscErrorCode DMPlexGetTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
4067 {
4068   PetscFunctionBeginHot;
4069   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4070   if (numPoints) PetscAssertPointer(numPoints, 4);
4071   if (points) PetscAssertPointer(points, 5);
4072   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, p, 0, useCone, numPoints, points));
4073   PetscFunctionReturn(PETSC_SUCCESS);
4074 }
4075 
4076 /*@C
4077   DMPlexRestoreTransitiveClosure - Restore the array of points on the transitive closure of the in-edges or out-edges for this point in the DAG
4078 
4079   Not Collective
4080 
4081   Input Parameters:
4082 + dm        - The `DMPLEX`
4083 . p         - The mesh point
4084 . useCone   - `PETSC_TRUE` for the closure, otherwise return the star
4085 . numPoints - The number of points in the closure, so points[] is of size 2*`numPoints`
4086 - points    - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...]
4087 
4088   Level: beginner
4089 
4090   Note:
4091   If not using internal storage (points is not `NULL` on input), this call is unnecessary
4092 
4093 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
4094 @*/
4095 PetscErrorCode DMPlexRestoreTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
4096 {
4097   PetscFunctionBeginHot;
4098   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4099   if (numPoints) *numPoints = 0;
4100   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, points));
4101   PetscFunctionReturn(PETSC_SUCCESS);
4102 }
4103 
4104 /*@
4105   DMPlexGetMaxSizes - Return the maximum number of in-edges (cone) and out-edges (support) for any point in the DAG
4106 
4107   Not Collective
4108 
4109   Input Parameter:
4110 . dm - The `DMPLEX`
4111 
4112   Output Parameters:
4113 + maxConeSize    - The maximum number of in-edges
4114 - maxSupportSize - The maximum number of out-edges
4115 
4116   Level: beginner
4117 
4118 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
4119 @*/
4120 PetscErrorCode DMPlexGetMaxSizes(DM dm, PetscInt *maxConeSize, PetscInt *maxSupportSize)
4121 {
4122   DM_Plex *mesh = (DM_Plex *)dm->data;
4123 
4124   PetscFunctionBegin;
4125   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4126   if (maxConeSize) PetscCall(PetscSectionGetMaxDof(mesh->coneSection, maxConeSize));
4127   if (maxSupportSize) PetscCall(PetscSectionGetMaxDof(mesh->supportSection, maxSupportSize));
4128   PetscFunctionReturn(PETSC_SUCCESS);
4129 }
4130 
4131 PetscErrorCode DMSetUp_Plex(DM dm)
4132 {
4133   DM_Plex *mesh = (DM_Plex *)dm->data;
4134   PetscInt size, maxSupportSize;
4135 
4136   PetscFunctionBegin;
4137   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4138   PetscCall(PetscSectionSetUp(mesh->coneSection));
4139   PetscCall(PetscSectionGetStorageSize(mesh->coneSection, &size));
4140   PetscCall(PetscMalloc1(size, &mesh->cones));
4141   PetscCall(PetscCalloc1(size, &mesh->coneOrientations));
4142   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
4143   if (maxSupportSize) {
4144     PetscCall(PetscSectionSetUp(mesh->supportSection));
4145     PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &size));
4146     PetscCall(PetscMalloc1(size, &mesh->supports));
4147   }
4148   PetscFunctionReturn(PETSC_SUCCESS);
4149 }
4150 
4151 PetscErrorCode DMCreateSubDM_Plex(DM dm, PetscInt numFields, const PetscInt fields[], IS *is, DM *subdm)
4152 {
4153   PetscFunctionBegin;
4154   if (subdm) PetscCall(DMClone(dm, subdm));
4155   PetscCall(DMCreateSectionSubDM(dm, numFields, fields, is, subdm));
4156   if (subdm) (*subdm)->useNatural = dm->useNatural;
4157   if (dm->useNatural && dm->sfMigration) {
4158     PetscSF sfNatural;
4159 
4160     (*subdm)->sfMigration = dm->sfMigration;
4161     PetscCall(PetscObjectReference((PetscObject)dm->sfMigration));
4162     PetscCall(DMPlexCreateGlobalToNaturalSF(*subdm, NULL, (*subdm)->sfMigration, &sfNatural));
4163     (*subdm)->sfNatural = sfNatural;
4164   }
4165   PetscFunctionReturn(PETSC_SUCCESS);
4166 }
4167 
4168 PetscErrorCode DMCreateSuperDM_Plex(DM dms[], PetscInt len, IS **is, DM *superdm)
4169 {
4170   PetscInt i = 0;
4171 
4172   PetscFunctionBegin;
4173   PetscCall(DMClone(dms[0], superdm));
4174   PetscCall(DMCreateSectionSuperDM(dms, len, is, superdm));
4175   (*superdm)->useNatural = PETSC_FALSE;
4176   for (i = 0; i < len; i++) {
4177     if (dms[i]->useNatural && dms[i]->sfMigration) {
4178       PetscSF sfNatural;
4179 
4180       (*superdm)->sfMigration = dms[i]->sfMigration;
4181       PetscCall(PetscObjectReference((PetscObject)dms[i]->sfMigration));
4182       (*superdm)->useNatural = PETSC_TRUE;
4183       PetscCall(DMPlexCreateGlobalToNaturalSF(*superdm, NULL, (*superdm)->sfMigration, &sfNatural));
4184       (*superdm)->sfNatural = sfNatural;
4185       break;
4186     }
4187   }
4188   PetscFunctionReturn(PETSC_SUCCESS);
4189 }
4190 
4191 /*@
4192   DMPlexSymmetrize - Create support (out-edge) information from cone (in-edge) information
4193 
4194   Not Collective
4195 
4196   Input Parameter:
4197 . dm - The `DMPLEX`
4198 
4199   Level: beginner
4200 
4201   Note:
4202   This should be called after all calls to `DMPlexSetCone()`
4203 
4204 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMPlexSetCone()`
4205 @*/
4206 PetscErrorCode DMPlexSymmetrize(DM dm)
4207 {
4208   DM_Plex  *mesh = (DM_Plex *)dm->data;
4209   PetscInt *offsets;
4210   PetscInt  supportSize;
4211   PetscInt  pStart, pEnd, p;
4212 
4213   PetscFunctionBegin;
4214   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4215   PetscCheck(!mesh->supports, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONGSTATE, "Supports were already setup in this DMPlex");
4216   PetscCall(PetscLogEventBegin(DMPLEX_Symmetrize, dm, 0, 0, 0));
4217   /* Calculate support sizes */
4218   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4219   for (p = pStart; p < pEnd; ++p) {
4220     PetscInt dof, off, c;
4221 
4222     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
4223     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
4224     for (c = off; c < off + dof; ++c) PetscCall(PetscSectionAddDof(mesh->supportSection, mesh->cones[c], 1));
4225   }
4226   PetscCall(PetscSectionSetUp(mesh->supportSection));
4227   /* Calculate supports */
4228   PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &supportSize));
4229   PetscCall(PetscMalloc1(supportSize, &mesh->supports));
4230   PetscCall(PetscCalloc1(pEnd - pStart, &offsets));
4231   for (p = pStart; p < pEnd; ++p) {
4232     PetscInt dof, off, c;
4233 
4234     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
4235     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
4236     for (c = off; c < off + dof; ++c) {
4237       const PetscInt q = mesh->cones[c];
4238       PetscInt       offS;
4239 
4240       PetscCall(PetscSectionGetOffset(mesh->supportSection, q, &offS));
4241 
4242       mesh->supports[offS + offsets[q]] = p;
4243       ++offsets[q];
4244     }
4245   }
4246   PetscCall(PetscFree(offsets));
4247   PetscCall(PetscLogEventEnd(DMPLEX_Symmetrize, dm, 0, 0, 0));
4248   PetscFunctionReturn(PETSC_SUCCESS);
4249 }
4250 
4251 static PetscErrorCode DMPlexCreateDepthStratum(DM dm, DMLabel label, PetscInt depth, PetscInt pStart, PetscInt pEnd)
4252 {
4253   IS stratumIS;
4254 
4255   PetscFunctionBegin;
4256   if (pStart >= pEnd) PetscFunctionReturn(PETSC_SUCCESS);
4257   if (PetscDefined(USE_DEBUG)) {
4258     PetscInt  qStart, qEnd, numLevels, level;
4259     PetscBool overlap = PETSC_FALSE;
4260     PetscCall(DMLabelGetNumValues(label, &numLevels));
4261     for (level = 0; level < numLevels; level++) {
4262       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4263       if ((pStart >= qStart && pStart < qEnd) || (pEnd > qStart && pEnd <= qEnd)) {
4264         overlap = PETSC_TRUE;
4265         break;
4266       }
4267     }
4268     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);
4269   }
4270   PetscCall(ISCreateStride(PETSC_COMM_SELF, pEnd - pStart, pStart, 1, &stratumIS));
4271   PetscCall(DMLabelSetStratumIS(label, depth, stratumIS));
4272   PetscCall(ISDestroy(&stratumIS));
4273   PetscFunctionReturn(PETSC_SUCCESS);
4274 }
4275 
4276 static PetscErrorCode DMPlexStratify_CellType_Private(DM dm, DMLabel label)
4277 {
4278   PetscInt *pMin, *pMax;
4279   PetscInt  pStart, pEnd;
4280   PetscInt  dmin = PETSC_MAX_INT, dmax = PETSC_MIN_INT;
4281 
4282   PetscFunctionBegin;
4283   {
4284     DMLabel label2;
4285 
4286     PetscCall(DMPlexGetCellTypeLabel(dm, &label2));
4287     PetscCall(PetscObjectViewFromOptions((PetscObject)label2, NULL, "-ct_view"));
4288   }
4289   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4290   for (PetscInt p = pStart; p < pEnd; ++p) {
4291     DMPolytopeType ct;
4292 
4293     PetscCall(DMPlexGetCellType(dm, p, &ct));
4294     dmin = PetscMin(DMPolytopeTypeGetDim(ct), dmin);
4295     dmax = PetscMax(DMPolytopeTypeGetDim(ct), dmax);
4296   }
4297   PetscCall(PetscMalloc2(dmax + 1, &pMin, dmax + 1, &pMax));
4298   for (PetscInt d = dmin; d <= dmax; ++d) {
4299     pMin[d] = PETSC_MAX_INT;
4300     pMax[d] = PETSC_MIN_INT;
4301   }
4302   for (PetscInt p = pStart; p < pEnd; ++p) {
4303     DMPolytopeType ct;
4304     PetscInt       d;
4305 
4306     PetscCall(DMPlexGetCellType(dm, p, &ct));
4307     d       = DMPolytopeTypeGetDim(ct);
4308     pMin[d] = PetscMin(p, pMin[d]);
4309     pMax[d] = PetscMax(p, pMax[d]);
4310   }
4311   for (PetscInt d = dmin; d <= dmax; ++d) {
4312     if (pMin[d] > pMax[d]) continue;
4313     PetscCall(DMPlexCreateDepthStratum(dm, label, d, pMin[d], pMax[d] + 1));
4314   }
4315   PetscCall(PetscFree2(pMin, pMax));
4316   PetscFunctionReturn(PETSC_SUCCESS);
4317 }
4318 
4319 static PetscErrorCode DMPlexStratify_Topological_Private(DM dm, DMLabel label)
4320 {
4321   PetscInt pStart, pEnd;
4322   PetscInt numRoots = 0, numLeaves = 0;
4323 
4324   PetscFunctionBegin;
4325   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4326   {
4327     /* Initialize roots and count leaves */
4328     PetscInt sMin = PETSC_MAX_INT;
4329     PetscInt sMax = PETSC_MIN_INT;
4330     PetscInt coneSize, supportSize;
4331 
4332     for (PetscInt p = pStart; p < pEnd; ++p) {
4333       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4334       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
4335       if (!coneSize && supportSize) {
4336         sMin = PetscMin(p, sMin);
4337         sMax = PetscMax(p, sMax);
4338         ++numRoots;
4339       } else if (!supportSize && coneSize) {
4340         ++numLeaves;
4341       } else if (!supportSize && !coneSize) {
4342         /* Isolated points */
4343         sMin = PetscMin(p, sMin);
4344         sMax = PetscMax(p, sMax);
4345       }
4346     }
4347     PetscCall(DMPlexCreateDepthStratum(dm, label, 0, sMin, sMax + 1));
4348   }
4349 
4350   if (numRoots + numLeaves == (pEnd - pStart)) {
4351     PetscInt sMin = PETSC_MAX_INT;
4352     PetscInt sMax = PETSC_MIN_INT;
4353     PetscInt coneSize, supportSize;
4354 
4355     for (PetscInt p = pStart; p < pEnd; ++p) {
4356       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4357       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
4358       if (!supportSize && coneSize) {
4359         sMin = PetscMin(p, sMin);
4360         sMax = PetscMax(p, sMax);
4361       }
4362     }
4363     PetscCall(DMPlexCreateDepthStratum(dm, label, 1, sMin, sMax + 1));
4364   } else {
4365     PetscInt level = 0;
4366     PetscInt qStart, qEnd;
4367 
4368     PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4369     while (qEnd > qStart) {
4370       PetscInt sMin = PETSC_MAX_INT;
4371       PetscInt sMax = PETSC_MIN_INT;
4372 
4373       for (PetscInt q = qStart; q < qEnd; ++q) {
4374         const PetscInt *support;
4375         PetscInt        supportSize;
4376 
4377         PetscCall(DMPlexGetSupportSize(dm, q, &supportSize));
4378         PetscCall(DMPlexGetSupport(dm, q, &support));
4379         for (PetscInt s = 0; s < supportSize; ++s) {
4380           sMin = PetscMin(support[s], sMin);
4381           sMax = PetscMax(support[s], sMax);
4382         }
4383       }
4384       PetscCall(DMLabelGetNumValues(label, &level));
4385       PetscCall(DMPlexCreateDepthStratum(dm, label, level, sMin, sMax + 1));
4386       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4387     }
4388   }
4389   PetscFunctionReturn(PETSC_SUCCESS);
4390 }
4391 
4392 /*@
4393   DMPlexStratify - Computes the strata for all points in the `DMPLEX`
4394 
4395   Collective
4396 
4397   Input Parameter:
4398 . dm - The `DMPLEX`
4399 
4400   Level: beginner
4401 
4402   Notes:
4403   The strata group all points of the same grade, and this function calculates the strata. This
4404   grade can be seen as the height (or depth) of the point in the DAG.
4405 
4406   The DAG for most topologies is a graded poset (https://en.wikipedia.org/wiki/Graded_poset), and
4407   can be illustrated by a Hasse Diagram (https://en.wikipedia.org/wiki/Hasse_diagram).
4408   Concretely, `DMPlexStratify()` creates a new label named "depth" containing the depth in the DAG of each point. For cell-vertex
4409   meshes, vertices are depth 0 and cells are depth 1. For fully interpolated meshes, depth 0 for vertices, 1 for edges, and so on
4410   until cells have depth equal to the dimension of the mesh. The depth label can be accessed through `DMPlexGetDepthLabel()` or `DMPlexGetDepthStratum()`, or
4411   manually via `DMGetLabel()`.  The height is defined implicitly by height = maxDimension - depth, and can be accessed
4412   via `DMPlexGetHeightStratum()`.  For example, cells have height 0 and faces have height 1.
4413 
4414   The depth of a point is calculated by executing a breadth-first search (BFS) on the DAG. This could produce surprising results
4415   if run on a partially interpolated mesh, meaning one that had some edges and faces, but not others. For example, suppose that
4416   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
4417   to interpolate only that one (e0), so that
4418 .vb
4419   cone(c0) = {e0, v2}
4420   cone(e0) = {v0, v1}
4421 .ve
4422   If `DMPlexStratify()` is run on this mesh, it will give depths
4423 .vb
4424    depth 0 = {v0, v1, v2}
4425    depth 1 = {e0, c0}
4426 .ve
4427   where the triangle has been given depth 1, instead of 2, because it is reachable from vertex v2.
4428 
4429   `DMPlexStratify()` should be called after all calls to `DMPlexSymmetrize()`
4430 
4431 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexComputeCellTypes()`
4432 @*/
4433 PetscErrorCode DMPlexStratify(DM dm)
4434 {
4435   DM_Plex  *mesh = (DM_Plex *)dm->data;
4436   DMLabel   label;
4437   PetscBool flg = PETSC_FALSE;
4438 
4439   PetscFunctionBegin;
4440   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4441   PetscCall(PetscLogEventBegin(DMPLEX_Stratify, dm, 0, 0, 0));
4442 
4443   // Create depth label
4444   PetscCall(DMCreateLabel(dm, "depth"));
4445   PetscCall(DMPlexGetDepthLabel(dm, &label));
4446 
4447   PetscCall(PetscOptionsGetBool(NULL, dm->hdr.prefix, "-dm_plex_stratify_celltype", &flg, NULL));
4448   if (flg) PetscCall(DMPlexStratify_CellType_Private(dm, label));
4449   else PetscCall(DMPlexStratify_Topological_Private(dm, label));
4450 
4451   { /* just in case there is an empty process */
4452     PetscInt numValues, maxValues = 0, v;
4453 
4454     PetscCall(DMLabelGetNumValues(label, &numValues));
4455     PetscCall(MPIU_Allreduce(&numValues, &maxValues, 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
4456     for (v = numValues; v < maxValues; v++) PetscCall(DMLabelAddStratum(label, v));
4457   }
4458   PetscCall(PetscObjectStateGet((PetscObject)label, &mesh->depthState));
4459   PetscCall(PetscLogEventEnd(DMPLEX_Stratify, dm, 0, 0, 0));
4460   PetscFunctionReturn(PETSC_SUCCESS);
4461 }
4462 
4463 PetscErrorCode DMPlexComputeCellType_Internal(DM dm, PetscInt p, PetscInt pdepth, DMPolytopeType *pt)
4464 {
4465   DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4466   PetscInt       dim, depth, pheight, coneSize;
4467 
4468   PetscFunctionBeginHot;
4469   PetscCall(DMGetDimension(dm, &dim));
4470   PetscCall(DMPlexGetDepth(dm, &depth));
4471   PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4472   pheight = depth - pdepth;
4473   if (depth <= 1) {
4474     switch (pdepth) {
4475     case 0:
4476       ct = DM_POLYTOPE_POINT;
4477       break;
4478     case 1:
4479       switch (coneSize) {
4480       case 2:
4481         ct = DM_POLYTOPE_SEGMENT;
4482         break;
4483       case 3:
4484         ct = DM_POLYTOPE_TRIANGLE;
4485         break;
4486       case 4:
4487         switch (dim) {
4488         case 2:
4489           ct = DM_POLYTOPE_QUADRILATERAL;
4490           break;
4491         case 3:
4492           ct = DM_POLYTOPE_TETRAHEDRON;
4493           break;
4494         default:
4495           break;
4496         }
4497         break;
4498       case 5:
4499         ct = DM_POLYTOPE_PYRAMID;
4500         break;
4501       case 6:
4502         ct = DM_POLYTOPE_TRI_PRISM_TENSOR;
4503         break;
4504       case 8:
4505         ct = DM_POLYTOPE_HEXAHEDRON;
4506         break;
4507       default:
4508         break;
4509       }
4510     }
4511   } else {
4512     if (pdepth == 0) {
4513       ct = DM_POLYTOPE_POINT;
4514     } else if (pheight == 0) {
4515       switch (dim) {
4516       case 1:
4517         switch (coneSize) {
4518         case 2:
4519           ct = DM_POLYTOPE_SEGMENT;
4520           break;
4521         default:
4522           break;
4523         }
4524         break;
4525       case 2:
4526         switch (coneSize) {
4527         case 3:
4528           ct = DM_POLYTOPE_TRIANGLE;
4529           break;
4530         case 4:
4531           ct = DM_POLYTOPE_QUADRILATERAL;
4532           break;
4533         default:
4534           break;
4535         }
4536         break;
4537       case 3:
4538         switch (coneSize) {
4539         case 4:
4540           ct = DM_POLYTOPE_TETRAHEDRON;
4541           break;
4542         case 5: {
4543           const PetscInt *cone;
4544           PetscInt        faceConeSize;
4545 
4546           PetscCall(DMPlexGetCone(dm, p, &cone));
4547           PetscCall(DMPlexGetConeSize(dm, cone[0], &faceConeSize));
4548           switch (faceConeSize) {
4549           case 3:
4550             ct = DM_POLYTOPE_TRI_PRISM_TENSOR;
4551             break;
4552           case 4:
4553             ct = DM_POLYTOPE_PYRAMID;
4554             break;
4555           }
4556         } break;
4557         case 6:
4558           ct = DM_POLYTOPE_HEXAHEDRON;
4559           break;
4560         default:
4561           break;
4562         }
4563         break;
4564       default:
4565         break;
4566       }
4567     } else if (pheight > 0) {
4568       switch (coneSize) {
4569       case 2:
4570         ct = DM_POLYTOPE_SEGMENT;
4571         break;
4572       case 3:
4573         ct = DM_POLYTOPE_TRIANGLE;
4574         break;
4575       case 4:
4576         ct = DM_POLYTOPE_QUADRILATERAL;
4577         break;
4578       default:
4579         break;
4580       }
4581     }
4582   }
4583   *pt = ct;
4584   PetscFunctionReturn(PETSC_SUCCESS);
4585 }
4586 
4587 /*@
4588   DMPlexComputeCellTypes - Infer the polytope type of every cell using its dimension and cone size.
4589 
4590   Collective
4591 
4592   Input Parameter:
4593 . dm - The `DMPLEX`
4594 
4595   Level: developer
4596 
4597   Note:
4598   This function is normally called automatically when a cell type is requested. It creates an
4599   internal `DMLabel` named "celltype" which can be directly accessed using `DMGetLabel()`. A user may disable
4600   automatic creation by creating the label manually, using `DMCreateLabel`(dm, "celltype").
4601 
4602   `DMPlexComputeCellTypes()` should be called after all calls to `DMPlexSymmetrize()` and `DMPlexStratify()`
4603 
4604 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexStratify()`, `DMGetLabel()`, `DMCreateLabel()`
4605 @*/
4606 PetscErrorCode DMPlexComputeCellTypes(DM dm)
4607 {
4608   DM_Plex *mesh;
4609   DMLabel  ctLabel;
4610   PetscInt pStart, pEnd, p;
4611 
4612   PetscFunctionBegin;
4613   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4614   mesh = (DM_Plex *)dm->data;
4615   PetscCall(DMCreateLabel(dm, "celltype"));
4616   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
4617   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4618   PetscCall(PetscFree(mesh->cellTypes));
4619   PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
4620   for (p = pStart; p < pEnd; ++p) {
4621     DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4622     PetscInt       pdepth;
4623 
4624     PetscCall(DMPlexGetPointDepth(dm, p, &pdepth));
4625     PetscCall(DMPlexComputeCellType_Internal(dm, p, pdepth, &ct));
4626     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);
4627     PetscCall(DMLabelSetValue(ctLabel, p, ct));
4628     mesh->cellTypes[p - pStart].value_as_uint8 = ct;
4629   }
4630   PetscCall(PetscObjectStateGet((PetscObject)ctLabel, &mesh->celltypeState));
4631   PetscCall(PetscObjectViewFromOptions((PetscObject)ctLabel, NULL, "-dm_plex_celltypes_view"));
4632   PetscFunctionReturn(PETSC_SUCCESS);
4633 }
4634 
4635 /*@C
4636   DMPlexGetJoin - Get an array for the join of the set of points
4637 
4638   Not Collective
4639 
4640   Input Parameters:
4641 + dm        - The `DMPLEX` object
4642 . numPoints - The number of input points for the join
4643 - points    - The input points
4644 
4645   Output Parameters:
4646 + numCoveredPoints - The number of points in the join
4647 - coveredPoints    - The points in the join
4648 
4649   Level: intermediate
4650 
4651   Note:
4652   Currently, this is restricted to a single level join
4653 
4654   Fortran Notes:
4655   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4656 
4657 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4658 @*/
4659 PetscErrorCode DMPlexGetJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4660 {
4661   DM_Plex  *mesh = (DM_Plex *)dm->data;
4662   PetscInt *join[2];
4663   PetscInt  joinSize, i = 0;
4664   PetscInt  dof, off, p, c, m;
4665   PetscInt  maxSupportSize;
4666 
4667   PetscFunctionBegin;
4668   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4669   PetscAssertPointer(points, 3);
4670   PetscAssertPointer(numCoveredPoints, 4);
4671   PetscAssertPointer(coveredPoints, 5);
4672   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
4673   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[0]));
4674   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[1]));
4675   /* Copy in support of first point */
4676   PetscCall(PetscSectionGetDof(mesh->supportSection, points[0], &dof));
4677   PetscCall(PetscSectionGetOffset(mesh->supportSection, points[0], &off));
4678   for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = mesh->supports[off + joinSize];
4679   /* Check each successive support */
4680   for (p = 1; p < numPoints; ++p) {
4681     PetscInt newJoinSize = 0;
4682 
4683     PetscCall(PetscSectionGetDof(mesh->supportSection, points[p], &dof));
4684     PetscCall(PetscSectionGetOffset(mesh->supportSection, points[p], &off));
4685     for (c = 0; c < dof; ++c) {
4686       const PetscInt point = mesh->supports[off + c];
4687 
4688       for (m = 0; m < joinSize; ++m) {
4689         if (point == join[i][m]) {
4690           join[1 - i][newJoinSize++] = point;
4691           break;
4692         }
4693       }
4694     }
4695     joinSize = newJoinSize;
4696     i        = 1 - i;
4697   }
4698   *numCoveredPoints = joinSize;
4699   *coveredPoints    = join[i];
4700   PetscCall(DMRestoreWorkArray(dm, maxSupportSize, MPIU_INT, &join[1 - i]));
4701   PetscFunctionReturn(PETSC_SUCCESS);
4702 }
4703 
4704 /*@C
4705   DMPlexRestoreJoin - Restore an array for the join of the set of points
4706 
4707   Not Collective
4708 
4709   Input Parameters:
4710 + dm        - The `DMPLEX` object
4711 . numPoints - The number of input points for the join
4712 - points    - The input points
4713 
4714   Output Parameters:
4715 + numCoveredPoints - The number of points in the join
4716 - coveredPoints    - The points in the join
4717 
4718   Level: intermediate
4719 
4720   Fortran Notes:
4721   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4722 
4723 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetJoin()`, `DMPlexGetFullJoin()`, `DMPlexGetMeet()`
4724 @*/
4725 PetscErrorCode DMPlexRestoreJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4726 {
4727   PetscFunctionBegin;
4728   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4729   if (points) PetscAssertPointer(points, 3);
4730   if (numCoveredPoints) PetscAssertPointer(numCoveredPoints, 4);
4731   PetscAssertPointer(coveredPoints, 5);
4732   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
4733   if (numCoveredPoints) *numCoveredPoints = 0;
4734   PetscFunctionReturn(PETSC_SUCCESS);
4735 }
4736 
4737 /*@C
4738   DMPlexGetFullJoin - Get an array for the join of the set of points
4739 
4740   Not Collective
4741 
4742   Input Parameters:
4743 + dm        - The `DMPLEX` object
4744 . numPoints - The number of input points for the join
4745 - points    - The input points
4746 
4747   Output Parameters:
4748 + numCoveredPoints - The number of points in the join
4749 - coveredPoints    - The points in the join
4750 
4751   Level: intermediate
4752 
4753   Fortran Notes:
4754   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4755 
4756 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetJoin()`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4757 @*/
4758 PetscErrorCode DMPlexGetFullJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4759 {
4760   PetscInt *offsets, **closures;
4761   PetscInt *join[2];
4762   PetscInt  depth = 0, maxSize, joinSize = 0, i = 0;
4763   PetscInt  p, d, c, m, ms;
4764 
4765   PetscFunctionBegin;
4766   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4767   PetscAssertPointer(points, 3);
4768   PetscAssertPointer(numCoveredPoints, 4);
4769   PetscAssertPointer(coveredPoints, 5);
4770 
4771   PetscCall(DMPlexGetDepth(dm, &depth));
4772   PetscCall(PetscCalloc1(numPoints, &closures));
4773   PetscCall(DMGetWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
4774   PetscCall(DMPlexGetMaxSizes(dm, NULL, &ms));
4775   maxSize = (ms > 1) ? ((PetscPowInt(ms, depth + 1) - 1) / (ms - 1)) : depth + 1;
4776   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[0]));
4777   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[1]));
4778 
4779   for (p = 0; p < numPoints; ++p) {
4780     PetscInt closureSize;
4781 
4782     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_FALSE, &closureSize, &closures[p]));
4783 
4784     offsets[p * (depth + 2) + 0] = 0;
4785     for (d = 0; d < depth + 1; ++d) {
4786       PetscInt pStart, pEnd, i;
4787 
4788       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
4789       for (i = offsets[p * (depth + 2) + d]; i < closureSize; ++i) {
4790         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
4791           offsets[p * (depth + 2) + d + 1] = i;
4792           break;
4793         }
4794       }
4795       if (i == closureSize) offsets[p * (depth + 2) + d + 1] = i;
4796     }
4797     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);
4798   }
4799   for (d = 0; d < depth + 1; ++d) {
4800     PetscInt dof;
4801 
4802     /* Copy in support of first point */
4803     dof = offsets[d + 1] - offsets[d];
4804     for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = closures[0][(offsets[d] + joinSize) * 2];
4805     /* Check each successive cone */
4806     for (p = 1; p < numPoints && joinSize; ++p) {
4807       PetscInt newJoinSize = 0;
4808 
4809       dof = offsets[p * (depth + 2) + d + 1] - offsets[p * (depth + 2) + d];
4810       for (c = 0; c < dof; ++c) {
4811         const PetscInt point = closures[p][(offsets[p * (depth + 2) + d] + c) * 2];
4812 
4813         for (m = 0; m < joinSize; ++m) {
4814           if (point == join[i][m]) {
4815             join[1 - i][newJoinSize++] = point;
4816             break;
4817           }
4818         }
4819       }
4820       joinSize = newJoinSize;
4821       i        = 1 - i;
4822     }
4823     if (joinSize) break;
4824   }
4825   *numCoveredPoints = joinSize;
4826   *coveredPoints    = join[i];
4827   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_FALSE, NULL, &closures[p]));
4828   PetscCall(PetscFree(closures));
4829   PetscCall(DMRestoreWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
4830   PetscCall(DMRestoreWorkArray(dm, ms, MPIU_INT, &join[1 - i]));
4831   PetscFunctionReturn(PETSC_SUCCESS);
4832 }
4833 
4834 /*@C
4835   DMPlexGetMeet - Get an array for the meet of the set of points
4836 
4837   Not Collective
4838 
4839   Input Parameters:
4840 + dm        - The `DMPLEX` object
4841 . numPoints - The number of input points for the meet
4842 - points    - The input points
4843 
4844   Output Parameters:
4845 + numCoveringPoints - The number of points in the meet
4846 - coveringPoints    - The points in the meet
4847 
4848   Level: intermediate
4849 
4850   Note:
4851   Currently, this is restricted to a single level meet
4852 
4853   Fortran Notes:
4854   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4855 
4856 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
4857 @*/
4858 PetscErrorCode DMPlexGetMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveringPoints, const PetscInt **coveringPoints)
4859 {
4860   DM_Plex  *mesh = (DM_Plex *)dm->data;
4861   PetscInt *meet[2];
4862   PetscInt  meetSize, i = 0;
4863   PetscInt  dof, off, p, c, m;
4864   PetscInt  maxConeSize;
4865 
4866   PetscFunctionBegin;
4867   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4868   PetscAssertPointer(points, 3);
4869   PetscAssertPointer(numCoveringPoints, 4);
4870   PetscAssertPointer(coveringPoints, 5);
4871   PetscCall(PetscSectionGetMaxDof(mesh->coneSection, &maxConeSize));
4872   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[0]));
4873   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[1]));
4874   /* Copy in cone of first point */
4875   PetscCall(PetscSectionGetDof(mesh->coneSection, points[0], &dof));
4876   PetscCall(PetscSectionGetOffset(mesh->coneSection, points[0], &off));
4877   for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = mesh->cones[off + meetSize];
4878   /* Check each successive cone */
4879   for (p = 1; p < numPoints; ++p) {
4880     PetscInt newMeetSize = 0;
4881 
4882     PetscCall(PetscSectionGetDof(mesh->coneSection, points[p], &dof));
4883     PetscCall(PetscSectionGetOffset(mesh->coneSection, points[p], &off));
4884     for (c = 0; c < dof; ++c) {
4885       const PetscInt point = mesh->cones[off + c];
4886 
4887       for (m = 0; m < meetSize; ++m) {
4888         if (point == meet[i][m]) {
4889           meet[1 - i][newMeetSize++] = point;
4890           break;
4891         }
4892       }
4893     }
4894     meetSize = newMeetSize;
4895     i        = 1 - i;
4896   }
4897   *numCoveringPoints = meetSize;
4898   *coveringPoints    = meet[i];
4899   PetscCall(DMRestoreWorkArray(dm, maxConeSize, MPIU_INT, &meet[1 - i]));
4900   PetscFunctionReturn(PETSC_SUCCESS);
4901 }
4902 
4903 /*@C
4904   DMPlexRestoreMeet - Restore an array for the meet of the set of points
4905 
4906   Not Collective
4907 
4908   Input Parameters:
4909 + dm        - The `DMPLEX` object
4910 . numPoints - The number of input points for the meet
4911 - points    - The input points
4912 
4913   Output Parameters:
4914 + numCoveredPoints - The number of points in the meet
4915 - coveredPoints    - The points in the meet
4916 
4917   Level: intermediate
4918 
4919   Fortran Notes:
4920   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4921 
4922 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetMeet()`, `DMPlexGetFullMeet()`, `DMPlexGetJoin()`
4923 @*/
4924 PetscErrorCode DMPlexRestoreMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4925 {
4926   PetscFunctionBegin;
4927   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4928   if (points) PetscAssertPointer(points, 3);
4929   if (numCoveredPoints) PetscAssertPointer(numCoveredPoints, 4);
4930   PetscAssertPointer(coveredPoints, 5);
4931   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
4932   if (numCoveredPoints) *numCoveredPoints = 0;
4933   PetscFunctionReturn(PETSC_SUCCESS);
4934 }
4935 
4936 /*@C
4937   DMPlexGetFullMeet - Get an array for the meet of the set of points
4938 
4939   Not Collective
4940 
4941   Input Parameters:
4942 + dm        - The `DMPLEX` object
4943 . numPoints - The number of input points for the meet
4944 - points    - The input points
4945 
4946   Output Parameters:
4947 + numCoveredPoints - The number of points in the meet
4948 - coveredPoints    - The points in the meet
4949 
4950   Level: intermediate
4951 
4952   Fortran Notes:
4953   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4954 
4955 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetMeet()`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
4956 @*/
4957 PetscErrorCode DMPlexGetFullMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4958 {
4959   PetscInt *offsets, **closures;
4960   PetscInt *meet[2];
4961   PetscInt  height = 0, maxSize, meetSize = 0, i = 0;
4962   PetscInt  p, h, c, m, mc;
4963 
4964   PetscFunctionBegin;
4965   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4966   PetscAssertPointer(points, 3);
4967   PetscAssertPointer(numCoveredPoints, 4);
4968   PetscAssertPointer(coveredPoints, 5);
4969 
4970   PetscCall(DMPlexGetDepth(dm, &height));
4971   PetscCall(PetscMalloc1(numPoints, &closures));
4972   PetscCall(DMGetWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
4973   PetscCall(DMPlexGetMaxSizes(dm, &mc, NULL));
4974   maxSize = (mc > 1) ? ((PetscPowInt(mc, height + 1) - 1) / (mc - 1)) : height + 1;
4975   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[0]));
4976   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[1]));
4977 
4978   for (p = 0; p < numPoints; ++p) {
4979     PetscInt closureSize;
4980 
4981     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_TRUE, &closureSize, &closures[p]));
4982 
4983     offsets[p * (height + 2) + 0] = 0;
4984     for (h = 0; h < height + 1; ++h) {
4985       PetscInt pStart, pEnd, i;
4986 
4987       PetscCall(DMPlexGetHeightStratum(dm, h, &pStart, &pEnd));
4988       for (i = offsets[p * (height + 2) + h]; i < closureSize; ++i) {
4989         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
4990           offsets[p * (height + 2) + h + 1] = i;
4991           break;
4992         }
4993       }
4994       if (i == closureSize) offsets[p * (height + 2) + h + 1] = i;
4995     }
4996     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);
4997   }
4998   for (h = 0; h < height + 1; ++h) {
4999     PetscInt dof;
5000 
5001     /* Copy in cone of first point */
5002     dof = offsets[h + 1] - offsets[h];
5003     for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = closures[0][(offsets[h] + meetSize) * 2];
5004     /* Check each successive cone */
5005     for (p = 1; p < numPoints && meetSize; ++p) {
5006       PetscInt newMeetSize = 0;
5007 
5008       dof = offsets[p * (height + 2) + h + 1] - offsets[p * (height + 2) + h];
5009       for (c = 0; c < dof; ++c) {
5010         const PetscInt point = closures[p][(offsets[p * (height + 2) + h] + c) * 2];
5011 
5012         for (m = 0; m < meetSize; ++m) {
5013           if (point == meet[i][m]) {
5014             meet[1 - i][newMeetSize++] = point;
5015             break;
5016           }
5017         }
5018       }
5019       meetSize = newMeetSize;
5020       i        = 1 - i;
5021     }
5022     if (meetSize) break;
5023   }
5024   *numCoveredPoints = meetSize;
5025   *coveredPoints    = meet[i];
5026   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_TRUE, NULL, &closures[p]));
5027   PetscCall(PetscFree(closures));
5028   PetscCall(DMRestoreWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
5029   PetscCall(DMRestoreWorkArray(dm, mc, MPIU_INT, &meet[1 - i]));
5030   PetscFunctionReturn(PETSC_SUCCESS);
5031 }
5032 
5033 /*@C
5034   DMPlexEqual - Determine if two `DM` have the same topology
5035 
5036   Not Collective
5037 
5038   Input Parameters:
5039 + dmA - A `DMPLEX` object
5040 - dmB - A `DMPLEX` object
5041 
5042   Output Parameter:
5043 . equal - `PETSC_TRUE` if the topologies are identical
5044 
5045   Level: intermediate
5046 
5047   Note:
5048   We are not solving graph isomorphism, so we do not permute.
5049 
5050 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCone()`
5051 @*/
5052 PetscErrorCode DMPlexEqual(DM dmA, DM dmB, PetscBool *equal)
5053 {
5054   PetscInt depth, depthB, pStart, pEnd, pStartB, pEndB, p;
5055 
5056   PetscFunctionBegin;
5057   PetscValidHeaderSpecific(dmA, DM_CLASSID, 1);
5058   PetscValidHeaderSpecific(dmB, DM_CLASSID, 2);
5059   PetscAssertPointer(equal, 3);
5060 
5061   *equal = PETSC_FALSE;
5062   PetscCall(DMPlexGetDepth(dmA, &depth));
5063   PetscCall(DMPlexGetDepth(dmB, &depthB));
5064   if (depth != depthB) PetscFunctionReturn(PETSC_SUCCESS);
5065   PetscCall(DMPlexGetChart(dmA, &pStart, &pEnd));
5066   PetscCall(DMPlexGetChart(dmB, &pStartB, &pEndB));
5067   if ((pStart != pStartB) || (pEnd != pEndB)) PetscFunctionReturn(PETSC_SUCCESS);
5068   for (p = pStart; p < pEnd; ++p) {
5069     const PetscInt *cone, *coneB, *ornt, *orntB, *support, *supportB;
5070     PetscInt        coneSize, coneSizeB, c, supportSize, supportSizeB, s;
5071 
5072     PetscCall(DMPlexGetConeSize(dmA, p, &coneSize));
5073     PetscCall(DMPlexGetCone(dmA, p, &cone));
5074     PetscCall(DMPlexGetConeOrientation(dmA, p, &ornt));
5075     PetscCall(DMPlexGetConeSize(dmB, p, &coneSizeB));
5076     PetscCall(DMPlexGetCone(dmB, p, &coneB));
5077     PetscCall(DMPlexGetConeOrientation(dmB, p, &orntB));
5078     if (coneSize != coneSizeB) PetscFunctionReturn(PETSC_SUCCESS);
5079     for (c = 0; c < coneSize; ++c) {
5080       if (cone[c] != coneB[c]) PetscFunctionReturn(PETSC_SUCCESS);
5081       if (ornt[c] != orntB[c]) PetscFunctionReturn(PETSC_SUCCESS);
5082     }
5083     PetscCall(DMPlexGetSupportSize(dmA, p, &supportSize));
5084     PetscCall(DMPlexGetSupport(dmA, p, &support));
5085     PetscCall(DMPlexGetSupportSize(dmB, p, &supportSizeB));
5086     PetscCall(DMPlexGetSupport(dmB, p, &supportB));
5087     if (supportSize != supportSizeB) PetscFunctionReturn(PETSC_SUCCESS);
5088     for (s = 0; s < supportSize; ++s) {
5089       if (support[s] != supportB[s]) PetscFunctionReturn(PETSC_SUCCESS);
5090     }
5091   }
5092   *equal = PETSC_TRUE;
5093   PetscFunctionReturn(PETSC_SUCCESS);
5094 }
5095 
5096 /*@C
5097   DMPlexGetNumFaceVertices - Returns the number of vertices on a face
5098 
5099   Not Collective
5100 
5101   Input Parameters:
5102 + dm         - The `DMPLEX`
5103 . cellDim    - The cell dimension
5104 - numCorners - The number of vertices on a cell
5105 
5106   Output Parameter:
5107 . numFaceVertices - The number of vertices on a face
5108 
5109   Level: developer
5110 
5111   Note:
5112   Of course this can only work for a restricted set of symmetric shapes
5113 
5114 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCone()`
5115 @*/
5116 PetscErrorCode DMPlexGetNumFaceVertices(DM dm, PetscInt cellDim, PetscInt numCorners, PetscInt *numFaceVertices)
5117 {
5118   MPI_Comm comm;
5119 
5120   PetscFunctionBegin;
5121   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
5122   PetscAssertPointer(numFaceVertices, 4);
5123   switch (cellDim) {
5124   case 0:
5125     *numFaceVertices = 0;
5126     break;
5127   case 1:
5128     *numFaceVertices = 1;
5129     break;
5130   case 2:
5131     switch (numCorners) {
5132     case 3:                 /* triangle */
5133       *numFaceVertices = 2; /* Edge has 2 vertices */
5134       break;
5135     case 4:                 /* quadrilateral */
5136       *numFaceVertices = 2; /* Edge has 2 vertices */
5137       break;
5138     case 6:                 /* quadratic triangle, tri and quad cohesive Lagrange cells */
5139       *numFaceVertices = 3; /* Edge has 3 vertices */
5140       break;
5141     case 9:                 /* quadratic quadrilateral, quadratic quad cohesive Lagrange cells */
5142       *numFaceVertices = 3; /* Edge has 3 vertices */
5143       break;
5144     default:
5145       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
5146     }
5147     break;
5148   case 3:
5149     switch (numCorners) {
5150     case 4:                 /* tetradehdron */
5151       *numFaceVertices = 3; /* Face has 3 vertices */
5152       break;
5153     case 6:                 /* tet cohesive cells */
5154       *numFaceVertices = 4; /* Face has 4 vertices */
5155       break;
5156     case 8:                 /* hexahedron */
5157       *numFaceVertices = 4; /* Face has 4 vertices */
5158       break;
5159     case 9:                 /* tet cohesive Lagrange cells */
5160       *numFaceVertices = 6; /* Face has 6 vertices */
5161       break;
5162     case 10:                /* quadratic tetrahedron */
5163       *numFaceVertices = 6; /* Face has 6 vertices */
5164       break;
5165     case 12:                /* hex cohesive Lagrange cells */
5166       *numFaceVertices = 6; /* Face has 6 vertices */
5167       break;
5168     case 18:                /* quadratic tet cohesive Lagrange cells */
5169       *numFaceVertices = 6; /* Face has 6 vertices */
5170       break;
5171     case 27:                /* quadratic hexahedron, quadratic hex cohesive Lagrange cells */
5172       *numFaceVertices = 9; /* Face has 9 vertices */
5173       break;
5174     default:
5175       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
5176     }
5177     break;
5178   default:
5179     SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid cell dimension %" PetscInt_FMT, cellDim);
5180   }
5181   PetscFunctionReturn(PETSC_SUCCESS);
5182 }
5183 
5184 /*@
5185   DMPlexGetDepthLabel - Get the `DMLabel` recording the depth of each point
5186 
5187   Not Collective
5188 
5189   Input Parameter:
5190 . dm - The `DMPLEX` object
5191 
5192   Output Parameter:
5193 . depthLabel - The `DMLabel` recording point depth
5194 
5195   Level: developer
5196 
5197 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepth()`, `DMPlexGetHeightStratum()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`,
5198 @*/
5199 PetscErrorCode DMPlexGetDepthLabel(DM dm, DMLabel *depthLabel)
5200 {
5201   PetscFunctionBegin;
5202   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5203   PetscAssertPointer(depthLabel, 2);
5204   *depthLabel = dm->depthLabel;
5205   PetscFunctionReturn(PETSC_SUCCESS);
5206 }
5207 
5208 /*@
5209   DMPlexGetDepth - Get the depth of the DAG representing this mesh
5210 
5211   Not Collective
5212 
5213   Input Parameter:
5214 . dm - The `DMPLEX` object
5215 
5216   Output Parameter:
5217 . depth - The number of strata (breadth first levels) in the DAG
5218 
5219   Level: developer
5220 
5221   Notes:
5222   This returns maximum of point depths over all points, i.e. maximum value of the label returned by `DMPlexGetDepthLabel()`.
5223 
5224   The point depth is described more in detail in `DMPlexGetDepthStratum()`.
5225 
5226   An empty mesh gives -1.
5227 
5228 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepthLabel()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`
5229 @*/
5230 PetscErrorCode DMPlexGetDepth(DM dm, PetscInt *depth)
5231 {
5232   DM_Plex *mesh = (DM_Plex *)dm->data;
5233   DMLabel  label;
5234   PetscInt d = 0;
5235 
5236   PetscFunctionBegin;
5237   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5238   PetscAssertPointer(depth, 2);
5239   if (mesh->tr) {
5240     PetscCall(DMPlexTransformGetDepth(mesh->tr, depth));
5241   } else {
5242     PetscCall(DMPlexGetDepthLabel(dm, &label));
5243     if (label) PetscCall(DMLabelGetNumValues(label, &d));
5244     *depth = d - 1;
5245   }
5246   PetscFunctionReturn(PETSC_SUCCESS);
5247 }
5248 
5249 /*@
5250   DMPlexGetDepthStratum - Get the bounds [`start`, `end`) for all points at a certain depth.
5251 
5252   Not Collective
5253 
5254   Input Parameters:
5255 + dm    - The `DMPLEX` object
5256 - depth - The requested depth
5257 
5258   Output Parameters:
5259 + start - The first point at this `depth`
5260 - end   - One beyond the last point at this `depth`
5261 
5262   Level: developer
5263 
5264   Notes:
5265   Depth indexing is related to topological dimension.  Depth stratum 0 contains the lowest topological dimension points,
5266   often "vertices".  If the mesh is "interpolated" (see `DMPlexInterpolate()`), then depth stratum 1 contains the next
5267   higher dimension, e.g., "edges".
5268 
5269 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetHeightStratum()`, `DMPlexGetCellTypeStratum()`, `DMPlexGetDepth()`, `DMPlexGetDepthLabel()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`, `DMPlexInterpolate()`
5270 @*/
5271 PetscErrorCode DMPlexGetDepthStratum(DM dm, PetscInt depth, PetscInt *start, PetscInt *end)
5272 {
5273   DM_Plex *mesh = (DM_Plex *)dm->data;
5274   DMLabel  label;
5275   PetscInt pStart, pEnd;
5276 
5277   PetscFunctionBegin;
5278   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5279   if (start) {
5280     PetscAssertPointer(start, 3);
5281     *start = 0;
5282   }
5283   if (end) {
5284     PetscAssertPointer(end, 4);
5285     *end = 0;
5286   }
5287   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
5288   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
5289   if (depth < 0) {
5290     if (start) *start = pStart;
5291     if (end) *end = pEnd;
5292     PetscFunctionReturn(PETSC_SUCCESS);
5293   }
5294   if (mesh->tr) {
5295     PetscCall(DMPlexTransformGetDepthStratum(mesh->tr, depth, start, end));
5296   } else {
5297     PetscCall(DMPlexGetDepthLabel(dm, &label));
5298     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named depth was found");
5299     PetscCall(DMLabelGetStratumBounds(label, depth, start, end));
5300   }
5301   PetscFunctionReturn(PETSC_SUCCESS);
5302 }
5303 
5304 /*@
5305   DMPlexGetHeightStratum - Get the bounds [`start`, `end`) for all points at a certain height.
5306 
5307   Not Collective
5308 
5309   Input Parameters:
5310 + dm     - The `DMPLEX` object
5311 - height - The requested height
5312 
5313   Output Parameters:
5314 + start - The first point at this `height`
5315 - end   - One beyond the last point at this `height`
5316 
5317   Level: developer
5318 
5319   Notes:
5320   Height indexing is related to topological codimension.  Height stratum 0 contains the highest topological dimension
5321   points, often called "cells" or "elements".  If the mesh is "interpolated" (see `DMPlexInterpolate()`), then height
5322   stratum 1 contains the boundary of these "cells", often called "faces" or "facets".
5323 
5324 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepthStratum()`, `DMPlexGetCellTypeStratum()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
5325 @*/
5326 PetscErrorCode DMPlexGetHeightStratum(DM dm, PetscInt height, PetscInt *start, PetscInt *end)
5327 {
5328   DMLabel  label;
5329   PetscInt depth, pStart, pEnd;
5330 
5331   PetscFunctionBegin;
5332   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5333   if (start) {
5334     PetscAssertPointer(start, 3);
5335     *start = 0;
5336   }
5337   if (end) {
5338     PetscAssertPointer(end, 4);
5339     *end = 0;
5340   }
5341   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
5342   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
5343   if (height < 0) {
5344     if (start) *start = pStart;
5345     if (end) *end = pEnd;
5346     PetscFunctionReturn(PETSC_SUCCESS);
5347   }
5348   PetscCall(DMPlexGetDepthLabel(dm, &label));
5349   if (label) PetscCall(DMLabelGetNumValues(label, &depth));
5350   else PetscCall(DMGetDimension(dm, &depth));
5351   PetscCheck(depth >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Depth not yet computed");
5352   PetscCall(DMPlexGetDepthStratum(dm, depth - 1 - height, start, end));
5353   PetscFunctionReturn(PETSC_SUCCESS);
5354 }
5355 
5356 /*@
5357   DMPlexGetPointDepth - Get the `depth` of a given point
5358 
5359   Not Collective
5360 
5361   Input Parameters:
5362 + dm    - The `DMPLEX` object
5363 - point - The point
5364 
5365   Output Parameter:
5366 . depth - The depth of the `point`
5367 
5368   Level: intermediate
5369 
5370 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
5371 @*/
5372 PetscErrorCode DMPlexGetPointDepth(DM dm, PetscInt point, PetscInt *depth)
5373 {
5374   PetscFunctionBegin;
5375   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5376   PetscAssertPointer(depth, 3);
5377   PetscCall(DMLabelGetValue(dm->depthLabel, point, depth));
5378   PetscFunctionReturn(PETSC_SUCCESS);
5379 }
5380 
5381 /*@
5382   DMPlexGetPointHeight - Get the `height` of a given point
5383 
5384   Not Collective
5385 
5386   Input Parameters:
5387 + dm    - The `DMPLEX` object
5388 - point - The point
5389 
5390   Output Parameter:
5391 . height - The height of the `point`
5392 
5393   Level: intermediate
5394 
5395 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointDepth()`
5396 @*/
5397 PetscErrorCode DMPlexGetPointHeight(DM dm, PetscInt point, PetscInt *height)
5398 {
5399   PetscInt n, pDepth;
5400 
5401   PetscFunctionBegin;
5402   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5403   PetscAssertPointer(height, 3);
5404   PetscCall(DMLabelGetNumValues(dm->depthLabel, &n));
5405   PetscCall(DMLabelGetValue(dm->depthLabel, point, &pDepth));
5406   *height = n - 1 - pDepth; /* DAG depth is n-1 */
5407   PetscFunctionReturn(PETSC_SUCCESS);
5408 }
5409 
5410 /*@
5411   DMPlexGetCellTypeLabel - Get the `DMLabel` recording the polytope type of each cell
5412 
5413   Not Collective
5414 
5415   Input Parameter:
5416 . dm - The `DMPLEX` object
5417 
5418   Output Parameter:
5419 . celltypeLabel - The `DMLabel` recording cell polytope type
5420 
5421   Level: developer
5422 
5423   Note:
5424   This function will trigger automatica computation of cell types. This can be disabled by calling
5425   `DMCreateLabel`(dm, "celltype") beforehand.
5426 
5427 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMCreateLabel()`
5428 @*/
5429 PetscErrorCode DMPlexGetCellTypeLabel(DM dm, DMLabel *celltypeLabel)
5430 {
5431   PetscFunctionBegin;
5432   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5433   PetscAssertPointer(celltypeLabel, 2);
5434   if (!dm->celltypeLabel) PetscCall(DMPlexComputeCellTypes(dm));
5435   *celltypeLabel = dm->celltypeLabel;
5436   PetscFunctionReturn(PETSC_SUCCESS);
5437 }
5438 
5439 /*@
5440   DMPlexGetCellType - Get the polytope type of a given cell
5441 
5442   Not Collective
5443 
5444   Input Parameters:
5445 + dm   - The `DMPLEX` object
5446 - cell - The cell
5447 
5448   Output Parameter:
5449 . celltype - The polytope type of the cell
5450 
5451   Level: intermediate
5452 
5453 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPolytopeType`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`
5454 @*/
5455 PetscErrorCode DMPlexGetCellType(DM dm, PetscInt cell, DMPolytopeType *celltype)
5456 {
5457   DM_Plex *mesh = (DM_Plex *)dm->data;
5458   DMLabel  label;
5459   PetscInt ct;
5460 
5461   PetscFunctionBegin;
5462   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5463   PetscAssertPointer(celltype, 3);
5464   if (mesh->tr) {
5465     PetscCall(DMPlexTransformGetCellType(mesh->tr, cell, celltype));
5466   } else {
5467     PetscInt pStart, pEnd;
5468 
5469     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, NULL));
5470     if (!mesh->cellTypes) { /* XXX remove? optimize? */
5471       PetscCall(PetscSectionGetChart(mesh->coneSection, NULL, &pEnd));
5472       PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
5473       PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5474       for (PetscInt p = pStart; p < pEnd; p++) {
5475         PetscCall(DMLabelGetValue(label, p, &ct));
5476         mesh->cellTypes[p - pStart].value_as_uint8 = (DMPolytopeType)ct;
5477       }
5478     }
5479     *celltype = (DMPolytopeType)mesh->cellTypes[cell - pStart].value_as_uint8;
5480     if (PetscDefined(USE_DEBUG)) {
5481       PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5482       PetscCall(DMLabelGetValue(label, cell, &ct));
5483       PetscCheck(ct >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Cell %" PetscInt_FMT " has not been assigned a cell type", cell);
5484       PetscCheck(ct == (PetscInt)*celltype, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid cellType for %" PetscInt_FMT ": %d != %" PetscInt_FMT, cell, (int)*celltype, ct);
5485     }
5486   }
5487   PetscFunctionReturn(PETSC_SUCCESS);
5488 }
5489 
5490 /*@
5491   DMPlexSetCellType - Set the polytope type of a given cell
5492 
5493   Not Collective
5494 
5495   Input Parameters:
5496 + dm       - The `DMPLEX` object
5497 . cell     - The cell
5498 - celltype - The polytope type of the cell
5499 
5500   Level: advanced
5501 
5502   Note:
5503   By default, cell types will be automatically computed using `DMPlexComputeCellTypes()` before this function
5504   is executed. This function will override the computed type. However, if automatic classification will not succeed
5505   and a user wants to manually specify all types, the classification must be disabled by calling
5506   DMCreateLabel(dm, "celltype") before getting or setting any cell types.
5507 
5508 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexComputeCellTypes()`, `DMCreateLabel()`
5509 @*/
5510 PetscErrorCode DMPlexSetCellType(DM dm, PetscInt cell, DMPolytopeType celltype)
5511 {
5512   DM_Plex *mesh = (DM_Plex *)dm->data;
5513   DMLabel  label;
5514   PetscInt pStart, pEnd;
5515 
5516   PetscFunctionBegin;
5517   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5518   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
5519   PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5520   PetscCall(DMLabelSetValue(label, cell, celltype));
5521   if (!mesh->cellTypes) PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
5522   mesh->cellTypes[cell - pStart].value_as_uint8 = celltype;
5523   PetscFunctionReturn(PETSC_SUCCESS);
5524 }
5525 
5526 PetscErrorCode DMCreateCoordinateDM_Plex(DM dm, DM *cdm)
5527 {
5528   PetscSection section, s;
5529   Mat          m;
5530   PetscInt     maxHeight;
5531   const char  *prefix;
5532 
5533   PetscFunctionBegin;
5534   PetscCall(DMClone(dm, cdm));
5535   PetscCall(PetscObjectGetOptionsPrefix((PetscObject)dm, &prefix));
5536   PetscCall(PetscObjectSetOptionsPrefix((PetscObject)*cdm, prefix));
5537   PetscCall(PetscObjectAppendOptionsPrefix((PetscObject)*cdm, "cdm_"));
5538   PetscCall(DMPlexGetMaxProjectionHeight(dm, &maxHeight));
5539   PetscCall(DMPlexSetMaxProjectionHeight(*cdm, maxHeight));
5540   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
5541   PetscCall(DMSetLocalSection(*cdm, section));
5542   PetscCall(PetscSectionDestroy(&section));
5543   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &s));
5544   PetscCall(MatCreate(PETSC_COMM_SELF, &m));
5545   PetscCall(DMSetDefaultConstraints(*cdm, s, m, NULL));
5546   PetscCall(PetscSectionDestroy(&s));
5547   PetscCall(MatDestroy(&m));
5548 
5549   PetscCall(DMSetNumFields(*cdm, 1));
5550   PetscCall(DMCreateDS(*cdm));
5551   (*cdm)->cloneOpts = PETSC_TRUE;
5552   if (dm->setfromoptionscalled) PetscCall(DMSetFromOptions(*cdm));
5553   PetscFunctionReturn(PETSC_SUCCESS);
5554 }
5555 
5556 PetscErrorCode DMCreateCoordinateField_Plex(DM dm, DMField *field)
5557 {
5558   Vec coordsLocal, cellCoordsLocal;
5559   DM  coordsDM, cellCoordsDM;
5560 
5561   PetscFunctionBegin;
5562   *field = NULL;
5563   PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
5564   PetscCall(DMGetCoordinateDM(dm, &coordsDM));
5565   PetscCall(DMGetCellCoordinatesLocal(dm, &cellCoordsLocal));
5566   PetscCall(DMGetCellCoordinateDM(dm, &cellCoordsDM));
5567   if (coordsLocal && coordsDM) {
5568     if (cellCoordsLocal && cellCoordsDM) PetscCall(DMFieldCreateDSWithDG(coordsDM, cellCoordsDM, 0, coordsLocal, cellCoordsLocal, field));
5569     else PetscCall(DMFieldCreateDS(coordsDM, 0, coordsLocal, field));
5570   }
5571   PetscFunctionReturn(PETSC_SUCCESS);
5572 }
5573 
5574 /*@C
5575   DMPlexGetConeSection - Return a section which describes the layout of cone data
5576 
5577   Not Collective
5578 
5579   Input Parameter:
5580 . dm - The `DMPLEX` object
5581 
5582   Output Parameter:
5583 . section - The `PetscSection` object
5584 
5585   Level: developer
5586 
5587 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSupportSection()`, `DMPlexGetCones()`, `DMPlexGetConeOrientations()`, `PetscSection`
5588 @*/
5589 PetscErrorCode DMPlexGetConeSection(DM dm, PetscSection *section)
5590 {
5591   DM_Plex *mesh = (DM_Plex *)dm->data;
5592 
5593   PetscFunctionBegin;
5594   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5595   if (section) *section = mesh->coneSection;
5596   PetscFunctionReturn(PETSC_SUCCESS);
5597 }
5598 
5599 /*@C
5600   DMPlexGetSupportSection - Return a section which describes the layout of support data
5601 
5602   Not Collective
5603 
5604   Input Parameter:
5605 . dm - The `DMPLEX` object
5606 
5607   Output Parameter:
5608 . section - The `PetscSection` object
5609 
5610   Level: developer
5611 
5612 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `PetscSection`
5613 @*/
5614 PetscErrorCode DMPlexGetSupportSection(DM dm, PetscSection *section)
5615 {
5616   DM_Plex *mesh = (DM_Plex *)dm->data;
5617 
5618   PetscFunctionBegin;
5619   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5620   if (section) *section = mesh->supportSection;
5621   PetscFunctionReturn(PETSC_SUCCESS);
5622 }
5623 
5624 /*@C
5625   DMPlexGetCones - Return cone data
5626 
5627   Not Collective
5628 
5629   Input Parameter:
5630 . dm - The `DMPLEX` object
5631 
5632   Output Parameter:
5633 . cones - The cone for each point
5634 
5635   Level: developer
5636 
5637 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`
5638 @*/
5639 PetscErrorCode DMPlexGetCones(DM dm, PetscInt *cones[])
5640 {
5641   DM_Plex *mesh = (DM_Plex *)dm->data;
5642 
5643   PetscFunctionBegin;
5644   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5645   if (cones) *cones = mesh->cones;
5646   PetscFunctionReturn(PETSC_SUCCESS);
5647 }
5648 
5649 /*@C
5650   DMPlexGetConeOrientations - Return cone orientation data
5651 
5652   Not Collective
5653 
5654   Input Parameter:
5655 . dm - The `DMPLEX` object
5656 
5657   Output Parameter:
5658 . coneOrientations - The array of cone orientations for all points
5659 
5660   Level: developer
5661 
5662   Notes:
5663   The `PetscSection` returned by `DMPlexGetConeSection()` partitions coneOrientations into cone orientations of particular points as returned by `DMPlexGetConeOrientation()`.
5664 
5665   The meaning of coneOrientations values is detailed in `DMPlexGetConeOrientation()`.
5666 
5667 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `DMPlexGetConeOrientation()`, `PetscSection`
5668 @*/
5669 PetscErrorCode DMPlexGetConeOrientations(DM dm, PetscInt *coneOrientations[])
5670 {
5671   DM_Plex *mesh = (DM_Plex *)dm->data;
5672 
5673   PetscFunctionBegin;
5674   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5675   if (coneOrientations) *coneOrientations = mesh->coneOrientations;
5676   PetscFunctionReturn(PETSC_SUCCESS);
5677 }
5678 
5679 /******************************** FEM Support **********************************/
5680 
5681 PetscErrorCode DMPlexGetAllCells_Internal(DM plex, IS *cellIS)
5682 {
5683   PetscInt depth;
5684 
5685   PetscFunctionBegin;
5686   PetscCall(DMPlexGetDepth(plex, &depth));
5687   PetscCall(DMGetStratumIS(plex, "dim", depth, cellIS));
5688   if (!*cellIS) PetscCall(DMGetStratumIS(plex, "depth", depth, cellIS));
5689   PetscFunctionReturn(PETSC_SUCCESS);
5690 }
5691 
5692 PetscErrorCode DMPlexGetAllFaces_Internal(DM plex, IS *faceIS)
5693 {
5694   PetscInt depth;
5695 
5696   PetscFunctionBegin;
5697   PetscCall(DMPlexGetDepth(plex, &depth));
5698   PetscCall(DMGetStratumIS(plex, "dim", depth - 1, faceIS));
5699   if (!*faceIS) PetscCall(DMGetStratumIS(plex, "depth", depth - 1, faceIS));
5700   PetscFunctionReturn(PETSC_SUCCESS);
5701 }
5702 
5703 /*
5704  Returns number of components and tensor degree for the field.  For interpolated meshes, line should be a point
5705  representing a line in the section.
5706 */
5707 static PetscErrorCode PetscSectionFieldGetTensorDegree_Private(DM dm, PetscSection section, PetscInt field, PetscInt line, PetscInt *Nc, PetscInt *k, PetscBool *continuous, PetscBool *tensor)
5708 {
5709   PetscObject  obj;
5710   PetscClassId id;
5711   PetscFE      fe = NULL;
5712 
5713   PetscFunctionBeginHot;
5714   PetscCall(PetscSectionGetFieldComponents(section, field, Nc));
5715   PetscCall(DMGetField(dm, field, NULL, &obj));
5716   PetscCall(PetscObjectGetClassId(obj, &id));
5717   if (id == PETSCFE_CLASSID) fe = (PetscFE)obj;
5718 
5719   if (!fe) {
5720     /* Assume the full interpolated mesh is in the chart; lines in particular */
5721     /* An order k SEM disc has k-1 dofs on an edge */
5722     PetscCall(PetscSectionGetFieldDof(section, line, field, k));
5723     *k = *k / *Nc + 1;
5724   } else {
5725     PetscInt       dual_space_size, dim;
5726     PetscDualSpace dsp;
5727 
5728     PetscCall(DMGetDimension(dm, &dim));
5729     PetscCall(PetscFEGetDualSpace(fe, &dsp));
5730     PetscCall(PetscDualSpaceGetDimension(dsp, &dual_space_size));
5731     *k = (PetscInt)PetscCeilReal(PetscPowReal(dual_space_size / *Nc, 1.0 / dim)) - 1;
5732     PetscCall(PetscDualSpaceLagrangeGetContinuity(dsp, continuous));
5733     PetscCall(PetscDualSpaceLagrangeGetTensor(dsp, tensor));
5734   }
5735   PetscFunctionReturn(PETSC_SUCCESS);
5736 }
5737 
5738 static PetscErrorCode GetFieldSize_Private(PetscInt dim, PetscInt k, PetscBool tensor, PetscInt *dof)
5739 {
5740   PetscFunctionBeginHot;
5741   if (tensor) {
5742     *dof = PetscPowInt(k + 1, dim);
5743   } else {
5744     switch (dim) {
5745     case 1:
5746       *dof = k + 1;
5747       break;
5748     case 2:
5749       *dof = ((k + 1) * (k + 2)) / 2;
5750       break;
5751     case 3:
5752       *dof = ((k + 1) * (k + 2) * (k + 3)) / 6;
5753       break;
5754     default:
5755       *dof = 0;
5756     }
5757   }
5758   PetscFunctionReturn(PETSC_SUCCESS);
5759 }
5760 
5761 /*@
5762 
5763   DMPlexSetClosurePermutationTensor - Create a permutation from the default (BFS) point ordering in the closure, to a
5764   lexicographic ordering over the tensor product cell (i.e., line, quad, hex, etc.), and set this permutation in the
5765   section provided (or the section of the `DM`).
5766 
5767   Input Parameters:
5768 + dm      - The `DM`
5769 . point   - Either a cell (highest dim point) or an edge (dim 1 point), or `PETSC_DETERMINE`
5770 - section - The `PetscSection` to reorder, or `NULL` for the default section
5771 
5772   Example:
5773   A typical interpolated single-quad mesh might order points as
5774 .vb
5775   [c0, v1, v2, v3, v4, e5, e6, e7, e8]
5776 
5777   v4 -- e6 -- v3
5778   |           |
5779   e7    c0    e8
5780   |           |
5781   v1 -- e5 -- v2
5782 .ve
5783 
5784   (There is no significance to the ordering described here.)  The default section for a Q3 quad might typically assign
5785   dofs in the order of points, e.g.,
5786 .vb
5787     c0 -> [0,1,2,3]
5788     v1 -> [4]
5789     ...
5790     e5 -> [8, 9]
5791 .ve
5792 
5793   which corresponds to the dofs
5794 .vb
5795     6   10  11  7
5796     13  2   3   15
5797     12  0   1   14
5798     4   8   9   5
5799 .ve
5800 
5801   The closure in BFS ordering works through height strata (cells, edges, vertices) to produce the ordering
5802 .vb
5803   0 1 2 3 8 9 14 15 11 10 13 12 4 5 7 6
5804 .ve
5805 
5806   After calling DMPlexSetClosurePermutationTensor(), the closure will be ordered lexicographically,
5807 .vb
5808    4 8 9 5 12 0 1 14 13 2 3 15 6 10 11 7
5809 .ve
5810 
5811   Level: developer
5812 
5813   Notes:
5814   The point is used to determine the number of dofs/field on an edge. For SEM, this is related to the polynomial
5815   degree of the basis.
5816 
5817   This is required to run with libCEED.
5818 
5819 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMGetLocalSection()`, `PetscSectionSetClosurePermutation()`, `DMSetGlobalSection()`
5820 @*/
5821 PetscErrorCode DMPlexSetClosurePermutationTensor(DM dm, PetscInt point, PetscSection section)
5822 {
5823   DMLabel   label;
5824   PetscInt  dim, depth = -1, eStart = -1, Nf;
5825   PetscBool continuous = PETSC_TRUE, tensor = PETSC_TRUE;
5826 
5827   PetscFunctionBegin;
5828   PetscCall(DMGetDimension(dm, &dim));
5829   if (dim < 1) PetscFunctionReturn(PETSC_SUCCESS);
5830   if (point < 0) {
5831     PetscInt sStart, sEnd;
5832 
5833     PetscCall(DMPlexGetDepthStratum(dm, 1, &sStart, &sEnd));
5834     point = sEnd - sStart ? sStart : point;
5835   }
5836   PetscCall(DMPlexGetDepthLabel(dm, &label));
5837   if (point >= 0) PetscCall(DMLabelGetValue(label, point, &depth));
5838   if (!section) PetscCall(DMGetLocalSection(dm, &section));
5839   if (depth == 1) {
5840     eStart = point;
5841   } else if (depth == dim) {
5842     const PetscInt *cone;
5843 
5844     PetscCall(DMPlexGetCone(dm, point, &cone));
5845     if (dim == 2) eStart = cone[0];
5846     else if (dim == 3) {
5847       const PetscInt *cone2;
5848       PetscCall(DMPlexGetCone(dm, cone[0], &cone2));
5849       eStart = cone2[0];
5850     } 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);
5851   } 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);
5852 
5853   PetscCall(PetscSectionGetNumFields(section, &Nf));
5854   for (PetscInt d = 1; d <= dim; d++) {
5855     PetscInt  k, f, Nc, c, i, j, size = 0, offset = 0, foffset = 0;
5856     PetscInt *perm;
5857 
5858     for (f = 0; f < Nf; ++f) {
5859       PetscInt dof;
5860 
5861       PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
5862       PetscCheck(dim == 1 || tensor || !continuous, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Continuous field %" PetscInt_FMT " must have a tensor product discretization", f);
5863       if (!continuous && d < dim) continue;
5864       PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
5865       size += dof * Nc;
5866     }
5867     PetscCall(PetscMalloc1(size, &perm));
5868     for (f = 0; f < Nf; ++f) {
5869       switch (d) {
5870       case 1:
5871         PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
5872         if (!continuous && d < dim) continue;
5873         /*
5874          Original ordering is [ edge of length k-1; vtx0; vtx1 ]
5875          We want              [ vtx0; edge of length k-1; vtx1 ]
5876          */
5877         if (continuous) {
5878           for (c = 0; c < Nc; c++, offset++) perm[offset] = (k - 1) * Nc + c + foffset;
5879           for (i = 0; i < k - 1; i++)
5880             for (c = 0; c < Nc; c++, offset++) perm[offset] = i * Nc + c + foffset;
5881           for (c = 0; c < Nc; c++, offset++) perm[offset] = k * Nc + c + foffset;
5882           foffset = offset;
5883         } else {
5884           PetscInt dof;
5885 
5886           PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
5887           for (i = 0; i < dof * Nc; ++i, ++offset) perm[offset] = i + foffset;
5888           foffset = offset;
5889         }
5890         break;
5891       case 2:
5892         /* The original quad closure is oriented clockwise, {f, e_b, e_r, e_t, e_l, v_lb, v_rb, v_tr, v_tl} */
5893         PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
5894         if (!continuous && d < dim) continue;
5895         /* The SEM order is
5896 
5897          v_lb, {e_b}, v_rb,
5898          e^{(k-1)-i}_l, {f^{i*(k-1)}}, e^i_r,
5899          v_lt, reverse {e_t}, v_rt
5900          */
5901         if (continuous) {
5902           const PetscInt of   = 0;
5903           const PetscInt oeb  = of + PetscSqr(k - 1);
5904           const PetscInt oer  = oeb + (k - 1);
5905           const PetscInt oet  = oer + (k - 1);
5906           const PetscInt oel  = oet + (k - 1);
5907           const PetscInt ovlb = oel + (k - 1);
5908           const PetscInt ovrb = ovlb + 1;
5909           const PetscInt ovrt = ovrb + 1;
5910           const PetscInt ovlt = ovrt + 1;
5911           PetscInt       o;
5912 
5913           /* bottom */
5914           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlb * Nc + c + foffset;
5915           for (o = oeb; o < oer; ++o)
5916             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5917           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrb * Nc + c + foffset;
5918           /* middle */
5919           for (i = 0; i < k - 1; ++i) {
5920             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oel + (k - 2) - i) * Nc + c + foffset;
5921             for (o = of + (k - 1) * i; o < of + (k - 1) * (i + 1); ++o)
5922               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5923             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oer + i) * Nc + c + foffset;
5924           }
5925           /* top */
5926           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlt * Nc + c + foffset;
5927           for (o = oel - 1; o >= oet; --o)
5928             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5929           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrt * Nc + c + foffset;
5930           foffset = offset;
5931         } else {
5932           PetscInt dof;
5933 
5934           PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
5935           for (i = 0; i < dof * Nc; ++i, ++offset) perm[offset] = i + foffset;
5936           foffset = offset;
5937         }
5938         break;
5939       case 3:
5940         /* The original hex closure is
5941 
5942          {c,
5943          f_b, f_t, f_f, f_b, f_r, f_l,
5944          e_bl, e_bb, e_br, e_bf,  e_tf, e_tr, e_tb, e_tl,  e_rf, e_lf, e_lb, e_rb,
5945          v_blf, v_blb, v_brb, v_brf, v_tlf, v_trf, v_trb, v_tlb}
5946          */
5947         PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
5948         if (!continuous && d < dim) continue;
5949         /* The SEM order is
5950          Bottom Slice
5951          v_blf, {e^{(k-1)-n}_bf}, v_brf,
5952          e^{i}_bl, f^{n*(k-1)+(k-1)-i}_b, e^{(k-1)-i}_br,
5953          v_blb, {e_bb}, v_brb,
5954 
5955          Middle Slice (j)
5956          {e^{(k-1)-j}_lf}, {f^{j*(k-1)+n}_f}, e^j_rf,
5957          f^{i*(k-1)+j}_l, {c^{(j*(k-1) + i)*(k-1)+n}_t}, f^{j*(k-1)+i}_r,
5958          e^j_lb, {f^{j*(k-1)+(k-1)-n}_b}, e^{(k-1)-j}_rb,
5959 
5960          Top Slice
5961          v_tlf, {e_tf}, v_trf,
5962          e^{(k-1)-i}_tl, {f^{i*(k-1)}_t}, e^{i}_tr,
5963          v_tlb, {e^{(k-1)-n}_tb}, v_trb,
5964          */
5965         if (continuous) {
5966           const PetscInt oc    = 0;
5967           const PetscInt ofb   = oc + PetscSqr(k - 1) * (k - 1);
5968           const PetscInt oft   = ofb + PetscSqr(k - 1);
5969           const PetscInt off   = oft + PetscSqr(k - 1);
5970           const PetscInt ofk   = off + PetscSqr(k - 1);
5971           const PetscInt ofr   = ofk + PetscSqr(k - 1);
5972           const PetscInt ofl   = ofr + PetscSqr(k - 1);
5973           const PetscInt oebl  = ofl + PetscSqr(k - 1);
5974           const PetscInt oebb  = oebl + (k - 1);
5975           const PetscInt oebr  = oebb + (k - 1);
5976           const PetscInt oebf  = oebr + (k - 1);
5977           const PetscInt oetf  = oebf + (k - 1);
5978           const PetscInt oetr  = oetf + (k - 1);
5979           const PetscInt oetb  = oetr + (k - 1);
5980           const PetscInt oetl  = oetb + (k - 1);
5981           const PetscInt oerf  = oetl + (k - 1);
5982           const PetscInt oelf  = oerf + (k - 1);
5983           const PetscInt oelb  = oelf + (k - 1);
5984           const PetscInt oerb  = oelb + (k - 1);
5985           const PetscInt ovblf = oerb + (k - 1);
5986           const PetscInt ovblb = ovblf + 1;
5987           const PetscInt ovbrb = ovblb + 1;
5988           const PetscInt ovbrf = ovbrb + 1;
5989           const PetscInt ovtlf = ovbrf + 1;
5990           const PetscInt ovtrf = ovtlf + 1;
5991           const PetscInt ovtrb = ovtrf + 1;
5992           const PetscInt ovtlb = ovtrb + 1;
5993           PetscInt       o, n;
5994 
5995           /* Bottom Slice */
5996           /*   bottom */
5997           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblf * Nc + c + foffset;
5998           for (o = oetf - 1; o >= oebf; --o)
5999             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6000           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrf * Nc + c + foffset;
6001           /*   middle */
6002           for (i = 0; i < k - 1; ++i) {
6003             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebl + i) * Nc + c + foffset;
6004             for (n = 0; n < k - 1; ++n) {
6005               o = ofb + n * (k - 1) + i;
6006               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6007             }
6008             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebr + (k - 2) - i) * Nc + c + foffset;
6009           }
6010           /*   top */
6011           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblb * Nc + c + foffset;
6012           for (o = oebb; o < oebr; ++o)
6013             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6014           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrb * Nc + c + foffset;
6015 
6016           /* Middle Slice */
6017           for (j = 0; j < k - 1; ++j) {
6018             /*   bottom */
6019             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelf + (k - 2) - j) * Nc + c + foffset;
6020             for (o = off + j * (k - 1); o < off + (j + 1) * (k - 1); ++o)
6021               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6022             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerf + j) * Nc + c + foffset;
6023             /*   middle */
6024             for (i = 0; i < k - 1; ++i) {
6025               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofl + i * (k - 1) + j) * Nc + c + foffset;
6026               for (n = 0; n < k - 1; ++n)
6027                 for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oc + (j * (k - 1) + i) * (k - 1) + n) * Nc + c + foffset;
6028               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofr + j * (k - 1) + i) * Nc + c + foffset;
6029             }
6030             /*   top */
6031             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelb + j) * Nc + c + foffset;
6032             for (o = ofk + j * (k - 1) + (k - 2); o >= ofk + j * (k - 1); --o)
6033               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6034             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerb + (k - 2) - j) * Nc + c + foffset;
6035           }
6036 
6037           /* Top Slice */
6038           /*   bottom */
6039           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlf * Nc + c + foffset;
6040           for (o = oetf; o < oetr; ++o)
6041             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6042           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrf * Nc + c + foffset;
6043           /*   middle */
6044           for (i = 0; i < k - 1; ++i) {
6045             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetl + (k - 2) - i) * Nc + c + foffset;
6046             for (n = 0; n < k - 1; ++n)
6047               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oft + i * (k - 1) + n) * Nc + c + foffset;
6048             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetr + i) * Nc + c + foffset;
6049           }
6050           /*   top */
6051           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlb * Nc + c + foffset;
6052           for (o = oetl - 1; o >= oetb; --o)
6053             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6054           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrb * Nc + c + foffset;
6055 
6056           foffset = offset;
6057         } else {
6058           PetscInt dof;
6059 
6060           PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
6061           for (i = 0; i < dof * Nc; ++i, ++offset) perm[offset] = i + foffset;
6062           foffset = offset;
6063         }
6064         break;
6065       default:
6066         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "No spectral ordering for dimension %" PetscInt_FMT, d);
6067       }
6068     }
6069     PetscCheck(offset == size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Number of permutation entries %" PetscInt_FMT " != %" PetscInt_FMT, offset, size);
6070     /* Check permutation */
6071     {
6072       PetscInt *check;
6073 
6074       PetscCall(PetscMalloc1(size, &check));
6075       for (i = 0; i < size; ++i) {
6076         check[i] = -1;
6077         PetscCheck(perm[i] >= 0 && perm[i] < size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid permutation index p[%" PetscInt_FMT "] = %" PetscInt_FMT, i, perm[i]);
6078       }
6079       for (i = 0; i < size; ++i) check[perm[i]] = i;
6080       for (i = 0; i < size; ++i) PetscCheck(check[i] >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Missing permutation index %" PetscInt_FMT, i);
6081       PetscCall(PetscFree(check));
6082     }
6083     PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size, PETSC_OWN_POINTER, perm));
6084     if (d == dim) { // Add permutation for localized (in case this is a coordinate DM)
6085       PetscInt *loc_perm;
6086       PetscCall(PetscMalloc1(size * 2, &loc_perm));
6087       for (PetscInt i = 0; i < size; i++) {
6088         loc_perm[i]        = perm[i];
6089         loc_perm[size + i] = size + perm[i];
6090       }
6091       PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size * 2, PETSC_OWN_POINTER, loc_perm));
6092     }
6093   }
6094   PetscFunctionReturn(PETSC_SUCCESS);
6095 }
6096 
6097 PetscErrorCode DMPlexGetPointDualSpaceFEM(DM dm, PetscInt point, PetscInt field, PetscDualSpace *dspace)
6098 {
6099   PetscDS  prob;
6100   PetscInt depth, Nf, h;
6101   DMLabel  label;
6102 
6103   PetscFunctionBeginHot;
6104   PetscCall(DMGetDS(dm, &prob));
6105   Nf      = prob->Nf;
6106   label   = dm->depthLabel;
6107   *dspace = NULL;
6108   if (field < Nf) {
6109     PetscObject disc = prob->disc[field];
6110 
6111     if (disc->classid == PETSCFE_CLASSID) {
6112       PetscDualSpace dsp;
6113 
6114       PetscCall(PetscFEGetDualSpace((PetscFE)disc, &dsp));
6115       PetscCall(DMLabelGetNumValues(label, &depth));
6116       PetscCall(DMLabelGetValue(label, point, &h));
6117       h = depth - 1 - h;
6118       if (h) {
6119         PetscCall(PetscDualSpaceGetHeightSubspace(dsp, h, dspace));
6120       } else {
6121         *dspace = dsp;
6122       }
6123     }
6124   }
6125   PetscFunctionReturn(PETSC_SUCCESS);
6126 }
6127 
6128 static inline PetscErrorCode DMPlexVecGetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6129 {
6130   PetscScalar       *array;
6131   const PetscScalar *vArray;
6132   const PetscInt    *cone, *coneO;
6133   PetscInt           pStart, pEnd, p, numPoints, size = 0, offset = 0;
6134 
6135   PetscFunctionBeginHot;
6136   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6137   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
6138   PetscCall(DMPlexGetCone(dm, point, &cone));
6139   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
6140   if (!values || !*values) {
6141     if ((point >= pStart) && (point < pEnd)) {
6142       PetscInt dof;
6143 
6144       PetscCall(PetscSectionGetDof(section, point, &dof));
6145       size += dof;
6146     }
6147     for (p = 0; p < numPoints; ++p) {
6148       const PetscInt cp = cone[p];
6149       PetscInt       dof;
6150 
6151       if ((cp < pStart) || (cp >= pEnd)) continue;
6152       PetscCall(PetscSectionGetDof(section, cp, &dof));
6153       size += dof;
6154     }
6155     if (!values) {
6156       if (csize) *csize = size;
6157       PetscFunctionReturn(PETSC_SUCCESS);
6158     }
6159     PetscCall(DMGetWorkArray(dm, size, MPIU_SCALAR, &array));
6160   } else {
6161     array = *values;
6162   }
6163   size = 0;
6164   PetscCall(VecGetArrayRead(v, &vArray));
6165   if ((point >= pStart) && (point < pEnd)) {
6166     PetscInt           dof, off, d;
6167     const PetscScalar *varr;
6168 
6169     PetscCall(PetscSectionGetDof(section, point, &dof));
6170     PetscCall(PetscSectionGetOffset(section, point, &off));
6171     varr = PetscSafePointerPlusOffset(vArray, off);
6172     for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
6173     size += dof;
6174   }
6175   for (p = 0; p < numPoints; ++p) {
6176     const PetscInt     cp = cone[p];
6177     PetscInt           o  = coneO[p];
6178     PetscInt           dof, off, d;
6179     const PetscScalar *varr;
6180 
6181     if ((cp < pStart) || (cp >= pEnd)) continue;
6182     PetscCall(PetscSectionGetDof(section, cp, &dof));
6183     PetscCall(PetscSectionGetOffset(section, cp, &off));
6184     varr = PetscSafePointerPlusOffset(vArray, off);
6185     if (o >= 0) {
6186       for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
6187     } else {
6188       for (d = dof - 1; d >= 0; --d, ++offset) array[offset] = varr[d];
6189     }
6190     size += dof;
6191   }
6192   PetscCall(VecRestoreArrayRead(v, &vArray));
6193   if (!*values) {
6194     if (csize) *csize = size;
6195     *values = array;
6196   } else {
6197     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
6198     *csize = size;
6199   }
6200   PetscFunctionReturn(PETSC_SUCCESS);
6201 }
6202 
6203 /* Compress out points not in the section */
6204 static inline PetscErrorCode CompressPoints_Private(PetscSection section, PetscInt *numPoints, PetscInt points[])
6205 {
6206   const PetscInt np = *numPoints;
6207   PetscInt       pStart, pEnd, p, q;
6208 
6209   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6210   for (p = 0, q = 0; p < np; ++p) {
6211     const PetscInt r = points[p * 2];
6212     if ((r >= pStart) && (r < pEnd)) {
6213       points[q * 2]     = r;
6214       points[q * 2 + 1] = points[p * 2 + 1];
6215       ++q;
6216     }
6217   }
6218   *numPoints = q;
6219   return PETSC_SUCCESS;
6220 }
6221 
6222 /* Compressed closure does not apply closure permutation */
6223 PetscErrorCode DMPlexGetCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt ornt, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
6224 {
6225   const PetscInt *cla = NULL;
6226   PetscInt        np, *pts = NULL;
6227 
6228   PetscFunctionBeginHot;
6229   PetscCall(PetscSectionGetClosureIndex(section, (PetscObject)dm, clSec, clPoints));
6230   if (!ornt && *clPoints) {
6231     PetscInt dof, off;
6232 
6233     PetscCall(PetscSectionGetDof(*clSec, point, &dof));
6234     PetscCall(PetscSectionGetOffset(*clSec, point, &off));
6235     PetscCall(ISGetIndices(*clPoints, &cla));
6236     np  = dof / 2;
6237     pts = PetscSafePointerPlusOffset((PetscInt *)cla, off);
6238   } else {
6239     PetscCall(DMPlexGetTransitiveClosure_Internal(dm, point, ornt, PETSC_TRUE, &np, &pts));
6240     PetscCall(CompressPoints_Private(section, &np, pts));
6241   }
6242   *numPoints = np;
6243   *points    = pts;
6244   *clp       = cla;
6245   PetscFunctionReturn(PETSC_SUCCESS);
6246 }
6247 
6248 PetscErrorCode DMPlexRestoreCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
6249 {
6250   PetscFunctionBeginHot;
6251   if (!*clPoints) {
6252     PetscCall(DMPlexRestoreTransitiveClosure(dm, point, PETSC_TRUE, numPoints, points));
6253   } else {
6254     PetscCall(ISRestoreIndices(*clPoints, clp));
6255   }
6256   *numPoints = 0;
6257   *points    = NULL;
6258   *clSec     = NULL;
6259   *clPoints  = NULL;
6260   *clp       = NULL;
6261   PetscFunctionReturn(PETSC_SUCCESS);
6262 }
6263 
6264 static inline PetscErrorCode DMPlexVecGetClosure_Static(DM dm, PetscSection section, PetscInt numPoints, const PetscInt points[], const PetscInt clperm[], const PetscScalar vArray[], PetscInt *size, PetscScalar array[])
6265 {
6266   PetscInt            offset = 0, p;
6267   const PetscInt    **perms  = NULL;
6268   const PetscScalar **flips  = NULL;
6269 
6270   PetscFunctionBeginHot;
6271   *size = 0;
6272   PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
6273   for (p = 0; p < numPoints; p++) {
6274     const PetscInt     point = points[2 * p];
6275     const PetscInt    *perm  = perms ? perms[p] : NULL;
6276     const PetscScalar *flip  = flips ? flips[p] : NULL;
6277     PetscInt           dof, off, d;
6278     const PetscScalar *varr;
6279 
6280     PetscCall(PetscSectionGetDof(section, point, &dof));
6281     PetscCall(PetscSectionGetOffset(section, point, &off));
6282     varr = PetscSafePointerPlusOffset(vArray, off);
6283     if (clperm) {
6284       if (perm) {
6285         for (d = 0; d < dof; d++) array[clperm[offset + perm[d]]] = varr[d];
6286       } else {
6287         for (d = 0; d < dof; d++) array[clperm[offset + d]] = varr[d];
6288       }
6289       if (flip) {
6290         for (d = 0; d < dof; d++) array[clperm[offset + d]] *= flip[d];
6291       }
6292     } else {
6293       if (perm) {
6294         for (d = 0; d < dof; d++) array[offset + perm[d]] = varr[d];
6295       } else {
6296         for (d = 0; d < dof; d++) array[offset + d] = varr[d];
6297       }
6298       if (flip) {
6299         for (d = 0; d < dof; d++) array[offset + d] *= flip[d];
6300       }
6301     }
6302     offset += dof;
6303   }
6304   PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
6305   *size = offset;
6306   PetscFunctionReturn(PETSC_SUCCESS);
6307 }
6308 
6309 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[])
6310 {
6311   PetscInt offset = 0, f;
6312 
6313   PetscFunctionBeginHot;
6314   *size = 0;
6315   for (f = 0; f < numFields; ++f) {
6316     PetscInt            p;
6317     const PetscInt    **perms = NULL;
6318     const PetscScalar **flips = NULL;
6319 
6320     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6321     for (p = 0; p < numPoints; p++) {
6322       const PetscInt     point = points[2 * p];
6323       PetscInt           fdof, foff, b;
6324       const PetscScalar *varr;
6325       const PetscInt    *perm = perms ? perms[p] : NULL;
6326       const PetscScalar *flip = flips ? flips[p] : NULL;
6327 
6328       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6329       PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6330       varr = &vArray[foff];
6331       if (clperm) {
6332         if (perm) {
6333           for (b = 0; b < fdof; b++) array[clperm[offset + perm[b]]] = varr[b];
6334         } else {
6335           for (b = 0; b < fdof; b++) array[clperm[offset + b]] = varr[b];
6336         }
6337         if (flip) {
6338           for (b = 0; b < fdof; b++) array[clperm[offset + b]] *= flip[b];
6339         }
6340       } else {
6341         if (perm) {
6342           for (b = 0; b < fdof; b++) array[offset + perm[b]] = varr[b];
6343         } else {
6344           for (b = 0; b < fdof; b++) array[offset + b] = varr[b];
6345         }
6346         if (flip) {
6347           for (b = 0; b < fdof; b++) array[offset + b] *= flip[b];
6348         }
6349       }
6350       offset += fdof;
6351     }
6352     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6353   }
6354   *size = offset;
6355   PetscFunctionReturn(PETSC_SUCCESS);
6356 }
6357 
6358 PetscErrorCode DMPlexVecGetOrientedClosure_Internal(DM dm, PetscSection section, PetscBool useClPerm, Vec v, PetscInt point, PetscInt ornt, PetscInt *csize, PetscScalar *values[])
6359 {
6360   PetscSection    clSection;
6361   IS              clPoints;
6362   PetscInt       *points = NULL;
6363   const PetscInt *clp, *perm = NULL;
6364   PetscInt        depth, numFields, numPoints, asize;
6365 
6366   PetscFunctionBeginHot;
6367   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6368   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6369   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6370   PetscValidHeaderSpecific(v, VEC_CLASSID, 4);
6371   PetscCall(DMPlexGetDepth(dm, &depth));
6372   PetscCall(PetscSectionGetNumFields(section, &numFields));
6373   if (depth == 1 && numFields < 2) {
6374     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6375     PetscFunctionReturn(PETSC_SUCCESS);
6376   }
6377   /* Get points */
6378   PetscCall(DMPlexGetCompressedClosure(dm, section, point, ornt, &numPoints, &points, &clSection, &clPoints, &clp));
6379   /* Get sizes */
6380   asize = 0;
6381   for (PetscInt p = 0; p < numPoints * 2; p += 2) {
6382     PetscInt dof;
6383     PetscCall(PetscSectionGetDof(section, points[p], &dof));
6384     asize += dof;
6385   }
6386   if (values) {
6387     const PetscScalar *vArray;
6388     PetscInt           size;
6389 
6390     if (*values) {
6391       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);
6392     } else PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, values));
6393     if (useClPerm) PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, asize, &perm));
6394     PetscCall(VecGetArrayRead(v, &vArray));
6395     /* Get values */
6396     if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, numPoints, points, numFields, perm, vArray, &size, *values));
6397     else PetscCall(DMPlexVecGetClosure_Static(dm, section, numPoints, points, perm, vArray, &size, *values));
6398     PetscCheck(asize == size, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Section size %" PetscInt_FMT " does not match Vec closure size %" PetscInt_FMT, asize, size);
6399     /* Cleanup array */
6400     PetscCall(VecRestoreArrayRead(v, &vArray));
6401   }
6402   if (csize) *csize = asize;
6403   /* Cleanup points */
6404   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6405   PetscFunctionReturn(PETSC_SUCCESS);
6406 }
6407 
6408 /*@C
6409   DMPlexVecGetClosure - Get an array of the values on the closure of 'point'
6410 
6411   Not collective
6412 
6413   Input Parameters:
6414 + dm      - The `DM`
6415 . section - The section describing the layout in `v`, or `NULL` to use the default section
6416 . v       - The local vector
6417 - point   - The point in the `DM`
6418 
6419   Input/Output Parameters:
6420 + csize  - The size of the input values array, or `NULL`; on output the number of values in the closure
6421 - values - An array to use for the values, or `NULL` to have it allocated automatically;
6422            if the user provided `NULL`, it is a borrowed array and should not be freed
6423 
6424   Level: intermediate
6425 
6426   Notes:
6427   `DMPlexVecGetClosure()`/`DMPlexVecRestoreClosure()` only allocates the values array if it set to `NULL` in the
6428   calling function. This is because `DMPlexVecGetClosure()` is typically called in the inner loop of a `Vec` or `Mat`
6429   assembly function, and a user may already have allocated storage for this operation.
6430 
6431   A typical use could be
6432 .vb
6433    values = NULL;
6434    PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
6435    for (cl = 0; cl < clSize; ++cl) {
6436      <Compute on closure>
6437    }
6438    PetscCall(DMPlexVecRestoreClosure(dm, NULL, v, p, &clSize, &values));
6439 .ve
6440   or
6441 .vb
6442    PetscMalloc1(clMaxSize, &values);
6443    for (p = pStart; p < pEnd; ++p) {
6444      clSize = clMaxSize;
6445      PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
6446      for (cl = 0; cl < clSize; ++cl) {
6447        <Compute on closure>
6448      }
6449    }
6450    PetscFree(values);
6451 .ve
6452 
6453   Fortran Notes:
6454   The `csize` argument is not present in the Fortran binding since it is internal to the array.
6455 
6456 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecRestoreClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6457 @*/
6458 PetscErrorCode DMPlexVecGetClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6459 {
6460   PetscFunctionBeginHot;
6461   PetscCall(DMPlexVecGetOrientedClosure_Internal(dm, section, PETSC_TRUE, v, point, 0, csize, values));
6462   PetscFunctionReturn(PETSC_SUCCESS);
6463 }
6464 
6465 PetscErrorCode DMPlexVecGetClosureAtDepth_Internal(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt depth, PetscInt *csize, PetscScalar *values[])
6466 {
6467   DMLabel            depthLabel;
6468   PetscSection       clSection;
6469   IS                 clPoints;
6470   PetscScalar       *array;
6471   const PetscScalar *vArray;
6472   PetscInt          *points = NULL;
6473   const PetscInt    *clp, *perm = NULL;
6474   PetscInt           mdepth, numFields, numPoints, Np = 0, p, clsize, size;
6475 
6476   PetscFunctionBeginHot;
6477   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6478   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6479   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6480   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6481   PetscCall(DMPlexGetDepth(dm, &mdepth));
6482   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
6483   PetscCall(PetscSectionGetNumFields(section, &numFields));
6484   if (mdepth == 1 && numFields < 2) {
6485     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6486     PetscFunctionReturn(PETSC_SUCCESS);
6487   }
6488   /* Get points */
6489   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
6490   for (clsize = 0, p = 0; p < Np; p++) {
6491     PetscInt dof;
6492     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
6493     clsize += dof;
6494   }
6495   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &perm));
6496   /* Filter points */
6497   for (p = 0; p < numPoints * 2; p += 2) {
6498     PetscInt dep;
6499 
6500     PetscCall(DMLabelGetValue(depthLabel, points[p], &dep));
6501     if (dep != depth) continue;
6502     points[Np * 2 + 0] = points[p];
6503     points[Np * 2 + 1] = points[p + 1];
6504     ++Np;
6505   }
6506   /* Get array */
6507   if (!values || !*values) {
6508     PetscInt asize = 0, dof;
6509 
6510     for (p = 0; p < Np * 2; p += 2) {
6511       PetscCall(PetscSectionGetDof(section, points[p], &dof));
6512       asize += dof;
6513     }
6514     if (!values) {
6515       PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6516       if (csize) *csize = asize;
6517       PetscFunctionReturn(PETSC_SUCCESS);
6518     }
6519     PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, &array));
6520   } else {
6521     array = *values;
6522   }
6523   PetscCall(VecGetArrayRead(v, &vArray));
6524   /* Get values */
6525   if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, Np, points, numFields, perm, vArray, &size, array));
6526   else PetscCall(DMPlexVecGetClosure_Static(dm, section, Np, points, perm, vArray, &size, array));
6527   /* Cleanup points */
6528   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6529   /* Cleanup array */
6530   PetscCall(VecRestoreArrayRead(v, &vArray));
6531   if (!*values) {
6532     if (csize) *csize = size;
6533     *values = array;
6534   } else {
6535     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
6536     *csize = size;
6537   }
6538   PetscFunctionReturn(PETSC_SUCCESS);
6539 }
6540 
6541 /*@C
6542   DMPlexVecRestoreClosure - Restore the array of the values on the closure of 'point'
6543 
6544   Not collective
6545 
6546   Input Parameters:
6547 + dm      - The `DM`
6548 . section - The section describing the layout in `v`, or `NULL` to use the default section
6549 . v       - The local vector
6550 . point   - The point in the `DM`
6551 . csize   - The number of values in the closure, or `NULL`
6552 - values  - The array of values, which is a borrowed array and should not be freed
6553 
6554   Level: intermediate
6555 
6556   Note:
6557   The array values are discarded and not copied back into `v`. In order to copy values back to `v`, use `DMPlexVecSetClosure()`
6558 
6559   Fortran Notes:
6560   The `csize` argument is not present in the Fortran binding since it is internal to the array.
6561 
6562 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6563 @*/
6564 PetscErrorCode DMPlexVecRestoreClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6565 {
6566   PetscInt size = 0;
6567 
6568   PetscFunctionBegin;
6569   /* Should work without recalculating size */
6570   PetscCall(DMRestoreWorkArray(dm, size, MPIU_SCALAR, (void *)values));
6571   *values = NULL;
6572   PetscFunctionReturn(PETSC_SUCCESS);
6573 }
6574 
6575 static inline void add(PetscScalar *x, PetscScalar y)
6576 {
6577   *x += y;
6578 }
6579 static inline void insert(PetscScalar *x, PetscScalar y)
6580 {
6581   *x = y;
6582 }
6583 
6584 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[])
6585 {
6586   PetscInt        cdof;  /* The number of constraints on this point */
6587   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6588   PetscScalar    *a;
6589   PetscInt        off, cind = 0, k;
6590 
6591   PetscFunctionBegin;
6592   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6593   PetscCall(PetscSectionGetOffset(section, point, &off));
6594   a = &array[off];
6595   if (!cdof || setBC) {
6596     if (clperm) {
6597       if (perm) {
6598         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6599       } else {
6600         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6601       }
6602     } else {
6603       if (perm) {
6604         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6605       } else {
6606         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6607       }
6608     }
6609   } else {
6610     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6611     if (clperm) {
6612       if (perm) {
6613         for (k = 0; k < dof; ++k) {
6614           if ((cind < cdof) && (k == cdofs[cind])) {
6615             ++cind;
6616             continue;
6617           }
6618           fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6619         }
6620       } else {
6621         for (k = 0; k < dof; ++k) {
6622           if ((cind < cdof) && (k == cdofs[cind])) {
6623             ++cind;
6624             continue;
6625           }
6626           fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6627         }
6628       }
6629     } else {
6630       if (perm) {
6631         for (k = 0; k < dof; ++k) {
6632           if ((cind < cdof) && (k == cdofs[cind])) {
6633             ++cind;
6634             continue;
6635           }
6636           fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6637         }
6638       } else {
6639         for (k = 0; k < dof; ++k) {
6640           if ((cind < cdof) && (k == cdofs[cind])) {
6641             ++cind;
6642             continue;
6643           }
6644           fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6645         }
6646       }
6647     }
6648   }
6649   PetscFunctionReturn(PETSC_SUCCESS);
6650 }
6651 
6652 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[])
6653 {
6654   PetscInt        cdof;  /* The number of constraints on this point */
6655   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6656   PetscScalar    *a;
6657   PetscInt        off, cind = 0, k;
6658 
6659   PetscFunctionBegin;
6660   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6661   PetscCall(PetscSectionGetOffset(section, point, &off));
6662   a = &array[off];
6663   if (cdof) {
6664     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6665     if (clperm) {
6666       if (perm) {
6667         for (k = 0; k < dof; ++k) {
6668           if ((cind < cdof) && (k == cdofs[cind])) {
6669             fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6670             cind++;
6671           }
6672         }
6673       } else {
6674         for (k = 0; k < dof; ++k) {
6675           if ((cind < cdof) && (k == cdofs[cind])) {
6676             fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6677             cind++;
6678           }
6679         }
6680       }
6681     } else {
6682       if (perm) {
6683         for (k = 0; k < dof; ++k) {
6684           if ((cind < cdof) && (k == cdofs[cind])) {
6685             fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6686             cind++;
6687           }
6688         }
6689       } else {
6690         for (k = 0; k < dof; ++k) {
6691           if ((cind < cdof) && (k == cdofs[cind])) {
6692             fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6693             cind++;
6694           }
6695         }
6696       }
6697     }
6698   }
6699   PetscFunctionReturn(PETSC_SUCCESS);
6700 }
6701 
6702 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[])
6703 {
6704   PetscScalar    *a;
6705   PetscInt        fdof, foff, fcdof, foffset = *offset;
6706   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6707   PetscInt        cind = 0, b;
6708 
6709   PetscFunctionBegin;
6710   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6711   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6712   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6713   a = &array[foff];
6714   if (!fcdof || setBC) {
6715     if (clperm) {
6716       if (perm) {
6717         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6718       } else {
6719         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6720       }
6721     } else {
6722       if (perm) {
6723         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6724       } else {
6725         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6726       }
6727     }
6728   } else {
6729     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6730     if (clperm) {
6731       if (perm) {
6732         for (b = 0; b < fdof; b++) {
6733           if ((cind < fcdof) && (b == fcdofs[cind])) {
6734             ++cind;
6735             continue;
6736           }
6737           fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6738         }
6739       } else {
6740         for (b = 0; b < fdof; b++) {
6741           if ((cind < fcdof) && (b == fcdofs[cind])) {
6742             ++cind;
6743             continue;
6744           }
6745           fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6746         }
6747       }
6748     } else {
6749       if (perm) {
6750         for (b = 0; b < fdof; b++) {
6751           if ((cind < fcdof) && (b == fcdofs[cind])) {
6752             ++cind;
6753             continue;
6754           }
6755           fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6756         }
6757       } else {
6758         for (b = 0; b < fdof; b++) {
6759           if ((cind < fcdof) && (b == fcdofs[cind])) {
6760             ++cind;
6761             continue;
6762           }
6763           fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6764         }
6765       }
6766     }
6767   }
6768   *offset += fdof;
6769   PetscFunctionReturn(PETSC_SUCCESS);
6770 }
6771 
6772 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[])
6773 {
6774   PetscScalar    *a;
6775   PetscInt        fdof, foff, fcdof, foffset = *offset;
6776   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6777   PetscInt        Nc, cind = 0, ncind = 0, b;
6778   PetscBool       ncSet, fcSet;
6779 
6780   PetscFunctionBegin;
6781   PetscCall(PetscSectionGetFieldComponents(section, f, &Nc));
6782   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6783   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6784   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6785   a = &array[foff];
6786   if (fcdof) {
6787     /* We just override fcdof and fcdofs with Ncc and comps */
6788     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6789     if (clperm) {
6790       if (perm) {
6791         if (comps) {
6792           for (b = 0; b < fdof; b++) {
6793             ncSet = fcSet = PETSC_FALSE;
6794             if (b % Nc == comps[ncind]) {
6795               ncind = (ncind + 1) % Ncc;
6796               ncSet = PETSC_TRUE;
6797             }
6798             if ((cind < fcdof) && (b == fcdofs[cind])) {
6799               ++cind;
6800               fcSet = PETSC_TRUE;
6801             }
6802             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6803           }
6804         } else {
6805           for (b = 0; b < fdof; b++) {
6806             if ((cind < fcdof) && (b == fcdofs[cind])) {
6807               fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6808               ++cind;
6809             }
6810           }
6811         }
6812       } else {
6813         if (comps) {
6814           for (b = 0; b < fdof; b++) {
6815             ncSet = fcSet = PETSC_FALSE;
6816             if (b % Nc == comps[ncind]) {
6817               ncind = (ncind + 1) % Ncc;
6818               ncSet = PETSC_TRUE;
6819             }
6820             if ((cind < fcdof) && (b == fcdofs[cind])) {
6821               ++cind;
6822               fcSet = PETSC_TRUE;
6823             }
6824             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6825           }
6826         } else {
6827           for (b = 0; b < fdof; b++) {
6828             if ((cind < fcdof) && (b == fcdofs[cind])) {
6829               fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6830               ++cind;
6831             }
6832           }
6833         }
6834       }
6835     } else {
6836       if (perm) {
6837         if (comps) {
6838           for (b = 0; b < fdof; b++) {
6839             ncSet = fcSet = PETSC_FALSE;
6840             if (b % Nc == comps[ncind]) {
6841               ncind = (ncind + 1) % Ncc;
6842               ncSet = PETSC_TRUE;
6843             }
6844             if ((cind < fcdof) && (b == fcdofs[cind])) {
6845               ++cind;
6846               fcSet = PETSC_TRUE;
6847             }
6848             if (ncSet && fcSet) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6849           }
6850         } else {
6851           for (b = 0; b < fdof; b++) {
6852             if ((cind < fcdof) && (b == fcdofs[cind])) {
6853               fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6854               ++cind;
6855             }
6856           }
6857         }
6858       } else {
6859         if (comps) {
6860           for (b = 0; b < fdof; b++) {
6861             ncSet = fcSet = PETSC_FALSE;
6862             if (b % Nc == comps[ncind]) {
6863               ncind = (ncind + 1) % Ncc;
6864               ncSet = PETSC_TRUE;
6865             }
6866             if ((cind < fcdof) && (b == fcdofs[cind])) {
6867               ++cind;
6868               fcSet = PETSC_TRUE;
6869             }
6870             if (ncSet && fcSet) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6871           }
6872         } else {
6873           for (b = 0; b < fdof; b++) {
6874             if ((cind < fcdof) && (b == fcdofs[cind])) {
6875               fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6876               ++cind;
6877             }
6878           }
6879         }
6880       }
6881     }
6882   }
6883   *offset += fdof;
6884   PetscFunctionReturn(PETSC_SUCCESS);
6885 }
6886 
6887 static inline PetscErrorCode DMPlexVecSetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6888 {
6889   PetscScalar    *array;
6890   const PetscInt *cone, *coneO;
6891   PetscInt        pStart, pEnd, p, numPoints, off, dof;
6892 
6893   PetscFunctionBeginHot;
6894   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6895   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
6896   PetscCall(DMPlexGetCone(dm, point, &cone));
6897   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
6898   PetscCall(VecGetArray(v, &array));
6899   for (p = 0, off = 0; p <= numPoints; ++p, off += dof) {
6900     const PetscInt cp = !p ? point : cone[p - 1];
6901     const PetscInt o  = !p ? 0 : coneO[p - 1];
6902 
6903     if ((cp < pStart) || (cp >= pEnd)) {
6904       dof = 0;
6905       continue;
6906     }
6907     PetscCall(PetscSectionGetDof(section, cp, &dof));
6908     /* ADD_VALUES */
6909     {
6910       const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6911       PetscScalar    *a;
6912       PetscInt        cdof, coff, cind = 0, k;
6913 
6914       PetscCall(PetscSectionGetConstraintDof(section, cp, &cdof));
6915       PetscCall(PetscSectionGetOffset(section, cp, &coff));
6916       a = &array[coff];
6917       if (!cdof) {
6918         if (o >= 0) {
6919           for (k = 0; k < dof; ++k) a[k] += values[off + k];
6920         } else {
6921           for (k = 0; k < dof; ++k) a[k] += values[off + dof - k - 1];
6922         }
6923       } else {
6924         PetscCall(PetscSectionGetConstraintIndices(section, cp, &cdofs));
6925         if (o >= 0) {
6926           for (k = 0; k < dof; ++k) {
6927             if ((cind < cdof) && (k == cdofs[cind])) {
6928               ++cind;
6929               continue;
6930             }
6931             a[k] += values[off + k];
6932           }
6933         } else {
6934           for (k = 0; k < dof; ++k) {
6935             if ((cind < cdof) && (k == cdofs[cind])) {
6936               ++cind;
6937               continue;
6938             }
6939             a[k] += values[off + dof - k - 1];
6940           }
6941         }
6942       }
6943     }
6944   }
6945   PetscCall(VecRestoreArray(v, &array));
6946   PetscFunctionReturn(PETSC_SUCCESS);
6947 }
6948 
6949 /*@C
6950   DMPlexVecSetClosure - Set an array of the values on the closure of `point`
6951 
6952   Not collective
6953 
6954   Input Parameters:
6955 + dm      - The `DM`
6956 . section - The section describing the layout in `v`, or `NULL` to use the default section
6957 . v       - The local vector
6958 . point   - The point in the `DM`
6959 . values  - The array of values
6960 - mode    - The insert mode. One of `INSERT_ALL_VALUES`, `ADD_ALL_VALUES`, `INSERT_VALUES`, `ADD_VALUES`, `INSERT_BC_VALUES`, and `ADD_BC_VALUES`,
6961          where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions.
6962 
6963   Level: intermediate
6964 
6965 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`
6966 @*/
6967 PetscErrorCode DMPlexVecSetClosure(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6968 {
6969   PetscSection    clSection;
6970   IS              clPoints;
6971   PetscScalar    *array;
6972   PetscInt       *points = NULL;
6973   const PetscInt *clp, *clperm = NULL;
6974   PetscInt        depth, numFields, numPoints, p, clsize;
6975 
6976   PetscFunctionBeginHot;
6977   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6978   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6979   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6980   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6981   PetscCall(DMPlexGetDepth(dm, &depth));
6982   PetscCall(PetscSectionGetNumFields(section, &numFields));
6983   if (depth == 1 && numFields < 2 && mode == ADD_VALUES) {
6984     PetscCall(DMPlexVecSetClosure_Depth1_Static(dm, section, v, point, values, mode));
6985     PetscFunctionReturn(PETSC_SUCCESS);
6986   }
6987   /* Get points */
6988   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
6989   for (clsize = 0, p = 0; p < numPoints; p++) {
6990     PetscInt dof;
6991     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
6992     clsize += dof;
6993   }
6994   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
6995   /* Get array */
6996   PetscCall(VecGetArray(v, &array));
6997   /* Get values */
6998   if (numFields > 0) {
6999     PetscInt offset = 0, f;
7000     for (f = 0; f < numFields; ++f) {
7001       const PetscInt    **perms = NULL;
7002       const PetscScalar **flips = NULL;
7003 
7004       PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7005       switch (mode) {
7006       case INSERT_VALUES:
7007         for (p = 0; p < numPoints; p++) {
7008           const PetscInt     point = points[2 * p];
7009           const PetscInt    *perm  = perms ? perms[p] : NULL;
7010           const PetscScalar *flip  = flips ? flips[p] : NULL;
7011           PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, clperm, values, &offset, array));
7012         }
7013         break;
7014       case INSERT_ALL_VALUES:
7015         for (p = 0; p < numPoints; p++) {
7016           const PetscInt     point = points[2 * p];
7017           const PetscInt    *perm  = perms ? perms[p] : NULL;
7018           const PetscScalar *flip  = flips ? flips[p] : NULL;
7019           PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, clperm, values, &offset, array));
7020         }
7021         break;
7022       case INSERT_BC_VALUES:
7023         for (p = 0; p < numPoints; p++) {
7024           const PetscInt     point = points[2 * p];
7025           const PetscInt    *perm  = perms ? perms[p] : NULL;
7026           const PetscScalar *flip  = flips ? flips[p] : NULL;
7027           PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, insert, clperm, values, &offset, array));
7028         }
7029         break;
7030       case ADD_VALUES:
7031         for (p = 0; p < numPoints; p++) {
7032           const PetscInt     point = points[2 * p];
7033           const PetscInt    *perm  = perms ? perms[p] : NULL;
7034           const PetscScalar *flip  = flips ? flips[p] : NULL;
7035           PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, clperm, values, &offset, array));
7036         }
7037         break;
7038       case ADD_ALL_VALUES:
7039         for (p = 0; p < numPoints; p++) {
7040           const PetscInt     point = points[2 * p];
7041           const PetscInt    *perm  = perms ? perms[p] : NULL;
7042           const PetscScalar *flip  = flips ? flips[p] : NULL;
7043           PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, clperm, values, &offset, array));
7044         }
7045         break;
7046       case ADD_BC_VALUES:
7047         for (p = 0; p < numPoints; p++) {
7048           const PetscInt     point = points[2 * p];
7049           const PetscInt    *perm  = perms ? perms[p] : NULL;
7050           const PetscScalar *flip  = flips ? flips[p] : NULL;
7051           PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, add, clperm, values, &offset, array));
7052         }
7053         break;
7054       default:
7055         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
7056       }
7057       PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7058     }
7059   } else {
7060     PetscInt            dof, off;
7061     const PetscInt    **perms = NULL;
7062     const PetscScalar **flips = NULL;
7063 
7064     PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
7065     switch (mode) {
7066     case INSERT_VALUES:
7067       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7068         const PetscInt     point = points[2 * p];
7069         const PetscInt    *perm  = perms ? perms[p] : NULL;
7070         const PetscScalar *flip  = flips ? flips[p] : NULL;
7071         PetscCall(PetscSectionGetDof(section, point, &dof));
7072         PetscCall(updatePoint_private(section, point, dof, insert, PETSC_FALSE, perm, flip, clperm, values, off, array));
7073       }
7074       break;
7075     case INSERT_ALL_VALUES:
7076       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7077         const PetscInt     point = points[2 * p];
7078         const PetscInt    *perm  = perms ? perms[p] : NULL;
7079         const PetscScalar *flip  = flips ? flips[p] : NULL;
7080         PetscCall(PetscSectionGetDof(section, point, &dof));
7081         PetscCall(updatePoint_private(section, point, dof, insert, PETSC_TRUE, perm, flip, clperm, values, off, array));
7082       }
7083       break;
7084     case INSERT_BC_VALUES:
7085       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7086         const PetscInt     point = points[2 * p];
7087         const PetscInt    *perm  = perms ? perms[p] : NULL;
7088         const PetscScalar *flip  = flips ? flips[p] : NULL;
7089         PetscCall(PetscSectionGetDof(section, point, &dof));
7090         PetscCall(updatePointBC_private(section, point, dof, insert, perm, flip, clperm, values, off, array));
7091       }
7092       break;
7093     case ADD_VALUES:
7094       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7095         const PetscInt     point = points[2 * p];
7096         const PetscInt    *perm  = perms ? perms[p] : NULL;
7097         const PetscScalar *flip  = flips ? flips[p] : NULL;
7098         PetscCall(PetscSectionGetDof(section, point, &dof));
7099         PetscCall(updatePoint_private(section, point, dof, add, PETSC_FALSE, perm, flip, clperm, values, off, array));
7100       }
7101       break;
7102     case ADD_ALL_VALUES:
7103       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7104         const PetscInt     point = points[2 * p];
7105         const PetscInt    *perm  = perms ? perms[p] : NULL;
7106         const PetscScalar *flip  = flips ? flips[p] : NULL;
7107         PetscCall(PetscSectionGetDof(section, point, &dof));
7108         PetscCall(updatePoint_private(section, point, dof, add, PETSC_TRUE, perm, flip, clperm, values, off, array));
7109       }
7110       break;
7111     case ADD_BC_VALUES:
7112       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7113         const PetscInt     point = points[2 * p];
7114         const PetscInt    *perm  = perms ? perms[p] : NULL;
7115         const PetscScalar *flip  = flips ? flips[p] : NULL;
7116         PetscCall(PetscSectionGetDof(section, point, &dof));
7117         PetscCall(updatePointBC_private(section, point, dof, add, perm, flip, clperm, values, off, array));
7118       }
7119       break;
7120     default:
7121       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
7122     }
7123     PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
7124   }
7125   /* Cleanup points */
7126   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
7127   /* Cleanup array */
7128   PetscCall(VecRestoreArray(v, &array));
7129   PetscFunctionReturn(PETSC_SUCCESS);
7130 }
7131 
7132 /* Check whether the given point is in the label. If not, update the offset to skip this point */
7133 static inline PetscErrorCode CheckPoint_Private(DMLabel label, PetscInt labelId, PetscSection section, PetscInt point, PetscInt f, PetscInt *offset, PetscBool *contains)
7134 {
7135   PetscFunctionBegin;
7136   *contains = PETSC_TRUE;
7137   if (label) {
7138     PetscInt fdof;
7139 
7140     PetscCall(DMLabelStratumHasPoint(label, labelId, point, contains));
7141     if (!*contains) {
7142       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7143       *offset += fdof;
7144       PetscFunctionReturn(PETSC_SUCCESS);
7145     }
7146   }
7147   PetscFunctionReturn(PETSC_SUCCESS);
7148 }
7149 
7150 /* Unlike DMPlexVecSetClosure(), this uses plex-native closure permutation, not a user-specified permutation such as DMPlexSetClosurePermutationTensor(). */
7151 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)
7152 {
7153   PetscSection    clSection;
7154   IS              clPoints;
7155   PetscScalar    *array;
7156   PetscInt       *points = NULL;
7157   const PetscInt *clp;
7158   PetscInt        numFields, numPoints, p;
7159   PetscInt        offset = 0, f;
7160 
7161   PetscFunctionBeginHot;
7162   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7163   if (!section) PetscCall(DMGetLocalSection(dm, &section));
7164   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7165   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
7166   PetscCall(PetscSectionGetNumFields(section, &numFields));
7167   /* Get points */
7168   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
7169   /* Get array */
7170   PetscCall(VecGetArray(v, &array));
7171   /* Get values */
7172   for (f = 0; f < numFields; ++f) {
7173     const PetscInt    **perms = NULL;
7174     const PetscScalar **flips = NULL;
7175     PetscBool           contains;
7176 
7177     if (!fieldActive[f]) {
7178       for (p = 0; p < numPoints * 2; p += 2) {
7179         PetscInt fdof;
7180         PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
7181         offset += fdof;
7182       }
7183       continue;
7184     }
7185     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7186     switch (mode) {
7187     case INSERT_VALUES:
7188       for (p = 0; p < numPoints; p++) {
7189         const PetscInt     point = points[2 * p];
7190         const PetscInt    *perm  = perms ? perms[p] : NULL;
7191         const PetscScalar *flip  = flips ? flips[p] : NULL;
7192         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7193         if (!contains) continue;
7194         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, NULL, values, &offset, array));
7195       }
7196       break;
7197     case INSERT_ALL_VALUES:
7198       for (p = 0; p < numPoints; p++) {
7199         const PetscInt     point = points[2 * p];
7200         const PetscInt    *perm  = perms ? perms[p] : NULL;
7201         const PetscScalar *flip  = flips ? flips[p] : NULL;
7202         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7203         if (!contains) continue;
7204         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, NULL, values, &offset, array));
7205       }
7206       break;
7207     case INSERT_BC_VALUES:
7208       for (p = 0; p < numPoints; p++) {
7209         const PetscInt     point = points[2 * p];
7210         const PetscInt    *perm  = perms ? perms[p] : NULL;
7211         const PetscScalar *flip  = flips ? flips[p] : NULL;
7212         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7213         if (!contains) continue;
7214         PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, Ncc, comps, insert, NULL, values, &offset, array));
7215       }
7216       break;
7217     case ADD_VALUES:
7218       for (p = 0; p < numPoints; p++) {
7219         const PetscInt     point = points[2 * p];
7220         const PetscInt    *perm  = perms ? perms[p] : NULL;
7221         const PetscScalar *flip  = flips ? flips[p] : NULL;
7222         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7223         if (!contains) continue;
7224         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, NULL, values, &offset, array));
7225       }
7226       break;
7227     case ADD_ALL_VALUES:
7228       for (p = 0; p < numPoints; p++) {
7229         const PetscInt     point = points[2 * p];
7230         const PetscInt    *perm  = perms ? perms[p] : NULL;
7231         const PetscScalar *flip  = flips ? flips[p] : NULL;
7232         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7233         if (!contains) continue;
7234         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, NULL, values, &offset, array));
7235       }
7236       break;
7237     default:
7238       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
7239     }
7240     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7241   }
7242   /* Cleanup points */
7243   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
7244   /* Cleanup array */
7245   PetscCall(VecRestoreArray(v, &array));
7246   PetscFunctionReturn(PETSC_SUCCESS);
7247 }
7248 
7249 static PetscErrorCode DMPlexPrintMatSetValues(PetscViewer viewer, Mat A, PetscInt point, PetscInt numRIndices, const PetscInt rindices[], PetscInt numCIndices, const PetscInt cindices[], const PetscScalar values[])
7250 {
7251   PetscMPIInt rank;
7252   PetscInt    i, j;
7253 
7254   PetscFunctionBegin;
7255   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7256   PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat for point %" PetscInt_FMT "\n", rank, point));
7257   for (i = 0; i < numRIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat row indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, rindices[i]));
7258   for (i = 0; i < numCIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat col indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, cindices[i]));
7259   numCIndices = numCIndices ? numCIndices : numRIndices;
7260   if (!values) PetscFunctionReturn(PETSC_SUCCESS);
7261   for (i = 0; i < numRIndices; i++) {
7262     PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]", rank));
7263     for (j = 0; j < numCIndices; j++) {
7264 #if defined(PETSC_USE_COMPLEX)
7265       PetscCall(PetscViewerASCIIPrintf(viewer, " (%g,%g)", (double)PetscRealPart(values[i * numCIndices + j]), (double)PetscImaginaryPart(values[i * numCIndices + j])));
7266 #else
7267       PetscCall(PetscViewerASCIIPrintf(viewer, " %g", (double)values[i * numCIndices + j]));
7268 #endif
7269     }
7270     PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
7271   }
7272   PetscFunctionReturn(PETSC_SUCCESS);
7273 }
7274 
7275 /*
7276   DMPlexGetIndicesPoint_Internal - Add the indices for dofs on a point to an index array
7277 
7278   Input Parameters:
7279 + section - The section for this data layout
7280 . islocal - Is the section (and thus indices being requested) local or global?
7281 . point   - The point contributing dofs with these indices
7282 . off     - The global offset of this point
7283 . loff    - The local offset of each field
7284 . setBC   - The flag determining whether to include indices of boundary values
7285 . perm    - A permutation of the dofs on this point, or NULL
7286 - indperm - A permutation of the entire indices array, or NULL
7287 
7288   Output Parameter:
7289 . indices - Indices for dofs on this point
7290 
7291   Level: developer
7292 
7293   Note: The indices could be local or global, depending on the value of 'off'.
7294 */
7295 PetscErrorCode DMPlexGetIndicesPoint_Internal(PetscSection section, PetscBool islocal, PetscInt point, PetscInt off, PetscInt *loff, PetscBool setBC, const PetscInt perm[], const PetscInt indperm[], PetscInt indices[])
7296 {
7297   PetscInt        dof;   /* The number of unknowns on this point */
7298   PetscInt        cdof;  /* The number of constraints on this point */
7299   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
7300   PetscInt        cind = 0, k;
7301 
7302   PetscFunctionBegin;
7303   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
7304   PetscCall(PetscSectionGetDof(section, point, &dof));
7305   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
7306   if (!cdof || setBC) {
7307     for (k = 0; k < dof; ++k) {
7308       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
7309       const PetscInt ind    = indperm ? indperm[preind] : preind;
7310 
7311       indices[ind] = off + k;
7312     }
7313   } else {
7314     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
7315     for (k = 0; k < dof; ++k) {
7316       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
7317       const PetscInt ind    = indperm ? indperm[preind] : preind;
7318 
7319       if ((cind < cdof) && (k == cdofs[cind])) {
7320         /* Insert check for returning constrained indices */
7321         indices[ind] = -(off + k + 1);
7322         ++cind;
7323       } else {
7324         indices[ind] = off + k - (islocal ? 0 : cind);
7325       }
7326     }
7327   }
7328   *loff += dof;
7329   PetscFunctionReturn(PETSC_SUCCESS);
7330 }
7331 
7332 /*
7333  DMPlexGetIndicesPointFields_Internal - gets section indices for a point in its canonical ordering.
7334 
7335  Input Parameters:
7336 + section - a section (global or local)
7337 - islocal - `PETSC_TRUE` if requesting local indices (i.e., section is local); `PETSC_FALSE` for global
7338 . point - point within section
7339 . off - The offset of this point in the (local or global) indexed space - should match islocal and (usually) the section
7340 . foffs - array of length numFields containing the offset in canonical point ordering (the location in indices) of each field
7341 . setBC - identify constrained (boundary condition) points via involution.
7342 . perms - perms[f][permsoff][:] is a permutation of dofs within each field
7343 . permsoff - offset
7344 - indperm - index permutation
7345 
7346  Output Parameter:
7347 . foffs - each entry is incremented by the number of (unconstrained if setBC=FALSE) dofs in that field
7348 . indices - array to hold indices (as defined by section) of each dof associated with point
7349 
7350  Notes:
7351  If section is local and setBC=true, there is no distinction between constrained and unconstrained dofs.
7352  If section is local and setBC=false, the indices for constrained points are the involution -(i+1) of their position
7353  in the local vector.
7354 
7355  If section is global and setBC=false, the indices for constrained points are negative (and their value is not
7356  significant).  It is invalid to call with a global section and setBC=true.
7357 
7358  Developer Note:
7359  The section is only used for field layout, so islocal is technically a statement about the offset (off).  At some point
7360  in the future, global sections may have fields set, in which case we could pass the global section and obtain the
7361  offset could be obtained from the section instead of passing it explicitly as we do now.
7362 
7363  Example:
7364  Suppose a point contains one field with three components, and for which the unconstrained indices are {10, 11, 12}.
7365  When the middle component is constrained, we get the array {10, -12, 12} for (islocal=TRUE, setBC=FALSE).
7366  Note that -12 is the involution of 11, so the user can involute negative indices to recover local indices.
7367  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.
7368 
7369  Level: developer
7370 */
7371 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[])
7372 {
7373   PetscInt numFields, foff, f;
7374 
7375   PetscFunctionBegin;
7376   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
7377   PetscCall(PetscSectionGetNumFields(section, &numFields));
7378   for (f = 0, foff = 0; f < numFields; ++f) {
7379     PetscInt        fdof, cfdof;
7380     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7381     PetscInt        cind = 0, b;
7382     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7383 
7384     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7385     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7386     if (!cfdof || setBC) {
7387       for (b = 0; b < fdof; ++b) {
7388         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7389         const PetscInt ind    = indperm ? indperm[preind] : preind;
7390 
7391         indices[ind] = off + foff + b;
7392       }
7393     } else {
7394       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7395       for (b = 0; b < fdof; ++b) {
7396         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7397         const PetscInt ind    = indperm ? indperm[preind] : preind;
7398 
7399         if ((cind < cfdof) && (b == fcdofs[cind])) {
7400           indices[ind] = -(off + foff + b + 1);
7401           ++cind;
7402         } else {
7403           indices[ind] = off + foff + b - (islocal ? 0 : cind);
7404         }
7405       }
7406     }
7407     foff += (setBC || islocal ? fdof : (fdof - cfdof));
7408     foffs[f] += fdof;
7409   }
7410   PetscFunctionReturn(PETSC_SUCCESS);
7411 }
7412 
7413 /*
7414   This version believes the globalSection offsets for each field, rather than just the point offset
7415 
7416  . foffs - The offset into 'indices' for each field, since it is segregated by field
7417 
7418  Notes:
7419  The semantics of this function relate to that of setBC=FALSE in DMPlexGetIndicesPointFields_Internal.
7420  Since this function uses global indices, setBC=TRUE would be invalid, so no such argument exists.
7421 */
7422 static PetscErrorCode DMPlexGetIndicesPointFieldsSplit_Internal(PetscSection section, PetscSection globalSection, PetscInt point, PetscInt foffs[], const PetscInt ***perms, PetscInt permsoff, const PetscInt indperm[], PetscInt indices[])
7423 {
7424   PetscInt numFields, foff, f;
7425 
7426   PetscFunctionBegin;
7427   PetscCall(PetscSectionGetNumFields(section, &numFields));
7428   for (f = 0; f < numFields; ++f) {
7429     PetscInt        fdof, cfdof;
7430     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7431     PetscInt        cind = 0, b;
7432     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7433 
7434     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7435     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7436     PetscCall(PetscSectionGetFieldOffset(globalSection, point, f, &foff));
7437     if (!cfdof) {
7438       for (b = 0; b < fdof; ++b) {
7439         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7440         const PetscInt ind    = indperm ? indperm[preind] : preind;
7441 
7442         indices[ind] = foff + b;
7443       }
7444     } else {
7445       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7446       for (b = 0; b < fdof; ++b) {
7447         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7448         const PetscInt ind    = indperm ? indperm[preind] : preind;
7449 
7450         if ((cind < cfdof) && (b == fcdofs[cind])) {
7451           indices[ind] = -(foff + b + 1);
7452           ++cind;
7453         } else {
7454           indices[ind] = foff + b - cind;
7455         }
7456       }
7457     }
7458     foffs[f] += fdof;
7459   }
7460   PetscFunctionReturn(PETSC_SUCCESS);
7461 }
7462 
7463 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)
7464 {
7465   Mat             cMat;
7466   PetscSection    aSec, cSec;
7467   IS              aIS;
7468   PetscInt        aStart = -1, aEnd = -1;
7469   const PetscInt *anchors;
7470   PetscInt        numFields, f, p, q, newP = 0;
7471   PetscInt        newNumPoints = 0, newNumIndices = 0;
7472   PetscInt       *newPoints, *indices, *newIndices;
7473   PetscInt        maxAnchor, maxDof;
7474   PetscInt        newOffsets[32];
7475   PetscInt       *pointMatOffsets[32];
7476   PetscInt       *newPointOffsets[32];
7477   PetscScalar    *pointMat[32];
7478   PetscScalar    *newValues      = NULL, *tmpValues;
7479   PetscBool       anyConstrained = PETSC_FALSE;
7480 
7481   PetscFunctionBegin;
7482   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7483   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7484   PetscCall(PetscSectionGetNumFields(section, &numFields));
7485 
7486   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
7487   /* if there are point-to-point constraints */
7488   if (aSec) {
7489     PetscCall(PetscArrayzero(newOffsets, 32));
7490     PetscCall(ISGetIndices(aIS, &anchors));
7491     PetscCall(PetscSectionGetChart(aSec, &aStart, &aEnd));
7492     /* figure out how many points are going to be in the new element matrix
7493      * (we allow double counting, because it's all just going to be summed
7494      * into the global matrix anyway) */
7495     for (p = 0; p < 2 * numPoints; p += 2) {
7496       PetscInt b    = points[p];
7497       PetscInt bDof = 0, bSecDof;
7498 
7499       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7500       if (!bSecDof) continue;
7501       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7502       if (bDof) {
7503         /* this point is constrained */
7504         /* it is going to be replaced by its anchors */
7505         PetscInt bOff, q;
7506 
7507         anyConstrained = PETSC_TRUE;
7508         newNumPoints += bDof;
7509         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7510         for (q = 0; q < bDof; q++) {
7511           PetscInt a = anchors[bOff + q];
7512           PetscInt aDof;
7513 
7514           PetscCall(PetscSectionGetDof(section, a, &aDof));
7515           newNumIndices += aDof;
7516           for (f = 0; f < numFields; ++f) {
7517             PetscInt fDof;
7518 
7519             PetscCall(PetscSectionGetFieldDof(section, a, f, &fDof));
7520             newOffsets[f + 1] += fDof;
7521           }
7522         }
7523       } else {
7524         /* this point is not constrained */
7525         newNumPoints++;
7526         newNumIndices += bSecDof;
7527         for (f = 0; f < numFields; ++f) {
7528           PetscInt fDof;
7529 
7530           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7531           newOffsets[f + 1] += fDof;
7532         }
7533       }
7534     }
7535   }
7536   if (!anyConstrained) {
7537     if (outNumPoints) *outNumPoints = 0;
7538     if (outNumIndices) *outNumIndices = 0;
7539     if (outPoints) *outPoints = NULL;
7540     if (outValues) *outValues = NULL;
7541     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7542     PetscFunctionReturn(PETSC_SUCCESS);
7543   }
7544 
7545   if (outNumPoints) *outNumPoints = newNumPoints;
7546   if (outNumIndices) *outNumIndices = newNumIndices;
7547 
7548   for (f = 0; f < numFields; ++f) newOffsets[f + 1] += newOffsets[f];
7549 
7550   if (!outPoints && !outValues) {
7551     if (offsets) {
7552       for (f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7553     }
7554     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7555     PetscFunctionReturn(PETSC_SUCCESS);
7556   }
7557 
7558   PetscCheck(!numFields || newOffsets[numFields] == newNumIndices, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, newOffsets[numFields], newNumIndices);
7559 
7560   PetscCall(DMGetDefaultConstraints(dm, &cSec, &cMat, NULL));
7561 
7562   /* workspaces */
7563   if (numFields) {
7564     for (f = 0; f < numFields; f++) {
7565       PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[f]));
7566       PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[f]));
7567     }
7568   } else {
7569     PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[0]));
7570     PetscCall(DMGetWorkArray(dm, numPoints, MPIU_INT, &newPointOffsets[0]));
7571   }
7572 
7573   /* get workspaces for the point-to-point matrices */
7574   if (numFields) {
7575     PetscInt totalOffset, totalMatOffset;
7576 
7577     for (p = 0; p < numPoints; p++) {
7578       PetscInt b    = points[2 * p];
7579       PetscInt bDof = 0, bSecDof;
7580 
7581       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7582       if (!bSecDof) {
7583         for (f = 0; f < numFields; f++) {
7584           newPointOffsets[f][p + 1] = 0;
7585           pointMatOffsets[f][p + 1] = 0;
7586         }
7587         continue;
7588       }
7589       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7590       if (bDof) {
7591         for (f = 0; f < numFields; f++) {
7592           PetscInt fDof, q, bOff, allFDof = 0;
7593 
7594           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7595           PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7596           for (q = 0; q < bDof; q++) {
7597             PetscInt a = anchors[bOff + q];
7598             PetscInt aFDof;
7599 
7600             PetscCall(PetscSectionGetFieldDof(section, a, f, &aFDof));
7601             allFDof += aFDof;
7602           }
7603           newPointOffsets[f][p + 1] = allFDof;
7604           pointMatOffsets[f][p + 1] = fDof * allFDof;
7605         }
7606       } else {
7607         for (f = 0; f < numFields; f++) {
7608           PetscInt fDof;
7609 
7610           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7611           newPointOffsets[f][p + 1] = fDof;
7612           pointMatOffsets[f][p + 1] = 0;
7613         }
7614       }
7615     }
7616     for (f = 0, totalOffset = 0, totalMatOffset = 0; f < numFields; f++) {
7617       newPointOffsets[f][0] = totalOffset;
7618       pointMatOffsets[f][0] = totalMatOffset;
7619       for (p = 0; p < numPoints; p++) {
7620         newPointOffsets[f][p + 1] += newPointOffsets[f][p];
7621         pointMatOffsets[f][p + 1] += pointMatOffsets[f][p];
7622       }
7623       totalOffset    = newPointOffsets[f][numPoints];
7624       totalMatOffset = pointMatOffsets[f][numPoints];
7625       PetscCall(DMGetWorkArray(dm, pointMatOffsets[f][numPoints], MPIU_SCALAR, &pointMat[f]));
7626     }
7627   } else {
7628     for (p = 0; p < numPoints; p++) {
7629       PetscInt b    = points[2 * p];
7630       PetscInt bDof = 0, bSecDof;
7631 
7632       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7633       if (!bSecDof) {
7634         newPointOffsets[0][p + 1] = 0;
7635         pointMatOffsets[0][p + 1] = 0;
7636         continue;
7637       }
7638       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7639       if (bDof) {
7640         PetscInt bOff, q, allDof = 0;
7641 
7642         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7643         for (q = 0; q < bDof; q++) {
7644           PetscInt a = anchors[bOff + q], aDof;
7645 
7646           PetscCall(PetscSectionGetDof(section, a, &aDof));
7647           allDof += aDof;
7648         }
7649         newPointOffsets[0][p + 1] = allDof;
7650         pointMatOffsets[0][p + 1] = bSecDof * allDof;
7651       } else {
7652         newPointOffsets[0][p + 1] = bSecDof;
7653         pointMatOffsets[0][p + 1] = 0;
7654       }
7655     }
7656     newPointOffsets[0][0] = 0;
7657     pointMatOffsets[0][0] = 0;
7658     for (p = 0; p < numPoints; p++) {
7659       newPointOffsets[0][p + 1] += newPointOffsets[0][p];
7660       pointMatOffsets[0][p + 1] += pointMatOffsets[0][p];
7661     }
7662     PetscCall(DMGetWorkArray(dm, pointMatOffsets[0][numPoints], MPIU_SCALAR, &pointMat[0]));
7663   }
7664 
7665   /* output arrays */
7666   PetscCall(DMGetWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7667 
7668   /* get the point-to-point matrices; construct newPoints */
7669   PetscCall(PetscSectionGetMaxDof(aSec, &maxAnchor));
7670   PetscCall(PetscSectionGetMaxDof(section, &maxDof));
7671   PetscCall(DMGetWorkArray(dm, maxDof, MPIU_INT, &indices));
7672   PetscCall(DMGetWorkArray(dm, maxAnchor * maxDof, MPIU_INT, &newIndices));
7673   if (numFields) {
7674     for (p = 0, newP = 0; p < numPoints; p++) {
7675       PetscInt b    = points[2 * p];
7676       PetscInt o    = points[2 * p + 1];
7677       PetscInt bDof = 0, bSecDof;
7678 
7679       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7680       if (!bSecDof) continue;
7681       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7682       if (bDof) {
7683         PetscInt fStart[32], fEnd[32], fAnchorStart[32], fAnchorEnd[32], bOff, q;
7684 
7685         fStart[0] = 0;
7686         fEnd[0]   = 0;
7687         for (f = 0; f < numFields; f++) {
7688           PetscInt fDof;
7689 
7690           PetscCall(PetscSectionGetFieldDof(cSec, b, f, &fDof));
7691           fStart[f + 1] = fStart[f] + fDof;
7692           fEnd[f + 1]   = fStart[f + 1];
7693         }
7694         PetscCall(PetscSectionGetOffset(cSec, b, &bOff));
7695         PetscCall(DMPlexGetIndicesPointFields_Internal(cSec, PETSC_TRUE, b, bOff, fEnd, PETSC_TRUE, perms, p, NULL, indices));
7696 
7697         fAnchorStart[0] = 0;
7698         fAnchorEnd[0]   = 0;
7699         for (f = 0; f < numFields; f++) {
7700           PetscInt fDof = newPointOffsets[f][p + 1] - newPointOffsets[f][p];
7701 
7702           fAnchorStart[f + 1] = fAnchorStart[f] + fDof;
7703           fAnchorEnd[f + 1]   = fAnchorStart[f + 1];
7704         }
7705         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7706         for (q = 0; q < bDof; q++) {
7707           PetscInt a = anchors[bOff + q], aOff;
7708 
7709           /* we take the orientation of ap into account in the order that we constructed the indices above: the newly added points have no orientation */
7710           newPoints[2 * (newP + q)]     = a;
7711           newPoints[2 * (newP + q) + 1] = 0;
7712           PetscCall(PetscSectionGetOffset(section, a, &aOff));
7713           PetscCall(DMPlexGetIndicesPointFields_Internal(section, PETSC_TRUE, a, aOff, fAnchorEnd, PETSC_TRUE, NULL, -1, NULL, newIndices));
7714         }
7715         newP += bDof;
7716 
7717         if (outValues) {
7718           /* get the point-to-point submatrix */
7719           for (f = 0; f < numFields; f++) PetscCall(MatGetValues(cMat, fEnd[f] - fStart[f], indices + fStart[f], fAnchorEnd[f] - fAnchorStart[f], newIndices + fAnchorStart[f], pointMat[f] + pointMatOffsets[f][p]));
7720         }
7721       } else {
7722         newPoints[2 * newP]     = b;
7723         newPoints[2 * newP + 1] = o;
7724         newP++;
7725       }
7726     }
7727   } else {
7728     for (p = 0; p < numPoints; p++) {
7729       PetscInt b    = points[2 * p];
7730       PetscInt o    = points[2 * p + 1];
7731       PetscInt bDof = 0, bSecDof;
7732 
7733       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7734       if (!bSecDof) continue;
7735       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7736       if (bDof) {
7737         PetscInt bEnd = 0, bAnchorEnd = 0, bOff;
7738 
7739         PetscCall(PetscSectionGetOffset(cSec, b, &bOff));
7740         PetscCall(DMPlexGetIndicesPoint_Internal(cSec, PETSC_TRUE, b, bOff, &bEnd, PETSC_TRUE, (perms && perms[0]) ? perms[0][p] : NULL, NULL, indices));
7741 
7742         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7743         for (q = 0; q < bDof; q++) {
7744           PetscInt a = anchors[bOff + q], aOff;
7745 
7746           /* we take the orientation of ap into account in the order that we constructed the indices above: the newly added points have no orientation */
7747 
7748           newPoints[2 * (newP + q)]     = a;
7749           newPoints[2 * (newP + q) + 1] = 0;
7750           PetscCall(PetscSectionGetOffset(section, a, &aOff));
7751           PetscCall(DMPlexGetIndicesPoint_Internal(section, PETSC_TRUE, a, aOff, &bAnchorEnd, PETSC_TRUE, NULL, NULL, newIndices));
7752         }
7753         newP += bDof;
7754 
7755         /* get the point-to-point submatrix */
7756         if (outValues) PetscCall(MatGetValues(cMat, bEnd, indices, bAnchorEnd, newIndices, pointMat[0] + pointMatOffsets[0][p]));
7757       } else {
7758         newPoints[2 * newP]     = b;
7759         newPoints[2 * newP + 1] = o;
7760         newP++;
7761       }
7762     }
7763   }
7764 
7765   if (outValues) {
7766     PetscCall(DMGetWorkArray(dm, newNumIndices * numIndices, MPIU_SCALAR, &tmpValues));
7767     PetscCall(PetscArrayzero(tmpValues, newNumIndices * numIndices));
7768     /* multiply constraints on the right */
7769     if (numFields) {
7770       for (f = 0; f < numFields; f++) {
7771         PetscInt oldOff = offsets[f];
7772 
7773         for (p = 0; p < numPoints; p++) {
7774           PetscInt cStart = newPointOffsets[f][p];
7775           PetscInt b      = points[2 * p];
7776           PetscInt c, r, k;
7777           PetscInt dof;
7778 
7779           PetscCall(PetscSectionGetFieldDof(section, b, f, &dof));
7780           if (!dof) continue;
7781           if (pointMatOffsets[f][p] < pointMatOffsets[f][p + 1]) {
7782             PetscInt           nCols = newPointOffsets[f][p + 1] - cStart;
7783             const PetscScalar *mat   = pointMat[f] + pointMatOffsets[f][p];
7784 
7785             for (r = 0; r < numIndices; r++) {
7786               for (c = 0; c < nCols; c++) {
7787                 for (k = 0; k < dof; k++) tmpValues[r * newNumIndices + cStart + c] += values[r * numIndices + oldOff + k] * mat[k * nCols + c];
7788               }
7789             }
7790           } else {
7791             /* copy this column as is */
7792             for (r = 0; r < numIndices; r++) {
7793               for (c = 0; c < dof; c++) tmpValues[r * newNumIndices + cStart + c] = values[r * numIndices + oldOff + c];
7794             }
7795           }
7796           oldOff += dof;
7797         }
7798       }
7799     } else {
7800       PetscInt oldOff = 0;
7801       for (p = 0; p < numPoints; p++) {
7802         PetscInt cStart = newPointOffsets[0][p];
7803         PetscInt b      = points[2 * p];
7804         PetscInt c, r, k;
7805         PetscInt dof;
7806 
7807         PetscCall(PetscSectionGetDof(section, b, &dof));
7808         if (!dof) continue;
7809         if (pointMatOffsets[0][p] < pointMatOffsets[0][p + 1]) {
7810           PetscInt           nCols = newPointOffsets[0][p + 1] - cStart;
7811           const PetscScalar *mat   = pointMat[0] + pointMatOffsets[0][p];
7812 
7813           for (r = 0; r < numIndices; r++) {
7814             for (c = 0; c < nCols; c++) {
7815               for (k = 0; k < dof; k++) tmpValues[r * newNumIndices + cStart + c] += mat[k * nCols + c] * values[r * numIndices + oldOff + k];
7816             }
7817           }
7818         } else {
7819           /* copy this column as is */
7820           for (r = 0; r < numIndices; r++) {
7821             for (c = 0; c < dof; c++) tmpValues[r * newNumIndices + cStart + c] = values[r * numIndices + oldOff + c];
7822           }
7823         }
7824         oldOff += dof;
7825       }
7826     }
7827 
7828     if (multiplyLeft) {
7829       PetscCall(DMGetWorkArray(dm, newNumIndices * newNumIndices, MPIU_SCALAR, &newValues));
7830       PetscCall(PetscArrayzero(newValues, newNumIndices * newNumIndices));
7831       /* multiply constraints transpose on the left */
7832       if (numFields) {
7833         for (f = 0; f < numFields; f++) {
7834           PetscInt oldOff = offsets[f];
7835 
7836           for (p = 0; p < numPoints; p++) {
7837             PetscInt rStart = newPointOffsets[f][p];
7838             PetscInt b      = points[2 * p];
7839             PetscInt c, r, k;
7840             PetscInt dof;
7841 
7842             PetscCall(PetscSectionGetFieldDof(section, b, f, &dof));
7843             if (pointMatOffsets[f][p] < pointMatOffsets[f][p + 1]) {
7844               PetscInt                          nRows = newPointOffsets[f][p + 1] - rStart;
7845               const PetscScalar *PETSC_RESTRICT mat   = pointMat[f] + pointMatOffsets[f][p];
7846 
7847               for (r = 0; r < nRows; r++) {
7848                 for (c = 0; c < newNumIndices; c++) {
7849                   for (k = 0; k < dof; k++) newValues[(rStart + r) * newNumIndices + c] += mat[k * nRows + r] * tmpValues[(oldOff + k) * newNumIndices + c];
7850                 }
7851               }
7852             } else {
7853               /* copy this row as is */
7854               for (r = 0; r < dof; r++) {
7855                 for (c = 0; c < newNumIndices; c++) newValues[(rStart + r) * newNumIndices + c] = tmpValues[(oldOff + r) * newNumIndices + c];
7856               }
7857             }
7858             oldOff += dof;
7859           }
7860         }
7861       } else {
7862         PetscInt oldOff = 0;
7863 
7864         for (p = 0; p < numPoints; p++) {
7865           PetscInt rStart = newPointOffsets[0][p];
7866           PetscInt b      = points[2 * p];
7867           PetscInt c, r, k;
7868           PetscInt dof;
7869 
7870           PetscCall(PetscSectionGetDof(section, b, &dof));
7871           if (pointMatOffsets[0][p] < pointMatOffsets[0][p + 1]) {
7872             PetscInt                          nRows = newPointOffsets[0][p + 1] - rStart;
7873             const PetscScalar *PETSC_RESTRICT mat   = pointMat[0] + pointMatOffsets[0][p];
7874 
7875             for (r = 0; r < nRows; r++) {
7876               for (c = 0; c < newNumIndices; c++) {
7877                 for (k = 0; k < dof; k++) newValues[(rStart + r) * newNumIndices + c] += mat[k * nRows + r] * tmpValues[(oldOff + k) * newNumIndices + c];
7878               }
7879             }
7880           } else {
7881             /* copy this row as is */
7882             for (r = 0; r < dof; r++) {
7883               for (c = 0; c < newNumIndices; c++) newValues[(rStart + r) * newNumIndices + c] = tmpValues[(oldOff + r) * newNumIndices + c];
7884             }
7885           }
7886           oldOff += dof;
7887         }
7888       }
7889 
7890       PetscCall(DMRestoreWorkArray(dm, newNumIndices * numIndices, MPIU_SCALAR, &tmpValues));
7891     } else {
7892       newValues = tmpValues;
7893     }
7894   }
7895 
7896   /* clean up */
7897   PetscCall(DMRestoreWorkArray(dm, maxDof, MPIU_INT, &indices));
7898   PetscCall(DMRestoreWorkArray(dm, maxAnchor * maxDof, MPIU_INT, &newIndices));
7899 
7900   if (numFields) {
7901     for (f = 0; f < numFields; f++) {
7902       PetscCall(DMRestoreWorkArray(dm, pointMatOffsets[f][numPoints], MPIU_SCALAR, &pointMat[f]));
7903       PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[f]));
7904       PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[f]));
7905     }
7906   } else {
7907     PetscCall(DMRestoreWorkArray(dm, pointMatOffsets[0][numPoints], MPIU_SCALAR, &pointMat[0]));
7908     PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[0]));
7909     PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[0]));
7910   }
7911   PetscCall(ISRestoreIndices(aIS, &anchors));
7912 
7913   /* output */
7914   if (outPoints) {
7915     *outPoints = newPoints;
7916   } else {
7917     PetscCall(DMRestoreWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7918   }
7919   if (outValues) *outValues = newValues;
7920   for (f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7921   PetscFunctionReturn(PETSC_SUCCESS);
7922 }
7923 
7924 /*@C
7925   DMPlexGetClosureIndices - Gets the global dof indices associated with the closure of the given point within the provided sections.
7926 
7927   Not collective
7928 
7929   Input Parameters:
7930 + dm         - The `DM`
7931 . section    - The `PetscSection` describing the points (a local section)
7932 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
7933 . point      - The point defining the closure
7934 - useClPerm  - Use the closure point permutation if available
7935 
7936   Output Parameters:
7937 + numIndices - The number of dof indices in the closure of point with the input sections
7938 . indices    - The dof indices
7939 . outOffsets - Array to write the field offsets into, or `NULL`
7940 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or `NULL`
7941 
7942   Level: advanced
7943 
7944   Notes:
7945   Must call `DMPlexRestoreClosureIndices()` to free allocated memory
7946 
7947   If `idxSection` is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
7948   of those indices is not significant.  If `idxSection` is local, the constrained dofs will yield the involution -(idx+1)
7949   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
7950   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when `idxSection` == section, otherwise global
7951   indices (with the above semantics) are implied.
7952 
7953 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`,
7954           `PetscSection`, `DMGetGlobalSection()`
7955 @*/
7956 PetscErrorCode DMPlexGetClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
7957 {
7958   /* Closure ordering */
7959   PetscSection    clSection;
7960   IS              clPoints;
7961   const PetscInt *clp;
7962   PetscInt       *points;
7963   const PetscInt *clperm = NULL;
7964   /* Dof permutation and sign flips */
7965   const PetscInt    **perms[32] = {NULL};
7966   const PetscScalar **flips[32] = {NULL};
7967   PetscScalar        *valCopy   = NULL;
7968   /* Hanging node constraints */
7969   PetscInt    *pointsC = NULL;
7970   PetscScalar *valuesC = NULL;
7971   PetscInt     NclC, NiC;
7972 
7973   PetscInt *idx;
7974   PetscInt  Nf, Ncl, Ni = 0, offsets[32], p, f;
7975   PetscBool isLocal = (section == idxSection) ? PETSC_TRUE : PETSC_FALSE;
7976 
7977   PetscFunctionBeginHot;
7978   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7979   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7980   PetscValidHeaderSpecific(idxSection, PETSC_SECTION_CLASSID, 3);
7981   if (numIndices) PetscAssertPointer(numIndices, 6);
7982   if (indices) PetscAssertPointer(indices, 7);
7983   if (outOffsets) PetscAssertPointer(outOffsets, 8);
7984   if (values) PetscAssertPointer(values, 9);
7985   PetscCall(PetscSectionGetNumFields(section, &Nf));
7986   PetscCheck(Nf <= 31, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", Nf);
7987   PetscCall(PetscArrayzero(offsets, 32));
7988   /* 1) Get points in closure */
7989   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &Ncl, &points, &clSection, &clPoints, &clp));
7990   if (useClPerm) {
7991     PetscInt depth, clsize;
7992     PetscCall(DMPlexGetPointDepth(dm, point, &depth));
7993     for (clsize = 0, p = 0; p < Ncl; p++) {
7994       PetscInt dof;
7995       PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
7996       clsize += dof;
7997     }
7998     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
7999   }
8000   /* 2) Get number of indices on these points and field offsets from section */
8001   for (p = 0; p < Ncl * 2; p += 2) {
8002     PetscInt dof, fdof;
8003 
8004     PetscCall(PetscSectionGetDof(section, points[p], &dof));
8005     for (f = 0; f < Nf; ++f) {
8006       PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
8007       offsets[f + 1] += fdof;
8008     }
8009     Ni += dof;
8010   }
8011   for (f = 1; f < Nf; ++f) offsets[f + 1] += offsets[f];
8012   PetscCheck(!Nf || offsets[Nf] == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, offsets[Nf], Ni);
8013   /* 3) Get symmetries and sign flips. Apply sign flips to values if passed in (only works for square values matrix) */
8014   for (f = 0; f < PetscMax(1, Nf); ++f) {
8015     if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
8016     else PetscCall(PetscSectionGetPointSyms(section, Ncl, points, &perms[f], &flips[f]));
8017     /* may need to apply sign changes to the element matrix */
8018     if (values && flips[f]) {
8019       PetscInt foffset = offsets[f];
8020 
8021       for (p = 0; p < Ncl; ++p) {
8022         PetscInt           pnt  = points[2 * p], fdof;
8023         const PetscScalar *flip = flips[f] ? flips[f][p] : NULL;
8024 
8025         if (!Nf) PetscCall(PetscSectionGetDof(section, pnt, &fdof));
8026         else PetscCall(PetscSectionGetFieldDof(section, pnt, f, &fdof));
8027         if (flip) {
8028           PetscInt i, j, k;
8029 
8030           if (!valCopy) {
8031             PetscCall(DMGetWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
8032             for (j = 0; j < Ni * Ni; ++j) valCopy[j] = (*values)[j];
8033             *values = valCopy;
8034           }
8035           for (i = 0; i < fdof; ++i) {
8036             PetscScalar fval = flip[i];
8037 
8038             for (k = 0; k < Ni; ++k) {
8039               valCopy[Ni * (foffset + i) + k] *= fval;
8040               valCopy[Ni * k + (foffset + i)] *= fval;
8041             }
8042           }
8043         }
8044         foffset += fdof;
8045       }
8046     }
8047   }
8048   /* 4) Apply hanging node constraints. Get new symmetries and replace all storage with constrained storage */
8049   PetscCall(DMPlexAnchorsModifyMat(dm, section, Ncl, Ni, points, perms, values ? *values : NULL, &NclC, &NiC, &pointsC, values ? &valuesC : NULL, offsets, PETSC_TRUE));
8050   if (NclC) {
8051     if (valCopy) PetscCall(DMRestoreWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
8052     for (f = 0; f < PetscMax(1, Nf); ++f) {
8053       if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
8054       else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
8055     }
8056     for (f = 0; f < PetscMax(1, Nf); ++f) {
8057       if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, NclC, pointsC, &perms[f], &flips[f]));
8058       else PetscCall(PetscSectionGetPointSyms(section, NclC, pointsC, &perms[f], &flips[f]));
8059     }
8060     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
8061     Ncl    = NclC;
8062     Ni     = NiC;
8063     points = pointsC;
8064     if (values) *values = valuesC;
8065   }
8066   /* 5) Calculate indices */
8067   PetscCall(DMGetWorkArray(dm, Ni, MPIU_INT, &idx));
8068   if (Nf) {
8069     PetscInt  idxOff;
8070     PetscBool useFieldOffsets;
8071 
8072     if (outOffsets) {
8073       for (f = 0; f <= Nf; f++) outOffsets[f] = offsets[f];
8074     }
8075     PetscCall(PetscSectionGetUseFieldOffsets(idxSection, &useFieldOffsets));
8076     if (useFieldOffsets) {
8077       for (p = 0; p < Ncl; ++p) {
8078         const PetscInt pnt = points[p * 2];
8079 
8080         PetscCall(DMPlexGetIndicesPointFieldsSplit_Internal(section, idxSection, pnt, offsets, perms, p, clperm, idx));
8081       }
8082     } else {
8083       for (p = 0; p < Ncl; ++p) {
8084         const PetscInt pnt = points[p * 2];
8085 
8086         PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
8087         /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
8088          * not (at the time of this writing) have fields set. They probably should, in which case we would pass the
8089          * global section. */
8090         PetscCall(DMPlexGetIndicesPointFields_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, offsets, PETSC_FALSE, perms, p, clperm, idx));
8091       }
8092     }
8093   } else {
8094     PetscInt off = 0, idxOff;
8095 
8096     for (p = 0; p < Ncl; ++p) {
8097       const PetscInt  pnt  = points[p * 2];
8098       const PetscInt *perm = perms[0] ? perms[0][p] : NULL;
8099 
8100       PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
8101       /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
8102        * not (at the time of this writing) have fields set. They probably should, in which case we would pass the global section. */
8103       PetscCall(DMPlexGetIndicesPoint_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, &off, PETSC_FALSE, perm, clperm, idx));
8104     }
8105   }
8106   /* 6) Cleanup */
8107   for (f = 0; f < PetscMax(1, Nf); ++f) {
8108     if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
8109     else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
8110   }
8111   if (NclC) {
8112     PetscCall(DMRestoreWorkArray(dm, NclC * 2, MPIU_INT, &pointsC));
8113   } else {
8114     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
8115   }
8116 
8117   if (numIndices) *numIndices = Ni;
8118   if (indices) *indices = idx;
8119   PetscFunctionReturn(PETSC_SUCCESS);
8120 }
8121 
8122 /*@C
8123   DMPlexRestoreClosureIndices - Restores the global dof indices associated with the closure of the given point within the provided sections.
8124 
8125   Not collective
8126 
8127   Input Parameters:
8128 + dm         - The `DM`
8129 . section    - The `PetscSection` describing the points (a local section)
8130 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
8131 . point      - The point defining the closure
8132 - useClPerm  - Use the closure point permutation if available
8133 
8134   Output Parameters:
8135 + numIndices - The number of dof indices in the closure of point with the input sections
8136 . indices    - The dof indices
8137 . outOffsets - Array to write the field offsets into, or `NULL`
8138 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or `NULL`
8139 
8140   Level: advanced
8141 
8142   Notes:
8143   If values were modified, the user is responsible for calling `DMRestoreWorkArray`(dm, 0, `MPIU_SCALAR`, &values).
8144 
8145   If idxSection is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
8146   of those indices is not significant.  If idxSection is local, the constrained dofs will yield the involution -(idx+1)
8147   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
8148   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when idxSection == section, otherwise global
8149   indices (with the above semantics) are implied.
8150 
8151 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`, `DMGetGlobalSection()`
8152 @*/
8153 PetscErrorCode DMPlexRestoreClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
8154 {
8155   PetscFunctionBegin;
8156   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8157   PetscAssertPointer(indices, 7);
8158   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, indices));
8159   PetscFunctionReturn(PETSC_SUCCESS);
8160 }
8161 
8162 PetscErrorCode DMPlexMatSetClosure_Internal(DM dm, PetscSection section, PetscSection globalSection, PetscBool useClPerm, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8163 {
8164   DM_Plex           *mesh = (DM_Plex *)dm->data;
8165   PetscInt          *indices;
8166   PetscInt           numIndices;
8167   const PetscScalar *valuesOrig = values;
8168   PetscErrorCode     ierr;
8169 
8170   PetscFunctionBegin;
8171   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8172   if (!section) PetscCall(DMGetLocalSection(dm, &section));
8173   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
8174   if (!globalSection) PetscCall(DMGetGlobalSection(dm, &globalSection));
8175   PetscValidHeaderSpecific(globalSection, PETSC_SECTION_CLASSID, 3);
8176   PetscValidHeaderSpecific(A, MAT_CLASSID, 5);
8177 
8178   PetscCall(DMPlexGetClosureIndices(dm, section, globalSection, point, useClPerm, &numIndices, &indices, NULL, (PetscScalar **)&values));
8179 
8180   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndices, indices, 0, NULL, values));
8181   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8182   ierr = MatSetValues(A, numIndices, indices, numIndices, indices, values, mode);
8183   if (ierr) {
8184     PetscMPIInt rank;
8185 
8186     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8187     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8188     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndices, indices, 0, NULL, values));
8189     PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
8190     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
8191     SETERRQ(PetscObjectComm((PetscObject)dm), ierr, "Not possible to set matrix values");
8192   }
8193   if (mesh->printFEM > 1) {
8194     PetscInt i;
8195     PetscCall(PetscPrintf(PETSC_COMM_SELF, "  Indices:"));
8196     for (i = 0; i < numIndices; ++i) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, indices[i]));
8197     PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8198   }
8199 
8200   PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
8201   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
8202   PetscFunctionReturn(PETSC_SUCCESS);
8203 }
8204 
8205 /*@C
8206   DMPlexMatSetClosure - Set an array of the values on the closure of 'point'
8207 
8208   Not collective
8209 
8210   Input Parameters:
8211 + dm            - The `DM`
8212 . section       - The section describing the layout in `v`, or `NULL` to use the default section
8213 . globalSection - The section describing the layout in `v`, or `NULL` to use the default global section
8214 . A             - The matrix
8215 . point         - The point in the `DM`
8216 . values        - The array of values
8217 - mode          - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
8218 
8219   Level: intermediate
8220 
8221 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosureGeneral()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
8222 @*/
8223 PetscErrorCode DMPlexMatSetClosure(DM dm, PetscSection section, PetscSection globalSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8224 {
8225   PetscFunctionBegin;
8226   PetscCall(DMPlexMatSetClosure_Internal(dm, section, globalSection, PETSC_TRUE, A, point, values, mode));
8227   PetscFunctionReturn(PETSC_SUCCESS);
8228 }
8229 
8230 /*@C
8231   DMPlexMatSetClosureGeneral - Set an array of the values on the closure of 'point' using a different row and column section
8232 
8233   Not collective
8234 
8235   Input Parameters:
8236 + dmRow            - The `DM` for the row fields
8237 . sectionRow       - The section describing the layout, or `NULL` to use the default section in `dmRow`
8238 . useRowPerm       - The flag to use the closure permutation of the `dmRow` if available
8239 . globalSectionRow - The section describing the layout, or `NULL` to use the default global section in `dmRow`
8240 . dmCol            - The `DM` for the column fields
8241 . sectionCol       - The section describing the layout, or `NULL` to use the default section in `dmCol`
8242 . useColPerm       - The flag to use the closure permutation of the `dmCol` if available
8243 . globalSectionCol - The section describing the layout, or `NULL` to use the default global section in `dmCol`
8244 . A                - The matrix
8245 . point            - The point in the `DM`
8246 . values           - The array of values
8247 - mode             - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
8248 
8249   Level: intermediate
8250 
8251 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosure()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
8252 @*/
8253 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)
8254 {
8255   DM_Plex           *mesh = (DM_Plex *)dmRow->data;
8256   PetscInt          *indicesRow, *indicesCol;
8257   PetscInt           numIndicesRow, numIndicesCol;
8258   const PetscScalar *valuesOrig = values;
8259   PetscErrorCode     ierr;
8260 
8261   PetscFunctionBegin;
8262   PetscValidHeaderSpecific(dmRow, DM_CLASSID, 1);
8263   if (!sectionRow) PetscCall(DMGetLocalSection(dmRow, &sectionRow));
8264   PetscValidHeaderSpecific(sectionRow, PETSC_SECTION_CLASSID, 2);
8265   if (!globalSectionRow) PetscCall(DMGetGlobalSection(dmRow, &globalSectionRow));
8266   PetscValidHeaderSpecific(globalSectionRow, PETSC_SECTION_CLASSID, 3);
8267   PetscValidHeaderSpecific(dmCol, DM_CLASSID, 5);
8268   if (!sectionCol) PetscCall(DMGetLocalSection(dmCol, &sectionCol));
8269   PetscValidHeaderSpecific(sectionCol, PETSC_SECTION_CLASSID, 6);
8270   if (!globalSectionCol) PetscCall(DMGetGlobalSection(dmCol, &globalSectionCol));
8271   PetscValidHeaderSpecific(globalSectionCol, PETSC_SECTION_CLASSID, 7);
8272   PetscValidHeaderSpecific(A, MAT_CLASSID, 9);
8273 
8274   PetscCall(DMPlexGetClosureIndices(dmRow, sectionRow, globalSectionRow, point, useRowPerm, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&values));
8275   PetscCall(DMPlexGetClosureIndices(dmCol, sectionCol, globalSectionCol, point, useColPerm, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&values));
8276 
8277   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
8278   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8279   ierr = MatSetValues(A, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values, mode);
8280   if (ierr) {
8281     PetscMPIInt rank;
8282 
8283     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8284     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8285     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
8286     PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&values));
8287     PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesRow, NULL, (PetscScalar **)&values));
8288     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &values));
8289   }
8290 
8291   PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, useRowPerm, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&values));
8292   PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, useColPerm, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&values));
8293   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &values));
8294   PetscFunctionReturn(PETSC_SUCCESS);
8295 }
8296 
8297 PetscErrorCode DMPlexMatSetClosureRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8298 {
8299   DM_Plex        *mesh    = (DM_Plex *)dmf->data;
8300   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
8301   PetscInt       *cpoints = NULL;
8302   PetscInt       *findices, *cindices;
8303   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
8304   PetscInt        foffsets[32], coffsets[32];
8305   DMPolytopeType  ct;
8306   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
8307   PetscErrorCode  ierr;
8308 
8309   PetscFunctionBegin;
8310   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
8311   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
8312   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
8313   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
8314   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
8315   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
8316   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
8317   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
8318   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
8319   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
8320   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
8321   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
8322   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
8323   PetscCall(PetscArrayzero(foffsets, 32));
8324   PetscCall(PetscArrayzero(coffsets, 32));
8325   /* Column indices */
8326   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8327   maxFPoints = numCPoints;
8328   /* Compress out points not in the section */
8329   /*   TODO: Squeeze out points with 0 dof as well */
8330   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
8331   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
8332     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
8333       cpoints[q * 2]     = cpoints[p];
8334       cpoints[q * 2 + 1] = cpoints[p + 1];
8335       ++q;
8336     }
8337   }
8338   numCPoints = q;
8339   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
8340     PetscInt fdof;
8341 
8342     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
8343     if (!dof) continue;
8344     for (f = 0; f < numFields; ++f) {
8345       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
8346       coffsets[f + 1] += fdof;
8347     }
8348     numCIndices += dof;
8349   }
8350   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
8351   /* Row indices */
8352   PetscCall(DMPlexGetCellType(dmc, point, &ct));
8353   {
8354     DMPlexTransform tr;
8355     DMPolytopeType *rct;
8356     PetscInt       *rsize, *rcone, *rornt, Nt;
8357 
8358     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8359     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8360     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8361     numSubcells = rsize[Nt - 1];
8362     PetscCall(DMPlexTransformDestroy(&tr));
8363   }
8364   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8365   for (r = 0, q = 0; r < numSubcells; ++r) {
8366     /* TODO Map from coarse to fine cells */
8367     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8368     /* Compress out points not in the section */
8369     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8370     for (p = 0; p < numFPoints * 2; p += 2) {
8371       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8372         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8373         if (!dof) continue;
8374         for (s = 0; s < q; ++s)
8375           if (fpoints[p] == ftotpoints[s * 2]) break;
8376         if (s < q) continue;
8377         ftotpoints[q * 2]     = fpoints[p];
8378         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8379         ++q;
8380       }
8381     }
8382     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8383   }
8384   numFPoints = q;
8385   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8386     PetscInt fdof;
8387 
8388     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8389     if (!dof) continue;
8390     for (f = 0; f < numFields; ++f) {
8391       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8392       foffsets[f + 1] += fdof;
8393     }
8394     numFIndices += dof;
8395   }
8396   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8397 
8398   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8399   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8400   PetscCall(DMGetWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8401   PetscCall(DMGetWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8402   if (numFields) {
8403     const PetscInt **permsF[32] = {NULL};
8404     const PetscInt **permsC[32] = {NULL};
8405 
8406     for (f = 0; f < numFields; f++) {
8407       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8408       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8409     }
8410     for (p = 0; p < numFPoints; p++) {
8411       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8412       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8413     }
8414     for (p = 0; p < numCPoints; p++) {
8415       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8416       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8417     }
8418     for (f = 0; f < numFields; f++) {
8419       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8420       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8421     }
8422   } else {
8423     const PetscInt **permsF = NULL;
8424     const PetscInt **permsC = NULL;
8425 
8426     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8427     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8428     for (p = 0, off = 0; p < numFPoints; p++) {
8429       const PetscInt *perm = permsF ? permsF[p] : NULL;
8430 
8431       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8432       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8433     }
8434     for (p = 0, off = 0; p < numCPoints; p++) {
8435       const PetscInt *perm = permsC ? permsC[p] : NULL;
8436 
8437       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8438       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8439     }
8440     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8441     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8442   }
8443   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8444   /* TODO: flips */
8445   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8446   ierr = MatSetValues(A, numFIndices, findices, numCIndices, cindices, values, mode);
8447   if (ierr) {
8448     PetscMPIInt rank;
8449 
8450     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8451     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8452     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8453     PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8454     PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8455   }
8456   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8457   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8458   PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8459   PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8460   PetscFunctionReturn(PETSC_SUCCESS);
8461 }
8462 
8463 PetscErrorCode DMPlexMatGetClosureIndicesRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, PetscInt point, PetscInt cindices[], PetscInt findices[])
8464 {
8465   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
8466   PetscInt       *cpoints      = NULL;
8467   PetscInt        foffsets[32] = {0}, coffsets[32] = {0};
8468   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
8469   DMPolytopeType  ct;
8470   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
8471 
8472   PetscFunctionBegin;
8473   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
8474   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
8475   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
8476   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
8477   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
8478   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
8479   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
8480   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
8481   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
8482   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
8483   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
8484   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
8485   /* Column indices */
8486   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8487   maxFPoints = numCPoints;
8488   /* Compress out points not in the section */
8489   /*   TODO: Squeeze out points with 0 dof as well */
8490   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
8491   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
8492     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
8493       cpoints[q * 2]     = cpoints[p];
8494       cpoints[q * 2 + 1] = cpoints[p + 1];
8495       ++q;
8496     }
8497   }
8498   numCPoints = q;
8499   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
8500     PetscInt fdof;
8501 
8502     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
8503     if (!dof) continue;
8504     for (f = 0; f < numFields; ++f) {
8505       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
8506       coffsets[f + 1] += fdof;
8507     }
8508     numCIndices += dof;
8509   }
8510   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
8511   /* Row indices */
8512   PetscCall(DMPlexGetCellType(dmc, point, &ct));
8513   {
8514     DMPlexTransform tr;
8515     DMPolytopeType *rct;
8516     PetscInt       *rsize, *rcone, *rornt, Nt;
8517 
8518     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8519     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8520     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8521     numSubcells = rsize[Nt - 1];
8522     PetscCall(DMPlexTransformDestroy(&tr));
8523   }
8524   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8525   for (r = 0, q = 0; r < numSubcells; ++r) {
8526     /* TODO Map from coarse to fine cells */
8527     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8528     /* Compress out points not in the section */
8529     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8530     for (p = 0; p < numFPoints * 2; p += 2) {
8531       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8532         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8533         if (!dof) continue;
8534         for (s = 0; s < q; ++s)
8535           if (fpoints[p] == ftotpoints[s * 2]) break;
8536         if (s < q) continue;
8537         ftotpoints[q * 2]     = fpoints[p];
8538         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8539         ++q;
8540       }
8541     }
8542     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8543   }
8544   numFPoints = q;
8545   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8546     PetscInt fdof;
8547 
8548     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8549     if (!dof) continue;
8550     for (f = 0; f < numFields; ++f) {
8551       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8552       foffsets[f + 1] += fdof;
8553     }
8554     numFIndices += dof;
8555   }
8556   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8557 
8558   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8559   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8560   if (numFields) {
8561     const PetscInt **permsF[32] = {NULL};
8562     const PetscInt **permsC[32] = {NULL};
8563 
8564     for (f = 0; f < numFields; f++) {
8565       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8566       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8567     }
8568     for (p = 0; p < numFPoints; p++) {
8569       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8570       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8571     }
8572     for (p = 0; p < numCPoints; p++) {
8573       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8574       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8575     }
8576     for (f = 0; f < numFields; f++) {
8577       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8578       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8579     }
8580   } else {
8581     const PetscInt **permsF = NULL;
8582     const PetscInt **permsC = NULL;
8583 
8584     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8585     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8586     for (p = 0, off = 0; p < numFPoints; p++) {
8587       const PetscInt *perm = permsF ? permsF[p] : NULL;
8588 
8589       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8590       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8591     }
8592     for (p = 0, off = 0; p < numCPoints; p++) {
8593       const PetscInt *perm = permsC ? permsC[p] : NULL;
8594 
8595       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8596       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8597     }
8598     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8599     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8600   }
8601   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8602   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8603   PetscFunctionReturn(PETSC_SUCCESS);
8604 }
8605 
8606 /*@C
8607   DMPlexGetVTKCellHeight - Returns the height in the DAG used to determine which points are cells (normally 0)
8608 
8609   Input Parameter:
8610 . dm - The `DMPLEX` object
8611 
8612   Output Parameter:
8613 . cellHeight - The height of a cell
8614 
8615   Level: developer
8616 
8617 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetVTKCellHeight()`
8618 @*/
8619 PetscErrorCode DMPlexGetVTKCellHeight(DM dm, PetscInt *cellHeight)
8620 {
8621   DM_Plex *mesh = (DM_Plex *)dm->data;
8622 
8623   PetscFunctionBegin;
8624   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8625   PetscAssertPointer(cellHeight, 2);
8626   *cellHeight = mesh->vtkCellHeight;
8627   PetscFunctionReturn(PETSC_SUCCESS);
8628 }
8629 
8630 /*@C
8631   DMPlexSetVTKCellHeight - Sets the height in the DAG used to determine which points are cells (normally 0)
8632 
8633   Input Parameters:
8634 + dm         - The `DMPLEX` object
8635 - cellHeight - The height of a cell
8636 
8637   Level: developer
8638 
8639 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetVTKCellHeight()`
8640 @*/
8641 PetscErrorCode DMPlexSetVTKCellHeight(DM dm, PetscInt cellHeight)
8642 {
8643   DM_Plex *mesh = (DM_Plex *)dm->data;
8644 
8645   PetscFunctionBegin;
8646   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8647   mesh->vtkCellHeight = cellHeight;
8648   PetscFunctionReturn(PETSC_SUCCESS);
8649 }
8650 
8651 /*@
8652   DMPlexGetCellTypeStratum - Get the range of cells of a given celltype
8653 
8654   Input Parameters:
8655 + dm - The `DMPLEX` object
8656 - ct - The `DMPolytopeType` of the cell
8657 
8658   Output Parameters:
8659 + start - The first cell of this type, or `NULL`
8660 - end   - The upper bound on this celltype, or `NULL`
8661 
8662   Level: advanced
8663 
8664 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexConstructGhostCells()`, `DMPlexGetDepthStratum()`, `DMPlexGetHeightStratum()`
8665 @*/
8666 PetscErrorCode DMPlexGetCellTypeStratum(DM dm, DMPolytopeType ct, PetscInt *start, PetscInt *end)
8667 {
8668   DM_Plex *mesh = (DM_Plex *)dm->data;
8669   DMLabel  label;
8670   PetscInt pStart, pEnd;
8671 
8672   PetscFunctionBegin;
8673   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8674   if (start) {
8675     PetscAssertPointer(start, 3);
8676     *start = 0;
8677   }
8678   if (end) {
8679     PetscAssertPointer(end, 4);
8680     *end = 0;
8681   }
8682   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8683   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
8684   if (mesh->tr) {
8685     PetscCall(DMPlexTransformGetCellTypeStratum(mesh->tr, ct, start, end));
8686   } else {
8687     PetscCall(DMPlexGetCellTypeLabel(dm, &label));
8688     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named celltype was found");
8689     PetscCall(DMLabelGetStratumBounds(label, ct, start, end));
8690   }
8691   PetscFunctionReturn(PETSC_SUCCESS);
8692 }
8693 
8694 PetscErrorCode DMPlexCreateNumbering_Plex(DM dm, PetscInt pStart, PetscInt pEnd, PetscInt shift, PetscInt *globalSize, PetscSF sf, IS *numbering)
8695 {
8696   PetscSection section, globalSection;
8697   PetscInt    *numbers, p;
8698 
8699   PetscFunctionBegin;
8700   if (PetscDefined(USE_DEBUG)) PetscCall(DMPlexCheckPointSF(dm, sf, PETSC_TRUE));
8701   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
8702   PetscCall(PetscSectionSetChart(section, pStart, pEnd));
8703   for (p = pStart; p < pEnd; ++p) PetscCall(PetscSectionSetDof(section, p, 1));
8704   PetscCall(PetscSectionSetUp(section));
8705   PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &globalSection));
8706   PetscCall(PetscMalloc1(pEnd - pStart, &numbers));
8707   for (p = pStart; p < pEnd; ++p) {
8708     PetscCall(PetscSectionGetOffset(globalSection, p, &numbers[p - pStart]));
8709     if (numbers[p - pStart] < 0) numbers[p - pStart] -= shift;
8710     else numbers[p - pStart] += shift;
8711   }
8712   PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), pEnd - pStart, numbers, PETSC_OWN_POINTER, numbering));
8713   if (globalSize) {
8714     PetscLayout layout;
8715     PetscCall(PetscSectionGetPointLayout(PetscObjectComm((PetscObject)dm), globalSection, &layout));
8716     PetscCall(PetscLayoutGetSize(layout, globalSize));
8717     PetscCall(PetscLayoutDestroy(&layout));
8718   }
8719   PetscCall(PetscSectionDestroy(&section));
8720   PetscCall(PetscSectionDestroy(&globalSection));
8721   PetscFunctionReturn(PETSC_SUCCESS);
8722 }
8723 
8724 PetscErrorCode DMPlexCreateCellNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalCellNumbers)
8725 {
8726   PetscInt cellHeight, cStart, cEnd;
8727 
8728   PetscFunctionBegin;
8729   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8730   if (includeHybrid) PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8731   else PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd));
8732   PetscCall(DMPlexCreateNumbering_Plex(dm, cStart, cEnd, 0, NULL, dm->sf, globalCellNumbers));
8733   PetscFunctionReturn(PETSC_SUCCESS);
8734 }
8735 
8736 /*@
8737   DMPlexGetCellNumbering - Get a global cell numbering for all cells on this process
8738 
8739   Input Parameter:
8740 . dm - The `DMPLEX` object
8741 
8742   Output Parameter:
8743 . globalCellNumbers - Global cell numbers for all cells on this process
8744 
8745   Level: developer
8746 
8747 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetVertexNumbering()`
8748 @*/
8749 PetscErrorCode DMPlexGetCellNumbering(DM dm, IS *globalCellNumbers)
8750 {
8751   DM_Plex *mesh = (DM_Plex *)dm->data;
8752 
8753   PetscFunctionBegin;
8754   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8755   if (!mesh->globalCellNumbers) PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_FALSE, &mesh->globalCellNumbers));
8756   *globalCellNumbers = mesh->globalCellNumbers;
8757   PetscFunctionReturn(PETSC_SUCCESS);
8758 }
8759 
8760 PetscErrorCode DMPlexCreateVertexNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalVertexNumbers)
8761 {
8762   PetscInt vStart, vEnd;
8763 
8764   PetscFunctionBegin;
8765   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8766   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8767   PetscCall(DMPlexCreateNumbering_Plex(dm, vStart, vEnd, 0, NULL, dm->sf, globalVertexNumbers));
8768   PetscFunctionReturn(PETSC_SUCCESS);
8769 }
8770 
8771 /*@
8772   DMPlexGetVertexNumbering - Get a global vertex numbering for all vertices on this process
8773 
8774   Input Parameter:
8775 . dm - The `DMPLEX` object
8776 
8777   Output Parameter:
8778 . globalVertexNumbers - Global vertex numbers for all vertices on this process
8779 
8780   Level: developer
8781 
8782 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
8783 @*/
8784 PetscErrorCode DMPlexGetVertexNumbering(DM dm, IS *globalVertexNumbers)
8785 {
8786   DM_Plex *mesh = (DM_Plex *)dm->data;
8787 
8788   PetscFunctionBegin;
8789   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8790   if (!mesh->globalVertexNumbers) PetscCall(DMPlexCreateVertexNumbering_Internal(dm, PETSC_FALSE, &mesh->globalVertexNumbers));
8791   *globalVertexNumbers = mesh->globalVertexNumbers;
8792   PetscFunctionReturn(PETSC_SUCCESS);
8793 }
8794 
8795 /*@
8796   DMPlexCreatePointNumbering - Create a global numbering for all points.
8797 
8798   Collective
8799 
8800   Input Parameter:
8801 . dm - The `DMPLEX` object
8802 
8803   Output Parameter:
8804 . globalPointNumbers - Global numbers for all points on this process
8805 
8806   Level: developer
8807 
8808   Notes:
8809   The point numbering `IS` is parallel, with local portion indexed by local points (see `DMGetLocalSection()`). The global
8810   points are taken as stratified, with each MPI rank owning a contiguous subset of each stratum. In the IS, owned points
8811   will have their non-negative value while points owned by different ranks will be involuted -(idx+1). As an example,
8812   consider a parallel mesh in which the first two elements and first two vertices are owned by rank 0.
8813 
8814   The partitioned mesh is
8815   ```
8816   (2)--0--(3)--1--(4)    (1)--0--(2)
8817   ```
8818   and its global numbering is
8819   ```
8820   (3)--0--(4)--1--(5)--2--(6)
8821   ```
8822   Then the global numbering is provided as
8823   ```
8824   [0] Number of indices in set 5
8825   [0] 0 0
8826   [0] 1 1
8827   [0] 2 3
8828   [0] 3 4
8829   [0] 4 -6
8830   [1] Number of indices in set 3
8831   [1] 0 2
8832   [1] 1 5
8833   [1] 2 6
8834   ```
8835 
8836 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
8837 @*/
8838 PetscErrorCode DMPlexCreatePointNumbering(DM dm, IS *globalPointNumbers)
8839 {
8840   IS        nums[4];
8841   PetscInt  depths[4], gdepths[4], starts[4];
8842   PetscInt  depth, d, shift = 0;
8843   PetscBool empty = PETSC_FALSE;
8844 
8845   PetscFunctionBegin;
8846   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8847   PetscCall(DMPlexGetDepth(dm, &depth));
8848   // For unstratified meshes use dim instead of depth
8849   if (depth < 0) PetscCall(DMGetDimension(dm, &depth));
8850   // If any stratum is empty, we must mark all empty
8851   for (d = 0; d <= depth; ++d) {
8852     PetscInt end;
8853 
8854     depths[d] = depth - d;
8855     PetscCall(DMPlexGetDepthStratum(dm, depths[d], &starts[d], &end));
8856     if (!(starts[d] - end)) empty = PETSC_TRUE;
8857   }
8858   if (empty)
8859     for (d = 0; d <= depth; ++d) {
8860       depths[d] = -1;
8861       starts[d] = -1;
8862     }
8863   else PetscCall(PetscSortIntWithArray(depth + 1, starts, depths));
8864   PetscCall(MPIU_Allreduce(depths, gdepths, depth + 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
8865   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]);
8866   // Note here that 'shift' is collective, so that the numbering is stratified by depth
8867   for (d = 0; d <= depth; ++d) {
8868     PetscInt pStart, pEnd, gsize;
8869 
8870     PetscCall(DMPlexGetDepthStratum(dm, gdepths[d], &pStart, &pEnd));
8871     PetscCall(DMPlexCreateNumbering_Plex(dm, pStart, pEnd, shift, &gsize, dm->sf, &nums[d]));
8872     shift += gsize;
8873   }
8874   PetscCall(ISConcatenate(PETSC_COMM_SELF, depth + 1, nums, globalPointNumbers));
8875   for (d = 0; d <= depth; ++d) PetscCall(ISDestroy(&nums[d]));
8876   PetscFunctionReturn(PETSC_SUCCESS);
8877 }
8878 
8879 /*@
8880   DMPlexCreateRankField - Create a cell field whose value is the rank of the owner
8881 
8882   Input Parameter:
8883 . dm - The `DMPLEX` object
8884 
8885   Output Parameter:
8886 . ranks - The rank field
8887 
8888   Options Database Key:
8889 . -dm_partition_view - Adds the rank field into the `DM` output from `-dm_view` using the same viewer
8890 
8891   Level: intermediate
8892 
8893 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`
8894 @*/
8895 PetscErrorCode DMPlexCreateRankField(DM dm, Vec *ranks)
8896 {
8897   DM             rdm;
8898   PetscFE        fe;
8899   PetscScalar   *r;
8900   PetscMPIInt    rank;
8901   DMPolytopeType ct;
8902   PetscInt       dim, cStart, cEnd, c;
8903   PetscBool      simplex;
8904 
8905   PetscFunctionBeginUser;
8906   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8907   PetscAssertPointer(ranks, 2);
8908   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
8909   PetscCall(DMClone(dm, &rdm));
8910   PetscCall(DMGetDimension(rdm, &dim));
8911   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
8912   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
8913   simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct) + 1 ? PETSC_TRUE : PETSC_FALSE;
8914   PetscCall(PetscFECreateDefault(PETSC_COMM_SELF, dim, 1, simplex, "PETSc___rank_", -1, &fe));
8915   PetscCall(PetscObjectSetName((PetscObject)fe, "rank"));
8916   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
8917   PetscCall(PetscFEDestroy(&fe));
8918   PetscCall(DMCreateDS(rdm));
8919   PetscCall(DMCreateGlobalVector(rdm, ranks));
8920   PetscCall(PetscObjectSetName((PetscObject)*ranks, "partition"));
8921   PetscCall(VecGetArray(*ranks, &r));
8922   for (c = cStart; c < cEnd; ++c) {
8923     PetscScalar *lr;
8924 
8925     PetscCall(DMPlexPointGlobalRef(rdm, c, r, &lr));
8926     if (lr) *lr = rank;
8927   }
8928   PetscCall(VecRestoreArray(*ranks, &r));
8929   PetscCall(DMDestroy(&rdm));
8930   PetscFunctionReturn(PETSC_SUCCESS);
8931 }
8932 
8933 /*@
8934   DMPlexCreateLabelField - Create a field whose value is the label value for that point
8935 
8936   Input Parameters:
8937 + dm    - The `DMPLEX`
8938 - label - The `DMLabel`
8939 
8940   Output Parameter:
8941 . val - The label value field
8942 
8943   Options Database Key:
8944 . -dm_label_view - Adds the label value field into the `DM` output from `-dm_view` using the same viewer
8945 
8946   Level: intermediate
8947 
8948 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`
8949 @*/
8950 PetscErrorCode DMPlexCreateLabelField(DM dm, DMLabel label, Vec *val)
8951 {
8952   DM             rdm, plex;
8953   Vec            lval;
8954   PetscSection   section;
8955   PetscFE        fe;
8956   PetscScalar   *v;
8957   PetscInt       dim, pStart, pEnd, p, cStart;
8958   DMPolytopeType ct;
8959   char           name[PETSC_MAX_PATH_LEN];
8960   const char    *lname, *prefix;
8961 
8962   PetscFunctionBeginUser;
8963   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8964   PetscAssertPointer(label, 2);
8965   PetscAssertPointer(val, 3);
8966   PetscCall(DMClone(dm, &rdm));
8967   PetscCall(DMConvert(rdm, DMPLEX, &plex));
8968   PetscCall(DMPlexGetHeightStratum(plex, 0, &cStart, NULL));
8969   PetscCall(DMPlexGetCellType(plex, cStart, &ct));
8970   PetscCall(DMDestroy(&plex));
8971   PetscCall(DMGetDimension(rdm, &dim));
8972   PetscCall(DMGetOptionsPrefix(dm, &prefix));
8973   PetscCall(PetscObjectGetName((PetscObject)label, &lname));
8974   PetscCall(PetscSNPrintf(name, sizeof(name), "%s%s_", prefix ? prefix : "", lname));
8975   PetscCall(PetscFECreateByCell(PETSC_COMM_SELF, dim, 1, ct, name, -1, &fe));
8976   PetscCall(PetscObjectSetName((PetscObject)fe, ""));
8977   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
8978   PetscCall(PetscFEDestroy(&fe));
8979   PetscCall(DMCreateDS(rdm));
8980   PetscCall(DMCreateGlobalVector(rdm, val));
8981   PetscCall(DMCreateLocalVector(rdm, &lval));
8982   PetscCall(PetscObjectSetName((PetscObject)*val, lname));
8983   PetscCall(DMGetLocalSection(rdm, &section));
8984   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
8985   PetscCall(VecGetArray(lval, &v));
8986   for (p = pStart; p < pEnd; ++p) {
8987     PetscInt cval, dof, off;
8988 
8989     PetscCall(PetscSectionGetDof(section, p, &dof));
8990     if (!dof) continue;
8991     PetscCall(DMLabelGetValue(label, p, &cval));
8992     PetscCall(PetscSectionGetOffset(section, p, &off));
8993     for (PetscInt d = 0; d < dof; d++) v[off + d] = cval;
8994   }
8995   PetscCall(VecRestoreArray(lval, &v));
8996   PetscCall(DMLocalToGlobal(rdm, lval, INSERT_VALUES, *val));
8997   PetscCall(VecDestroy(&lval));
8998   PetscCall(DMDestroy(&rdm));
8999   PetscFunctionReturn(PETSC_SUCCESS);
9000 }
9001 
9002 /*@
9003   DMPlexCheckSymmetry - Check that the adjacency information in the mesh is symmetric.
9004 
9005   Input Parameter:
9006 . dm - The `DMPLEX` object
9007 
9008   Level: developer
9009 
9010   Notes:
9011   This is a useful diagnostic when creating meshes programmatically.
9012 
9013   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9014 
9015 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9016 @*/
9017 PetscErrorCode DMPlexCheckSymmetry(DM dm)
9018 {
9019   PetscSection    coneSection, supportSection;
9020   const PetscInt *cone, *support;
9021   PetscInt        coneSize, c, supportSize, s;
9022   PetscInt        pStart, pEnd, p, pp, csize, ssize;
9023   PetscBool       storagecheck = PETSC_TRUE;
9024 
9025   PetscFunctionBegin;
9026   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9027   PetscCall(DMViewFromOptions(dm, NULL, "-sym_dm_view"));
9028   PetscCall(DMPlexGetConeSection(dm, &coneSection));
9029   PetscCall(DMPlexGetSupportSection(dm, &supportSection));
9030   /* Check that point p is found in the support of its cone points, and vice versa */
9031   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
9032   for (p = pStart; p < pEnd; ++p) {
9033     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
9034     PetscCall(DMPlexGetCone(dm, p, &cone));
9035     for (c = 0; c < coneSize; ++c) {
9036       PetscBool dup = PETSC_FALSE;
9037       PetscInt  d;
9038       for (d = c - 1; d >= 0; --d) {
9039         if (cone[c] == cone[d]) {
9040           dup = PETSC_TRUE;
9041           break;
9042         }
9043       }
9044       PetscCall(DMPlexGetSupportSize(dm, cone[c], &supportSize));
9045       PetscCall(DMPlexGetSupport(dm, cone[c], &support));
9046       for (s = 0; s < supportSize; ++s) {
9047         if (support[s] == p) break;
9048       }
9049       if ((s >= supportSize) || (dup && (support[s + 1] != p))) {
9050         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", p));
9051         for (s = 0; s < coneSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[s]));
9052         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9053         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", cone[c]));
9054         for (s = 0; s < supportSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[s]));
9055         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9056         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]);
9057         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in support of cone point %" PetscInt_FMT, p, cone[c]);
9058       }
9059     }
9060     PetscCall(DMPlexGetTreeParent(dm, p, &pp, NULL));
9061     if (p != pp) {
9062       storagecheck = PETSC_FALSE;
9063       continue;
9064     }
9065     PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
9066     PetscCall(DMPlexGetSupport(dm, p, &support));
9067     for (s = 0; s < supportSize; ++s) {
9068       PetscCall(DMPlexGetConeSize(dm, support[s], &coneSize));
9069       PetscCall(DMPlexGetCone(dm, support[s], &cone));
9070       for (c = 0; c < coneSize; ++c) {
9071         PetscCall(DMPlexGetTreeParent(dm, cone[c], &pp, NULL));
9072         if (cone[c] != pp) {
9073           c = 0;
9074           break;
9075         }
9076         if (cone[c] == p) break;
9077       }
9078       if (c >= coneSize) {
9079         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", p));
9080         for (c = 0; c < supportSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[c]));
9081         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9082         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", support[s]));
9083         for (c = 0; c < coneSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[c]));
9084         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9085         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in cone of support point %" PetscInt_FMT, p, support[s]);
9086       }
9087     }
9088   }
9089   if (storagecheck) {
9090     PetscCall(PetscSectionGetStorageSize(coneSection, &csize));
9091     PetscCall(PetscSectionGetStorageSize(supportSection, &ssize));
9092     PetscCheck(csize == ssize, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Total cone size %" PetscInt_FMT " != Total support size %" PetscInt_FMT, csize, ssize);
9093   }
9094   PetscFunctionReturn(PETSC_SUCCESS);
9095 }
9096 
9097 /*
9098   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.
9099 */
9100 static PetscErrorCode DMPlexCellUnsplitVertices_Private(DM dm, PetscInt c, DMPolytopeType ct, PetscInt *unsplit)
9101 {
9102   DMPolytopeType  cct;
9103   PetscInt        ptpoints[4];
9104   const PetscInt *cone, *ccone, *ptcone;
9105   PetscInt        coneSize, cp, cconeSize, ccp, npt = 0, pt;
9106 
9107   PetscFunctionBegin;
9108   *unsplit = 0;
9109   switch (ct) {
9110   case DM_POLYTOPE_POINT_PRISM_TENSOR:
9111     ptpoints[npt++] = c;
9112     break;
9113   case DM_POLYTOPE_SEG_PRISM_TENSOR:
9114     PetscCall(DMPlexGetCone(dm, c, &cone));
9115     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9116     for (cp = 0; cp < coneSize; ++cp) {
9117       PetscCall(DMPlexGetCellType(dm, cone[cp], &cct));
9118       if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) ptpoints[npt++] = cone[cp];
9119     }
9120     break;
9121   case DM_POLYTOPE_TRI_PRISM_TENSOR:
9122   case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9123     PetscCall(DMPlexGetCone(dm, c, &cone));
9124     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9125     for (cp = 0; cp < coneSize; ++cp) {
9126       PetscCall(DMPlexGetCone(dm, cone[cp], &ccone));
9127       PetscCall(DMPlexGetConeSize(dm, cone[cp], &cconeSize));
9128       for (ccp = 0; ccp < cconeSize; ++ccp) {
9129         PetscCall(DMPlexGetCellType(dm, ccone[ccp], &cct));
9130         if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) {
9131           PetscInt p;
9132           for (p = 0; p < npt; ++p)
9133             if (ptpoints[p] == ccone[ccp]) break;
9134           if (p == npt) ptpoints[npt++] = ccone[ccp];
9135         }
9136       }
9137     }
9138     break;
9139   default:
9140     break;
9141   }
9142   for (pt = 0; pt < npt; ++pt) {
9143     PetscCall(DMPlexGetCone(dm, ptpoints[pt], &ptcone));
9144     if (ptcone[0] == ptcone[1]) ++(*unsplit);
9145   }
9146   PetscFunctionReturn(PETSC_SUCCESS);
9147 }
9148 
9149 /*@
9150   DMPlexCheckSkeleton - Check that each cell has the correct number of vertices
9151 
9152   Input Parameters:
9153 + dm         - The `DMPLEX` object
9154 - cellHeight - Normally 0
9155 
9156   Level: developer
9157 
9158   Notes:
9159   This is a useful diagnostic when creating meshes programmatically.
9160   Currently applicable only to homogeneous simplex or tensor meshes.
9161 
9162   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9163 
9164 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9165 @*/
9166 PetscErrorCode DMPlexCheckSkeleton(DM dm, PetscInt cellHeight)
9167 {
9168   DMPlexInterpolatedFlag interp;
9169   DMPolytopeType         ct;
9170   PetscInt               vStart, vEnd, cStart, cEnd, c;
9171 
9172   PetscFunctionBegin;
9173   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9174   PetscCall(DMPlexIsInterpolated(dm, &interp));
9175   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9176   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
9177   for (c = cStart; c < cEnd; ++c) {
9178     PetscInt *closure = NULL;
9179     PetscInt  coneSize, closureSize, cl, Nv = 0;
9180 
9181     PetscCall(DMPlexGetCellType(dm, c, &ct));
9182     PetscCheck((PetscInt)ct >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell %" PetscInt_FMT " has no cell type", c);
9183     if (ct == DM_POLYTOPE_UNKNOWN) continue;
9184     if (interp == DMPLEX_INTERPOLATED_FULL) {
9185       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9186       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));
9187     }
9188     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9189     for (cl = 0; cl < closureSize * 2; cl += 2) {
9190       const PetscInt p = closure[cl];
9191       if ((p >= vStart) && (p < vEnd)) ++Nv;
9192     }
9193     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9194     /* Special Case: Tensor faces with identified vertices */
9195     if (Nv < DMPolytopeTypeGetNumVertices(ct)) {
9196       PetscInt unsplit;
9197 
9198       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9199       if (Nv + unsplit == DMPolytopeTypeGetNumVertices(ct)) continue;
9200     }
9201     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));
9202   }
9203   PetscFunctionReturn(PETSC_SUCCESS);
9204 }
9205 
9206 /*@
9207   DMPlexCheckFaces - Check that the faces of each cell give a vertex order this is consistent with what we expect from the cell type
9208 
9209   Collective
9210 
9211   Input Parameters:
9212 + dm         - The `DMPLEX` object
9213 - cellHeight - Normally 0
9214 
9215   Level: developer
9216 
9217   Notes:
9218   This is a useful diagnostic when creating meshes programmatically.
9219   This routine is only relevant for meshes that are fully interpolated across all ranks.
9220   It will error out if a partially interpolated mesh is given on some rank.
9221   It will do nothing for locally uninterpolated mesh (as there is nothing to check).
9222 
9223   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9224 
9225 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMPlexGetVTKCellHeight()`, `DMSetFromOptions()`
9226 @*/
9227 PetscErrorCode DMPlexCheckFaces(DM dm, PetscInt cellHeight)
9228 {
9229   PetscInt               dim, depth, vStart, vEnd, cStart, cEnd, c, h;
9230   DMPlexInterpolatedFlag interpEnum;
9231 
9232   PetscFunctionBegin;
9233   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9234   PetscCall(DMPlexIsInterpolatedCollective(dm, &interpEnum));
9235   if (interpEnum == DMPLEX_INTERPOLATED_NONE) PetscFunctionReturn(PETSC_SUCCESS);
9236   if (interpEnum != DMPLEX_INTERPOLATED_FULL) {
9237     PetscCall(PetscPrintf(PetscObjectComm((PetscObject)dm), "DMPlexCheckFaces() warning: Mesh is only partially interpolated, this is currently not supported"));
9238     PetscFunctionReturn(PETSC_SUCCESS);
9239   }
9240 
9241   PetscCall(DMGetDimension(dm, &dim));
9242   PetscCall(DMPlexGetDepth(dm, &depth));
9243   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
9244   for (h = cellHeight; h < PetscMin(depth, dim); ++h) {
9245     PetscCall(DMPlexGetHeightStratum(dm, h, &cStart, &cEnd));
9246     for (c = cStart; c < cEnd; ++c) {
9247       const PetscInt       *cone, *ornt, *faceSizes, *faces;
9248       const DMPolytopeType *faceTypes;
9249       DMPolytopeType        ct;
9250       PetscInt              numFaces, coneSize, f;
9251       PetscInt             *closure = NULL, closureSize, cl, numCorners = 0, fOff = 0, unsplit;
9252 
9253       PetscCall(DMPlexGetCellType(dm, c, &ct));
9254       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9255       if (unsplit) continue;
9256       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9257       PetscCall(DMPlexGetCone(dm, c, &cone));
9258       PetscCall(DMPlexGetConeOrientation(dm, c, &ornt));
9259       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9260       for (cl = 0; cl < closureSize * 2; cl += 2) {
9261         const PetscInt p = closure[cl];
9262         if ((p >= vStart) && (p < vEnd)) closure[numCorners++] = p;
9263       }
9264       PetscCall(DMPlexGetRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
9265       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);
9266       for (f = 0; f < numFaces; ++f) {
9267         DMPolytopeType fct;
9268         PetscInt      *fclosure = NULL, fclosureSize, cl, fnumCorners = 0, v;
9269 
9270         PetscCall(DMPlexGetCellType(dm, cone[f], &fct));
9271         PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[f], ornt[f], PETSC_TRUE, &fclosureSize, &fclosure));
9272         for (cl = 0; cl < fclosureSize * 2; cl += 2) {
9273           const PetscInt p = fclosure[cl];
9274           if ((p >= vStart) && (p < vEnd)) fclosure[fnumCorners++] = p;
9275         }
9276         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]);
9277         for (v = 0; v < fnumCorners; ++v) {
9278           if (fclosure[v] != faces[fOff + v]) {
9279             PetscInt v1;
9280 
9281             PetscCall(PetscPrintf(PETSC_COMM_SELF, "face closure:"));
9282             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, fclosure[v1]));
9283             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\ncell face:"));
9284             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, faces[fOff + v1]));
9285             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9286             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]);
9287           }
9288         }
9289         PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[f], PETSC_TRUE, &fclosureSize, &fclosure));
9290         fOff += faceSizes[f];
9291       }
9292       PetscCall(DMPlexRestoreRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
9293       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9294     }
9295   }
9296   PetscFunctionReturn(PETSC_SUCCESS);
9297 }
9298 
9299 /*@
9300   DMPlexCheckGeometry - Check the geometry of mesh cells
9301 
9302   Input Parameter:
9303 . dm - The `DMPLEX` object
9304 
9305   Level: developer
9306 
9307   Notes:
9308   This is a useful diagnostic when creating meshes programmatically.
9309 
9310   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9311 
9312 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9313 @*/
9314 PetscErrorCode DMPlexCheckGeometry(DM dm)
9315 {
9316   Vec       coordinates;
9317   PetscReal detJ, J[9], refVol = 1.0;
9318   PetscReal vol;
9319   PetscInt  dim, depth, dE, d, cStart, cEnd, c;
9320 
9321   PetscFunctionBegin;
9322   PetscCall(DMGetDimension(dm, &dim));
9323   PetscCall(DMGetCoordinateDim(dm, &dE));
9324   if (dim != dE) PetscFunctionReturn(PETSC_SUCCESS);
9325   PetscCall(DMPlexGetDepth(dm, &depth));
9326   for (d = 0; d < dim; ++d) refVol *= 2.0;
9327   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
9328   /* Make sure local coordinates are created, because that step is collective */
9329   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
9330   if (!coordinates) PetscFunctionReturn(PETSC_SUCCESS);
9331   for (c = cStart; c < cEnd; ++c) {
9332     DMPolytopeType ct;
9333     PetscInt       unsplit;
9334     PetscBool      ignoreZeroVol = PETSC_FALSE;
9335 
9336     PetscCall(DMPlexGetCellType(dm, c, &ct));
9337     switch (ct) {
9338     case DM_POLYTOPE_SEG_PRISM_TENSOR:
9339     case DM_POLYTOPE_TRI_PRISM_TENSOR:
9340     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9341       ignoreZeroVol = PETSC_TRUE;
9342       break;
9343     default:
9344       break;
9345     }
9346     switch (ct) {
9347     case DM_POLYTOPE_TRI_PRISM:
9348     case DM_POLYTOPE_TRI_PRISM_TENSOR:
9349     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9350     case DM_POLYTOPE_PYRAMID:
9351       continue;
9352     default:
9353       break;
9354     }
9355     PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9356     if (unsplit) continue;
9357     PetscCall(DMPlexComputeCellGeometryFEM(dm, c, NULL, NULL, J, NULL, &detJ));
9358     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);
9359     PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FEM Volume %g\n", c, (double)(detJ * refVol)));
9360     /* This should work with periodicity since DG coordinates should be used */
9361     if (depth > 1) {
9362       PetscCall(DMPlexComputeCellGeometryFVM(dm, c, &vol, NULL, NULL));
9363       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);
9364       PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FVM Volume %g\n", c, (double)vol));
9365     }
9366   }
9367   PetscFunctionReturn(PETSC_SUCCESS);
9368 }
9369 
9370 /*@
9371   DMPlexCheckPointSF - Check that several necessary conditions are met for the point `PetscSF` of this plex.
9372 
9373   Collective
9374 
9375   Input Parameters:
9376 + dm              - The `DMPLEX` object
9377 . pointSF         - The `PetscSF`, or `NULL` for `PointSF` attached to `DM`
9378 - allowExtraRoots - Flag to allow extra points not present in the `DM`
9379 
9380   Level: developer
9381 
9382   Notes:
9383   This is mainly intended for debugging/testing purposes.
9384 
9385   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9386 
9387   Extra roots can come from periodic cuts, where additional points appear on the boundary
9388 
9389 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMGetPointSF()`, `DMSetFromOptions()`
9390 @*/
9391 PetscErrorCode DMPlexCheckPointSF(DM dm, PetscSF pointSF, PetscBool allowExtraRoots)
9392 {
9393   PetscInt           l, nleaves, nroots, overlap;
9394   const PetscInt    *locals;
9395   const PetscSFNode *remotes;
9396   PetscBool          distributed;
9397   MPI_Comm           comm;
9398   PetscMPIInt        rank;
9399 
9400   PetscFunctionBegin;
9401   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9402   if (pointSF) PetscValidHeaderSpecific(pointSF, PETSCSF_CLASSID, 2);
9403   else pointSF = dm->sf;
9404   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9405   PetscCheck(pointSF, comm, PETSC_ERR_ARG_WRONGSTATE, "DMPlex must have Point SF attached");
9406   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9407   {
9408     PetscMPIInt mpiFlag;
9409 
9410     PetscCallMPI(MPI_Comm_compare(comm, PetscObjectComm((PetscObject)pointSF), &mpiFlag));
9411     PetscCheck(mpiFlag == MPI_CONGRUENT || mpiFlag == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "DM and Point SF have different communicators (flag %d)", mpiFlag);
9412   }
9413   PetscCall(PetscSFGetGraph(pointSF, &nroots, &nleaves, &locals, &remotes));
9414   PetscCall(DMPlexIsDistributed(dm, &distributed));
9415   if (!distributed) {
9416     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);
9417     PetscFunctionReturn(PETSC_SUCCESS);
9418   }
9419   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);
9420   PetscCall(DMPlexGetOverlap(dm, &overlap));
9421 
9422   /* Check SF graph is compatible with DMPlex chart */
9423   {
9424     PetscInt pStart, pEnd, maxLeaf;
9425 
9426     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
9427     PetscCall(PetscSFGetLeafRange(pointSF, NULL, &maxLeaf));
9428     PetscCheck(allowExtraRoots || pEnd - pStart == nroots, PETSC_COMM_SELF, PETSC_ERR_PLIB, "pEnd - pStart = %" PetscInt_FMT " != nroots = %" PetscInt_FMT, pEnd - pStart, nroots);
9429     PetscCheck(maxLeaf < pEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "maxLeaf = %" PetscInt_FMT " >= pEnd = %" PetscInt_FMT, maxLeaf, pEnd);
9430   }
9431 
9432   /* Check Point SF has no local points referenced */
9433   for (l = 0; l < nleaves; l++) {
9434     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);
9435   }
9436 
9437   /* Check there are no cells in interface */
9438   if (!overlap) {
9439     PetscInt cellHeight, cStart, cEnd;
9440 
9441     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9442     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9443     for (l = 0; l < nleaves; ++l) {
9444       const PetscInt point = locals ? locals[l] : l;
9445 
9446       PetscCheck(point < cStart || point >= cEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " which is a cell", point);
9447     }
9448   }
9449 
9450   /* If some point is in interface, then all its cone points must be also in interface (either as leaves or roots) */
9451   {
9452     const PetscInt *rootdegree;
9453 
9454     PetscCall(PetscSFComputeDegreeBegin(pointSF, &rootdegree));
9455     PetscCall(PetscSFComputeDegreeEnd(pointSF, &rootdegree));
9456     for (l = 0; l < nleaves; ++l) {
9457       const PetscInt  point = locals ? locals[l] : l;
9458       const PetscInt *cone;
9459       PetscInt        coneSize, c, idx;
9460 
9461       PetscCall(DMPlexGetConeSize(dm, point, &coneSize));
9462       PetscCall(DMPlexGetCone(dm, point, &cone));
9463       for (c = 0; c < coneSize; ++c) {
9464         if (!rootdegree[cone[c]]) {
9465           if (locals) {
9466             PetscCall(PetscFindInt(cone[c], nleaves, locals, &idx));
9467           } else {
9468             idx = (cone[c] < nleaves) ? cone[c] : -1;
9469           }
9470           PetscCheck(idx >= 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " but not %" PetscInt_FMT " from its cone", point, cone[c]);
9471         }
9472       }
9473     }
9474   }
9475   PetscFunctionReturn(PETSC_SUCCESS);
9476 }
9477 
9478 /*@
9479   DMPlexCheck - Perform various checks of `DMPLEX` sanity
9480 
9481   Input Parameter:
9482 . dm - The `DMPLEX` object
9483 
9484   Level: developer
9485 
9486   Notes:
9487   This is a useful diagnostic when creating meshes programmatically.
9488 
9489   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9490 
9491   Currently does not include `DMPlexCheckCellShape()`.
9492 
9493 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9494 @*/
9495 PetscErrorCode DMPlexCheck(DM dm)
9496 {
9497   PetscInt cellHeight;
9498 
9499   PetscFunctionBegin;
9500   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9501   PetscCall(DMPlexCheckSymmetry(dm));
9502   PetscCall(DMPlexCheckSkeleton(dm, cellHeight));
9503   PetscCall(DMPlexCheckFaces(dm, cellHeight));
9504   PetscCall(DMPlexCheckGeometry(dm));
9505   PetscCall(DMPlexCheckPointSF(dm, NULL, PETSC_FALSE));
9506   PetscCall(DMPlexCheckInterfaceCones(dm));
9507   PetscFunctionReturn(PETSC_SUCCESS);
9508 }
9509 
9510 typedef struct cell_stats {
9511   PetscReal min, max, sum, squaresum;
9512   PetscInt  count;
9513 } cell_stats_t;
9514 
9515 static void MPIAPI cell_stats_reduce(void *a, void *b, int *len, MPI_Datatype *datatype)
9516 {
9517   PetscInt i, N = *len;
9518 
9519   for (i = 0; i < N; i++) {
9520     cell_stats_t *A = (cell_stats_t *)a;
9521     cell_stats_t *B = (cell_stats_t *)b;
9522 
9523     B->min = PetscMin(A->min, B->min);
9524     B->max = PetscMax(A->max, B->max);
9525     B->sum += A->sum;
9526     B->squaresum += A->squaresum;
9527     B->count += A->count;
9528   }
9529 }
9530 
9531 /*@
9532   DMPlexCheckCellShape - Checks the Jacobian of the mapping from reference to real cells and computes some minimal statistics.
9533 
9534   Collective
9535 
9536   Input Parameters:
9537 + dm        - The `DMPLEX` object
9538 . output    - If true, statistics will be displayed on `stdout`
9539 - condLimit - Display all cells above this condition number, or `PETSC_DETERMINE` for no cell output
9540 
9541   Level: developer
9542 
9543   Notes:
9544   This is mainly intended for debugging/testing purposes.
9545 
9546   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9547 
9548 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexComputeOrthogonalQuality()`
9549 @*/
9550 PetscErrorCode DMPlexCheckCellShape(DM dm, PetscBool output, PetscReal condLimit)
9551 {
9552   DM           dmCoarse;
9553   cell_stats_t stats, globalStats;
9554   MPI_Comm     comm = PetscObjectComm((PetscObject)dm);
9555   PetscReal   *J, *invJ, min = 0, max = 0, mean = 0, stdev = 0;
9556   PetscReal    limit = condLimit > 0 ? condLimit : PETSC_MAX_REAL;
9557   PetscInt     cdim, cStart, cEnd, c, eStart, eEnd, count = 0;
9558   PetscMPIInt  rank, size;
9559 
9560   PetscFunctionBegin;
9561   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9562   stats.min = PETSC_MAX_REAL;
9563   stats.max = PETSC_MIN_REAL;
9564   stats.sum = stats.squaresum = 0.;
9565   stats.count                 = 0;
9566 
9567   PetscCallMPI(MPI_Comm_size(comm, &size));
9568   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9569   PetscCall(DMGetCoordinateDim(dm, &cdim));
9570   PetscCall(PetscMalloc2(PetscSqr(cdim), &J, PetscSqr(cdim), &invJ));
9571   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
9572   PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
9573   for (c = cStart; c < cEnd; c++) {
9574     PetscInt  i;
9575     PetscReal frobJ = 0., frobInvJ = 0., cond2, cond, detJ;
9576 
9577     PetscCall(DMPlexComputeCellGeometryAffineFEM(dm, c, NULL, J, invJ, &detJ));
9578     PetscCheck(detJ >= 0.0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Mesh cell %" PetscInt_FMT " is inverted", c);
9579     for (i = 0; i < PetscSqr(cdim); ++i) {
9580       frobJ += J[i] * J[i];
9581       frobInvJ += invJ[i] * invJ[i];
9582     }
9583     cond2 = frobJ * frobInvJ;
9584     cond  = PetscSqrtReal(cond2);
9585 
9586     stats.min = PetscMin(stats.min, cond);
9587     stats.max = PetscMax(stats.max, cond);
9588     stats.sum += cond;
9589     stats.squaresum += cond2;
9590     stats.count++;
9591     if (output && cond > limit) {
9592       PetscSection coordSection;
9593       Vec          coordsLocal;
9594       PetscScalar *coords = NULL;
9595       PetscInt     Nv, d, clSize, cl, *closure = NULL;
9596 
9597       PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
9598       PetscCall(DMGetCoordinateSection(dm, &coordSection));
9599       PetscCall(DMPlexVecGetClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9600       PetscCall(PetscSynchronizedPrintf(comm, "[%d] Cell %" PetscInt_FMT " cond %g\n", rank, c, (double)cond));
9601       for (i = 0; i < Nv / cdim; ++i) {
9602         PetscCall(PetscSynchronizedPrintf(comm, "  Vertex %" PetscInt_FMT ": (", i));
9603         for (d = 0; d < cdim; ++d) {
9604           if (d > 0) PetscCall(PetscSynchronizedPrintf(comm, ", "));
9605           PetscCall(PetscSynchronizedPrintf(comm, "%g", (double)PetscRealPart(coords[i * cdim + d])));
9606         }
9607         PetscCall(PetscSynchronizedPrintf(comm, ")\n"));
9608       }
9609       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9610       for (cl = 0; cl < clSize * 2; cl += 2) {
9611         const PetscInt edge = closure[cl];
9612 
9613         if ((edge >= eStart) && (edge < eEnd)) {
9614           PetscReal len;
9615 
9616           PetscCall(DMPlexComputeCellGeometryFVM(dm, edge, &len, NULL, NULL));
9617           PetscCall(PetscSynchronizedPrintf(comm, "  Edge %" PetscInt_FMT ": length %g\n", edge, (double)len));
9618         }
9619       }
9620       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9621       PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9622     }
9623   }
9624   if (output) PetscCall(PetscSynchronizedFlush(comm, NULL));
9625 
9626   if (size > 1) {
9627     PetscMPIInt  blockLengths[2] = {4, 1};
9628     MPI_Aint     blockOffsets[2] = {offsetof(cell_stats_t, min), offsetof(cell_stats_t, count)};
9629     MPI_Datatype blockTypes[2]   = {MPIU_REAL, MPIU_INT}, statType;
9630     MPI_Op       statReduce;
9631 
9632     PetscCallMPI(MPI_Type_create_struct(2, blockLengths, blockOffsets, blockTypes, &statType));
9633     PetscCallMPI(MPI_Type_commit(&statType));
9634     PetscCallMPI(MPI_Op_create(cell_stats_reduce, PETSC_TRUE, &statReduce));
9635     PetscCallMPI(MPI_Reduce(&stats, &globalStats, 1, statType, statReduce, 0, comm));
9636     PetscCallMPI(MPI_Op_free(&statReduce));
9637     PetscCallMPI(MPI_Type_free(&statType));
9638   } else {
9639     PetscCall(PetscArraycpy(&globalStats, &stats, 1));
9640   }
9641   if (rank == 0) {
9642     count = globalStats.count;
9643     min   = globalStats.min;
9644     max   = globalStats.max;
9645     mean  = globalStats.sum / globalStats.count;
9646     stdev = globalStats.count > 1 ? PetscSqrtReal(PetscMax((globalStats.squaresum - globalStats.count * mean * mean) / (globalStats.count - 1), 0)) : 0.0;
9647   }
9648 
9649   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));
9650   PetscCall(PetscFree2(J, invJ));
9651 
9652   PetscCall(DMGetCoarseDM(dm, &dmCoarse));
9653   if (dmCoarse) {
9654     PetscBool isplex;
9655 
9656     PetscCall(PetscObjectTypeCompare((PetscObject)dmCoarse, DMPLEX, &isplex));
9657     if (isplex) PetscCall(DMPlexCheckCellShape(dmCoarse, output, condLimit));
9658   }
9659   PetscFunctionReturn(PETSC_SUCCESS);
9660 }
9661 
9662 /*@
9663   DMPlexComputeOrthogonalQuality - Compute cell-wise orthogonal quality mesh statistic. Optionally tags all cells with
9664   orthogonal quality below given tolerance.
9665 
9666   Collective
9667 
9668   Input Parameters:
9669 + dm   - The `DMPLEX` object
9670 . fv   - Optional `PetscFV` object for pre-computed cell/face centroid information
9671 - atol - [0, 1] Absolute tolerance for tagging cells.
9672 
9673   Output Parameters:
9674 + OrthQual      - `Vec` containing orthogonal quality per cell
9675 - OrthQualLabel - `DMLabel` tagging cells below atol with `DM_ADAPT_REFINE`
9676 
9677   Options Database Keys:
9678 + -dm_plex_orthogonal_quality_label_view - view OrthQualLabel if label is requested. Currently only `PETSCVIEWERASCII` is supported.
9679 - -dm_plex_orthogonal_quality_vec_view   - view OrthQual vector.
9680 
9681   Level: intermediate
9682 
9683   Notes:
9684   Orthogonal quality is given by the following formula\:
9685 
9686   $ \min \left[ \frac{A_i \cdot f_i}{\|A_i\| \|f_i\|} , \frac{A_i \cdot c_i}{\|A_i\| \|c_i\|} \right]$
9687 
9688   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
9689   is the vector from the current cells centroid to the centroid of its i'th neighbor (which shares a face with the
9690   current cell). This computes the vector similarity between each cell face and its corresponding neighbor centroid by
9691   calculating the cosine of the angle between these vectors.
9692 
9693   Orthogonal quality ranges from 1 (best) to 0 (worst).
9694 
9695   This routine is mainly useful for FVM, however is not restricted to only FVM. The `PetscFV` object is optionally used to check for
9696   pre-computed FVM cell data, but if it is not passed in then this data will be computed.
9697 
9698   Cells are tagged if they have an orthogonal quality less than or equal to the absolute tolerance.
9699 
9700 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCheckCellShape()`, `DMCreateLabel()`, `PetscFV`, `DMLabel`, `Vec`
9701 @*/
9702 PetscErrorCode DMPlexComputeOrthogonalQuality(DM dm, PetscFV fv, PetscReal atol, Vec *OrthQual, DMLabel *OrthQualLabel)
9703 {
9704   PetscInt               nc, cellHeight, cStart, cEnd, cell, cellIter = 0;
9705   PetscInt              *idx;
9706   PetscScalar           *oqVals;
9707   const PetscScalar     *cellGeomArr, *faceGeomArr;
9708   PetscReal             *ci, *fi, *Ai;
9709   MPI_Comm               comm;
9710   Vec                    cellgeom, facegeom;
9711   DM                     dmFace, dmCell;
9712   IS                     glob;
9713   ISLocalToGlobalMapping ltog;
9714   PetscViewer            vwr;
9715 
9716   PetscFunctionBegin;
9717   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9718   if (fv) PetscValidHeaderSpecific(fv, PETSCFV_CLASSID, 2);
9719   PetscAssertPointer(OrthQual, 4);
9720   PetscCheck(atol >= 0.0 && atol <= 1.0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Absolute tolerance %g not in [0,1]", (double)atol);
9721   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9722   PetscCall(DMGetDimension(dm, &nc));
9723   PetscCheck(nc >= 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must have dimension >= 2 (current %" PetscInt_FMT ")", nc);
9724   {
9725     DMPlexInterpolatedFlag interpFlag;
9726 
9727     PetscCall(DMPlexIsInterpolated(dm, &interpFlag));
9728     if (interpFlag != DMPLEX_INTERPOLATED_FULL) {
9729       PetscMPIInt rank;
9730 
9731       PetscCallMPI(MPI_Comm_rank(comm, &rank));
9732       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must be fully interpolated, DM on rank %d is not fully interpolated", rank);
9733     }
9734   }
9735   if (OrthQualLabel) {
9736     PetscAssertPointer(OrthQualLabel, 5);
9737     PetscCall(DMCreateLabel(dm, "Orthogonal_Quality"));
9738     PetscCall(DMGetLabel(dm, "Orthogonal_Quality", OrthQualLabel));
9739   } else {
9740     *OrthQualLabel = NULL;
9741   }
9742   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9743   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9744   PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_TRUE, &glob));
9745   PetscCall(ISLocalToGlobalMappingCreateIS(glob, &ltog));
9746   PetscCall(ISLocalToGlobalMappingSetType(ltog, ISLOCALTOGLOBALMAPPINGHASH));
9747   PetscCall(VecCreate(comm, OrthQual));
9748   PetscCall(VecSetType(*OrthQual, VECSTANDARD));
9749   PetscCall(VecSetSizes(*OrthQual, cEnd - cStart, PETSC_DETERMINE));
9750   PetscCall(VecSetLocalToGlobalMapping(*OrthQual, ltog));
9751   PetscCall(VecSetUp(*OrthQual));
9752   PetscCall(ISDestroy(&glob));
9753   PetscCall(ISLocalToGlobalMappingDestroy(&ltog));
9754   PetscCall(DMPlexGetDataFVM(dm, fv, &cellgeom, &facegeom, NULL));
9755   PetscCall(VecGetArrayRead(cellgeom, &cellGeomArr));
9756   PetscCall(VecGetArrayRead(facegeom, &faceGeomArr));
9757   PetscCall(VecGetDM(cellgeom, &dmCell));
9758   PetscCall(VecGetDM(facegeom, &dmFace));
9759   PetscCall(PetscMalloc5(cEnd - cStart, &idx, cEnd - cStart, &oqVals, nc, &ci, nc, &fi, nc, &Ai));
9760   for (cell = cStart; cell < cEnd; cellIter++, cell++) {
9761     PetscInt         cellneigh, cellneighiter = 0, adjSize = PETSC_DETERMINE;
9762     PetscInt         cellarr[2], *adj = NULL;
9763     PetscScalar     *cArr, *fArr;
9764     PetscReal        minvalc = 1.0, minvalf = 1.0;
9765     PetscFVCellGeom *cg;
9766 
9767     idx[cellIter] = cell - cStart;
9768     cellarr[0]    = cell;
9769     /* Make indexing into cellGeom easier */
9770     PetscCall(DMPlexPointLocalRead(dmCell, cell, cellGeomArr, &cg));
9771     PetscCall(DMPlexGetAdjacency_Internal(dm, cell, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &adjSize, &adj));
9772     /* Technically 1 too big, but easier than fiddling with empty adjacency array */
9773     PetscCall(PetscCalloc2(adjSize, &cArr, adjSize, &fArr));
9774     for (cellneigh = 0; cellneigh < adjSize; cellneighiter++, cellneigh++) {
9775       PetscInt         i;
9776       const PetscInt   neigh  = adj[cellneigh];
9777       PetscReal        normci = 0, normfi = 0, normai = 0;
9778       PetscFVCellGeom *cgneigh;
9779       PetscFVFaceGeom *fg;
9780 
9781       /* Don't count ourselves in the neighbor list */
9782       if (neigh == cell) continue;
9783       PetscCall(DMPlexPointLocalRead(dmCell, neigh, cellGeomArr, &cgneigh));
9784       cellarr[1] = neigh;
9785       {
9786         PetscInt        numcovpts;
9787         const PetscInt *covpts;
9788 
9789         PetscCall(DMPlexGetMeet(dm, 2, cellarr, &numcovpts, &covpts));
9790         PetscCall(DMPlexPointLocalRead(dmFace, covpts[0], faceGeomArr, &fg));
9791         PetscCall(DMPlexRestoreMeet(dm, 2, cellarr, &numcovpts, &covpts));
9792       }
9793 
9794       /* Compute c_i, f_i and their norms */
9795       for (i = 0; i < nc; i++) {
9796         ci[i] = cgneigh->centroid[i] - cg->centroid[i];
9797         fi[i] = fg->centroid[i] - cg->centroid[i];
9798         Ai[i] = fg->normal[i];
9799         normci += PetscPowReal(ci[i], 2);
9800         normfi += PetscPowReal(fi[i], 2);
9801         normai += PetscPowReal(Ai[i], 2);
9802       }
9803       normci = PetscSqrtReal(normci);
9804       normfi = PetscSqrtReal(normfi);
9805       normai = PetscSqrtReal(normai);
9806 
9807       /* Normalize and compute for each face-cell-normal pair */
9808       for (i = 0; i < nc; i++) {
9809         ci[i] = ci[i] / normci;
9810         fi[i] = fi[i] / normfi;
9811         Ai[i] = Ai[i] / normai;
9812         /* PetscAbs because I don't know if normals are guaranteed to point out */
9813         cArr[cellneighiter] += PetscAbs(Ai[i] * ci[i]);
9814         fArr[cellneighiter] += PetscAbs(Ai[i] * fi[i]);
9815       }
9816       if (PetscRealPart(cArr[cellneighiter]) < minvalc) minvalc = PetscRealPart(cArr[cellneighiter]);
9817       if (PetscRealPart(fArr[cellneighiter]) < minvalf) minvalf = PetscRealPart(fArr[cellneighiter]);
9818     }
9819     PetscCall(PetscFree(adj));
9820     PetscCall(PetscFree2(cArr, fArr));
9821     /* Defer to cell if they're equal */
9822     oqVals[cellIter] = PetscMin(minvalf, minvalc);
9823     if (OrthQualLabel) {
9824       if (PetscRealPart(oqVals[cellIter]) <= atol) PetscCall(DMLabelSetValue(*OrthQualLabel, cell, DM_ADAPT_REFINE));
9825     }
9826   }
9827   PetscCall(VecSetValuesLocal(*OrthQual, cEnd - cStart, idx, oqVals, INSERT_VALUES));
9828   PetscCall(VecAssemblyBegin(*OrthQual));
9829   PetscCall(VecAssemblyEnd(*OrthQual));
9830   PetscCall(VecRestoreArrayRead(cellgeom, &cellGeomArr));
9831   PetscCall(VecRestoreArrayRead(facegeom, &faceGeomArr));
9832   PetscCall(PetscOptionsGetViewer(comm, NULL, NULL, "-dm_plex_orthogonal_quality_label_view", &vwr, NULL, NULL));
9833   if (OrthQualLabel) {
9834     if (vwr) PetscCall(DMLabelView(*OrthQualLabel, vwr));
9835   }
9836   PetscCall(PetscFree5(idx, oqVals, ci, fi, Ai));
9837   PetscCall(PetscOptionsRestoreViewer(&vwr));
9838   PetscCall(VecViewFromOptions(*OrthQual, NULL, "-dm_plex_orthogonal_quality_vec_view"));
9839   PetscFunctionReturn(PETSC_SUCCESS);
9840 }
9841 
9842 /* this is here instead of DMGetOutputDM because output DM still has constraints in the local indices that affect
9843  * interpolator construction */
9844 static PetscErrorCode DMGetFullDM(DM dm, DM *odm)
9845 {
9846   PetscSection section, newSection, gsection;
9847   PetscSF      sf;
9848   PetscBool    hasConstraints, ghasConstraints;
9849 
9850   PetscFunctionBegin;
9851   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9852   PetscAssertPointer(odm, 2);
9853   PetscCall(DMGetLocalSection(dm, &section));
9854   PetscCall(PetscSectionHasConstraints(section, &hasConstraints));
9855   PetscCall(MPIU_Allreduce(&hasConstraints, &ghasConstraints, 1, MPIU_BOOL, MPI_LOR, PetscObjectComm((PetscObject)dm)));
9856   if (!ghasConstraints) {
9857     PetscCall(PetscObjectReference((PetscObject)dm));
9858     *odm = dm;
9859     PetscFunctionReturn(PETSC_SUCCESS);
9860   }
9861   PetscCall(DMClone(dm, odm));
9862   PetscCall(DMCopyFields(dm, *odm));
9863   PetscCall(DMGetLocalSection(*odm, &newSection));
9864   PetscCall(DMGetPointSF(*odm, &sf));
9865   PetscCall(PetscSectionCreateGlobalSection(newSection, sf, PETSC_TRUE, PETSC_TRUE, PETSC_FALSE, &gsection));
9866   PetscCall(DMSetGlobalSection(*odm, gsection));
9867   PetscCall(PetscSectionDestroy(&gsection));
9868   PetscFunctionReturn(PETSC_SUCCESS);
9869 }
9870 
9871 static PetscErrorCode DMCreateAffineInterpolationCorrection_Plex(DM dmc, DM dmf, Vec *shift)
9872 {
9873   DM        dmco, dmfo;
9874   Mat       interpo;
9875   Vec       rscale;
9876   Vec       cglobalo, clocal;
9877   Vec       fglobal, fglobalo, flocal;
9878   PetscBool regular;
9879 
9880   PetscFunctionBegin;
9881   PetscCall(DMGetFullDM(dmc, &dmco));
9882   PetscCall(DMGetFullDM(dmf, &dmfo));
9883   PetscCall(DMSetCoarseDM(dmfo, dmco));
9884   PetscCall(DMPlexGetRegularRefinement(dmf, &regular));
9885   PetscCall(DMPlexSetRegularRefinement(dmfo, regular));
9886   PetscCall(DMCreateInterpolation(dmco, dmfo, &interpo, &rscale));
9887   PetscCall(DMCreateGlobalVector(dmco, &cglobalo));
9888   PetscCall(DMCreateLocalVector(dmc, &clocal));
9889   PetscCall(VecSet(cglobalo, 0.));
9890   PetscCall(VecSet(clocal, 0.));
9891   PetscCall(DMCreateGlobalVector(dmf, &fglobal));
9892   PetscCall(DMCreateGlobalVector(dmfo, &fglobalo));
9893   PetscCall(DMCreateLocalVector(dmf, &flocal));
9894   PetscCall(VecSet(fglobal, 0.));
9895   PetscCall(VecSet(fglobalo, 0.));
9896   PetscCall(VecSet(flocal, 0.));
9897   PetscCall(DMPlexInsertBoundaryValues(dmc, PETSC_TRUE, clocal, 0., NULL, NULL, NULL));
9898   PetscCall(DMLocalToGlobalBegin(dmco, clocal, INSERT_VALUES, cglobalo));
9899   PetscCall(DMLocalToGlobalEnd(dmco, clocal, INSERT_VALUES, cglobalo));
9900   PetscCall(MatMult(interpo, cglobalo, fglobalo));
9901   PetscCall(DMGlobalToLocalBegin(dmfo, fglobalo, INSERT_VALUES, flocal));
9902   PetscCall(DMGlobalToLocalEnd(dmfo, fglobalo, INSERT_VALUES, flocal));
9903   PetscCall(DMLocalToGlobalBegin(dmf, flocal, INSERT_VALUES, fglobal));
9904   PetscCall(DMLocalToGlobalEnd(dmf, flocal, INSERT_VALUES, fglobal));
9905   *shift = fglobal;
9906   PetscCall(VecDestroy(&flocal));
9907   PetscCall(VecDestroy(&fglobalo));
9908   PetscCall(VecDestroy(&clocal));
9909   PetscCall(VecDestroy(&cglobalo));
9910   PetscCall(VecDestroy(&rscale));
9911   PetscCall(MatDestroy(&interpo));
9912   PetscCall(DMDestroy(&dmfo));
9913   PetscCall(DMDestroy(&dmco));
9914   PetscFunctionReturn(PETSC_SUCCESS);
9915 }
9916 
9917 PETSC_INTERN PetscErrorCode DMInterpolateSolution_Plex(DM coarse, DM fine, Mat interp, Vec coarseSol, Vec fineSol)
9918 {
9919   PetscObject shifto;
9920   Vec         shift;
9921 
9922   PetscFunctionBegin;
9923   if (!interp) {
9924     Vec rscale;
9925 
9926     PetscCall(DMCreateInterpolation(coarse, fine, &interp, &rscale));
9927     PetscCall(VecDestroy(&rscale));
9928   } else {
9929     PetscCall(PetscObjectReference((PetscObject)interp));
9930   }
9931   PetscCall(PetscObjectQuery((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", &shifto));
9932   if (!shifto) {
9933     PetscCall(DMCreateAffineInterpolationCorrection_Plex(coarse, fine, &shift));
9934     PetscCall(PetscObjectCompose((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", (PetscObject)shift));
9935     shifto = (PetscObject)shift;
9936     PetscCall(VecDestroy(&shift));
9937   }
9938   shift = (Vec)shifto;
9939   PetscCall(MatInterpolate(interp, coarseSol, fineSol));
9940   PetscCall(VecAXPY(fineSol, 1.0, shift));
9941   PetscCall(MatDestroy(&interp));
9942   PetscFunctionReturn(PETSC_SUCCESS);
9943 }
9944 
9945 /* Pointwise interpolation
9946      Just code FEM for now
9947      u^f = I u^c
9948      sum_k u^f_k phi^f_k = I sum_j u^c_j phi^c_j
9949      u^f_i = sum_j psi^f_i I phi^c_j u^c_j
9950      I_{ij} = psi^f_i phi^c_j
9951 */
9952 PetscErrorCode DMCreateInterpolation_Plex(DM dmCoarse, DM dmFine, Mat *interpolation, Vec *scaling)
9953 {
9954   PetscSection gsc, gsf;
9955   PetscInt     m, n;
9956   void        *ctx;
9957   DM           cdm;
9958   PetscBool    regular, ismatis, isRefined = dmCoarse->data == dmFine->data ? PETSC_FALSE : PETSC_TRUE;
9959 
9960   PetscFunctionBegin;
9961   PetscCall(DMGetGlobalSection(dmFine, &gsf));
9962   PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
9963   PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
9964   PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
9965 
9966   PetscCall(PetscStrcmp(dmCoarse->mattype, MATIS, &ismatis));
9967   PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), interpolation));
9968   PetscCall(MatSetSizes(*interpolation, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
9969   PetscCall(MatSetType(*interpolation, ismatis ? MATAIJ : dmCoarse->mattype));
9970   PetscCall(DMGetApplicationContext(dmFine, &ctx));
9971 
9972   PetscCall(DMGetCoarseDM(dmFine, &cdm));
9973   PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
9974   if (!isRefined || (regular && cdm == dmCoarse)) PetscCall(DMPlexComputeInterpolatorNested(dmCoarse, dmFine, isRefined, *interpolation, ctx));
9975   else PetscCall(DMPlexComputeInterpolatorGeneral(dmCoarse, dmFine, *interpolation, ctx));
9976   PetscCall(MatViewFromOptions(*interpolation, NULL, "-interp_mat_view"));
9977   if (scaling) {
9978     /* Use naive scaling */
9979     PetscCall(DMCreateInterpolationScale(dmCoarse, dmFine, *interpolation, scaling));
9980   }
9981   PetscFunctionReturn(PETSC_SUCCESS);
9982 }
9983 
9984 PetscErrorCode DMCreateInjection_Plex(DM dmCoarse, DM dmFine, Mat *mat)
9985 {
9986   VecScatter ctx;
9987 
9988   PetscFunctionBegin;
9989   PetscCall(DMPlexComputeInjectorFEM(dmCoarse, dmFine, &ctx, NULL));
9990   PetscCall(MatCreateScatter(PetscObjectComm((PetscObject)ctx), ctx, mat));
9991   PetscCall(VecScatterDestroy(&ctx));
9992   PetscFunctionReturn(PETSC_SUCCESS);
9993 }
9994 
9995 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[])
9996 {
9997   const PetscInt Nc = uOff[1] - uOff[0];
9998   PetscInt       c;
9999   for (c = 0; c < Nc; ++c) g0[c * Nc + c] = 1.0;
10000 }
10001 
10002 PetscErrorCode DMCreateMassMatrixLumped_Plex(DM dm, Vec *mass)
10003 {
10004   DM           dmc;
10005   PetscDS      ds;
10006   Vec          ones, locmass;
10007   IS           cellIS;
10008   PetscFormKey key;
10009   PetscInt     depth;
10010 
10011   PetscFunctionBegin;
10012   PetscCall(DMClone(dm, &dmc));
10013   PetscCall(DMCopyDisc(dm, dmc));
10014   PetscCall(DMGetDS(dmc, &ds));
10015   PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
10016   PetscCall(DMCreateGlobalVector(dmc, mass));
10017   PetscCall(DMGetLocalVector(dmc, &ones));
10018   PetscCall(DMGetLocalVector(dmc, &locmass));
10019   PetscCall(DMPlexGetDepth(dmc, &depth));
10020   PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
10021   PetscCall(VecSet(locmass, 0.0));
10022   PetscCall(VecSet(ones, 1.0));
10023   key.label = NULL;
10024   key.value = 0;
10025   key.field = 0;
10026   key.part  = 0;
10027   PetscCall(DMPlexComputeJacobian_Action_Internal(dmc, key, cellIS, 0.0, 0.0, ones, NULL, ones, locmass, NULL));
10028   PetscCall(ISDestroy(&cellIS));
10029   PetscCall(VecSet(*mass, 0.0));
10030   PetscCall(DMLocalToGlobalBegin(dmc, locmass, ADD_VALUES, *mass));
10031   PetscCall(DMLocalToGlobalEnd(dmc, locmass, ADD_VALUES, *mass));
10032   PetscCall(DMRestoreLocalVector(dmc, &ones));
10033   PetscCall(DMRestoreLocalVector(dmc, &locmass));
10034   PetscCall(DMDestroy(&dmc));
10035   PetscFunctionReturn(PETSC_SUCCESS);
10036 }
10037 
10038 PetscErrorCode DMCreateMassMatrix_Plex(DM dmCoarse, DM dmFine, Mat *mass)
10039 {
10040   PetscSection gsc, gsf;
10041   PetscInt     m, n;
10042   void        *ctx;
10043   DM           cdm;
10044   PetscBool    regular;
10045 
10046   PetscFunctionBegin;
10047   if (dmFine == dmCoarse) {
10048     DM            dmc;
10049     PetscDS       ds;
10050     PetscWeakForm wf;
10051     Vec           u;
10052     IS            cellIS;
10053     PetscFormKey  key;
10054     PetscInt      depth;
10055 
10056     PetscCall(DMClone(dmFine, &dmc));
10057     PetscCall(DMCopyDisc(dmFine, dmc));
10058     PetscCall(DMGetDS(dmc, &ds));
10059     PetscCall(PetscDSGetWeakForm(ds, &wf));
10060     PetscCall(PetscWeakFormClear(wf));
10061     PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
10062     PetscCall(DMCreateMatrix(dmc, mass));
10063     PetscCall(DMGetLocalVector(dmc, &u));
10064     PetscCall(DMPlexGetDepth(dmc, &depth));
10065     PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
10066     PetscCall(MatZeroEntries(*mass));
10067     key.label = NULL;
10068     key.value = 0;
10069     key.field = 0;
10070     key.part  = 0;
10071     PetscCall(DMPlexComputeJacobian_Internal(dmc, key, cellIS, 0.0, 0.0, u, NULL, *mass, *mass, NULL));
10072     PetscCall(ISDestroy(&cellIS));
10073     PetscCall(DMRestoreLocalVector(dmc, &u));
10074     PetscCall(DMDestroy(&dmc));
10075   } else {
10076     PetscCall(DMGetGlobalSection(dmFine, &gsf));
10077     PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
10078     PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
10079     PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
10080 
10081     PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), mass));
10082     PetscCall(MatSetSizes(*mass, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
10083     PetscCall(MatSetType(*mass, dmCoarse->mattype));
10084     PetscCall(DMGetApplicationContext(dmFine, &ctx));
10085 
10086     PetscCall(DMGetCoarseDM(dmFine, &cdm));
10087     PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
10088     if (regular && cdm == dmCoarse) PetscCall(DMPlexComputeMassMatrixNested(dmCoarse, dmFine, *mass, ctx));
10089     else PetscCall(DMPlexComputeMassMatrixGeneral(dmCoarse, dmFine, *mass, ctx));
10090   }
10091   PetscCall(MatViewFromOptions(*mass, NULL, "-mass_mat_view"));
10092   PetscFunctionReturn(PETSC_SUCCESS);
10093 }
10094 
10095 /*@
10096   DMPlexGetRegularRefinement - Get the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
10097 
10098   Input Parameter:
10099 . dm - The `DMPLEX` object
10100 
10101   Output Parameter:
10102 . regular - The flag
10103 
10104   Level: intermediate
10105 
10106 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetRegularRefinement()`
10107 @*/
10108 PetscErrorCode DMPlexGetRegularRefinement(DM dm, PetscBool *regular)
10109 {
10110   PetscFunctionBegin;
10111   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10112   PetscAssertPointer(regular, 2);
10113   *regular = ((DM_Plex *)dm->data)->regularRefinement;
10114   PetscFunctionReturn(PETSC_SUCCESS);
10115 }
10116 
10117 /*@
10118   DMPlexSetRegularRefinement - Set the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
10119 
10120   Input Parameters:
10121 + dm      - The `DMPLEX` object
10122 - regular - The flag
10123 
10124   Level: intermediate
10125 
10126 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetRegularRefinement()`
10127 @*/
10128 PetscErrorCode DMPlexSetRegularRefinement(DM dm, PetscBool regular)
10129 {
10130   PetscFunctionBegin;
10131   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10132   ((DM_Plex *)dm->data)->regularRefinement = regular;
10133   PetscFunctionReturn(PETSC_SUCCESS);
10134 }
10135 
10136 /*@
10137   DMPlexGetAnchors - Get the layout of the anchor (point-to-point) constraints.  Typically, the user will not have to
10138   call DMPlexGetAnchors() directly: if there are anchors, then `DMPlexGetAnchors()` is called during `DMGetDefaultConstraints()`.
10139 
10140   Not Collective
10141 
10142   Input Parameter:
10143 . dm - The `DMPLEX` object
10144 
10145   Output Parameters:
10146 + anchorSection - If not `NULL`, set to the section describing which points anchor the constrained points.
10147 - anchorIS      - If not `NULL`, set to the list of anchors indexed by `anchorSection`
10148 
10149   Level: intermediate
10150 
10151 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`, `IS`, `PetscSection`
10152 @*/
10153 PetscErrorCode DMPlexGetAnchors(DM dm, PetscSection *anchorSection, IS *anchorIS)
10154 {
10155   DM_Plex *plex = (DM_Plex *)dm->data;
10156 
10157   PetscFunctionBegin;
10158   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10159   if (!plex->anchorSection && !plex->anchorIS && plex->createanchors) PetscCall((*plex->createanchors)(dm));
10160   if (anchorSection) *anchorSection = plex->anchorSection;
10161   if (anchorIS) *anchorIS = plex->anchorIS;
10162   PetscFunctionReturn(PETSC_SUCCESS);
10163 }
10164 
10165 /*@
10166   DMPlexSetAnchors - Set the layout of the local anchor (point-to-point) constraints.
10167 
10168   Collective
10169 
10170   Input Parameters:
10171 + dm            - The `DMPLEX` object
10172 . anchorSection - The section that describes the mapping from constrained points to the anchor points listed in anchorIS.
10173                   Must have a local communicator (`PETSC_COMM_SELF` or derivative).
10174 - anchorIS      - The list of all anchor points.  Must have a local communicator (`PETSC_COMM_SELF` or derivative).
10175 
10176   Level: intermediate
10177 
10178   Notes:
10179   Unlike boundary conditions, when a point's degrees of freedom in a section are constrained to
10180   an outside value, the anchor constraints set a point's degrees of freedom to be a linear
10181   combination of other points' degrees of freedom.
10182 
10183   After specifying the layout of constraints with `DMPlexSetAnchors()`, one specifies the constraints by calling
10184   `DMGetDefaultConstraints()` and filling in the entries in the constraint matrix.
10185 
10186   The reference counts of `anchorSection` and `anchorIS` are incremented.
10187 
10188 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`
10189 @*/
10190 PetscErrorCode DMPlexSetAnchors(DM dm, PetscSection anchorSection, IS anchorIS)
10191 {
10192   DM_Plex    *plex = (DM_Plex *)dm->data;
10193   PetscMPIInt result;
10194 
10195   PetscFunctionBegin;
10196   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10197   if (anchorSection) {
10198     PetscValidHeaderSpecific(anchorSection, PETSC_SECTION_CLASSID, 2);
10199     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorSection), &result));
10200     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor section must have local communicator");
10201   }
10202   if (anchorIS) {
10203     PetscValidHeaderSpecific(anchorIS, IS_CLASSID, 3);
10204     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorIS), &result));
10205     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor IS must have local communicator");
10206   }
10207 
10208   PetscCall(PetscObjectReference((PetscObject)anchorSection));
10209   PetscCall(PetscSectionDestroy(&plex->anchorSection));
10210   plex->anchorSection = anchorSection;
10211 
10212   PetscCall(PetscObjectReference((PetscObject)anchorIS));
10213   PetscCall(ISDestroy(&plex->anchorIS));
10214   plex->anchorIS = anchorIS;
10215 
10216   if (PetscUnlikelyDebug(anchorIS && anchorSection)) {
10217     PetscInt        size, a, pStart, pEnd;
10218     const PetscInt *anchors;
10219 
10220     PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
10221     PetscCall(ISGetLocalSize(anchorIS, &size));
10222     PetscCall(ISGetIndices(anchorIS, &anchors));
10223     for (a = 0; a < size; a++) {
10224       PetscInt p;
10225 
10226       p = anchors[a];
10227       if (p >= pStart && p < pEnd) {
10228         PetscInt dof;
10229 
10230         PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
10231         if (dof) {
10232           PetscCall(ISRestoreIndices(anchorIS, &anchors));
10233           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Point %" PetscInt_FMT " cannot be constrained and an anchor", p);
10234         }
10235       }
10236     }
10237     PetscCall(ISRestoreIndices(anchorIS, &anchors));
10238   }
10239   /* reset the generic constraints */
10240   PetscCall(DMSetDefaultConstraints(dm, NULL, NULL, NULL));
10241   PetscFunctionReturn(PETSC_SUCCESS);
10242 }
10243 
10244 static PetscErrorCode DMPlexCreateConstraintSection_Anchors(DM dm, PetscSection section, PetscSection *cSec)
10245 {
10246   PetscSection anchorSection;
10247   PetscInt     pStart, pEnd, sStart, sEnd, p, dof, numFields, f;
10248 
10249   PetscFunctionBegin;
10250   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10251   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
10252   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, cSec));
10253   PetscCall(PetscSectionGetNumFields(section, &numFields));
10254   if (numFields) {
10255     PetscInt f;
10256     PetscCall(PetscSectionSetNumFields(*cSec, numFields));
10257 
10258     for (f = 0; f < numFields; f++) {
10259       PetscInt numComp;
10260 
10261       PetscCall(PetscSectionGetFieldComponents(section, f, &numComp));
10262       PetscCall(PetscSectionSetFieldComponents(*cSec, f, numComp));
10263     }
10264   }
10265   PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
10266   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
10267   pStart = PetscMax(pStart, sStart);
10268   pEnd   = PetscMin(pEnd, sEnd);
10269   pEnd   = PetscMax(pStart, pEnd);
10270   PetscCall(PetscSectionSetChart(*cSec, pStart, pEnd));
10271   for (p = pStart; p < pEnd; p++) {
10272     PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
10273     if (dof) {
10274       PetscCall(PetscSectionGetDof(section, p, &dof));
10275       PetscCall(PetscSectionSetDof(*cSec, p, dof));
10276       for (f = 0; f < numFields; f++) {
10277         PetscCall(PetscSectionGetFieldDof(section, p, f, &dof));
10278         PetscCall(PetscSectionSetFieldDof(*cSec, p, f, dof));
10279       }
10280     }
10281   }
10282   PetscCall(PetscSectionSetUp(*cSec));
10283   PetscCall(PetscObjectSetName((PetscObject)*cSec, "Constraint Section"));
10284   PetscFunctionReturn(PETSC_SUCCESS);
10285 }
10286 
10287 static PetscErrorCode DMPlexCreateConstraintMatrix_Anchors(DM dm, PetscSection section, PetscSection cSec, Mat *cMat)
10288 {
10289   PetscSection    aSec;
10290   PetscInt        pStart, pEnd, p, sStart, sEnd, dof, aDof, aOff, off, nnz, annz, m, n, q, a, offset, *i, *j;
10291   const PetscInt *anchors;
10292   PetscInt        numFields, f;
10293   IS              aIS;
10294   MatType         mtype;
10295   PetscBool       iscuda, iskokkos;
10296 
10297   PetscFunctionBegin;
10298   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10299   PetscCall(PetscSectionGetStorageSize(cSec, &m));
10300   PetscCall(PetscSectionGetStorageSize(section, &n));
10301   PetscCall(MatCreate(PETSC_COMM_SELF, cMat));
10302   PetscCall(MatSetSizes(*cMat, m, n, m, n));
10303   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJCUSPARSE, &iscuda));
10304   if (!iscuda) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJCUSPARSE, &iscuda));
10305   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJKOKKOS, &iskokkos));
10306   if (!iskokkos) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJKOKKOS, &iskokkos));
10307   if (iscuda) mtype = MATSEQAIJCUSPARSE;
10308   else if (iskokkos) mtype = MATSEQAIJKOKKOS;
10309   else mtype = MATSEQAIJ;
10310   PetscCall(MatSetType(*cMat, mtype));
10311   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
10312   PetscCall(ISGetIndices(aIS, &anchors));
10313   /* cSec will be a subset of aSec and section */
10314   PetscCall(PetscSectionGetChart(cSec, &pStart, &pEnd));
10315   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
10316   PetscCall(PetscMalloc1(m + 1, &i));
10317   i[0] = 0;
10318   PetscCall(PetscSectionGetNumFields(section, &numFields));
10319   for (p = pStart; p < pEnd; p++) {
10320     PetscInt rDof, rOff, r;
10321 
10322     PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10323     if (!rDof) continue;
10324     PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10325     if (numFields) {
10326       for (f = 0; f < numFields; f++) {
10327         annz = 0;
10328         for (r = 0; r < rDof; r++) {
10329           a = anchors[rOff + r];
10330           if (a < sStart || a >= sEnd) continue;
10331           PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
10332           annz += aDof;
10333         }
10334         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
10335         PetscCall(PetscSectionGetFieldOffset(cSec, p, f, &off));
10336         for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
10337       }
10338     } else {
10339       annz = 0;
10340       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10341       for (q = 0; q < dof; q++) {
10342         a = anchors[rOff + q];
10343         if (a < sStart || a >= sEnd) continue;
10344         PetscCall(PetscSectionGetDof(section, a, &aDof));
10345         annz += aDof;
10346       }
10347       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10348       PetscCall(PetscSectionGetOffset(cSec, p, &off));
10349       for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
10350     }
10351   }
10352   nnz = i[m];
10353   PetscCall(PetscMalloc1(nnz, &j));
10354   offset = 0;
10355   for (p = pStart; p < pEnd; p++) {
10356     if (numFields) {
10357       for (f = 0; f < numFields; f++) {
10358         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
10359         for (q = 0; q < dof; q++) {
10360           PetscInt rDof, rOff, r;
10361           PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10362           PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10363           for (r = 0; r < rDof; r++) {
10364             PetscInt s;
10365 
10366             a = anchors[rOff + r];
10367             if (a < sStart || a >= sEnd) continue;
10368             PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
10369             PetscCall(PetscSectionGetFieldOffset(section, a, f, &aOff));
10370             for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
10371           }
10372         }
10373       }
10374     } else {
10375       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10376       for (q = 0; q < dof; q++) {
10377         PetscInt rDof, rOff, r;
10378         PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10379         PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10380         for (r = 0; r < rDof; r++) {
10381           PetscInt s;
10382 
10383           a = anchors[rOff + r];
10384           if (a < sStart || a >= sEnd) continue;
10385           PetscCall(PetscSectionGetDof(section, a, &aDof));
10386           PetscCall(PetscSectionGetOffset(section, a, &aOff));
10387           for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
10388         }
10389       }
10390     }
10391   }
10392   PetscCall(MatSeqAIJSetPreallocationCSR(*cMat, i, j, NULL));
10393   PetscCall(PetscFree(i));
10394   PetscCall(PetscFree(j));
10395   PetscCall(ISRestoreIndices(aIS, &anchors));
10396   PetscFunctionReturn(PETSC_SUCCESS);
10397 }
10398 
10399 PetscErrorCode DMCreateDefaultConstraints_Plex(DM dm)
10400 {
10401   DM_Plex     *plex = (DM_Plex *)dm->data;
10402   PetscSection anchorSection, section, cSec;
10403   Mat          cMat;
10404 
10405   PetscFunctionBegin;
10406   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10407   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
10408   if (anchorSection) {
10409     PetscInt Nf;
10410 
10411     PetscCall(DMGetLocalSection(dm, &section));
10412     PetscCall(DMPlexCreateConstraintSection_Anchors(dm, section, &cSec));
10413     PetscCall(DMPlexCreateConstraintMatrix_Anchors(dm, section, cSec, &cMat));
10414     PetscCall(DMGetNumFields(dm, &Nf));
10415     if (Nf && plex->computeanchormatrix) PetscCall((*plex->computeanchormatrix)(dm, section, cSec, cMat));
10416     PetscCall(DMSetDefaultConstraints(dm, cSec, cMat, NULL));
10417     PetscCall(PetscSectionDestroy(&cSec));
10418     PetscCall(MatDestroy(&cMat));
10419   }
10420   PetscFunctionReturn(PETSC_SUCCESS);
10421 }
10422 
10423 PetscErrorCode DMCreateSubDomainDM_Plex(DM dm, DMLabel label, PetscInt value, IS *is, DM *subdm)
10424 {
10425   IS           subis;
10426   PetscSection section, subsection;
10427 
10428   PetscFunctionBegin;
10429   PetscCall(DMGetLocalSection(dm, &section));
10430   PetscCheck(section, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set default section for DM before splitting subdomain");
10431   PetscCheck(subdm, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set output subDM for splitting subdomain");
10432   /* Create subdomain */
10433   PetscCall(DMPlexFilter(dm, label, value, subdm));
10434   /* Create submodel */
10435   PetscCall(DMPlexGetSubpointIS(*subdm, &subis));
10436   PetscCall(PetscSectionCreateSubmeshSection(section, subis, &subsection));
10437   PetscCall(DMSetLocalSection(*subdm, subsection));
10438   PetscCall(PetscSectionDestroy(&subsection));
10439   PetscCall(DMCopyDisc(dm, *subdm));
10440   /* Create map from submodel to global model */
10441   if (is) {
10442     PetscSection    sectionGlobal, subsectionGlobal;
10443     IS              spIS;
10444     const PetscInt *spmap;
10445     PetscInt       *subIndices;
10446     PetscInt        subSize = 0, subOff = 0, pStart, pEnd, p;
10447     PetscInt        Nf, f, bs = -1, bsLocal[2], bsMinMax[2];
10448 
10449     PetscCall(DMPlexGetSubpointIS(*subdm, &spIS));
10450     PetscCall(ISGetIndices(spIS, &spmap));
10451     PetscCall(PetscSectionGetNumFields(section, &Nf));
10452     PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
10453     PetscCall(DMGetGlobalSection(*subdm, &subsectionGlobal));
10454     PetscCall(PetscSectionGetChart(subsection, &pStart, &pEnd));
10455     for (p = pStart; p < pEnd; ++p) {
10456       PetscInt gdof, pSubSize = 0;
10457 
10458       PetscCall(PetscSectionGetDof(sectionGlobal, p, &gdof));
10459       if (gdof > 0) {
10460         for (f = 0; f < Nf; ++f) {
10461           PetscInt fdof, fcdof;
10462 
10463           PetscCall(PetscSectionGetFieldDof(subsection, p, f, &fdof));
10464           PetscCall(PetscSectionGetFieldConstraintDof(subsection, p, f, &fcdof));
10465           pSubSize += fdof - fcdof;
10466         }
10467         subSize += pSubSize;
10468         if (pSubSize) {
10469           if (bs < 0) {
10470             bs = pSubSize;
10471           } else if (bs != pSubSize) {
10472             /* Layout does not admit a pointwise block size */
10473             bs = 1;
10474           }
10475         }
10476       }
10477     }
10478     /* Must have same blocksize on all procs (some might have no points) */
10479     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs;
10480     bsLocal[1] = bs;
10481     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
10482     if (bsMinMax[0] != bsMinMax[1]) {
10483       bs = 1;
10484     } else {
10485       bs = bsMinMax[0];
10486     }
10487     PetscCall(PetscMalloc1(subSize, &subIndices));
10488     for (p = pStart; p < pEnd; ++p) {
10489       PetscInt gdof, goff;
10490 
10491       PetscCall(PetscSectionGetDof(subsectionGlobal, p, &gdof));
10492       if (gdof > 0) {
10493         const PetscInt point = spmap[p];
10494 
10495         PetscCall(PetscSectionGetOffset(sectionGlobal, point, &goff));
10496         for (f = 0; f < Nf; ++f) {
10497           PetscInt fdof, fcdof, fc, f2, poff = 0;
10498 
10499           /* Can get rid of this loop by storing field information in the global section */
10500           for (f2 = 0; f2 < f; ++f2) {
10501             PetscCall(PetscSectionGetFieldDof(section, p, f2, &fdof));
10502             PetscCall(PetscSectionGetFieldConstraintDof(section, p, f2, &fcdof));
10503             poff += fdof - fcdof;
10504           }
10505           PetscCall(PetscSectionGetFieldDof(section, p, f, &fdof));
10506           PetscCall(PetscSectionGetFieldConstraintDof(section, p, f, &fcdof));
10507           for (fc = 0; fc < fdof - fcdof; ++fc, ++subOff) subIndices[subOff] = goff + poff + fc;
10508         }
10509       }
10510     }
10511     PetscCall(ISRestoreIndices(spIS, &spmap));
10512     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), subSize, subIndices, PETSC_OWN_POINTER, is));
10513     if (bs > 1) {
10514       /* We need to check that the block size does not come from non-contiguous fields */
10515       PetscInt i, j, set = 1;
10516       for (i = 0; i < subSize; i += bs) {
10517         for (j = 0; j < bs; ++j) {
10518           if (subIndices[i + j] != subIndices[i] + j) {
10519             set = 0;
10520             break;
10521           }
10522         }
10523       }
10524       if (set) PetscCall(ISSetBlockSize(*is, bs));
10525     }
10526     /* Attach nullspace */
10527     for (f = 0; f < Nf; ++f) {
10528       (*subdm)->nullspaceConstructors[f] = dm->nullspaceConstructors[f];
10529       if ((*subdm)->nullspaceConstructors[f]) break;
10530     }
10531     if (f < Nf) {
10532       MatNullSpace nullSpace;
10533       PetscCall((*(*subdm)->nullspaceConstructors[f])(*subdm, f, f, &nullSpace));
10534 
10535       PetscCall(PetscObjectCompose((PetscObject)*is, "nullspace", (PetscObject)nullSpace));
10536       PetscCall(MatNullSpaceDestroy(&nullSpace));
10537     }
10538   }
10539   PetscFunctionReturn(PETSC_SUCCESS);
10540 }
10541 
10542 /*@
10543   DMPlexMonitorThroughput - Report the cell throughput of FE integration
10544 
10545   Input Parameters:
10546 + dm    - The `DM`
10547 - dummy - unused argument
10548 
10549   Options Database Key:
10550 . -dm_plex_monitor_throughput - Activate the monitor
10551 
10552   Level: developer
10553 
10554 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexCreate()`
10555 @*/
10556 PetscErrorCode DMPlexMonitorThroughput(DM dm, void *dummy)
10557 {
10558   PetscLogHandler default_handler;
10559 
10560   PetscFunctionBegin;
10561   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10562   PetscCall(PetscLogGetDefaultHandler(&default_handler));
10563   if (default_handler) {
10564     PetscLogEvent      event;
10565     PetscEventPerfInfo eventInfo;
10566     PetscReal          cellRate, flopRate;
10567     PetscInt           cStart, cEnd, Nf, N;
10568     const char        *name;
10569 
10570     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
10571     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
10572     PetscCall(DMGetNumFields(dm, &Nf));
10573     PetscCall(PetscLogEventGetId("DMPlexResidualFE", &event));
10574     PetscCall(PetscLogEventGetPerfInfo(PETSC_DEFAULT, event, &eventInfo));
10575     N        = (cEnd - cStart) * Nf * eventInfo.count;
10576     flopRate = eventInfo.flops / eventInfo.time;
10577     cellRate = N / eventInfo.time;
10578     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)));
10579   } else {
10580     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.");
10581   }
10582   PetscFunctionReturn(PETSC_SUCCESS);
10583 }
10584