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