xref: /petsc/src/dm/impls/plex/plex.c (revision 3658078f29e69a7ed0aa71262741702b3025258f) !
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   /* the vcdof and globalvcdof are sized to allow every polytope type and simple vertex at DM_NUM_POLYTOPES */
131   PetscCall(PetscArrayzero(vcdof, DM_NUM_POLYTOPES + 1));
132   PetscCall(DMGetCoordinateDim(dm, &cdim));
133   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
134   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
135   if (field >= 0) {
136     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, vStart, field, &vcdof[DM_NUM_POLYTOPES]));
137   } else {
138     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetDof(section, vStart, &vcdof[DM_NUM_POLYTOPES]));
139   }
140 
141   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
142   PetscCall(DMPlexGetDepth(dm, &depth));
143   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
144   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
145   for (c = 0; c < DM_NUM_POLYTOPES; ++c) {
146     const DMPolytopeType ict = (DMPolytopeType)c;
147     PetscInt             dep;
148 
149     if (ict == DM_POLYTOPE_FV_GHOST) continue;
150     PetscCall(DMLabelGetStratumBounds(ctLabel, ict, &cStart, &cEnd));
151     if (pStart >= 0) {
152       PetscCall(DMLabelGetValue(depthLabel, cStart, &dep));
153       if (dep != depth - cellHeight) continue;
154     }
155     if (field >= 0) {
156       if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, cStart, field, &vcdof[c]));
157     } else {
158       if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetDof(section, cStart, &vcdof[c]));
159     }
160   }
161 
162   PetscCall(MPIU_Allreduce(vcdof, globalvcdof, DM_NUM_POLYTOPES + 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
163   *types = 0;
164 
165   for (c = 0; c < DM_NUM_POLYTOPES + 1; ++c) {
166     if (globalvcdof[c]) ++(*types);
167   }
168 
169   PetscCall(PetscMalloc3(*types, &sStart, *types, &sEnd, *types, &ft));
170   t = 0;
171   if (globalvcdof[DM_NUM_POLYTOPES]) {
172     sStart[t] = vStart;
173     sEnd[t]   = vEnd;
174     ft[t]     = (globalvcdof[t] == cdim) ? PETSC_VTK_POINT_VECTOR_FIELD : PETSC_VTK_POINT_FIELD;
175     ++t;
176   }
177 
178   for (c = 0; c < DM_NUM_POLYTOPES; ++c) {
179     if (globalvcdof[c]) {
180       const DMPolytopeType ict = (DMPolytopeType)c;
181 
182       PetscCall(DMLabelGetStratumBounds(ctLabel, ict, &cStart, &cEnd));
183       sStart[t] = cStart;
184       sEnd[t]   = cEnd;
185       ft[t]     = (globalvcdof[c] == cdim) ? PETSC_VTK_CELL_VECTOR_FIELD : PETSC_VTK_CELL_FIELD;
186       ++t;
187     }
188   }
189 
190   if (!*types) {
191     if (field >= 0) {
192       const char *fieldname;
193 
194       PetscCall(PetscSectionGetFieldName(section, field, &fieldname));
195       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section field %" PetscInt_FMT " \"%s\"\n", field, fieldname));
196     } else {
197       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section\n"));
198     }
199   }
200 
201   *ssStart = sStart;
202   *ssEnd   = sEnd;
203   *sft     = ft;
204   PetscFunctionReturn(PETSC_SUCCESS);
205 }
206 
207 PetscErrorCode DMPlexRestoreFieldTypes_Internal(DM dm, PetscSection section, PetscInt field, PetscInt *types, PetscInt **sStart, PetscInt **sEnd, PetscViewerVTKFieldType **ft)
208 {
209   PetscFunctionBegin;
210   PetscCall(PetscFree3(*sStart, *sEnd, *ft));
211   PetscFunctionReturn(PETSC_SUCCESS);
212 }
213 
214 PetscErrorCode DMPlexGetFieldType_Internal(DM dm, PetscSection section, PetscInt field, PetscInt *sStart, PetscInt *sEnd, PetscViewerVTKFieldType *ft)
215 {
216   PetscInt cdim, pStart, pEnd, vStart, vEnd, cStart, cEnd;
217   PetscInt vcdof[2] = {0, 0}, globalvcdof[2];
218 
219   PetscFunctionBegin;
220   *ft = PETSC_VTK_INVALID;
221   PetscCall(DMGetCoordinateDim(dm, &cdim));
222   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
223   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
224   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
225   if (field >= 0) {
226     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, vStart, field, &vcdof[0]));
227     if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, cStart, field, &vcdof[1]));
228   } else {
229     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetDof(section, vStart, &vcdof[0]));
230     if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetDof(section, cStart, &vcdof[1]));
231   }
232   PetscCall(MPIU_Allreduce(vcdof, globalvcdof, 2, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
233   if (globalvcdof[0]) {
234     *sStart = vStart;
235     *sEnd   = vEnd;
236     if (globalvcdof[0] == cdim) *ft = PETSC_VTK_POINT_VECTOR_FIELD;
237     else *ft = PETSC_VTK_POINT_FIELD;
238   } else if (globalvcdof[1]) {
239     *sStart = cStart;
240     *sEnd   = cEnd;
241     if (globalvcdof[1] == cdim) *ft = PETSC_VTK_CELL_VECTOR_FIELD;
242     else *ft = PETSC_VTK_CELL_FIELD;
243   } else {
244     if (field >= 0) {
245       const char *fieldname;
246 
247       PetscCall(PetscSectionGetFieldName(section, field, &fieldname));
248       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section field %" PetscInt_FMT " \"%s\"\n", field, fieldname));
249     } else {
250       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section\n"));
251     }
252   }
253   PetscFunctionReturn(PETSC_SUCCESS);
254 }
255 
256 /*@
257   DMPlexVecView1D - Plot many 1D solutions on the same line graph
258 
259   Collective
260 
261   Input Parameters:
262 + dm     - The `DMPLEX` object
263 . n      - The number of vectors
264 . u      - The array of local vectors
265 - viewer - The `PetscViewer`
266 
267   Level: advanced
268 
269 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `VecViewFromOptions()`, `VecView()`
270 @*/
271 PetscErrorCode DMPlexVecView1D(DM dm, PetscInt n, Vec u[], PetscViewer viewer)
272 {
273   PetscDS            ds;
274   PetscDraw          draw = NULL;
275   PetscDrawLG        lg;
276   Vec                coordinates;
277   const PetscScalar *coords, **sol;
278   PetscReal         *vals;
279   PetscInt          *Nc;
280   PetscInt           Nf, f, c, Nl, l, i, vStart, vEnd, v;
281   char             **names;
282 
283   PetscFunctionBegin;
284   PetscCall(DMGetDS(dm, &ds));
285   PetscCall(PetscDSGetNumFields(ds, &Nf));
286   PetscCall(PetscDSGetTotalComponents(ds, &Nl));
287   PetscCall(PetscDSGetComponents(ds, &Nc));
288 
289   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
290   if (!draw) PetscFunctionReturn(PETSC_SUCCESS);
291   PetscCall(PetscDrawLGCreate(draw, n * Nl, &lg));
292 
293   PetscCall(PetscMalloc3(n, &sol, n * Nl, &names, n * Nl, &vals));
294   for (i = 0, l = 0; i < n; ++i) {
295     const char *vname;
296 
297     PetscCall(PetscObjectGetName((PetscObject)u[i], &vname));
298     for (f = 0; f < Nf; ++f) {
299       PetscObject disc;
300       const char *fname;
301       char        tmpname[PETSC_MAX_PATH_LEN];
302 
303       PetscCall(PetscDSGetDiscretization(ds, f, &disc));
304       /* TODO Create names for components */
305       for (c = 0; c < Nc[f]; ++c, ++l) {
306         PetscCall(PetscObjectGetName(disc, &fname));
307         PetscCall(PetscStrncpy(tmpname, vname, sizeof(tmpname)));
308         PetscCall(PetscStrlcat(tmpname, ":", sizeof(tmpname)));
309         PetscCall(PetscStrlcat(tmpname, fname, sizeof(tmpname)));
310         PetscCall(PetscStrallocpy(tmpname, &names[l]));
311       }
312     }
313   }
314   PetscCall(PetscDrawLGSetLegend(lg, (const char *const *)names));
315   /* Just add P_1 support for now */
316   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
317   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
318   PetscCall(VecGetArrayRead(coordinates, &coords));
319   for (i = 0; i < n; ++i) PetscCall(VecGetArrayRead(u[i], &sol[i]));
320   for (v = vStart; v < vEnd; ++v) {
321     PetscScalar *x, *svals;
322 
323     PetscCall(DMPlexPointLocalRead(dm, v, coords, &x));
324     for (i = 0; i < n; ++i) {
325       PetscCall(DMPlexPointLocalRead(dm, v, sol[i], &svals));
326       for (l = 0; l < Nl; ++l) vals[i * Nl + l] = PetscRealPart(svals[l]);
327     }
328     PetscCall(PetscDrawLGAddCommonPoint(lg, PetscRealPart(x[0]), vals));
329   }
330   PetscCall(VecRestoreArrayRead(coordinates, &coords));
331   for (i = 0; i < n; ++i) PetscCall(VecRestoreArrayRead(u[i], &sol[i]));
332   for (l = 0; l < n * Nl; ++l) PetscCall(PetscFree(names[l]));
333   PetscCall(PetscFree3(sol, names, vals));
334 
335   PetscCall(PetscDrawLGDraw(lg));
336   PetscCall(PetscDrawLGDestroy(&lg));
337   PetscFunctionReturn(PETSC_SUCCESS);
338 }
339 
340 static PetscErrorCode VecView_Plex_Local_Draw_1D(Vec u, PetscViewer viewer)
341 {
342   DM dm;
343 
344   PetscFunctionBegin;
345   PetscCall(VecGetDM(u, &dm));
346   PetscCall(DMPlexVecView1D(dm, 1, &u, viewer));
347   PetscFunctionReturn(PETSC_SUCCESS);
348 }
349 
350 static PetscErrorCode VecView_Plex_Local_Draw_2D(Vec v, PetscViewer viewer)
351 {
352   DM                 dm;
353   PetscSection       s;
354   PetscDraw          draw, popup;
355   DM                 cdm;
356   PetscSection       coordSection;
357   Vec                coordinates;
358   const PetscScalar *array;
359   PetscReal          lbound[3], ubound[3];
360   PetscReal          vbound[2], time;
361   PetscBool          flg;
362   PetscInt           dim, Nf, f, Nc, comp, vStart, vEnd, cStart, cEnd, c, N, level, step, w = 0;
363   const char        *name;
364   char               title[PETSC_MAX_PATH_LEN];
365 
366   PetscFunctionBegin;
367   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
368   PetscCall(VecGetDM(v, &dm));
369   PetscCall(DMGetCoordinateDim(dm, &dim));
370   PetscCall(DMGetLocalSection(dm, &s));
371   PetscCall(PetscSectionGetNumFields(s, &Nf));
372   PetscCall(DMGetCoarsenLevel(dm, &level));
373   PetscCall(DMGetCoordinateDM(dm, &cdm));
374   PetscCall(DMGetLocalSection(cdm, &coordSection));
375   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
376   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
377   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
378 
379   PetscCall(PetscObjectGetName((PetscObject)v, &name));
380   PetscCall(DMGetOutputSequenceNumber(dm, &step, &time));
381 
382   PetscCall(VecGetLocalSize(coordinates, &N));
383   PetscCall(DMGetBoundingBox(dm, lbound, ubound));
384   PetscCall(PetscDrawClear(draw));
385 
386   /* Could implement something like DMDASelectFields() */
387   for (f = 0; f < Nf; ++f) {
388     DM          fdm = dm;
389     Vec         fv  = v;
390     IS          fis;
391     char        prefix[PETSC_MAX_PATH_LEN];
392     const char *fname;
393 
394     PetscCall(PetscSectionGetFieldComponents(s, f, &Nc));
395     PetscCall(PetscSectionGetFieldName(s, f, &fname));
396 
397     if (v->hdr.prefix) PetscCall(PetscStrncpy(prefix, v->hdr.prefix, sizeof(prefix)));
398     else prefix[0] = '\0';
399     if (Nf > 1) {
400       PetscCall(DMCreateSubDM(dm, 1, &f, &fis, &fdm));
401       PetscCall(VecGetSubVector(v, fis, &fv));
402       PetscCall(PetscStrlcat(prefix, fname, sizeof(prefix)));
403       PetscCall(PetscStrlcat(prefix, "_", sizeof(prefix)));
404     }
405     for (comp = 0; comp < Nc; ++comp, ++w) {
406       PetscInt nmax = 2;
407 
408       PetscCall(PetscViewerDrawGetDraw(viewer, w, &draw));
409       if (Nc > 1) PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s_%" PetscInt_FMT " Step: %" PetscInt_FMT " Time: %.4g", name, fname, comp, step, (double)time));
410       else PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s Step: %" PetscInt_FMT " Time: %.4g", name, fname, step, (double)time));
411       PetscCall(PetscDrawSetTitle(draw, title));
412 
413       /* TODO Get max and min only for this component */
414       PetscCall(PetscOptionsGetRealArray(NULL, prefix, "-vec_view_bounds", vbound, &nmax, &flg));
415       if (!flg) {
416         PetscCall(VecMin(fv, NULL, &vbound[0]));
417         PetscCall(VecMax(fv, NULL, &vbound[1]));
418         if (vbound[1] <= vbound[0]) vbound[1] = vbound[0] + 1.0;
419       }
420 
421       PetscCall(PetscDrawGetPopup(draw, &popup));
422       PetscCall(PetscDrawScalePopup(popup, vbound[0], vbound[1]));
423       PetscCall(PetscDrawSetCoordinates(draw, lbound[0], lbound[1], ubound[0], ubound[1]));
424       PetscCall(VecGetArrayRead(fv, &array));
425       for (c = cStart; c < cEnd; ++c) {
426         PetscScalar       *coords = NULL, *a = NULL;
427         const PetscScalar *coords_arr;
428         PetscBool          isDG;
429         PetscInt           numCoords, color[4] = {-1, -1, -1, -1};
430 
431         PetscCall(DMPlexPointLocalRead(fdm, c, array, &a));
432         if (a) {
433           color[0] = PetscDrawRealToColor(PetscRealPart(a[comp]), vbound[0], vbound[1]);
434           color[1] = color[2] = color[3] = color[0];
435         } else {
436           PetscScalar *vals = NULL;
437           PetscInt     numVals, va;
438 
439           PetscCall(DMPlexVecGetClosure(fdm, NULL, fv, c, &numVals, &vals));
440           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);
441           switch (numVals / Nc) {
442           case 3: /* P1 Triangle */
443           case 4: /* P1 Quadrangle */
444             for (va = 0; va < numVals / Nc; ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp]), vbound[0], vbound[1]);
445             break;
446           case 6: /* P2 Triangle */
447           case 8: /* P2 Quadrangle */
448             for (va = 0; va < numVals / (Nc * 2); ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp + numVals / (Nc * 2)]), vbound[0], vbound[1]);
449             break;
450           default:
451             SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of values for cell closure %" PetscInt_FMT " cannot be handled", numVals / Nc);
452           }
453           PetscCall(DMPlexVecRestoreClosure(fdm, NULL, fv, c, &numVals, &vals));
454         }
455         PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
456         switch (numCoords) {
457         case 6:
458         case 12: /* Localized triangle */
459           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]));
460           break;
461         case 8:
462         case 16: /* Localized quadrilateral */
463           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]));
464           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]));
465           break;
466         default:
467           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells with %" PetscInt_FMT " coordinates", numCoords);
468         }
469         PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
470       }
471       PetscCall(VecRestoreArrayRead(fv, &array));
472       PetscCall(PetscDrawFlush(draw));
473       PetscCall(PetscDrawPause(draw));
474       PetscCall(PetscDrawSave(draw));
475     }
476     if (Nf > 1) {
477       PetscCall(VecRestoreSubVector(v, fis, &fv));
478       PetscCall(ISDestroy(&fis));
479       PetscCall(DMDestroy(&fdm));
480     }
481   }
482   PetscFunctionReturn(PETSC_SUCCESS);
483 }
484 
485 static PetscErrorCode VecView_Plex_Local_Draw(Vec v, PetscViewer viewer)
486 {
487   DM        dm;
488   PetscDraw draw;
489   PetscInt  dim;
490   PetscBool isnull;
491 
492   PetscFunctionBegin;
493   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
494   PetscCall(PetscDrawIsNull(draw, &isnull));
495   if (isnull) PetscFunctionReturn(PETSC_SUCCESS);
496 
497   PetscCall(VecGetDM(v, &dm));
498   PetscCall(DMGetCoordinateDim(dm, &dim));
499   switch (dim) {
500   case 1:
501     PetscCall(VecView_Plex_Local_Draw_1D(v, viewer));
502     break;
503   case 2:
504     PetscCall(VecView_Plex_Local_Draw_2D(v, viewer));
505     break;
506   default:
507     SETERRQ(PetscObjectComm((PetscObject)v), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT ". Try PETSCVIEWERGLVIS", dim);
508   }
509   PetscFunctionReturn(PETSC_SUCCESS);
510 }
511 
512 static PetscErrorCode VecView_Plex_Local_VTK(Vec v, PetscViewer viewer)
513 {
514   DM                      dm;
515   Vec                     locv;
516   const char             *name;
517   PetscSection            section;
518   PetscInt                pStart, pEnd;
519   PetscInt                numFields;
520   PetscViewerVTKFieldType ft;
521 
522   PetscFunctionBegin;
523   PetscCall(VecGetDM(v, &dm));
524   PetscCall(DMCreateLocalVector(dm, &locv)); /* VTK viewer requires exclusive ownership of the vector */
525   PetscCall(PetscObjectGetName((PetscObject)v, &name));
526   PetscCall(PetscObjectSetName((PetscObject)locv, name));
527   PetscCall(VecCopy(v, locv));
528   PetscCall(DMGetLocalSection(dm, &section));
529   PetscCall(PetscSectionGetNumFields(section, &numFields));
530   if (!numFields) {
531     PetscCall(DMPlexGetFieldType_Internal(dm, section, PETSC_DETERMINE, &pStart, &pEnd, &ft));
532     PetscCall(PetscViewerVTKAddField(viewer, (PetscObject)dm, DMPlexVTKWriteAll, PETSC_DEFAULT, ft, PETSC_TRUE, (PetscObject)locv));
533   } else {
534     PetscInt f;
535 
536     for (f = 0; f < numFields; f++) {
537       PetscCall(DMPlexGetFieldType_Internal(dm, section, f, &pStart, &pEnd, &ft));
538       if (ft == PETSC_VTK_INVALID) continue;
539       PetscCall(PetscObjectReference((PetscObject)locv));
540       PetscCall(PetscViewerVTKAddField(viewer, (PetscObject)dm, DMPlexVTKWriteAll, f, ft, PETSC_TRUE, (PetscObject)locv));
541     }
542     PetscCall(VecDestroy(&locv));
543   }
544   PetscFunctionReturn(PETSC_SUCCESS);
545 }
546 
547 PetscErrorCode VecView_Plex_Local(Vec v, PetscViewer viewer)
548 {
549   DM        dm;
550   PetscBool isvtk, ishdf5, isdraw, isglvis, iscgns;
551 
552   PetscFunctionBegin;
553   PetscCall(VecGetDM(v, &dm));
554   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
555   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
556   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
557   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
558   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
559   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
560   if (isvtk || ishdf5 || isdraw || isglvis || iscgns) {
561     PetscInt    i, numFields;
562     PetscObject fe;
563     PetscBool   fem  = PETSC_FALSE;
564     Vec         locv = v;
565     const char *name;
566     PetscInt    step;
567     PetscReal   time;
568 
569     PetscCall(DMGetNumFields(dm, &numFields));
570     for (i = 0; i < numFields; i++) {
571       PetscCall(DMGetField(dm, i, NULL, &fe));
572       if (fe->classid == PETSCFE_CLASSID) {
573         fem = PETSC_TRUE;
574         break;
575       }
576     }
577     if (fem) {
578       PetscObject isZero;
579 
580       PetscCall(DMGetLocalVector(dm, &locv));
581       PetscCall(PetscObjectGetName((PetscObject)v, &name));
582       PetscCall(PetscObjectSetName((PetscObject)locv, name));
583       PetscCall(PetscObjectQuery((PetscObject)v, "__Vec_bc_zero__", &isZero));
584       PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", isZero));
585       PetscCall(VecCopy(v, locv));
586       PetscCall(DMGetOutputSequenceNumber(dm, NULL, &time));
587       PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_TRUE, locv, time, NULL, NULL, NULL));
588     }
589     if (isvtk) {
590       PetscCall(VecView_Plex_Local_VTK(locv, viewer));
591     } else if (ishdf5) {
592 #if defined(PETSC_HAVE_HDF5)
593       PetscCall(VecView_Plex_Local_HDF5_Internal(locv, viewer));
594 #else
595       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
596 #endif
597     } else if (isdraw) {
598       PetscCall(VecView_Plex_Local_Draw(locv, viewer));
599     } else if (isglvis) {
600       PetscCall(DMGetOutputSequenceNumber(dm, &step, NULL));
601       PetscCall(PetscViewerGLVisSetSnapId(viewer, step));
602       PetscCall(VecView_GLVis(locv, viewer));
603     } else if (iscgns) {
604 #if defined(PETSC_HAVE_CGNS)
605       PetscCall(VecView_Plex_Local_CGNS(locv, viewer));
606 #else
607       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "CGNS not supported in this build.\nPlease reconfigure using --download-cgns");
608 #endif
609     }
610     if (fem) {
611       PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", NULL));
612       PetscCall(DMRestoreLocalVector(dm, &locv));
613     }
614   } else {
615     PetscBool isseq;
616 
617     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
618     if (isseq) PetscCall(VecView_Seq(v, viewer));
619     else PetscCall(VecView_MPI(v, viewer));
620   }
621   PetscFunctionReturn(PETSC_SUCCESS);
622 }
623 
624 PetscErrorCode VecView_Plex(Vec v, PetscViewer viewer)
625 {
626   DM        dm;
627   PetscBool isvtk, ishdf5, isdraw, isglvis, isexodusii, iscgns;
628 
629   PetscFunctionBegin;
630   PetscCall(VecGetDM(v, &dm));
631   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
632   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
633   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
634   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
635   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
636   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
637   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodusii));
638   if (isvtk || isdraw || isglvis || iscgns) {
639     Vec         locv;
640     PetscObject isZero;
641     const char *name;
642 
643     PetscCall(DMGetLocalVector(dm, &locv));
644     PetscCall(PetscObjectGetName((PetscObject)v, &name));
645     PetscCall(PetscObjectSetName((PetscObject)locv, name));
646     PetscCall(DMGlobalToLocalBegin(dm, v, INSERT_VALUES, locv));
647     PetscCall(DMGlobalToLocalEnd(dm, v, INSERT_VALUES, locv));
648     PetscCall(PetscObjectQuery((PetscObject)v, "__Vec_bc_zero__", &isZero));
649     PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", isZero));
650     PetscCall(VecView_Plex_Local(locv, viewer));
651     PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", NULL));
652     PetscCall(DMRestoreLocalVector(dm, &locv));
653   } else if (ishdf5) {
654 #if defined(PETSC_HAVE_HDF5)
655     PetscCall(VecView_Plex_HDF5_Internal(v, viewer));
656 #else
657     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
658 #endif
659   } else if (isexodusii) {
660 #if defined(PETSC_HAVE_EXODUSII)
661     PetscCall(VecView_PlexExodusII_Internal(v, viewer));
662 #else
663     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
664 #endif
665   } else {
666     PetscBool isseq;
667 
668     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
669     if (isseq) PetscCall(VecView_Seq(v, viewer));
670     else PetscCall(VecView_MPI(v, viewer));
671   }
672   PetscFunctionReturn(PETSC_SUCCESS);
673 }
674 
675 PetscErrorCode VecView_Plex_Native(Vec originalv, PetscViewer viewer)
676 {
677   DM                dm;
678   MPI_Comm          comm;
679   PetscViewerFormat format;
680   Vec               v;
681   PetscBool         isvtk, ishdf5;
682 
683   PetscFunctionBegin;
684   PetscCall(VecGetDM(originalv, &dm));
685   PetscCall(PetscObjectGetComm((PetscObject)originalv, &comm));
686   PetscCheck(dm, comm, PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
687   PetscCall(PetscViewerGetFormat(viewer, &format));
688   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
689   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
690   if (format == PETSC_VIEWER_NATIVE) {
691     /* Natural ordering is the common case for DMDA, NATIVE means plain vector, for PLEX is the opposite */
692     /* this need a better fix */
693     if (dm->useNatural) {
694       if (dm->sfNatural) {
695         const char *vecname;
696         PetscInt    n, nroots;
697 
698         PetscCall(VecGetLocalSize(originalv, &n));
699         PetscCall(PetscSFGetGraph(dm->sfNatural, &nroots, NULL, NULL, NULL));
700         if (n == nroots) {
701           PetscCall(DMPlexCreateNaturalVector(dm, &v));
702           PetscCall(DMPlexGlobalToNaturalBegin(dm, originalv, v));
703           PetscCall(DMPlexGlobalToNaturalEnd(dm, originalv, v));
704           PetscCall(PetscObjectGetName((PetscObject)originalv, &vecname));
705           PetscCall(PetscObjectSetName((PetscObject)v, vecname));
706         } else SETERRQ(comm, PETSC_ERR_ARG_WRONG, "DM global to natural SF only handles global vectors");
707       } else SETERRQ(comm, PETSC_ERR_ARG_WRONGSTATE, "DM global to natural SF was not created");
708     } else v = originalv;
709   } else v = originalv;
710 
711   if (ishdf5) {
712 #if defined(PETSC_HAVE_HDF5)
713     PetscCall(VecView_Plex_HDF5_Native_Internal(v, viewer));
714 #else
715     SETERRQ(comm, PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
716 #endif
717   } else if (isvtk) {
718     SETERRQ(comm, PETSC_ERR_SUP, "VTK format does not support viewing in natural order. Please switch to HDF5.");
719   } else {
720     PetscBool isseq;
721 
722     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
723     if (isseq) PetscCall(VecView_Seq(v, viewer));
724     else PetscCall(VecView_MPI(v, viewer));
725   }
726   if (v != originalv) PetscCall(VecDestroy(&v));
727   PetscFunctionReturn(PETSC_SUCCESS);
728 }
729 
730 PetscErrorCode VecLoad_Plex_Local(Vec v, PetscViewer viewer)
731 {
732   DM        dm;
733   PetscBool ishdf5;
734 
735   PetscFunctionBegin;
736   PetscCall(VecGetDM(v, &dm));
737   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
738   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
739   if (ishdf5) {
740     DM          dmBC;
741     Vec         gv;
742     const char *name;
743 
744     PetscCall(DMGetOutputDM(dm, &dmBC));
745     PetscCall(DMGetGlobalVector(dmBC, &gv));
746     PetscCall(PetscObjectGetName((PetscObject)v, &name));
747     PetscCall(PetscObjectSetName((PetscObject)gv, name));
748     PetscCall(VecLoad_Default(gv, viewer));
749     PetscCall(DMGlobalToLocalBegin(dmBC, gv, INSERT_VALUES, v));
750     PetscCall(DMGlobalToLocalEnd(dmBC, gv, INSERT_VALUES, v));
751     PetscCall(DMRestoreGlobalVector(dmBC, &gv));
752   } else PetscCall(VecLoad_Default(v, viewer));
753   PetscFunctionReturn(PETSC_SUCCESS);
754 }
755 
756 PetscErrorCode VecLoad_Plex(Vec v, PetscViewer viewer)
757 {
758   DM        dm;
759   PetscBool ishdf5, isexodusii;
760 
761   PetscFunctionBegin;
762   PetscCall(VecGetDM(v, &dm));
763   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
764   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
765   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodusii));
766   if (ishdf5) {
767 #if defined(PETSC_HAVE_HDF5)
768     PetscCall(VecLoad_Plex_HDF5_Internal(v, viewer));
769 #else
770     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
771 #endif
772   } else if (isexodusii) {
773 #if defined(PETSC_HAVE_EXODUSII)
774     PetscCall(VecLoad_PlexExodusII_Internal(v, viewer));
775 #else
776     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
777 #endif
778   } else PetscCall(VecLoad_Default(v, viewer));
779   PetscFunctionReturn(PETSC_SUCCESS);
780 }
781 
782 PetscErrorCode VecLoad_Plex_Native(Vec originalv, PetscViewer viewer)
783 {
784   DM                dm;
785   PetscViewerFormat format;
786   PetscBool         ishdf5;
787 
788   PetscFunctionBegin;
789   PetscCall(VecGetDM(originalv, &dm));
790   PetscCheck(dm, PetscObjectComm((PetscObject)originalv), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
791   PetscCall(PetscViewerGetFormat(viewer, &format));
792   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
793   if (format == PETSC_VIEWER_NATIVE) {
794     if (dm->useNatural) {
795       if (dm->sfNatural) {
796         if (ishdf5) {
797 #if defined(PETSC_HAVE_HDF5)
798           Vec         v;
799           const char *vecname;
800 
801           PetscCall(DMPlexCreateNaturalVector(dm, &v));
802           PetscCall(PetscObjectGetName((PetscObject)originalv, &vecname));
803           PetscCall(PetscObjectSetName((PetscObject)v, vecname));
804           PetscCall(VecLoad_Plex_HDF5_Native_Internal(v, viewer));
805           PetscCall(DMPlexNaturalToGlobalBegin(dm, v, originalv));
806           PetscCall(DMPlexNaturalToGlobalEnd(dm, v, originalv));
807           PetscCall(VecDestroy(&v));
808 #else
809           SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
810 #endif
811         } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Reading in natural order is not supported for anything but HDF5.");
812       }
813     } else PetscCall(VecLoad_Default(originalv, viewer));
814   }
815   PetscFunctionReturn(PETSC_SUCCESS);
816 }
817 
818 PETSC_UNUSED static PetscErrorCode DMPlexView_Ascii_Geometry(DM dm, PetscViewer viewer)
819 {
820   PetscSection       coordSection;
821   Vec                coordinates;
822   DMLabel            depthLabel, celltypeLabel;
823   const char        *name[4];
824   const PetscScalar *a;
825   PetscInt           dim, pStart, pEnd, cStart, cEnd, c;
826 
827   PetscFunctionBegin;
828   PetscCall(DMGetDimension(dm, &dim));
829   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
830   PetscCall(DMGetCoordinateSection(dm, &coordSection));
831   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
832   PetscCall(DMPlexGetCellTypeLabel(dm, &celltypeLabel));
833   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
834   PetscCall(PetscSectionGetChart(coordSection, &pStart, &pEnd));
835   PetscCall(VecGetArrayRead(coordinates, &a));
836   name[0]       = "vertex";
837   name[1]       = "edge";
838   name[dim - 1] = "face";
839   name[dim]     = "cell";
840   for (c = cStart; c < cEnd; ++c) {
841     PetscInt *closure = NULL;
842     PetscInt  closureSize, cl, ct;
843 
844     PetscCall(DMLabelGetValue(celltypeLabel, c, &ct));
845     PetscCall(PetscViewerASCIIPrintf(viewer, "Geometry for cell %" PetscInt_FMT " polytope type %s:\n", c, DMPolytopeTypes[ct]));
846     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
847     PetscCall(PetscViewerASCIIPushTab(viewer));
848     for (cl = 0; cl < closureSize * 2; cl += 2) {
849       PetscInt point = closure[cl], depth, dof, off, d, p;
850 
851       if ((point < pStart) || (point >= pEnd)) continue;
852       PetscCall(PetscSectionGetDof(coordSection, point, &dof));
853       if (!dof) continue;
854       PetscCall(DMLabelGetValue(depthLabel, point, &depth));
855       PetscCall(PetscSectionGetOffset(coordSection, point, &off));
856       PetscCall(PetscViewerASCIIPrintf(viewer, "%s %" PetscInt_FMT " coords:", name[depth], point));
857       for (p = 0; p < dof / dim; ++p) {
858         PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
859         for (d = 0; d < dim; ++d) {
860           if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
861           PetscCall(PetscViewerASCIIPrintf(viewer, "%g", (double)PetscRealPart(a[off + p * dim + d])));
862         }
863         PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
864       }
865       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
866     }
867     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
868     PetscCall(PetscViewerASCIIPopTab(viewer));
869   }
870   PetscCall(VecRestoreArrayRead(coordinates, &a));
871   PetscFunctionReturn(PETSC_SUCCESS);
872 }
873 
874 typedef enum {
875   CS_CARTESIAN,
876   CS_POLAR,
877   CS_CYLINDRICAL,
878   CS_SPHERICAL
879 } CoordSystem;
880 const char *CoordSystems[] = {"cartesian", "polar", "cylindrical", "spherical", "CoordSystem", "CS_", NULL};
881 
882 static PetscErrorCode DMPlexView_Ascii_Coordinates(PetscViewer viewer, CoordSystem cs, PetscInt dim, const PetscScalar x[])
883 {
884   PetscInt i;
885 
886   PetscFunctionBegin;
887   if (dim > 3) {
888     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double)PetscRealPart(x[i])));
889   } else {
890     PetscReal coords[3], trcoords[3] = {0., 0., 0.};
891 
892     for (i = 0; i < dim; ++i) coords[i] = PetscRealPart(x[i]);
893     switch (cs) {
894     case CS_CARTESIAN:
895       for (i = 0; i < dim; ++i) trcoords[i] = coords[i];
896       break;
897     case CS_POLAR:
898       PetscCheck(dim == 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Polar coordinates are for 2 dimension, not %" PetscInt_FMT, dim);
899       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
900       trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
901       break;
902     case CS_CYLINDRICAL:
903       PetscCheck(dim == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cylindrical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
904       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
905       trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
906       trcoords[2] = coords[2];
907       break;
908     case CS_SPHERICAL:
909       PetscCheck(dim == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Spherical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
910       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]) + PetscSqr(coords[2]));
911       trcoords[1] = PetscAtan2Real(PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1])), coords[2]);
912       trcoords[2] = PetscAtan2Real(coords[1], coords[0]);
913       break;
914     }
915     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double)trcoords[i]));
916   }
917   PetscFunctionReturn(PETSC_SUCCESS);
918 }
919 
920 static PetscErrorCode DMPlexView_Ascii(DM dm, PetscViewer viewer)
921 {
922   DM_Plex          *mesh = (DM_Plex *)dm->data;
923   DM                cdm, cdmCell;
924   PetscSection      coordSection, coordSectionCell;
925   Vec               coordinates, coordinatesCell;
926   PetscViewerFormat format;
927 
928   PetscFunctionBegin;
929   PetscCall(PetscViewerGetFormat(viewer, &format));
930   if (format == PETSC_VIEWER_ASCII_INFO_DETAIL) {
931     const char *name;
932     PetscInt    dim, cellHeight, maxConeSize, maxSupportSize;
933     PetscInt    pStart, pEnd, p, numLabels, l;
934     PetscMPIInt rank, size;
935 
936     PetscCall(DMGetCoordinateDM(dm, &cdm));
937     PetscCall(DMGetCoordinateSection(dm, &coordSection));
938     PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
939     PetscCall(DMGetCellCoordinateDM(dm, &cdmCell));
940     PetscCall(DMGetCellCoordinateSection(dm, &coordSectionCell));
941     PetscCall(DMGetCellCoordinatesLocal(dm, &coordinatesCell));
942     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
943     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
944     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
945     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
946     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
947     PetscCall(DMGetDimension(dm, &dim));
948     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
949     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
950     else PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
951     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
952     PetscCall(PetscViewerASCIIPrintf(viewer, "Supports:\n"));
953     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
954     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max support size: %" PetscInt_FMT "\n", rank, maxSupportSize));
955     for (p = pStart; p < pEnd; ++p) {
956       PetscInt dof, off, s;
957 
958       PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
959       PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
960       for (s = off; s < off + dof; ++s) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d]: %" PetscInt_FMT " ----> %" PetscInt_FMT "\n", rank, p, mesh->supports[s]));
961     }
962     PetscCall(PetscViewerFlush(viewer));
963     PetscCall(PetscViewerASCIIPrintf(viewer, "Cones:\n"));
964     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max cone size: %" PetscInt_FMT "\n", rank, maxConeSize));
965     for (p = pStart; p < pEnd; ++p) {
966       PetscInt dof, off, c;
967 
968       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
969       PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
970       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]));
971     }
972     PetscCall(PetscViewerFlush(viewer));
973     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
974     if (coordSection && coordinates) {
975       CoordSystem        cs = CS_CARTESIAN;
976       const PetscScalar *array, *arrayCell = NULL;
977       PetscInt           Nf, Nc, pvStart, pvEnd, pcStart = PETSC_MAX_INT, pcEnd = PETSC_MIN_INT, pStart, pEnd, p;
978       PetscMPIInt        rank;
979       const char        *name;
980 
981       PetscCall(PetscOptionsGetEnum(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_coord_system", CoordSystems, (PetscEnum *)&cs, NULL));
982       PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)viewer), &rank));
983       PetscCall(PetscSectionGetNumFields(coordSection, &Nf));
984       PetscCheck(Nf == 1, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Coordinate section should have 1 field, not %" PetscInt_FMT, Nf);
985       PetscCall(PetscSectionGetFieldComponents(coordSection, 0, &Nc));
986       PetscCall(PetscSectionGetChart(coordSection, &pvStart, &pvEnd));
987       if (coordSectionCell) PetscCall(PetscSectionGetChart(coordSectionCell, &pcStart, &pcEnd));
988       pStart = PetscMin(pvStart, pcStart);
989       pEnd   = PetscMax(pvEnd, pcEnd);
990       PetscCall(PetscObjectGetName((PetscObject)coordinates, &name));
991       PetscCall(PetscViewerASCIIPrintf(viewer, "%s with %" PetscInt_FMT " fields\n", name, Nf));
992       PetscCall(PetscViewerASCIIPrintf(viewer, "  field 0 with %" PetscInt_FMT " components\n", Nc));
993       if (cs != CS_CARTESIAN) PetscCall(PetscViewerASCIIPrintf(viewer, "  output coordinate system: %s\n", CoordSystems[cs]));
994 
995       PetscCall(VecGetArrayRead(coordinates, &array));
996       if (coordinatesCell) PetscCall(VecGetArrayRead(coordinatesCell, &arrayCell));
997       PetscCall(PetscViewerASCIIPushSynchronized(viewer));
998       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "Process %d:\n", rank));
999       for (p = pStart; p < pEnd; ++p) {
1000         PetscInt dof, off;
1001 
1002         if (p >= pvStart && p < pvEnd) {
1003           PetscCall(PetscSectionGetDof(coordSection, p, &dof));
1004           PetscCall(PetscSectionGetOffset(coordSection, p, &off));
1005           if (dof) {
1006             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dim %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
1007             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &array[off]));
1008             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
1009           }
1010         }
1011         if (cdmCell && p >= pcStart && p < pcEnd) {
1012           PetscCall(PetscSectionGetDof(coordSectionCell, p, &dof));
1013           PetscCall(PetscSectionGetOffset(coordSectionCell, p, &off));
1014           if (dof) {
1015             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dim %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
1016             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &arrayCell[off]));
1017             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
1018           }
1019         }
1020       }
1021       PetscCall(PetscViewerFlush(viewer));
1022       PetscCall(PetscViewerASCIIPopSynchronized(viewer));
1023       PetscCall(VecRestoreArrayRead(coordinates, &array));
1024       if (coordinatesCell) PetscCall(VecRestoreArrayRead(coordinatesCell, &arrayCell));
1025     }
1026     PetscCall(DMGetNumLabels(dm, &numLabels));
1027     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
1028     for (l = 0; l < numLabels; ++l) {
1029       DMLabel     label;
1030       PetscBool   isdepth;
1031       const char *name;
1032 
1033       PetscCall(DMGetLabelName(dm, l, &name));
1034       PetscCall(PetscStrcmp(name, "depth", &isdepth));
1035       if (isdepth) continue;
1036       PetscCall(DMGetLabel(dm, name, &label));
1037       PetscCall(DMLabelView(label, viewer));
1038     }
1039     if (size > 1) {
1040       PetscSF sf;
1041 
1042       PetscCall(DMGetPointSF(dm, &sf));
1043       PetscCall(PetscSFView(sf, viewer));
1044     }
1045     if (mesh->periodic.face_sf) PetscCall(PetscSFView(mesh->periodic.face_sf, viewer));
1046     PetscCall(PetscViewerFlush(viewer));
1047   } else if (format == PETSC_VIEWER_ASCII_LATEX) {
1048     const char  *name, *color;
1049     const char  *defcolors[3]  = {"gray", "orange", "green"};
1050     const char  *deflcolors[4] = {"blue", "cyan", "red", "magenta"};
1051     char         lname[PETSC_MAX_PATH_LEN];
1052     PetscReal    scale      = 2.0;
1053     PetscReal    tikzscale  = 1.0;
1054     PetscBool    useNumbers = PETSC_TRUE, drawNumbers[4], drawColors[4], useLabels, useColors, plotEdges, drawHasse = PETSC_FALSE;
1055     double       tcoords[3];
1056     PetscScalar *coords;
1057     PetscInt     numLabels, l, numColors, numLColors, dim, d, depth, cStart, cEnd, c, vStart, vEnd, v, eStart = 0, eEnd = 0, e, p, n;
1058     PetscMPIInt  rank, size;
1059     char       **names, **colors, **lcolors;
1060     PetscBool    flg, lflg;
1061     PetscBT      wp = NULL;
1062     PetscInt     pEnd, pStart;
1063 
1064     PetscCall(DMGetCoordinateDM(dm, &cdm));
1065     PetscCall(DMGetCoordinateSection(dm, &coordSection));
1066     PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1067     PetscCall(DMGetCellCoordinateDM(dm, &cdmCell));
1068     PetscCall(DMGetCellCoordinateSection(dm, &coordSectionCell));
1069     PetscCall(DMGetCellCoordinatesLocal(dm, &coordinatesCell));
1070     PetscCall(DMGetDimension(dm, &dim));
1071     PetscCall(DMPlexGetDepth(dm, &depth));
1072     PetscCall(DMGetNumLabels(dm, &numLabels));
1073     numLabels  = PetscMax(numLabels, 10);
1074     numColors  = 10;
1075     numLColors = 10;
1076     PetscCall(PetscCalloc3(numLabels, &names, numColors, &colors, numLColors, &lcolors));
1077     PetscCall(PetscOptionsGetReal(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_scale", &scale, NULL));
1078     PetscCall(PetscOptionsGetReal(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_tikzscale", &tikzscale, NULL));
1079     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_numbers", &useNumbers, NULL));
1080     for (d = 0; d < 4; ++d) drawNumbers[d] = useNumbers;
1081     for (d = 0; d < 4; ++d) drawColors[d] = PETSC_TRUE;
1082     n = 4;
1083     PetscCall(PetscOptionsGetBoolArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_numbers_depth", drawNumbers, &n, &flg));
1084     PetscCheck(!flg || n == dim + 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim + 1);
1085     n = 4;
1086     PetscCall(PetscOptionsGetBoolArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_colors_depth", drawColors, &n, &flg));
1087     PetscCheck(!flg || n == dim + 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim + 1);
1088     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_labels", names, &numLabels, &useLabels));
1089     if (!useLabels) numLabels = 0;
1090     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_colors", colors, &numColors, &useColors));
1091     if (!useColors) {
1092       numColors = 3;
1093       for (c = 0; c < numColors; ++c) PetscCall(PetscStrallocpy(defcolors[c], &colors[c]));
1094     }
1095     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_lcolors", lcolors, &numLColors, &useColors));
1096     if (!useColors) {
1097       numLColors = 4;
1098       for (c = 0; c < numLColors; ++c) PetscCall(PetscStrallocpy(deflcolors[c], &lcolors[c]));
1099     }
1100     PetscCall(PetscOptionsGetString(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_label_filter", lname, sizeof(lname), &lflg));
1101     plotEdges = (PetscBool)(depth > 1 && drawNumbers[1] && dim < 3);
1102     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_edges", &plotEdges, &flg));
1103     PetscCheck(!flg || !plotEdges || depth >= dim, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Mesh must be interpolated");
1104     if (depth < dim) plotEdges = PETSC_FALSE;
1105     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_hasse", &drawHasse, NULL));
1106 
1107     /* filter points with labelvalue != labeldefaultvalue */
1108     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
1109     PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1110     PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
1111     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1112     if (lflg) {
1113       DMLabel lbl;
1114 
1115       PetscCall(DMGetLabel(dm, lname, &lbl));
1116       if (lbl) {
1117         PetscInt val, defval;
1118 
1119         PetscCall(DMLabelGetDefaultValue(lbl, &defval));
1120         PetscCall(PetscBTCreate(pEnd - pStart, &wp));
1121         for (c = pStart; c < pEnd; c++) {
1122           PetscInt *closure = NULL;
1123           PetscInt  closureSize;
1124 
1125           PetscCall(DMLabelGetValue(lbl, c, &val));
1126           if (val == defval) continue;
1127 
1128           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1129           for (p = 0; p < closureSize * 2; p += 2) PetscCall(PetscBTSet(wp, closure[p] - pStart));
1130           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1131         }
1132       }
1133     }
1134 
1135     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1136     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
1137     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1138     PetscCall(PetscViewerASCIIPrintf(viewer, "\
1139 \\documentclass[tikz]{standalone}\n\n\
1140 \\usepackage{pgflibraryshapes}\n\
1141 \\usetikzlibrary{backgrounds}\n\
1142 \\usetikzlibrary{arrows}\n\
1143 \\begin{document}\n"));
1144     if (size > 1) {
1145       PetscCall(PetscViewerASCIIPrintf(viewer, "%s for process ", name));
1146       for (p = 0; p < size; ++p) {
1147         if (p) PetscCall(PetscViewerASCIIPrintf(viewer, (p == size - 1) ? ", and " : ", "));
1148         PetscCall(PetscViewerASCIIPrintf(viewer, "{\\textcolor{%s}%" PetscInt_FMT "}", colors[p % numColors], p));
1149       }
1150       PetscCall(PetscViewerASCIIPrintf(viewer, ".\n\n\n"));
1151     }
1152     if (drawHasse) {
1153       PetscInt maxStratum = PetscMax(vEnd - vStart, PetscMax(eEnd - eStart, cEnd - cStart));
1154 
1155       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vStart}{%" PetscInt_FMT "}\n", vStart));
1156       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vEnd}{%" PetscInt_FMT "}\n", vEnd - 1));
1157       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numVertices}{%" PetscInt_FMT "}\n", vEnd - vStart));
1158       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vShift}{%.2f}\n", 3 + (maxStratum - (vEnd - vStart)) / 2.));
1159       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eStart}{%" PetscInt_FMT "}\n", eStart));
1160       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eEnd}{%" PetscInt_FMT "}\n", eEnd - 1));
1161       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eShift}{%.2f}\n", 3 + (maxStratum - (eEnd - eStart)) / 2.));
1162       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numEdges}{%" PetscInt_FMT "}\n", eEnd - eStart));
1163       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cStart}{%" PetscInt_FMT "}\n", cStart));
1164       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cEnd}{%" PetscInt_FMT "}\n", cEnd - 1));
1165       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numCells}{%" PetscInt_FMT "}\n", cEnd - cStart));
1166       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cShift}{%.2f}\n", 3 + (maxStratum - (cEnd - cStart)) / 2.));
1167     }
1168     PetscCall(PetscViewerASCIIPrintf(viewer, "\\begin{tikzpicture}[scale = %g,font=\\fontsize{8}{8}\\selectfont]\n", (double)tikzscale));
1169 
1170     /* Plot vertices */
1171     PetscCall(VecGetArray(coordinates, &coords));
1172     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
1173     for (v = vStart; v < vEnd; ++v) {
1174       PetscInt  off, dof, d;
1175       PetscBool isLabeled = PETSC_FALSE;
1176 
1177       if (wp && !PetscBTLookup(wp, v - pStart)) continue;
1178       PetscCall(PetscSectionGetDof(coordSection, v, &dof));
1179       PetscCall(PetscSectionGetOffset(coordSection, v, &off));
1180       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1181       PetscCheck(dof <= 3, PETSC_COMM_SELF, PETSC_ERR_PLIB, "coordSection vertex %" PetscInt_FMT " has dof %" PetscInt_FMT " > 3", v, dof);
1182       for (d = 0; d < dof; ++d) {
1183         tcoords[d] = (double)(scale * PetscRealPart(coords[off + d]));
1184         tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1185       }
1186       /* Rotate coordinates since PGF makes z point out of the page instead of up */
1187       if (dim == 3) {
1188         PetscReal tmp = tcoords[1];
1189         tcoords[1]    = tcoords[2];
1190         tcoords[2]    = -tmp;
1191       }
1192       for (d = 0; d < dof; ++d) {
1193         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1194         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)tcoords[d]));
1195       }
1196       if (drawHasse) color = colors[0 % numColors];
1197       else color = colors[rank % numColors];
1198       for (l = 0; l < numLabels; ++l) {
1199         PetscInt val;
1200         PetscCall(DMGetLabelValue(dm, names[l], v, &val));
1201         if (val >= 0) {
1202           color     = lcolors[l % numLColors];
1203           isLabeled = PETSC_TRUE;
1204           break;
1205         }
1206       }
1207       if (drawNumbers[0]) {
1208         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", v, rank, color, v));
1209       } else if (drawColors[0]) {
1210         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", v, rank, !isLabeled ? 1 : 2, color));
1211       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", v, rank));
1212     }
1213     PetscCall(VecRestoreArray(coordinates, &coords));
1214     PetscCall(PetscViewerFlush(viewer));
1215     /* Plot edges */
1216     if (plotEdges) {
1217       PetscCall(VecGetArray(coordinates, &coords));
1218       PetscCall(PetscViewerASCIIPrintf(viewer, "\\path\n"));
1219       for (e = eStart; e < eEnd; ++e) {
1220         const PetscInt *cone;
1221         PetscInt        coneSize, offA, offB, dof, d;
1222 
1223         if (wp && !PetscBTLookup(wp, e - pStart)) continue;
1224         PetscCall(DMPlexGetConeSize(dm, e, &coneSize));
1225         PetscCheck(coneSize == 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Edge %" PetscInt_FMT " cone should have two vertices, not %" PetscInt_FMT, e, coneSize);
1226         PetscCall(DMPlexGetCone(dm, e, &cone));
1227         PetscCall(PetscSectionGetDof(coordSection, cone[0], &dof));
1228         PetscCall(PetscSectionGetOffset(coordSection, cone[0], &offA));
1229         PetscCall(PetscSectionGetOffset(coordSection, cone[1], &offB));
1230         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "("));
1231         for (d = 0; d < dof; ++d) {
1232           tcoords[d] = (double)(0.5 * scale * PetscRealPart(coords[offA + d] + coords[offB + d]));
1233           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1234         }
1235         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1236         if (dim == 3) {
1237           PetscReal tmp = tcoords[1];
1238           tcoords[1]    = tcoords[2];
1239           tcoords[2]    = -tmp;
1240         }
1241         for (d = 0; d < dof; ++d) {
1242           if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1243           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)tcoords[d]));
1244         }
1245         if (drawHasse) color = colors[1 % numColors];
1246         else color = colors[rank % numColors];
1247         for (l = 0; l < numLabels; ++l) {
1248           PetscInt val;
1249           PetscCall(DMGetLabelValue(dm, names[l], e, &val));
1250           if (val >= 0) {
1251             color = lcolors[l % numLColors];
1252             break;
1253           }
1254         }
1255         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "} --\n", e, rank, color, e));
1256       }
1257       PetscCall(VecRestoreArray(coordinates, &coords));
1258       PetscCall(PetscViewerFlush(viewer));
1259       PetscCall(PetscViewerASCIIPrintf(viewer, "(0,0);\n"));
1260     }
1261     /* Plot cells */
1262     if (dim == 3 || !drawNumbers[1]) {
1263       for (e = eStart; e < eEnd; ++e) {
1264         const PetscInt *cone;
1265 
1266         if (wp && !PetscBTLookup(wp, e - pStart)) continue;
1267         color = colors[rank % numColors];
1268         for (l = 0; l < numLabels; ++l) {
1269           PetscInt val;
1270           PetscCall(DMGetLabelValue(dm, names[l], e, &val));
1271           if (val >= 0) {
1272             color = lcolors[l % numLColors];
1273             break;
1274           }
1275         }
1276         PetscCall(DMPlexGetCone(dm, e, &cone));
1277         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", color, cone[0], rank, cone[1], rank));
1278       }
1279     } else {
1280       DMPolytopeType ct;
1281 
1282       /* Drawing a 2D polygon */
1283       for (c = cStart; c < cEnd; ++c) {
1284         if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1285         PetscCall(DMPlexGetCellType(dm, c, &ct));
1286         if (DMPolytopeTypeIsHybrid(ct)) {
1287           const PetscInt *cone;
1288           PetscInt        coneSize, e;
1289 
1290           PetscCall(DMPlexGetCone(dm, c, &cone));
1291           PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
1292           for (e = 0; e < coneSize; ++e) {
1293             const PetscInt *econe;
1294 
1295             PetscCall(DMPlexGetCone(dm, cone[e], &econe));
1296             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));
1297           }
1298         } else {
1299           PetscInt *closure = NULL;
1300           PetscInt  closureSize, Nv = 0, v;
1301 
1302           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1303           for (p = 0; p < closureSize * 2; p += 2) {
1304             const PetscInt point = closure[p];
1305 
1306             if ((point >= vStart) && (point < vEnd)) closure[Nv++] = point;
1307           }
1308           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] ", colors[rank % numColors]));
1309           for (v = 0; v <= Nv; ++v) {
1310             const PetscInt vertex = closure[v % Nv];
1311 
1312             if (v > 0) {
1313               if (plotEdges) {
1314                 const PetscInt *edge;
1315                 PetscInt        endpoints[2], ne;
1316 
1317                 endpoints[0] = closure[v - 1];
1318                 endpoints[1] = vertex;
1319                 PetscCall(DMPlexGetJoin(dm, 2, endpoints, &ne, &edge));
1320                 PetscCheck(ne == 1, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Could not find edge for vertices %" PetscInt_FMT ", %" PetscInt_FMT, endpoints[0], endpoints[1]);
1321                 PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- (%" PetscInt_FMT "_%d) -- ", edge[0], rank));
1322                 PetscCall(DMPlexRestoreJoin(dm, 2, endpoints, &ne, &edge));
1323               } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- "));
1324             }
1325             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "(%" PetscInt_FMT "_%d)", vertex, rank));
1326           }
1327           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ";\n"));
1328           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1329         }
1330       }
1331     }
1332     for (c = cStart; c < cEnd; ++c) {
1333       double             ccoords[3] = {0.0, 0.0, 0.0};
1334       PetscBool          isLabeled  = PETSC_FALSE;
1335       PetscScalar       *cellCoords = NULL;
1336       const PetscScalar *array;
1337       PetscInt           numCoords, cdim, d;
1338       PetscBool          isDG;
1339 
1340       if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1341       PetscCall(DMGetCoordinateDim(dm, &cdim));
1342       PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1343       PetscCheck(!(numCoords % cdim), PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "coordinate dim %" PetscInt_FMT " does not divide numCoords %" PetscInt_FMT, cdim, numCoords);
1344       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1345       for (p = 0; p < numCoords / cdim; ++p) {
1346         for (d = 0; d < cdim; ++d) {
1347           tcoords[d] = (double)(scale * PetscRealPart(cellCoords[p * cdim + d]));
1348           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1349         }
1350         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1351         if (cdim == 3) {
1352           PetscReal tmp = tcoords[1];
1353           tcoords[1]    = tcoords[2];
1354           tcoords[2]    = -tmp;
1355         }
1356         for (d = 0; d < dim; ++d) ccoords[d] += tcoords[d];
1357       }
1358       for (d = 0; d < cdim; ++d) ccoords[d] /= (numCoords / cdim);
1359       PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1360       for (d = 0; d < cdim; ++d) {
1361         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1362         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)ccoords[d]));
1363       }
1364       if (drawHasse) color = colors[depth % numColors];
1365       else color = colors[rank % numColors];
1366       for (l = 0; l < numLabels; ++l) {
1367         PetscInt val;
1368         PetscCall(DMGetLabelValue(dm, names[l], c, &val));
1369         if (val >= 0) {
1370           color     = lcolors[l % numLColors];
1371           isLabeled = PETSC_TRUE;
1372           break;
1373         }
1374       }
1375       if (drawNumbers[dim]) {
1376         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", c, rank, color, c));
1377       } else if (drawColors[dim]) {
1378         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", c, rank, !isLabeled ? 1 : 2, color));
1379       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", c, rank));
1380     }
1381     if (drawHasse) {
1382       color = colors[depth % numColors];
1383       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Cells\n"));
1384       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\c in {\\cStart,...,\\cEnd}\n"));
1385       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1386       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\c_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\cShift+\\c-\\cStart,0) {\\c};\n", rank, color));
1387       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1388 
1389       color = colors[1 % numColors];
1390       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Edges\n"));
1391       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\e in {\\eStart,...,\\eEnd}\n"));
1392       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1393       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\e_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\eShift+\\e-\\eStart,1) {\\e};\n", rank, color));
1394       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1395 
1396       color = colors[0 % numColors];
1397       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Vertices\n"));
1398       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\v in {\\vStart,...,\\vEnd}\n"));
1399       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1400       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\v_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\vShift+\\v-\\vStart,2) {\\v};\n", rank, color));
1401       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1402 
1403       for (p = pStart; p < pEnd; ++p) {
1404         const PetscInt *cone;
1405         PetscInt        coneSize, cp;
1406 
1407         PetscCall(DMPlexGetCone(dm, p, &cone));
1408         PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
1409         for (cp = 0; cp < coneSize; ++cp) PetscCall(PetscViewerASCIIPrintf(viewer, "\\draw[->, shorten >=1pt] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", cone[cp], rank, p, rank));
1410       }
1411     }
1412     PetscCall(PetscViewerFlush(viewer));
1413     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
1414     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{tikzpicture}\n"));
1415     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{document}\n"));
1416     for (l = 0; l < numLabels; ++l) PetscCall(PetscFree(names[l]));
1417     for (c = 0; c < numColors; ++c) PetscCall(PetscFree(colors[c]));
1418     for (c = 0; c < numLColors; ++c) PetscCall(PetscFree(lcolors[c]));
1419     PetscCall(PetscFree3(names, colors, lcolors));
1420     PetscCall(PetscBTDestroy(&wp));
1421   } else if (format == PETSC_VIEWER_LOAD_BALANCE) {
1422     Vec                    cown, acown;
1423     VecScatter             sct;
1424     ISLocalToGlobalMapping g2l;
1425     IS                     gid, acis;
1426     MPI_Comm               comm, ncomm = MPI_COMM_NULL;
1427     MPI_Group              ggroup, ngroup;
1428     PetscScalar           *array, nid;
1429     const PetscInt        *idxs;
1430     PetscInt              *idxs2, *start, *adjacency, *work;
1431     PetscInt64             lm[3], gm[3];
1432     PetscInt               i, c, cStart, cEnd, cum, numVertices, ect, ectn, cellHeight;
1433     PetscMPIInt            d1, d2, rank;
1434 
1435     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1436     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1437 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1438     PetscCallMPI(MPI_Comm_split_type(comm, MPI_COMM_TYPE_SHARED, rank, MPI_INFO_NULL, &ncomm));
1439 #endif
1440     if (ncomm != MPI_COMM_NULL) {
1441       PetscCallMPI(MPI_Comm_group(comm, &ggroup));
1442       PetscCallMPI(MPI_Comm_group(ncomm, &ngroup));
1443       d1 = 0;
1444       PetscCallMPI(MPI_Group_translate_ranks(ngroup, 1, &d1, ggroup, &d2));
1445       nid = d2;
1446       PetscCallMPI(MPI_Group_free(&ggroup));
1447       PetscCallMPI(MPI_Group_free(&ngroup));
1448       PetscCallMPI(MPI_Comm_free(&ncomm));
1449     } else nid = 0.0;
1450 
1451     /* Get connectivity */
1452     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1453     PetscCall(DMPlexCreatePartitionerGraph(dm, cellHeight, &numVertices, &start, &adjacency, &gid));
1454 
1455     /* filter overlapped local cells */
1456     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
1457     PetscCall(ISGetIndices(gid, &idxs));
1458     PetscCall(ISGetLocalSize(gid, &cum));
1459     PetscCall(PetscMalloc1(cum, &idxs2));
1460     for (c = cStart, cum = 0; c < cEnd; c++) {
1461       if (idxs[c - cStart] < 0) continue;
1462       idxs2[cum++] = idxs[c - cStart];
1463     }
1464     PetscCall(ISRestoreIndices(gid, &idxs));
1465     PetscCheck(numVertices == cum, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Unexpected %" PetscInt_FMT " != %" PetscInt_FMT, numVertices, cum);
1466     PetscCall(ISDestroy(&gid));
1467     PetscCall(ISCreateGeneral(comm, numVertices, idxs2, PETSC_OWN_POINTER, &gid));
1468 
1469     /* support for node-aware cell locality */
1470     PetscCall(ISCreateGeneral(comm, start[numVertices], adjacency, PETSC_USE_POINTER, &acis));
1471     PetscCall(VecCreateSeq(PETSC_COMM_SELF, start[numVertices], &acown));
1472     PetscCall(VecCreateMPI(comm, numVertices, PETSC_DECIDE, &cown));
1473     PetscCall(VecGetArray(cown, &array));
1474     for (c = 0; c < numVertices; c++) array[c] = nid;
1475     PetscCall(VecRestoreArray(cown, &array));
1476     PetscCall(VecScatterCreate(cown, acis, acown, NULL, &sct));
1477     PetscCall(VecScatterBegin(sct, cown, acown, INSERT_VALUES, SCATTER_FORWARD));
1478     PetscCall(VecScatterEnd(sct, cown, acown, INSERT_VALUES, SCATTER_FORWARD));
1479     PetscCall(ISDestroy(&acis));
1480     PetscCall(VecScatterDestroy(&sct));
1481     PetscCall(VecDestroy(&cown));
1482 
1483     /* compute edgeCut */
1484     for (c = 0, cum = 0; c < numVertices; c++) cum = PetscMax(cum, start[c + 1] - start[c]);
1485     PetscCall(PetscMalloc1(cum, &work));
1486     PetscCall(ISLocalToGlobalMappingCreateIS(gid, &g2l));
1487     PetscCall(ISLocalToGlobalMappingSetType(g2l, ISLOCALTOGLOBALMAPPINGHASH));
1488     PetscCall(ISDestroy(&gid));
1489     PetscCall(VecGetArray(acown, &array));
1490     for (c = 0, ect = 0, ectn = 0; c < numVertices; c++) {
1491       PetscInt totl;
1492 
1493       totl = start[c + 1] - start[c];
1494       PetscCall(ISGlobalToLocalMappingApply(g2l, IS_GTOLM_MASK, totl, adjacency + start[c], NULL, work));
1495       for (i = 0; i < totl; i++) {
1496         if (work[i] < 0) {
1497           ect += 1;
1498           ectn += (array[i + start[c]] != nid) ? 0 : 1;
1499         }
1500       }
1501     }
1502     PetscCall(PetscFree(work));
1503     PetscCall(VecRestoreArray(acown, &array));
1504     lm[0] = numVertices > 0 ? numVertices : PETSC_MAX_INT;
1505     lm[1] = -numVertices;
1506     PetscCall(MPIU_Allreduce(lm, gm, 2, MPIU_INT64, MPI_MIN, comm));
1507     PetscCall(PetscViewerASCIIPrintf(viewer, "  Cell balance: %.2f (max %" PetscInt_FMT ", min %" PetscInt_FMT, -((double)gm[1]) / ((double)gm[0]), -(PetscInt)gm[1], (PetscInt)gm[0]));
1508     lm[0] = ect;                     /* edgeCut */
1509     lm[1] = ectn;                    /* node-aware edgeCut */
1510     lm[2] = numVertices > 0 ? 0 : 1; /* empty processes */
1511     PetscCall(MPIU_Allreduce(lm, gm, 3, MPIU_INT64, MPI_SUM, comm));
1512     PetscCall(PetscViewerASCIIPrintf(viewer, ", empty %" PetscInt_FMT ")\n", (PetscInt)gm[2]));
1513 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1514     PetscCall(PetscViewerASCIIPrintf(viewer, "  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n", (PetscInt)(gm[0] / 2), gm[0] ? ((double)gm[1]) / ((double)gm[0]) : 1.));
1515 #else
1516     PetscCall(PetscViewerASCIIPrintf(viewer, "  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n", (PetscInt)(gm[0] / 2), 0.0));
1517 #endif
1518     PetscCall(ISLocalToGlobalMappingDestroy(&g2l));
1519     PetscCall(PetscFree(start));
1520     PetscCall(PetscFree(adjacency));
1521     PetscCall(VecDestroy(&acown));
1522   } else {
1523     const char    *name;
1524     PetscInt      *sizes, *hybsizes, *ghostsizes;
1525     PetscInt       locDepth, depth, cellHeight, dim, d;
1526     PetscInt       pStart, pEnd, p, gcStart, gcEnd, gcNum;
1527     PetscInt       numLabels, l, maxSize = 17;
1528     DMPolytopeType ct0 = DM_POLYTOPE_UNKNOWN;
1529     MPI_Comm       comm;
1530     PetscMPIInt    size, rank;
1531 
1532     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1533     PetscCallMPI(MPI_Comm_size(comm, &size));
1534     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1535     PetscCall(DMGetDimension(dm, &dim));
1536     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1537     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1538     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
1539     else PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
1540     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
1541     PetscCall(DMPlexGetDepth(dm, &locDepth));
1542     PetscCall(MPIU_Allreduce(&locDepth, &depth, 1, MPIU_INT, MPI_MAX, comm));
1543     PetscCall(DMPlexGetCellTypeStratum(dm, DM_POLYTOPE_FV_GHOST, &gcStart, &gcEnd));
1544     gcNum = gcEnd - gcStart;
1545     if (size < maxSize) PetscCall(PetscCalloc3(size, &sizes, size, &hybsizes, size, &ghostsizes));
1546     else PetscCall(PetscCalloc3(3, &sizes, 3, &hybsizes, 3, &ghostsizes));
1547     for (d = 0; d <= depth; d++) {
1548       PetscInt Nc[2] = {0, 0}, ict;
1549 
1550       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
1551       if (pStart < pEnd) PetscCall(DMPlexGetCellType(dm, pStart, &ct0));
1552       ict = ct0;
1553       PetscCallMPI(MPI_Bcast(&ict, 1, MPIU_INT, 0, comm));
1554       ct0 = (DMPolytopeType)ict;
1555       for (p = pStart; p < pEnd; ++p) {
1556         DMPolytopeType ct;
1557 
1558         PetscCall(DMPlexGetCellType(dm, p, &ct));
1559         if (ct == ct0) ++Nc[0];
1560         else ++Nc[1];
1561       }
1562       if (size < maxSize) {
1563         PetscCallMPI(MPI_Gather(&Nc[0], 1, MPIU_INT, sizes, 1, MPIU_INT, 0, comm));
1564         PetscCallMPI(MPI_Gather(&Nc[1], 1, MPIU_INT, hybsizes, 1, MPIU_INT, 0, comm));
1565         if (d == depth) PetscCallMPI(MPI_Gather(&gcNum, 1, MPIU_INT, ghostsizes, 1, MPIU_INT, 0, comm));
1566         PetscCall(PetscViewerASCIIPrintf(viewer, "  Number of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1567         for (p = 0; p < size; ++p) {
1568           if (rank == 0) {
1569             PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT, sizes[p] + hybsizes[p]));
1570             if (hybsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT ")", hybsizes[p]));
1571             if (ghostsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "]", ghostsizes[p]));
1572           }
1573         }
1574       } else {
1575         PetscInt locMinMax[2];
1576 
1577         locMinMax[0] = Nc[0] + Nc[1];
1578         locMinMax[1] = Nc[0] + Nc[1];
1579         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, sizes));
1580         locMinMax[0] = Nc[1];
1581         locMinMax[1] = Nc[1];
1582         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, hybsizes));
1583         if (d == depth) {
1584           locMinMax[0] = gcNum;
1585           locMinMax[1] = gcNum;
1586           PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, ghostsizes));
1587         }
1588         PetscCall(PetscViewerASCIIPrintf(viewer, "  Min/Max of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1589         PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT "/%" PetscInt_FMT, sizes[0], sizes[1]));
1590         if (hybsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT "/%" PetscInt_FMT ")", hybsizes[0], hybsizes[1]));
1591         if (ghostsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "/%" PetscInt_FMT "]", ghostsizes[0], ghostsizes[1]));
1592       }
1593       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
1594     }
1595     PetscCall(PetscFree3(sizes, hybsizes, ghostsizes));
1596     {
1597       const PetscReal *maxCell;
1598       const PetscReal *L;
1599       PetscBool        localized;
1600 
1601       PetscCall(DMGetPeriodicity(dm, &maxCell, NULL, &L));
1602       PetscCall(DMGetCoordinatesLocalized(dm, &localized));
1603       if (L || localized) {
1604         PetscCall(PetscViewerASCIIPrintf(viewer, "Periodic mesh"));
1605         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1606         if (L) {
1607           PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
1608           for (d = 0; d < dim; ++d) {
1609             if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1610             PetscCall(PetscViewerASCIIPrintf(viewer, "%s", L[d] > 0.0 ? "PERIODIC" : "NONE"));
1611           }
1612           PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
1613         }
1614         PetscCall(PetscViewerASCIIPrintf(viewer, " coordinates %s\n", localized ? "localized" : "not localized"));
1615         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1616       }
1617     }
1618     PetscCall(DMGetNumLabels(dm, &numLabels));
1619     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
1620     for (l = 0; l < numLabels; ++l) {
1621       DMLabel         label;
1622       const char     *name;
1623       IS              valueIS;
1624       const PetscInt *values;
1625       PetscInt        numValues, v;
1626 
1627       PetscCall(DMGetLabelName(dm, l, &name));
1628       PetscCall(DMGetLabel(dm, name, &label));
1629       PetscCall(DMLabelGetNumValues(label, &numValues));
1630       PetscCall(PetscViewerASCIIPrintf(viewer, "  %s: %" PetscInt_FMT " strata with value/size (", name, numValues));
1631       PetscCall(DMLabelGetValueIS(label, &valueIS));
1632       PetscCall(ISGetIndices(valueIS, &values));
1633       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1634       for (v = 0; v < numValues; ++v) {
1635         PetscInt size;
1636 
1637         PetscCall(DMLabelGetStratumSize(label, values[v], &size));
1638         if (v > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1639         PetscCall(PetscViewerASCIIPrintf(viewer, "%" PetscInt_FMT " (%" PetscInt_FMT ")", values[v], size));
1640       }
1641       PetscCall(PetscViewerASCIIPrintf(viewer, ")\n"));
1642       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1643       PetscCall(ISRestoreIndices(valueIS, &values));
1644       PetscCall(ISDestroy(&valueIS));
1645     }
1646     {
1647       char    **labelNames;
1648       PetscInt  Nl = numLabels;
1649       PetscBool flg;
1650 
1651       PetscCall(PetscMalloc1(Nl, &labelNames));
1652       PetscCall(PetscOptionsGetStringArray(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_plex_view_labels", labelNames, &Nl, &flg));
1653       for (l = 0; l < Nl; ++l) {
1654         DMLabel label;
1655 
1656         PetscCall(DMHasLabel(dm, labelNames[l], &flg));
1657         if (flg) {
1658           PetscCall(DMGetLabel(dm, labelNames[l], &label));
1659           PetscCall(DMLabelView(label, viewer));
1660         }
1661         PetscCall(PetscFree(labelNames[l]));
1662       }
1663       PetscCall(PetscFree(labelNames));
1664     }
1665     /* If no fields are specified, people do not want to see adjacency */
1666     if (dm->Nf) {
1667       PetscInt f;
1668 
1669       for (f = 0; f < dm->Nf; ++f) {
1670         const char *name;
1671 
1672         PetscCall(PetscObjectGetName(dm->fields[f].disc, &name));
1673         if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Field %s:\n", name));
1674         PetscCall(PetscViewerASCIIPushTab(viewer));
1675         if (dm->fields[f].label) PetscCall(DMLabelView(dm->fields[f].label, viewer));
1676         if (dm->fields[f].adjacency[0]) {
1677           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM++\n"));
1678           else PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM\n"));
1679         } else {
1680           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FEM\n"));
1681           else PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FUNKY\n"));
1682         }
1683         PetscCall(PetscViewerASCIIPopTab(viewer));
1684       }
1685     }
1686     PetscCall(DMGetCoarseDM(dm, &cdm));
1687     if (cdm) {
1688       PetscCall(PetscViewerASCIIPushTab(viewer));
1689       PetscCall(PetscViewerASCIIPrintf(viewer, "Defined by transform from:\n"));
1690       PetscCall(DMPlexView_Ascii(cdm, viewer));
1691       PetscCall(PetscViewerASCIIPopTab(viewer));
1692     }
1693   }
1694   PetscFunctionReturn(PETSC_SUCCESS);
1695 }
1696 
1697 static PetscErrorCode DMPlexDrawCell(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[])
1698 {
1699   DMPolytopeType ct;
1700   PetscMPIInt    rank;
1701   PetscInt       cdim;
1702 
1703   PetscFunctionBegin;
1704   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1705   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1706   PetscCall(DMGetCoordinateDim(dm, &cdim));
1707   switch (ct) {
1708   case DM_POLYTOPE_SEGMENT:
1709   case DM_POLYTOPE_POINT_PRISM_TENSOR:
1710     switch (cdim) {
1711     case 1: {
1712       const PetscReal y  = 0.5;  /* TODO Put it in the middle of the viewport */
1713       const PetscReal dy = 0.05; /* TODO Make it a fraction of the total length */
1714 
1715       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y, PetscRealPart(coords[1]), y, PETSC_DRAW_BLACK));
1716       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y + dy, PetscRealPart(coords[0]), y - dy, PETSC_DRAW_BLACK));
1717       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[1]), y + dy, PetscRealPart(coords[1]), y - dy, PETSC_DRAW_BLACK));
1718     } break;
1719     case 2: {
1720       const PetscReal dx = (PetscRealPart(coords[3]) - PetscRealPart(coords[1]));
1721       const PetscReal dy = (PetscRealPart(coords[2]) - PetscRealPart(coords[0]));
1722       const PetscReal l  = 0.1 / PetscSqrtReal(dx * dx + dy * dy);
1723 
1724       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1725       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));
1726       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));
1727     } break;
1728     default:
1729       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of dimension %" PetscInt_FMT, cdim);
1730     }
1731     break;
1732   case DM_POLYTOPE_TRIANGLE:
1733     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));
1734     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1735     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1736     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1737     break;
1738   case DM_POLYTOPE_QUADRILATERAL:
1739     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));
1740     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));
1741     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1742     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1743     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_BLACK));
1744     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1745     break;
1746   case DM_POLYTOPE_SEG_PRISM_TENSOR:
1747     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));
1748     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));
1749     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1750     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_BLACK));
1751     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1752     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1753     break;
1754   case DM_POLYTOPE_FV_GHOST:
1755     break;
1756   default:
1757     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1758   }
1759   PetscFunctionReturn(PETSC_SUCCESS);
1760 }
1761 
1762 static PetscErrorCode DrawPolygon_Private(DM dm, PetscDraw draw, PetscInt cell, PetscInt Nv, const PetscReal refVertices[], const PetscScalar coords[], PetscInt edgeDiv, PetscReal refCoords[], PetscReal edgeCoords[])
1763 {
1764   PetscReal   centroid[2] = {0., 0.};
1765   PetscMPIInt rank;
1766   PetscInt    fillColor;
1767 
1768   PetscFunctionBegin;
1769   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1770   fillColor = PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2;
1771   for (PetscInt v = 0; v < Nv; ++v) {
1772     centroid[0] += PetscRealPart(coords[v * 2 + 0]) / Nv;
1773     centroid[1] += PetscRealPart(coords[v * 2 + 1]) / Nv;
1774   }
1775   for (PetscInt e = 0; e < Nv; ++e) {
1776     refCoords[0] = refVertices[e * 2 + 0];
1777     refCoords[1] = refVertices[e * 2 + 1];
1778     for (PetscInt d = 1; d <= edgeDiv; ++d) {
1779       refCoords[d * 2 + 0] = refCoords[0] + (refVertices[(e + 1) % Nv * 2 + 0] - refCoords[0]) * d / edgeDiv;
1780       refCoords[d * 2 + 1] = refCoords[1] + (refVertices[(e + 1) % Nv * 2 + 1] - refCoords[1]) * d / edgeDiv;
1781     }
1782     PetscCall(DMPlexReferenceToCoordinates(dm, cell, edgeDiv + 1, refCoords, edgeCoords));
1783     for (PetscInt d = 0; d < edgeDiv; ++d) {
1784       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));
1785       PetscCall(PetscDrawLine(draw, edgeCoords[d * 2 + 0], edgeCoords[d * 2 + 1], edgeCoords[(d + 1) * 2 + 0], edgeCoords[(d + 1) * 2 + 1], PETSC_DRAW_BLACK));
1786     }
1787   }
1788   PetscFunctionReturn(PETSC_SUCCESS);
1789 }
1790 
1791 static PetscErrorCode DMPlexDrawCellHighOrder(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[], PetscInt edgeDiv, PetscReal refCoords[], PetscReal edgeCoords[])
1792 {
1793   DMPolytopeType ct;
1794 
1795   PetscFunctionBegin;
1796   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1797   switch (ct) {
1798   case DM_POLYTOPE_TRIANGLE: {
1799     PetscReal refVertices[6] = {-1., -1., 1., -1., -1., 1.};
1800 
1801     PetscCall(DrawPolygon_Private(dm, draw, cell, 3, refVertices, coords, edgeDiv, refCoords, edgeCoords));
1802   } break;
1803   case DM_POLYTOPE_QUADRILATERAL: {
1804     PetscReal refVertices[8] = {-1., -1., 1., -1., 1., 1., -1., 1.};
1805 
1806     PetscCall(DrawPolygon_Private(dm, draw, cell, 4, refVertices, coords, edgeDiv, refCoords, edgeCoords));
1807   } break;
1808   default:
1809     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1810   }
1811   PetscFunctionReturn(PETSC_SUCCESS);
1812 }
1813 
1814 static PetscErrorCode DMPlexView_Draw(DM dm, PetscViewer viewer)
1815 {
1816   PetscDraw    draw;
1817   DM           cdm;
1818   PetscSection coordSection;
1819   Vec          coordinates;
1820   PetscReal    xyl[3], xyr[3];
1821   PetscReal   *refCoords, *edgeCoords;
1822   PetscBool    isnull, drawAffine;
1823   PetscInt     dim, vStart, vEnd, cStart, cEnd, c, cDegree, edgeDiv;
1824 
1825   PetscFunctionBegin;
1826   PetscCall(DMGetCoordinateDim(dm, &dim));
1827   PetscCheck(dim <= 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT, dim);
1828   PetscCall(DMGetCoordinateDegree_Internal(dm, &cDegree));
1829   drawAffine = cDegree > 1 ? PETSC_FALSE : PETSC_TRUE;
1830   edgeDiv    = cDegree + 1;
1831   PetscCall(PetscOptionsGetBool(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_view_draw_affine", &drawAffine, NULL));
1832   if (!drawAffine) PetscCall(PetscMalloc2((edgeDiv + 1) * dim, &refCoords, (edgeDiv + 1) * dim, &edgeCoords));
1833   PetscCall(DMGetCoordinateDM(dm, &cdm));
1834   PetscCall(DMGetLocalSection(cdm, &coordSection));
1835   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1836   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1837   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1838 
1839   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
1840   PetscCall(PetscDrawIsNull(draw, &isnull));
1841   if (isnull) PetscFunctionReturn(PETSC_SUCCESS);
1842   PetscCall(PetscDrawSetTitle(draw, "Mesh"));
1843 
1844   PetscCall(DMGetBoundingBox(dm, xyl, xyr));
1845   PetscCall(PetscDrawSetCoordinates(draw, xyl[0], xyl[1], xyr[0], xyr[1]));
1846   PetscCall(PetscDrawClear(draw));
1847 
1848   for (c = cStart; c < cEnd; ++c) {
1849     PetscScalar       *coords = NULL;
1850     const PetscScalar *coords_arr;
1851     PetscInt           numCoords;
1852     PetscBool          isDG;
1853 
1854     PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
1855     if (drawAffine) PetscCall(DMPlexDrawCell(dm, draw, c, coords));
1856     else PetscCall(DMPlexDrawCellHighOrder(dm, draw, c, coords, edgeDiv, refCoords, edgeCoords));
1857     PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
1858   }
1859   if (!drawAffine) PetscCall(PetscFree2(refCoords, edgeCoords));
1860   PetscCall(PetscDrawFlush(draw));
1861   PetscCall(PetscDrawPause(draw));
1862   PetscCall(PetscDrawSave(draw));
1863   PetscFunctionReturn(PETSC_SUCCESS);
1864 }
1865 
1866 static PetscErrorCode DMPlexCreateHighOrderSurrogate_Internal(DM dm, DM *hdm)
1867 {
1868   DM           odm = dm, rdm = dm, cdm;
1869   PetscFE      fe;
1870   PetscSpace   sp;
1871   PetscClassId id;
1872   PetscInt     degree;
1873   PetscBool    hoView = PETSC_TRUE;
1874 
1875   PetscFunctionBegin;
1876   PetscObjectOptionsBegin((PetscObject)dm);
1877   PetscCall(PetscOptionsBool("-dm_plex_high_order_view", "Subsample to view meshes with high order coordinates", "DMPlexCreateHighOrderSurrogate_Internal", hoView, &hoView, NULL));
1878   PetscOptionsEnd();
1879   PetscCall(PetscObjectReference((PetscObject)dm));
1880   *hdm = dm;
1881   if (!hoView) PetscFunctionReturn(PETSC_SUCCESS);
1882   PetscCall(DMGetCoordinateDM(dm, &cdm));
1883   PetscCall(DMGetField(cdm, 0, NULL, (PetscObject *)&fe));
1884   PetscCall(PetscObjectGetClassId((PetscObject)fe, &id));
1885   if (id != PETSCFE_CLASSID) PetscFunctionReturn(PETSC_SUCCESS);
1886   PetscCall(PetscFEGetBasisSpace(fe, &sp));
1887   PetscCall(PetscSpaceGetDegree(sp, &degree, NULL));
1888   for (PetscInt r = 0, rd = PetscCeilReal(((PetscReal)degree) / 2.); r < (PetscInt)PetscCeilReal(PetscLog2Real(degree)); ++r, rd = PetscCeilReal(((PetscReal)rd) / 2.)) {
1889     DM  cdm, rcdm;
1890     Mat In;
1891     Vec cl, rcl;
1892 
1893     PetscCall(DMRefine(odm, PetscObjectComm((PetscObject)odm), &rdm));
1894     PetscCall(DMPlexCreateCoordinateSpace(rdm, rd, PETSC_FALSE, NULL));
1895     PetscCall(PetscObjectSetName((PetscObject)rdm, "Refined Mesh with Linear Coordinates"));
1896     PetscCall(DMGetCoordinateDM(odm, &cdm));
1897     PetscCall(DMGetCoordinateDM(rdm, &rcdm));
1898     PetscCall(DMGetCoordinatesLocal(odm, &cl));
1899     PetscCall(DMGetCoordinatesLocal(rdm, &rcl));
1900     PetscCall(DMSetCoarseDM(rcdm, cdm));
1901     PetscCall(DMCreateInterpolation(cdm, rcdm, &In, NULL));
1902     PetscCall(MatMult(In, cl, rcl));
1903     PetscCall(MatDestroy(&In));
1904     PetscCall(DMSetCoordinatesLocal(rdm, rcl));
1905     PetscCall(DMDestroy(&odm));
1906     odm = rdm;
1907   }
1908   *hdm = rdm;
1909   PetscFunctionReturn(PETSC_SUCCESS);
1910 }
1911 
1912 #if defined(PETSC_HAVE_EXODUSII)
1913   #include <exodusII.h>
1914   #include <petscviewerexodusii.h>
1915 #endif
1916 
1917 PetscErrorCode DMView_Plex(DM dm, PetscViewer viewer)
1918 {
1919   PetscBool iascii, ishdf5, isvtk, isdraw, flg, isglvis, isexodus, iscgns;
1920   char      name[PETSC_MAX_PATH_LEN];
1921 
1922   PetscFunctionBegin;
1923   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1924   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1925   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &iascii));
1926   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
1927   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1928   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
1929   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
1930   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodus));
1931   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
1932   if (iascii) {
1933     PetscViewerFormat format;
1934     PetscCall(PetscViewerGetFormat(viewer, &format));
1935     if (format == PETSC_VIEWER_ASCII_GLVIS) PetscCall(DMPlexView_GLVis(dm, viewer));
1936     else PetscCall(DMPlexView_Ascii(dm, viewer));
1937   } else if (ishdf5) {
1938 #if defined(PETSC_HAVE_HDF5)
1939     PetscCall(DMPlexView_HDF5_Internal(dm, viewer));
1940 #else
1941     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1942 #endif
1943   } else if (isvtk) {
1944     PetscCall(DMPlexVTKWriteAll((PetscObject)dm, viewer));
1945   } else if (isdraw) {
1946     DM hdm;
1947 
1948     PetscCall(DMPlexCreateHighOrderSurrogate_Internal(dm, &hdm));
1949     PetscCall(DMPlexView_Draw(hdm, viewer));
1950     PetscCall(DMDestroy(&hdm));
1951   } else if (isglvis) {
1952     PetscCall(DMPlexView_GLVis(dm, viewer));
1953 #if defined(PETSC_HAVE_EXODUSII)
1954   } else if (isexodus) {
1955     /*
1956       exodusII requires that all sets be part of exactly one cell set.
1957       If the dm does not have a "Cell Sets" label defined, we create one
1958       with ID 1, containing all cells.
1959       Note that if the Cell Sets label is defined but does not cover all cells,
1960       we may still have a problem. This should probably be checked here or in the viewer;
1961     */
1962     PetscInt numCS;
1963     PetscCall(DMGetLabelSize(dm, "Cell Sets", &numCS));
1964     if (!numCS) {
1965       PetscInt cStart, cEnd, c;
1966       PetscCall(DMCreateLabel(dm, "Cell Sets"));
1967       PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1968       for (c = cStart; c < cEnd; ++c) PetscCall(DMSetLabelValue(dm, "Cell Sets", c, 1));
1969     }
1970     PetscCall(DMView_PlexExodusII(dm, viewer));
1971 #endif
1972 #if defined(PETSC_HAVE_CGNS)
1973   } else if (iscgns) {
1974     PetscCall(DMView_PlexCGNS(dm, viewer));
1975 #endif
1976   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex writing", ((PetscObject)viewer)->type_name);
1977   /* Optionally view the partition */
1978   PetscCall(PetscOptionsHasName(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_partition_view", &flg));
1979   if (flg) {
1980     Vec ranks;
1981     PetscCall(DMPlexCreateRankField(dm, &ranks));
1982     PetscCall(VecView(ranks, viewer));
1983     PetscCall(VecDestroy(&ranks));
1984   }
1985   /* Optionally view a label */
1986   PetscCall(PetscOptionsGetString(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_label_view", name, sizeof(name), &flg));
1987   if (flg) {
1988     DMLabel label;
1989     Vec     val;
1990 
1991     PetscCall(DMGetLabel(dm, name, &label));
1992     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Label %s provided to -dm_label_view does not exist in this DM", name);
1993     PetscCall(DMPlexCreateLabelField(dm, label, &val));
1994     PetscCall(VecView(val, viewer));
1995     PetscCall(VecDestroy(&val));
1996   }
1997   PetscFunctionReturn(PETSC_SUCCESS);
1998 }
1999 
2000 /*@
2001   DMPlexTopologyView - Saves a `DMPLEX` topology into a file
2002 
2003   Collective
2004 
2005   Input Parameters:
2006 + dm     - The `DM` whose topology is to be saved
2007 - viewer - The `PetscViewer` to save it in
2008 
2009   Level: advanced
2010 
2011 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexTopologyLoad()`, `PetscViewer`
2012 @*/
2013 PetscErrorCode DMPlexTopologyView(DM dm, PetscViewer viewer)
2014 {
2015   PetscBool ishdf5;
2016 
2017   PetscFunctionBegin;
2018   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2019   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2020   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2021   PetscCall(PetscLogEventBegin(DMPLEX_TopologyView, viewer, 0, 0, 0));
2022   if (ishdf5) {
2023 #if defined(PETSC_HAVE_HDF5)
2024     PetscViewerFormat format;
2025     PetscCall(PetscViewerGetFormat(viewer, &format));
2026     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2027       IS globalPointNumbering;
2028 
2029       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
2030       PetscCall(DMPlexTopologyView_HDF5_Internal(dm, globalPointNumbering, viewer));
2031       PetscCall(ISDestroy(&globalPointNumbering));
2032     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
2033 #else
2034     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2035 #endif
2036   }
2037   PetscCall(PetscLogEventEnd(DMPLEX_TopologyView, viewer, 0, 0, 0));
2038   PetscFunctionReturn(PETSC_SUCCESS);
2039 }
2040 
2041 /*@
2042   DMPlexCoordinatesView - Saves `DMPLEX` coordinates into a file
2043 
2044   Collective
2045 
2046   Input Parameters:
2047 + dm     - The `DM` whose coordinates are to be saved
2048 - viewer - The `PetscViewer` for saving
2049 
2050   Level: advanced
2051 
2052 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexLabelsView()`, `DMPlexCoordinatesLoad()`, `PetscViewer`
2053 @*/
2054 PetscErrorCode DMPlexCoordinatesView(DM dm, PetscViewer viewer)
2055 {
2056   PetscBool ishdf5;
2057 
2058   PetscFunctionBegin;
2059   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2060   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2061   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2062   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
2063   if (ishdf5) {
2064 #if defined(PETSC_HAVE_HDF5)
2065     PetscViewerFormat format;
2066     PetscCall(PetscViewerGetFormat(viewer, &format));
2067     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2068       PetscCall(DMPlexCoordinatesView_HDF5_Internal(dm, viewer));
2069     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
2070 #else
2071     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2072 #endif
2073   }
2074   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
2075   PetscFunctionReturn(PETSC_SUCCESS);
2076 }
2077 
2078 /*@
2079   DMPlexLabelsView - Saves `DMPLEX` labels into a file
2080 
2081   Collective
2082 
2083   Input Parameters:
2084 + dm     - The `DM` whose labels are to be saved
2085 - viewer - The `PetscViewer` for saving
2086 
2087   Level: advanced
2088 
2089 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsLoad()`, `PetscViewer`
2090 @*/
2091 PetscErrorCode DMPlexLabelsView(DM dm, PetscViewer viewer)
2092 {
2093   PetscBool ishdf5;
2094 
2095   PetscFunctionBegin;
2096   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2097   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2098   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2099   PetscCall(PetscLogEventBegin(DMPLEX_LabelsView, viewer, 0, 0, 0));
2100   if (ishdf5) {
2101 #if defined(PETSC_HAVE_HDF5)
2102     IS                globalPointNumbering;
2103     PetscViewerFormat format;
2104 
2105     PetscCall(PetscViewerGetFormat(viewer, &format));
2106     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2107       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
2108       PetscCall(DMPlexLabelsView_HDF5_Internal(dm, globalPointNumbering, viewer));
2109       PetscCall(ISDestroy(&globalPointNumbering));
2110     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2111 #else
2112     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2113 #endif
2114   }
2115   PetscCall(PetscLogEventEnd(DMPLEX_LabelsView, viewer, 0, 0, 0));
2116   PetscFunctionReturn(PETSC_SUCCESS);
2117 }
2118 
2119 /*@
2120   DMPlexSectionView - Saves a section associated with a `DMPLEX`
2121 
2122   Collective
2123 
2124   Input Parameters:
2125 + dm        - The `DM` that contains the topology on which the section to be saved is defined
2126 . viewer    - The `PetscViewer` for saving
2127 - sectiondm - The `DM` that contains the section to be saved, can be `NULL`
2128 
2129   Level: advanced
2130 
2131   Notes:
2132   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.
2133 
2134   In general `dm` and `sectiondm` are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object (or in case `sectiondm` is `NULL`) if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2135 
2136 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`, `PetscSectionView()`, `DMPlexSectionLoad()`, `PetscViewer`
2137 @*/
2138 PetscErrorCode DMPlexSectionView(DM dm, PetscViewer viewer, DM sectiondm)
2139 {
2140   PetscBool ishdf5;
2141 
2142   PetscFunctionBegin;
2143   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2144   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2145   if (!sectiondm) sectiondm = dm;
2146   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2147   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2148   PetscCall(PetscLogEventBegin(DMPLEX_SectionView, viewer, 0, 0, 0));
2149   if (ishdf5) {
2150 #if defined(PETSC_HAVE_HDF5)
2151     PetscCall(DMPlexSectionView_HDF5_Internal(dm, viewer, sectiondm));
2152 #else
2153     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2154 #endif
2155   }
2156   PetscCall(PetscLogEventEnd(DMPLEX_SectionView, viewer, 0, 0, 0));
2157   PetscFunctionReturn(PETSC_SUCCESS);
2158 }
2159 
2160 /*@
2161   DMPlexGlobalVectorView - Saves a global vector
2162 
2163   Collective
2164 
2165   Input Parameters:
2166 + dm        - The `DM` that represents the topology
2167 . viewer    - The `PetscViewer` to save data with
2168 . sectiondm - The `DM` that contains the global section on which vec is defined, can be `NULL`
2169 - vec       - The global vector to be saved
2170 
2171   Level: advanced
2172 
2173   Notes:
2174   In general `dm` and `sectiondm` are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object (or in case `sectiondm` is `NULL`) if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2175 
2176   Calling sequence:
2177 .vb
2178        DMCreate(PETSC_COMM_WORLD, &dm);
2179        DMSetType(dm, DMPLEX);
2180        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2181        DMClone(dm, &sectiondm);
2182        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2183        PetscSectionCreate(PETSC_COMM_WORLD, &section);
2184        DMPlexGetChart(sectiondm, &pStart, &pEnd);
2185        PetscSectionSetChart(section, pStart, pEnd);
2186        PetscSectionSetUp(section);
2187        DMSetLocalSection(sectiondm, section);
2188        PetscSectionDestroy(&section);
2189        DMGetGlobalVector(sectiondm, &vec);
2190        PetscObjectSetName((PetscObject)vec, "vec_name");
2191        DMPlexTopologyView(dm, viewer);
2192        DMPlexSectionView(dm, viewer, sectiondm);
2193        DMPlexGlobalVectorView(dm, viewer, sectiondm, vec);
2194        DMRestoreGlobalVector(sectiondm, &vec);
2195        DMDestroy(&sectiondm);
2196        DMDestroy(&dm);
2197 .ve
2198 
2199 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexLocalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2200 @*/
2201 PetscErrorCode DMPlexGlobalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2202 {
2203   PetscBool ishdf5;
2204 
2205   PetscFunctionBegin;
2206   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2207   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2208   if (!sectiondm) sectiondm = dm;
2209   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2210   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2211   /* Check consistency */
2212   {
2213     PetscSection section;
2214     PetscBool    includesConstraints;
2215     PetscInt     m, m1;
2216 
2217     PetscCall(VecGetLocalSize(vec, &m1));
2218     PetscCall(DMGetGlobalSection(sectiondm, &section));
2219     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2220     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2221     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2222     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2223   }
2224   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2225   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorView, viewer, 0, 0, 0));
2226   if (ishdf5) {
2227 #if defined(PETSC_HAVE_HDF5)
2228     PetscCall(DMPlexGlobalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2229 #else
2230     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2231 #endif
2232   }
2233   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorView, viewer, 0, 0, 0));
2234   PetscFunctionReturn(PETSC_SUCCESS);
2235 }
2236 
2237 /*@
2238   DMPlexLocalVectorView - Saves a local vector
2239 
2240   Collective
2241 
2242   Input Parameters:
2243 + dm        - The `DM` that represents the topology
2244 . viewer    - The `PetscViewer` to save data with
2245 . sectiondm - The `DM` that contains the local section on which `vec` is defined, can be `NULL`
2246 - vec       - The local vector to be saved
2247 
2248   Level: advanced
2249 
2250   Note:
2251   In general `dm` and `sectiondm` are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object (or in case `sectiondm` is `NULL`) if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2252 
2253   Calling sequence:
2254 .vb
2255        DMCreate(PETSC_COMM_WORLD, &dm);
2256        DMSetType(dm, DMPLEX);
2257        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2258        DMClone(dm, &sectiondm);
2259        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2260        PetscSectionCreate(PETSC_COMM_WORLD, &section);
2261        DMPlexGetChart(sectiondm, &pStart, &pEnd);
2262        PetscSectionSetChart(section, pStart, pEnd);
2263        PetscSectionSetUp(section);
2264        DMSetLocalSection(sectiondm, section);
2265        DMGetLocalVector(sectiondm, &vec);
2266        PetscObjectSetName((PetscObject)vec, "vec_name");
2267        DMPlexTopologyView(dm, viewer);
2268        DMPlexSectionView(dm, viewer, sectiondm);
2269        DMPlexLocalVectorView(dm, viewer, sectiondm, vec);
2270        DMRestoreLocalVector(sectiondm, &vec);
2271        DMDestroy(&sectiondm);
2272        DMDestroy(&dm);
2273 .ve
2274 
2275 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexGlobalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2276 @*/
2277 PetscErrorCode DMPlexLocalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2278 {
2279   PetscBool ishdf5;
2280 
2281   PetscFunctionBegin;
2282   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2283   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2284   if (!sectiondm) sectiondm = dm;
2285   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2286   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2287   /* Check consistency */
2288   {
2289     PetscSection section;
2290     PetscBool    includesConstraints;
2291     PetscInt     m, m1;
2292 
2293     PetscCall(VecGetLocalSize(vec, &m1));
2294     PetscCall(DMGetLocalSection(sectiondm, &section));
2295     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2296     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2297     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2298     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2299   }
2300   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2301   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorView, viewer, 0, 0, 0));
2302   if (ishdf5) {
2303 #if defined(PETSC_HAVE_HDF5)
2304     PetscCall(DMPlexLocalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2305 #else
2306     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2307 #endif
2308   }
2309   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorView, viewer, 0, 0, 0));
2310   PetscFunctionReturn(PETSC_SUCCESS);
2311 }
2312 
2313 PetscErrorCode DMLoad_Plex(DM dm, PetscViewer viewer)
2314 {
2315   PetscBool ishdf5;
2316 
2317   PetscFunctionBegin;
2318   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2319   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2320   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2321   if (ishdf5) {
2322 #if defined(PETSC_HAVE_HDF5)
2323     PetscViewerFormat format;
2324     PetscCall(PetscViewerGetFormat(viewer, &format));
2325     if (format == PETSC_VIEWER_HDF5_XDMF || format == PETSC_VIEWER_HDF5_VIZ) {
2326       PetscCall(DMPlexLoad_HDF5_Xdmf_Internal(dm, viewer));
2327     } else if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2328       PetscCall(DMPlexLoad_HDF5_Internal(dm, viewer));
2329     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2330     PetscFunctionReturn(PETSC_SUCCESS);
2331 #else
2332     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2333 #endif
2334   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex loading", ((PetscObject)viewer)->type_name);
2335 }
2336 
2337 /*@
2338   DMPlexTopologyLoad - Loads a topology into a `DMPLEX`
2339 
2340   Collective
2341 
2342   Input Parameters:
2343 + dm     - The `DM` into which the topology is loaded
2344 - viewer - The `PetscViewer` for the saved topology
2345 
2346   Output Parameter:
2347 . 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
2348 
2349   Level: advanced
2350 
2351 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2352           `PetscViewer`, `PetscSF`
2353 @*/
2354 PetscErrorCode DMPlexTopologyLoad(DM dm, PetscViewer viewer, PetscSF *globalToLocalPointSF)
2355 {
2356   PetscBool ishdf5;
2357 
2358   PetscFunctionBegin;
2359   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2360   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2361   if (globalToLocalPointSF) PetscAssertPointer(globalToLocalPointSF, 3);
2362   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2363   PetscCall(PetscLogEventBegin(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2364   if (ishdf5) {
2365 #if defined(PETSC_HAVE_HDF5)
2366     PetscViewerFormat format;
2367     PetscCall(PetscViewerGetFormat(viewer, &format));
2368     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2369       PetscCall(DMPlexTopologyLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2370     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2371 #else
2372     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2373 #endif
2374   }
2375   PetscCall(PetscLogEventEnd(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2376   PetscFunctionReturn(PETSC_SUCCESS);
2377 }
2378 
2379 /*@
2380   DMPlexCoordinatesLoad - Loads coordinates into a `DMPLEX`
2381 
2382   Collective
2383 
2384   Input Parameters:
2385 + dm                   - The `DM` into which the coordinates are loaded
2386 . viewer               - The `PetscViewer` for the saved coordinates
2387 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad()` when loading dm from viewer
2388 
2389   Level: advanced
2390 
2391 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2392           `PetscSF`, `PetscViewer`
2393 @*/
2394 PetscErrorCode DMPlexCoordinatesLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2395 {
2396   PetscBool ishdf5;
2397 
2398   PetscFunctionBegin;
2399   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2400   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2401   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2402   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2403   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesLoad, viewer, 0, 0, 0));
2404   if (ishdf5) {
2405 #if defined(PETSC_HAVE_HDF5)
2406     PetscViewerFormat format;
2407     PetscCall(PetscViewerGetFormat(viewer, &format));
2408     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2409       PetscCall(DMPlexCoordinatesLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2410     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2411 #else
2412     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2413 #endif
2414   }
2415   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesLoad, viewer, 0, 0, 0));
2416   PetscFunctionReturn(PETSC_SUCCESS);
2417 }
2418 
2419 /*@
2420   DMPlexLabelsLoad - Loads labels into a `DMPLEX`
2421 
2422   Collective
2423 
2424   Input Parameters:
2425 + dm                   - The `DM` into which the labels are loaded
2426 . viewer               - The `PetscViewer` for the saved labels
2427 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad()` when loading `dm` from viewer
2428 
2429   Level: advanced
2430 
2431   Note:
2432   The `PetscSF` argument must not be NULL if the `DM` is distributed, otherwise an error occurs.
2433 
2434 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2435           `PetscSF`, `PetscViewer`
2436 @*/
2437 PetscErrorCode DMPlexLabelsLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2438 {
2439   PetscBool ishdf5;
2440 
2441   PetscFunctionBegin;
2442   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2443   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2444   if (globalToLocalPointSF) PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2445   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2446   PetscCall(PetscLogEventBegin(DMPLEX_LabelsLoad, viewer, 0, 0, 0));
2447   if (ishdf5) {
2448 #if defined(PETSC_HAVE_HDF5)
2449     PetscViewerFormat format;
2450 
2451     PetscCall(PetscViewerGetFormat(viewer, &format));
2452     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2453       PetscCall(DMPlexLabelsLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2454     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2455 #else
2456     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2457 #endif
2458   }
2459   PetscCall(PetscLogEventEnd(DMPLEX_LabelsLoad, viewer, 0, 0, 0));
2460   PetscFunctionReturn(PETSC_SUCCESS);
2461 }
2462 
2463 /*@
2464   DMPlexSectionLoad - Loads section into a `DMPLEX`
2465 
2466   Collective
2467 
2468   Input Parameters:
2469 + dm                   - The `DM` that represents the topology
2470 . viewer               - The `PetscViewer` that represents the on-disk section (sectionA)
2471 . sectiondm            - The `DM` into which the on-disk section (sectionA) is migrated, can be `NULL`
2472 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad(`) when loading dm from viewer
2473 
2474   Output Parameters:
2475 + 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)
2476 - 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)
2477 
2478   Level: advanced
2479 
2480   Notes:
2481   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.
2482 
2483   In general `dm` and `sectiondm` are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object (or in case `sectiondm` is `NULL`) if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2484 
2485   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.
2486 
2487   Example using 2 processes:
2488 .vb
2489   NX (number of points on dm): 4
2490   sectionA                   : the on-disk section
2491   vecA                       : a vector associated with sectionA
2492   sectionB                   : sectiondm's local section constructed in this function
2493   vecB (local)               : a vector associated with sectiondm's local section
2494   vecB (global)              : a vector associated with sectiondm's global section
2495 
2496                                      rank 0    rank 1
2497   vecA (global)                  : [.0 .4 .1 | .2 .3]        <- to be loaded in DMPlexGlobalVectorLoad() or DMPlexLocalVectorLoad()
2498   sectionA->atlasOff             :       0 2 | 1             <- loaded in PetscSectionLoad()
2499   sectionA->atlasDof             :       1 3 | 1             <- loaded in PetscSectionLoad()
2500   sectionA's global point numbers:       0 2 | 3             <- loaded in DMPlexSectionLoad()
2501   [0, NX)                        :       0 1 | 2 3           <- conceptual partition used in globalToLocalPointSF
2502   sectionB's global point numbers:     0 1 3 | 3 2           <- associated with [0, NX) by globalToLocalPointSF
2503   sectionB->atlasDof             :     1 0 1 | 1 3
2504   sectionB->atlasOff (no perm)   :     0 1 1 | 0 1
2505   vecB (local)                   :   [.0 .4] | [.4 .1 .2 .3] <- to be constructed by calling DMPlexLocalVectorLoad() with localDofSF
2506   vecB (global)                  :    [.0 .4 | .1 .2 .3]     <- to be constructed by calling DMPlexGlobalVectorLoad() with globalDofSF
2507 .ve
2508   where "|" represents a partition of loaded data, and global point 3 is assumed to be owned by rank 0.
2509 
2510 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`, `PetscSectionLoad()`, `DMPlexSectionView()`, `PetscSF`, `PetscViewer`
2511 @*/
2512 PetscErrorCode DMPlexSectionLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF globalToLocalPointSF, PetscSF *globalDofSF, PetscSF *localDofSF)
2513 {
2514   PetscBool ishdf5;
2515 
2516   PetscFunctionBegin;
2517   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2518   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2519   if (!sectiondm) sectiondm = dm;
2520   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2521   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 4);
2522   if (globalDofSF) PetscAssertPointer(globalDofSF, 5);
2523   if (localDofSF) PetscAssertPointer(localDofSF, 6);
2524   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2525   PetscCall(PetscLogEventBegin(DMPLEX_SectionLoad, viewer, 0, 0, 0));
2526   if (ishdf5) {
2527 #if defined(PETSC_HAVE_HDF5)
2528     PetscCall(DMPlexSectionLoad_HDF5_Internal(dm, viewer, sectiondm, globalToLocalPointSF, globalDofSF, localDofSF));
2529 #else
2530     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2531 #endif
2532   }
2533   PetscCall(PetscLogEventEnd(DMPLEX_SectionLoad, viewer, 0, 0, 0));
2534   PetscFunctionReturn(PETSC_SUCCESS);
2535 }
2536 
2537 /*@
2538   DMPlexGlobalVectorLoad - Loads on-disk vector data into a global vector
2539 
2540   Collective
2541 
2542   Input Parameters:
2543 + dm        - The `DM` that represents the topology
2544 . viewer    - The `PetscViewer` that represents the on-disk vector data
2545 . sectiondm - The `DM` that contains the global section on which vec is defined, can be `NULL`
2546 . sf        - The `PetscSF` that migrates the on-disk vector data into vec
2547 - vec       - The global vector to set values of
2548 
2549   Level: advanced
2550 
2551   Notes:
2552   In general dm and sectiondm are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object (or in case `sectiondm` is `NULL`) if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2553 
2554   Calling sequence:
2555 .vb
2556        DMCreate(PETSC_COMM_WORLD, &dm);
2557        DMSetType(dm, DMPLEX);
2558        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2559        DMPlexTopologyLoad(dm, viewer, &sfX);
2560        DMClone(dm, &sectiondm);
2561        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2562        DMPlexSectionLoad(dm, viewer, sectiondm, sfX, &gsf, NULL);
2563        DMGetGlobalVector(sectiondm, &vec);
2564        PetscObjectSetName((PetscObject)vec, "vec_name");
2565        DMPlexGlobalVectorLoad(dm, viewer, sectiondm, gsf, vec);
2566        DMRestoreGlobalVector(sectiondm, &vec);
2567        PetscSFDestroy(&gsf);
2568        PetscSFDestroy(&sfX);
2569        DMDestroy(&sectiondm);
2570        DMDestroy(&dm);
2571 .ve
2572 
2573 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexLocalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`,
2574           `PetscSF`, `PetscViewer`
2575 @*/
2576 PetscErrorCode DMPlexGlobalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2577 {
2578   PetscBool ishdf5;
2579 
2580   PetscFunctionBegin;
2581   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2582   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2583   if (!sectiondm) sectiondm = dm;
2584   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2585   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2586   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2587   /* Check consistency */
2588   {
2589     PetscSection section;
2590     PetscBool    includesConstraints;
2591     PetscInt     m, m1;
2592 
2593     PetscCall(VecGetLocalSize(vec, &m1));
2594     PetscCall(DMGetGlobalSection(sectiondm, &section));
2595     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2596     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2597     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2598     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2599   }
2600   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2601   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorLoad, viewer, 0, 0, 0));
2602   if (ishdf5) {
2603 #if defined(PETSC_HAVE_HDF5)
2604     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2605 #else
2606     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2607 #endif
2608   }
2609   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorLoad, viewer, 0, 0, 0));
2610   PetscFunctionReturn(PETSC_SUCCESS);
2611 }
2612 
2613 /*@
2614   DMPlexLocalVectorLoad - Loads on-disk vector data into a local vector
2615 
2616   Collective
2617 
2618   Input Parameters:
2619 + dm        - The `DM` that represents the topology
2620 . viewer    - The `PetscViewer` that represents the on-disk vector data
2621 . sectiondm - The `DM` that contains the local section on which vec is defined, can be `NULL`
2622 . sf        - The `PetscSF` that migrates the on-disk vector data into vec
2623 - vec       - The local vector to set values of
2624 
2625   Level: advanced
2626 
2627   Notes:
2628   In general `dm` and `sectiondm` are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object (or in case `sectiondm` is `NULL`) if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2629 
2630   Calling sequence:
2631 .vb
2632        DMCreate(PETSC_COMM_WORLD, &dm);
2633        DMSetType(dm, DMPLEX);
2634        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2635        DMPlexTopologyLoad(dm, viewer, &sfX);
2636        DMClone(dm, &sectiondm);
2637        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2638        DMPlexSectionLoad(dm, viewer, sectiondm, sfX, NULL, &lsf);
2639        DMGetLocalVector(sectiondm, &vec);
2640        PetscObjectSetName((PetscObject)vec, "vec_name");
2641        DMPlexLocalVectorLoad(dm, viewer, sectiondm, lsf, vec);
2642        DMRestoreLocalVector(sectiondm, &vec);
2643        PetscSFDestroy(&lsf);
2644        PetscSFDestroy(&sfX);
2645        DMDestroy(&sectiondm);
2646        DMDestroy(&dm);
2647 .ve
2648 
2649 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`,
2650           `PetscSF`, `PetscViewer`
2651 @*/
2652 PetscErrorCode DMPlexLocalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2653 {
2654   PetscBool ishdf5;
2655 
2656   PetscFunctionBegin;
2657   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2658   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2659   if (!sectiondm) sectiondm = dm;
2660   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2661   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2662   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2663   /* Check consistency */
2664   {
2665     PetscSection section;
2666     PetscBool    includesConstraints;
2667     PetscInt     m, m1;
2668 
2669     PetscCall(VecGetLocalSize(vec, &m1));
2670     PetscCall(DMGetLocalSection(sectiondm, &section));
2671     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2672     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2673     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2674     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2675   }
2676   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2677   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorLoad, viewer, 0, 0, 0));
2678   if (ishdf5) {
2679 #if defined(PETSC_HAVE_HDF5)
2680     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2681 #else
2682     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2683 #endif
2684   }
2685   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorLoad, viewer, 0, 0, 0));
2686   PetscFunctionReturn(PETSC_SUCCESS);
2687 }
2688 
2689 PetscErrorCode DMDestroy_Plex(DM dm)
2690 {
2691   DM_Plex *mesh = (DM_Plex *)dm->data;
2692 
2693   PetscFunctionBegin;
2694   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMSetUpGLVisViewer_C", NULL));
2695   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertBoundaryValues_C", NULL));
2696   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMCreateNeumannOverlap_C", NULL));
2697   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMInterpolateSolution_C", NULL));
2698   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertTimeDerivativeBoundaryValues_C", NULL));
2699   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", NULL));
2700   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeGetDefault_C", NULL));
2701   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeSetDefault_C", NULL));
2702   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "MatComputeNeumannOverlap_C", NULL));
2703   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderGetDefault_C", NULL));
2704   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderSetDefault_C", NULL));
2705   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionGetDefault_C", NULL));
2706   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionSetDefault_C", NULL));
2707   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionGetType_C", NULL));
2708   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionSetType_C", NULL));
2709   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", NULL));
2710   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexSetOverlap_C", NULL));
2711   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetUseCeed_C", NULL));
2712   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexSetUseCeed_C", NULL));
2713   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMGetIsoperiodicPointSF_C", NULL));
2714   if (--mesh->refct > 0) PetscFunctionReturn(PETSC_SUCCESS);
2715   PetscCall(PetscSectionDestroy(&mesh->coneSection));
2716   PetscCall(PetscFree(mesh->cones));
2717   PetscCall(PetscFree(mesh->coneOrientations));
2718   PetscCall(PetscSectionDestroy(&mesh->supportSection));
2719   PetscCall(PetscSectionDestroy(&mesh->subdomainSection));
2720   PetscCall(PetscFree(mesh->supports));
2721   PetscCall(PetscFree(mesh->cellTypes));
2722   PetscCall(DMPlexTransformDestroy(&mesh->tr));
2723   PetscCall(PetscFree(mesh->tetgenOpts));
2724   PetscCall(PetscFree(mesh->triangleOpts));
2725   PetscCall(PetscFree(mesh->transformType));
2726   PetscCall(PetscFree(mesh->distributionName));
2727   PetscCall(PetscPartitionerDestroy(&mesh->partitioner));
2728   PetscCall(DMLabelDestroy(&mesh->subpointMap));
2729   PetscCall(ISDestroy(&mesh->subpointIS));
2730   PetscCall(ISDestroy(&mesh->globalVertexNumbers));
2731   PetscCall(ISDestroy(&mesh->globalCellNumbers));
2732   PetscCall(PetscSFDestroy(&mesh->periodic.face_sf));
2733   PetscCall(PetscSFDestroy(&mesh->periodic.composed_sf));
2734   PetscCall(ISDestroy(&mesh->periodic.periodic_points));
2735   PetscCall(PetscSectionDestroy(&mesh->anchorSection));
2736   PetscCall(ISDestroy(&mesh->anchorIS));
2737   PetscCall(PetscSectionDestroy(&mesh->parentSection));
2738   PetscCall(PetscFree(mesh->parents));
2739   PetscCall(PetscFree(mesh->childIDs));
2740   PetscCall(PetscSectionDestroy(&mesh->childSection));
2741   PetscCall(PetscFree(mesh->children));
2742   PetscCall(DMDestroy(&mesh->referenceTree));
2743   PetscCall(PetscGridHashDestroy(&mesh->lbox));
2744   PetscCall(PetscFree(mesh->neighbors));
2745   if (mesh->metricCtx) PetscCall(PetscFree(mesh->metricCtx));
2746   /* This was originally freed in DMDestroy(), but that prevents reference counting of backend objects */
2747   PetscCall(PetscFree(mesh));
2748   PetscFunctionReturn(PETSC_SUCCESS);
2749 }
2750 
2751 PetscErrorCode DMCreateMatrix_Plex(DM dm, Mat *J)
2752 {
2753   PetscSection           sectionGlobal, sectionLocal;
2754   PetscInt               bs = -1, mbs;
2755   PetscInt               localSize, localStart = 0;
2756   PetscBool              isShell, isBlock, isSeqBlock, isMPIBlock, isSymBlock, isSymSeqBlock, isSymMPIBlock, isMatIS;
2757   MatType                mtype;
2758   ISLocalToGlobalMapping ltog;
2759 
2760   PetscFunctionBegin;
2761   PetscCall(MatInitializePackage());
2762   mtype = dm->mattype;
2763   PetscCall(DMGetLocalSection(dm, &sectionLocal));
2764   PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
2765   /* PetscCall(PetscSectionGetStorageSize(sectionGlobal, &localSize)); */
2766   PetscCall(PetscSectionGetConstrainedStorageSize(sectionGlobal, &localSize));
2767   PetscCallMPI(MPI_Exscan(&localSize, &localStart, 1, MPIU_INT, MPI_SUM, PetscObjectComm((PetscObject)dm)));
2768   PetscCall(MatCreate(PetscObjectComm((PetscObject)dm), J));
2769   PetscCall(MatSetSizes(*J, localSize, localSize, PETSC_DETERMINE, PETSC_DETERMINE));
2770   PetscCall(MatSetType(*J, mtype));
2771   PetscCall(MatSetFromOptions(*J));
2772   PetscCall(MatGetBlockSize(*J, &mbs));
2773   if (mbs > 1) bs = mbs;
2774   PetscCall(PetscStrcmp(mtype, MATSHELL, &isShell));
2775   PetscCall(PetscStrcmp(mtype, MATBAIJ, &isBlock));
2776   PetscCall(PetscStrcmp(mtype, MATSEQBAIJ, &isSeqBlock));
2777   PetscCall(PetscStrcmp(mtype, MATMPIBAIJ, &isMPIBlock));
2778   PetscCall(PetscStrcmp(mtype, MATSBAIJ, &isSymBlock));
2779   PetscCall(PetscStrcmp(mtype, MATSEQSBAIJ, &isSymSeqBlock));
2780   PetscCall(PetscStrcmp(mtype, MATMPISBAIJ, &isSymMPIBlock));
2781   PetscCall(PetscStrcmp(mtype, MATIS, &isMatIS));
2782   if (!isShell) {
2783     PetscBool fillMatrix = (PetscBool)(!dm->prealloc_only && !isMatIS);
2784     PetscInt *dnz, *onz, *dnzu, *onzu, bsLocal[2], bsMinMax[2], *pblocks;
2785     PetscInt  pStart, pEnd, p, dof, cdof, num_fields;
2786 
2787     PetscCall(DMGetLocalToGlobalMapping(dm, &ltog));
2788 
2789     PetscCall(PetscCalloc1(localSize, &pblocks));
2790     PetscCall(PetscSectionGetChart(sectionGlobal, &pStart, &pEnd));
2791     PetscCall(PetscSectionGetNumFields(sectionGlobal, &num_fields));
2792     for (p = pStart; p < pEnd; ++p) {
2793       switch (dm->blocking_type) {
2794       case DM_BLOCKING_TOPOLOGICAL_POINT: { // One block per topological point
2795         PetscInt bdof, offset;
2796 
2797         PetscCall(PetscSectionGetDof(sectionGlobal, p, &dof));
2798         PetscCall(PetscSectionGetOffset(sectionGlobal, p, &offset));
2799         PetscCall(PetscSectionGetConstraintDof(sectionGlobal, p, &cdof));
2800         for (PetscInt i = 0; i < dof - cdof; ++i) pblocks[offset - localStart + i] = dof - cdof;
2801         // Signal block concatenation
2802         if (dof - cdof && sectionLocal->blockStarts && !PetscBTLookup(sectionLocal->blockStarts, p)) pblocks[offset - localStart] = -(dof - cdof);
2803         dof  = dof < 0 ? -(dof + 1) : dof;
2804         bdof = cdof && (dof - cdof) ? 1 : dof;
2805         if (dof) {
2806           if (bs < 0) {
2807             bs = bdof;
2808           } else if (bs != bdof) {
2809             bs = 1;
2810           }
2811         }
2812       } break;
2813       case DM_BLOCKING_FIELD_NODE: {
2814         for (PetscInt field = 0; field < num_fields; field++) {
2815           PetscInt num_comp, bdof, offset;
2816           PetscCall(PetscSectionGetFieldComponents(sectionGlobal, field, &num_comp));
2817           PetscCall(PetscSectionGetFieldDof(sectionGlobal, p, field, &dof));
2818           if (dof < 0) continue;
2819           PetscCall(PetscSectionGetFieldOffset(sectionGlobal, p, field, &offset));
2820           PetscCall(PetscSectionGetFieldConstraintDof(sectionGlobal, p, field, &cdof));
2821           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);
2822           PetscInt num_nodes = dof / num_comp;
2823           for (PetscInt i = 0; i < dof - cdof; i++) pblocks[offset - localStart + i] = (dof - cdof) / num_nodes;
2824           // Handle possibly constant block size (unlikely)
2825           bdof = cdof && (dof - cdof) ? 1 : dof;
2826           if (dof) {
2827             if (bs < 0) {
2828               bs = bdof;
2829             } else if (bs != bdof) {
2830               bs = 1;
2831             }
2832           }
2833         }
2834       } break;
2835       }
2836     }
2837     /* Must have same blocksize on all procs (some might have no points) */
2838     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs;
2839     bsLocal[1] = bs;
2840     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
2841     if (bsMinMax[0] != bsMinMax[1]) bs = 1;
2842     else bs = bsMinMax[0];
2843     bs = PetscMax(1, bs);
2844     PetscCall(MatSetLocalToGlobalMapping(*J, ltog, ltog));
2845     if (dm->prealloc_skip) { // User will likely use MatSetPreallocationCOO(), but still set structural parameters
2846       PetscCall(MatSetBlockSize(*J, bs));
2847       PetscCall(MatSetUp(*J));
2848     } else {
2849       PetscCall(PetscCalloc4(localSize / bs, &dnz, localSize / bs, &onz, localSize / bs, &dnzu, localSize / bs, &onzu));
2850       PetscCall(DMPlexPreallocateOperator(dm, bs, dnz, onz, dnzu, onzu, *J, fillMatrix));
2851       PetscCall(PetscFree4(dnz, onz, dnzu, onzu));
2852     }
2853     { // Consolidate blocks
2854       PetscInt nblocks = 0;
2855       for (PetscInt i = 0; i < localSize; i += PetscMax(1, pblocks[i])) {
2856         if (pblocks[i] == 0) continue;
2857         // Negative block size indicates the blocks should be concatenated
2858         if (pblocks[i] < 0) {
2859           pblocks[i] = -pblocks[i];
2860           pblocks[nblocks - 1] += pblocks[i];
2861         } else {
2862           pblocks[nblocks++] = pblocks[i]; // nblocks always <= i
2863         }
2864         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]);
2865       }
2866       PetscCall(MatSetVariableBlockSizes(*J, nblocks, pblocks));
2867     }
2868     PetscCall(PetscFree(pblocks));
2869   }
2870   PetscCall(MatSetDM(*J, dm));
2871   PetscFunctionReturn(PETSC_SUCCESS);
2872 }
2873 
2874 /*@
2875   DMPlexGetSubdomainSection - Returns the section associated with the subdomain
2876 
2877   Not Collective
2878 
2879   Input Parameter:
2880 . dm - The `DMPLEX`
2881 
2882   Output Parameter:
2883 . subsection - The subdomain section
2884 
2885   Level: developer
2886 
2887 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `PetscSection`
2888 @*/
2889 PetscErrorCode DMPlexGetSubdomainSection(DM dm, PetscSection *subsection)
2890 {
2891   DM_Plex *mesh = (DM_Plex *)dm->data;
2892 
2893   PetscFunctionBegin;
2894   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2895   if (!mesh->subdomainSection) {
2896     PetscSection section;
2897     PetscSF      sf;
2898 
2899     PetscCall(PetscSFCreate(PETSC_COMM_SELF, &sf));
2900     PetscCall(DMGetLocalSection(dm, &section));
2901     PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_TRUE, PETSC_FALSE, PETSC_TRUE, &mesh->subdomainSection));
2902     PetscCall(PetscSFDestroy(&sf));
2903   }
2904   *subsection = mesh->subdomainSection;
2905   PetscFunctionReturn(PETSC_SUCCESS);
2906 }
2907 
2908 /*@
2909   DMPlexGetChart - Return the interval for all mesh points [`pStart`, `pEnd`)
2910 
2911   Not Collective
2912 
2913   Input Parameter:
2914 . dm - The `DMPLEX`
2915 
2916   Output Parameters:
2917 + pStart - The first mesh point
2918 - pEnd   - The upper bound for mesh points
2919 
2920   Level: beginner
2921 
2922 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetChart()`
2923 @*/
2924 PetscErrorCode DMPlexGetChart(DM dm, PetscInt *pStart, PetscInt *pEnd)
2925 {
2926   DM_Plex *mesh = (DM_Plex *)dm->data;
2927 
2928   PetscFunctionBegin;
2929   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2930   if (mesh->tr) PetscCall(DMPlexTransformGetChart(mesh->tr, pStart, pEnd));
2931   else PetscCall(PetscSectionGetChart(mesh->coneSection, pStart, pEnd));
2932   PetscFunctionReturn(PETSC_SUCCESS);
2933 }
2934 
2935 /*@
2936   DMPlexSetChart - Set the interval for all mesh points [`pStart`, `pEnd`)
2937 
2938   Not Collective
2939 
2940   Input Parameters:
2941 + dm     - The `DMPLEX`
2942 . pStart - The first mesh point
2943 - pEnd   - The upper bound for mesh points
2944 
2945   Level: beginner
2946 
2947 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetChart()`
2948 @*/
2949 PetscErrorCode DMPlexSetChart(DM dm, PetscInt pStart, PetscInt pEnd)
2950 {
2951   DM_Plex *mesh = (DM_Plex *)dm->data;
2952 
2953   PetscFunctionBegin;
2954   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2955   PetscCall(PetscSectionSetChart(mesh->coneSection, pStart, pEnd));
2956   PetscCall(PetscSectionSetChart(mesh->supportSection, pStart, pEnd));
2957   PetscCall(PetscFree(mesh->cellTypes));
2958   PetscFunctionReturn(PETSC_SUCCESS);
2959 }
2960 
2961 /*@
2962   DMPlexGetConeSize - Return the number of in-edges for this point in the DAG
2963 
2964   Not Collective
2965 
2966   Input Parameters:
2967 + dm - The `DMPLEX`
2968 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
2969 
2970   Output Parameter:
2971 . size - The cone size for point `p`
2972 
2973   Level: beginner
2974 
2975 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
2976 @*/
2977 PetscErrorCode DMPlexGetConeSize(DM dm, PetscInt p, PetscInt *size)
2978 {
2979   DM_Plex *mesh = (DM_Plex *)dm->data;
2980 
2981   PetscFunctionBegin;
2982   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2983   PetscAssertPointer(size, 3);
2984   if (mesh->tr) PetscCall(DMPlexTransformGetConeSize(mesh->tr, p, size));
2985   else PetscCall(PetscSectionGetDof(mesh->coneSection, p, size));
2986   PetscFunctionReturn(PETSC_SUCCESS);
2987 }
2988 
2989 /*@
2990   DMPlexSetConeSize - Set the number of in-edges for this point in the DAG
2991 
2992   Not Collective
2993 
2994   Input Parameters:
2995 + dm   - The `DMPLEX`
2996 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
2997 - size - The cone size for point `p`
2998 
2999   Level: beginner
3000 
3001   Note:
3002   This should be called after `DMPlexSetChart()`.
3003 
3004 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetConeSize()`, `DMPlexSetChart()`
3005 @*/
3006 PetscErrorCode DMPlexSetConeSize(DM dm, PetscInt p, PetscInt size)
3007 {
3008   DM_Plex *mesh = (DM_Plex *)dm->data;
3009 
3010   PetscFunctionBegin;
3011   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3012   PetscCheck(!mesh->tr, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Cannot call DMPlexSetConeSize() on a mesh with a transform defined.");
3013   PetscCall(PetscSectionSetDof(mesh->coneSection, p, size));
3014   PetscFunctionReturn(PETSC_SUCCESS);
3015 }
3016 
3017 /*@C
3018   DMPlexGetCone - Return the points on the in-edges for this point in the DAG
3019 
3020   Not Collective
3021 
3022   Input Parameters:
3023 + dm - The `DMPLEX`
3024 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3025 
3026   Output Parameter:
3027 . cone - An array of points which are on the in-edges for point `p`
3028 
3029   Level: beginner
3030 
3031   Fortran Notes:
3032   You must also call `DMPlexRestoreCone()` after you finish using the returned array.
3033   `DMPlexRestoreCone()` is not needed/available in C.
3034 
3035 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSize()`, `DMPlexSetCone()`, `DMPlexGetConeTuple()`, `DMPlexSetChart()`, `DMPlexRestoreCone()`
3036 @*/
3037 PetscErrorCode DMPlexGetCone(DM dm, PetscInt p, const PetscInt *cone[])
3038 {
3039   DM_Plex *mesh = (DM_Plex *)dm->data;
3040   PetscInt off;
3041 
3042   PetscFunctionBegin;
3043   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3044   PetscAssertPointer(cone, 3);
3045   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3046   *cone = PetscSafePointerPlusOffset(mesh->cones, off);
3047   PetscFunctionReturn(PETSC_SUCCESS);
3048 }
3049 
3050 /*@C
3051   DMPlexGetConeTuple - Return the points on the in-edges of several points in the DAG
3052 
3053   Not Collective
3054 
3055   Input Parameters:
3056 + dm - The `DMPLEX`
3057 - p  - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3058 
3059   Output Parameters:
3060 + pConesSection - `PetscSection` describing the layout of `pCones`
3061 - pCones        - An array of points which are on the in-edges for the point set `p`
3062 
3063   Level: intermediate
3064 
3065 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeRecursive()`, `DMPlexSetChart()`, `PetscSection`, `IS`
3066 @*/
3067 PetscErrorCode DMPlexGetConeTuple(DM dm, IS p, PetscSection *pConesSection, IS *pCones)
3068 {
3069   PetscSection cs, newcs;
3070   PetscInt    *cones;
3071   PetscInt    *newarr = NULL;
3072   PetscInt     n;
3073 
3074   PetscFunctionBegin;
3075   PetscCall(DMPlexGetCones(dm, &cones));
3076   PetscCall(DMPlexGetConeSection(dm, &cs));
3077   PetscCall(PetscSectionExtractDofsFromArray(cs, MPIU_INT, cones, p, &newcs, pCones ? ((void **)&newarr) : NULL));
3078   if (pConesSection) *pConesSection = newcs;
3079   if (pCones) {
3080     PetscCall(PetscSectionGetStorageSize(newcs, &n));
3081     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)p), n, newarr, PETSC_OWN_POINTER, pCones));
3082   }
3083   PetscFunctionReturn(PETSC_SUCCESS);
3084 }
3085 
3086 /*@
3087   DMPlexGetConeRecursiveVertices - Expand each given point into its cone points and do that recursively until we end up just with vertices.
3088 
3089   Not Collective
3090 
3091   Input Parameters:
3092 + dm     - The `DMPLEX`
3093 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3094 
3095   Output Parameter:
3096 . expandedPoints - An array of vertices recursively expanded from input points
3097 
3098   Level: advanced
3099 
3100   Notes:
3101   Like `DMPlexGetConeRecursive()` but returns only the 0-depth `IS` (i.e. vertices only) and no sections.
3102 
3103   There is no corresponding Restore function, just call `ISDestroy()` on the returned `IS` to deallocate.
3104 
3105 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexRestoreConeRecursive()`,
3106           `DMPlexGetDepth()`, `IS`
3107 @*/
3108 PetscErrorCode DMPlexGetConeRecursiveVertices(DM dm, IS points, IS *expandedPoints)
3109 {
3110   IS      *expandedPointsAll;
3111   PetscInt depth;
3112 
3113   PetscFunctionBegin;
3114   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3115   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
3116   PetscAssertPointer(expandedPoints, 3);
3117   PetscCall(DMPlexGetConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
3118   *expandedPoints = expandedPointsAll[0];
3119   PetscCall(PetscObjectReference((PetscObject)expandedPointsAll[0]));
3120   PetscCall(DMPlexRestoreConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
3121   PetscFunctionReturn(PETSC_SUCCESS);
3122 }
3123 
3124 /*@
3125   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).
3126 
3127   Not Collective
3128 
3129   Input Parameters:
3130 + dm     - The `DMPLEX`
3131 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3132 
3133   Output Parameters:
3134 + depth          - (optional) Size of the output arrays, equal to `DMPLEX` depth, returned by `DMPlexGetDepth()`
3135 . expandedPoints - (optional) An array of index sets with recursively expanded cones
3136 - sections       - (optional) An array of sections which describe mappings from points to their cone points
3137 
3138   Level: advanced
3139 
3140   Notes:
3141   Like `DMPlexGetConeTuple()` but recursive.
3142 
3143   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.
3144   For example, for d=0 it contains only vertices, for d=1 it can contain vertices and edges, etc.
3145 
3146   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\:
3147   (1) DAG points in `expandedPoints`[d+1] with `depth` d+1 to their cone points in `expandedPoints`[d];
3148   (2) DAG points in `expandedPoints`[d+1] with `depth` in [0,d] to the same points in `expandedPoints`[d].
3149 
3150 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexRestoreConeRecursive()`, `DMPlexGetConeRecursiveVertices()`,
3151           `DMPlexGetDepth()`, `PetscSection`, `IS`
3152 @*/
3153 PetscErrorCode DMPlexGetConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
3154 {
3155   const PetscInt *arr0 = NULL, *cone = NULL;
3156   PetscInt       *arr = NULL, *newarr = NULL;
3157   PetscInt        d, depth_, i, n, newn, cn, co, start, end;
3158   IS             *expandedPoints_;
3159   PetscSection   *sections_;
3160 
3161   PetscFunctionBegin;
3162   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3163   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
3164   if (depth) PetscAssertPointer(depth, 3);
3165   if (expandedPoints) PetscAssertPointer(expandedPoints, 4);
3166   if (sections) PetscAssertPointer(sections, 5);
3167   PetscCall(ISGetLocalSize(points, &n));
3168   PetscCall(ISGetIndices(points, &arr0));
3169   PetscCall(DMPlexGetDepth(dm, &depth_));
3170   PetscCall(PetscCalloc1(depth_, &expandedPoints_));
3171   PetscCall(PetscCalloc1(depth_, &sections_));
3172   arr = (PetscInt *)arr0; /* this is ok because first generation of arr is not modified */
3173   for (d = depth_ - 1; d >= 0; d--) {
3174     PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &sections_[d]));
3175     PetscCall(PetscSectionSetChart(sections_[d], 0, n));
3176     for (i = 0; i < n; i++) {
3177       PetscCall(DMPlexGetDepthStratum(dm, d + 1, &start, &end));
3178       if (arr[i] >= start && arr[i] < end) {
3179         PetscCall(DMPlexGetConeSize(dm, arr[i], &cn));
3180         PetscCall(PetscSectionSetDof(sections_[d], i, cn));
3181       } else {
3182         PetscCall(PetscSectionSetDof(sections_[d], i, 1));
3183       }
3184     }
3185     PetscCall(PetscSectionSetUp(sections_[d]));
3186     PetscCall(PetscSectionGetStorageSize(sections_[d], &newn));
3187     PetscCall(PetscMalloc1(newn, &newarr));
3188     for (i = 0; i < n; i++) {
3189       PetscCall(PetscSectionGetDof(sections_[d], i, &cn));
3190       PetscCall(PetscSectionGetOffset(sections_[d], i, &co));
3191       if (cn > 1) {
3192         PetscCall(DMPlexGetCone(dm, arr[i], &cone));
3193         PetscCall(PetscMemcpy(&newarr[co], cone, cn * sizeof(PetscInt)));
3194       } else {
3195         newarr[co] = arr[i];
3196       }
3197     }
3198     PetscCall(ISCreateGeneral(PETSC_COMM_SELF, newn, newarr, PETSC_OWN_POINTER, &expandedPoints_[d]));
3199     arr = newarr;
3200     n   = newn;
3201   }
3202   PetscCall(ISRestoreIndices(points, &arr0));
3203   *depth = depth_;
3204   if (expandedPoints) *expandedPoints = expandedPoints_;
3205   else {
3206     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&expandedPoints_[d]));
3207     PetscCall(PetscFree(expandedPoints_));
3208   }
3209   if (sections) *sections = sections_;
3210   else {
3211     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&sections_[d]));
3212     PetscCall(PetscFree(sections_));
3213   }
3214   PetscFunctionReturn(PETSC_SUCCESS);
3215 }
3216 
3217 /*@
3218   DMPlexRestoreConeRecursive - Deallocates arrays created by `DMPlexGetConeRecursive()`
3219 
3220   Not Collective
3221 
3222   Input Parameters:
3223 + dm     - The `DMPLEX`
3224 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3225 
3226   Output Parameters:
3227 + depth          - (optional) Size of the output arrays, equal to `DMPLEX` depth, returned by `DMPlexGetDepth()`
3228 . expandedPoints - (optional) An array of recursively expanded cones
3229 - sections       - (optional) An array of sections which describe mappings from points to their cone points
3230 
3231   Level: advanced
3232 
3233   Note:
3234   See `DMPlexGetConeRecursive()`
3235 
3236 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexGetConeRecursiveVertices()`,
3237           `DMPlexGetDepth()`, `IS`, `PetscSection`
3238 @*/
3239 PetscErrorCode DMPlexRestoreConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
3240 {
3241   PetscInt d, depth_;
3242 
3243   PetscFunctionBegin;
3244   PetscCall(DMPlexGetDepth(dm, &depth_));
3245   PetscCheck(!depth || *depth == depth_, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "depth changed since last call to DMPlexGetConeRecursive");
3246   if (depth) *depth = 0;
3247   if (expandedPoints) {
3248     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&((*expandedPoints)[d])));
3249     PetscCall(PetscFree(*expandedPoints));
3250   }
3251   if (sections) {
3252     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&((*sections)[d])));
3253     PetscCall(PetscFree(*sections));
3254   }
3255   PetscFunctionReturn(PETSC_SUCCESS);
3256 }
3257 
3258 /*@
3259   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
3260 
3261   Not Collective
3262 
3263   Input Parameters:
3264 + dm   - The `DMPLEX`
3265 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3266 - cone - An array of points which are on the in-edges for point `p`
3267 
3268   Level: beginner
3269 
3270   Note:
3271   This should be called after all calls to `DMPlexSetConeSize()` and `DMSetUp()`.
3272 
3273 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`, `DMPlexSetSupport()`, `DMPlexSetSupportSize()`
3274 @*/
3275 PetscErrorCode DMPlexSetCone(DM dm, PetscInt p, const PetscInt cone[])
3276 {
3277   DM_Plex *mesh = (DM_Plex *)dm->data;
3278   PetscInt dof, off, c;
3279 
3280   PetscFunctionBegin;
3281   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3282   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3283   if (dof) PetscAssertPointer(cone, 3);
3284   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3285   if (PetscDefined(USE_DEBUG)) {
3286     PetscInt pStart, pEnd;
3287     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3288     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);
3289     for (c = 0; c < dof; ++c) {
3290       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);
3291       mesh->cones[off + c] = cone[c];
3292     }
3293   } else {
3294     for (c = 0; c < dof; ++c) mesh->cones[off + c] = cone[c];
3295   }
3296   PetscFunctionReturn(PETSC_SUCCESS);
3297 }
3298 
3299 /*@C
3300   DMPlexGetConeOrientation - Return the orientations on the in-edges for this point in the DAG
3301 
3302   Not Collective
3303 
3304   Input Parameters:
3305 + dm - The `DMPLEX`
3306 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3307 
3308   Output Parameter:
3309 . coneOrientation - An array of orientations which are on the in-edges for point `p`. An orientation is an
3310                     integer giving the prescription for cone traversal.
3311 
3312   Level: beginner
3313 
3314   Note:
3315   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3316   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3317   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3318   with the identity.
3319 
3320   Fortran Notes:
3321   You must also call `DMPlexRestoreConeOrientation()` after you finish using the returned array.
3322   `DMPlexRestoreConeOrientation()` is not needed/available in C.
3323 
3324 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPolytopeTypeComposeOrientation()`, `DMPolytopeTypeComposeOrientationInv()`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetCone()`, `DMPlexSetChart()`
3325 @*/
3326 PetscErrorCode DMPlexGetConeOrientation(DM dm, PetscInt p, const PetscInt *coneOrientation[])
3327 {
3328   DM_Plex *mesh = (DM_Plex *)dm->data;
3329   PetscInt off;
3330 
3331   PetscFunctionBegin;
3332   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3333   if (PetscDefined(USE_DEBUG)) {
3334     PetscInt dof;
3335     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3336     if (dof) PetscAssertPointer(coneOrientation, 3);
3337   }
3338   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3339 
3340   *coneOrientation = &mesh->coneOrientations[off];
3341   PetscFunctionReturn(PETSC_SUCCESS);
3342 }
3343 
3344 /*@
3345   DMPlexSetConeOrientation - Set the orientations on the in-edges for this point in the DAG
3346 
3347   Not Collective
3348 
3349   Input Parameters:
3350 + dm              - The `DMPLEX`
3351 . p               - The point, which must lie in the chart set with `DMPlexSetChart()`
3352 - coneOrientation - An array of orientations
3353 
3354   Level: beginner
3355 
3356   Notes:
3357   This should be called after all calls to `DMPlexSetConeSize()` and `DMSetUp()`.
3358 
3359   The meaning of coneOrientation is detailed in `DMPlexGetConeOrientation()`.
3360 
3361 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetConeOrientation()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3362 @*/
3363 PetscErrorCode DMPlexSetConeOrientation(DM dm, PetscInt p, const PetscInt coneOrientation[])
3364 {
3365   DM_Plex *mesh = (DM_Plex *)dm->data;
3366   PetscInt pStart, pEnd;
3367   PetscInt dof, off, c;
3368 
3369   PetscFunctionBegin;
3370   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3371   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3372   if (dof) PetscAssertPointer(coneOrientation, 3);
3373   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3374   if (PetscDefined(USE_DEBUG)) {
3375     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3376     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);
3377     for (c = 0; c < dof; ++c) {
3378       PetscInt cdof, o = coneOrientation[c];
3379 
3380       PetscCall(PetscSectionGetDof(mesh->coneSection, mesh->cones[off + c], &cdof));
3381       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);
3382       mesh->coneOrientations[off + c] = o;
3383     }
3384   } else {
3385     for (c = 0; c < dof; ++c) mesh->coneOrientations[off + c] = coneOrientation[c];
3386   }
3387   PetscFunctionReturn(PETSC_SUCCESS);
3388 }
3389 
3390 /*@
3391   DMPlexInsertCone - Insert a point into the in-edges for the point p in the DAG
3392 
3393   Not Collective
3394 
3395   Input Parameters:
3396 + dm        - The `DMPLEX`
3397 . p         - The point, which must lie in the chart set with `DMPlexSetChart()`
3398 . conePos   - The local index in the cone where the point should be put
3399 - conePoint - The mesh point to insert
3400 
3401   Level: beginner
3402 
3403 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3404 @*/
3405 PetscErrorCode DMPlexInsertCone(DM dm, PetscInt p, PetscInt conePos, PetscInt conePoint)
3406 {
3407   DM_Plex *mesh = (DM_Plex *)dm->data;
3408   PetscInt pStart, pEnd;
3409   PetscInt dof, off;
3410 
3411   PetscFunctionBegin;
3412   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3413   if (PetscDefined(USE_DEBUG)) {
3414     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3415     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);
3416     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);
3417     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3418     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);
3419   }
3420   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3421   mesh->cones[off + conePos] = conePoint;
3422   PetscFunctionReturn(PETSC_SUCCESS);
3423 }
3424 
3425 /*@
3426   DMPlexInsertConeOrientation - Insert a point orientation for the in-edge for the point p in the DAG
3427 
3428   Not Collective
3429 
3430   Input Parameters:
3431 + dm              - The `DMPLEX`
3432 . p               - The point, which must lie in the chart set with `DMPlexSetChart()`
3433 . conePos         - The local index in the cone where the point should be put
3434 - coneOrientation - The point orientation to insert
3435 
3436   Level: beginner
3437 
3438   Note:
3439   The meaning of coneOrientation values is detailed in `DMPlexGetConeOrientation()`.
3440 
3441 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3442 @*/
3443 PetscErrorCode DMPlexInsertConeOrientation(DM dm, PetscInt p, PetscInt conePos, PetscInt coneOrientation)
3444 {
3445   DM_Plex *mesh = (DM_Plex *)dm->data;
3446   PetscInt pStart, pEnd;
3447   PetscInt dof, off;
3448 
3449   PetscFunctionBegin;
3450   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3451   if (PetscDefined(USE_DEBUG)) {
3452     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3453     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);
3454     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3455     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);
3456   }
3457   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3458   mesh->coneOrientations[off + conePos] = coneOrientation;
3459   PetscFunctionReturn(PETSC_SUCCESS);
3460 }
3461 
3462 /*@C
3463   DMPlexGetOrientedCone - Return the points and orientations on the in-edges for this point in the DAG
3464 
3465   Not collective
3466 
3467   Input Parameters:
3468 + dm - The DMPlex
3469 - p  - The point, which must lie in the chart set with DMPlexSetChart()
3470 
3471   Output Parameters:
3472 + cone - An array of points which are on the in-edges for point `p`
3473 - ornt - An array of orientations which are on the in-edges for point `p`. An orientation is an
3474         integer giving the prescription for cone traversal.
3475 
3476   Level: beginner
3477 
3478   Notes:
3479   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3480   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3481   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3482   with the identity.
3483 
3484   Fortran Notes:
3485   You must also call `DMPlexRestoreCone()` after you finish using the returned array.
3486   `DMPlexRestoreCone()` is not needed/available in C.
3487 
3488 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreOrientedCone()`, `DMPlexGetConeSize()`, `DMPlexGetCone()`, `DMPlexGetChart()`
3489 @*/
3490 PetscErrorCode DMPlexGetOrientedCone(DM dm, PetscInt p, const PetscInt *cone[], const PetscInt *ornt[])
3491 {
3492   DM_Plex *mesh = (DM_Plex *)dm->data;
3493 
3494   PetscFunctionBegin;
3495   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3496   if (mesh->tr) {
3497     PetscCall(DMPlexTransformGetCone(mesh->tr, p, cone, ornt));
3498   } else {
3499     PetscInt off;
3500     if (PetscDefined(USE_DEBUG)) {
3501       PetscInt dof;
3502       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3503       if (dof) {
3504         if (cone) PetscAssertPointer(cone, 3);
3505         if (ornt) PetscAssertPointer(ornt, 4);
3506       }
3507     }
3508     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3509     if (cone) *cone = PetscSafePointerPlusOffset(mesh->cones, off);
3510     if (ornt) *ornt = PetscSafePointerPlusOffset(mesh->coneOrientations, off);
3511   }
3512   PetscFunctionReturn(PETSC_SUCCESS);
3513 }
3514 
3515 /*@C
3516   DMPlexRestoreOrientedCone - Restore the points and orientations on the in-edges for this point in the DAG
3517 
3518   Not Collective
3519 
3520   Input Parameters:
3521 + dm   - The DMPlex
3522 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3523 . cone - An array of points which are on the in-edges for point p
3524 - ornt - An array of orientations which are on the in-edges for point `p`. An orientation is an
3525         integer giving the prescription for cone traversal.
3526 
3527   Level: beginner
3528 
3529   Notes:
3530   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3531   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3532   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3533   with the identity.
3534 
3535   Fortran Notes:
3536   You must also call `DMPlexRestoreCone()` after you finish using the returned array.
3537   `DMPlexRestoreCone()` is not needed/available in C.
3538 
3539 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetOrientedCone()`, `DMPlexGetConeSize()`, `DMPlexGetCone()`, `DMPlexGetChart()`
3540 @*/
3541 PetscErrorCode DMPlexRestoreOrientedCone(DM dm, PetscInt p, const PetscInt *cone[], const PetscInt *ornt[])
3542 {
3543   DM_Plex *mesh = (DM_Plex *)dm->data;
3544 
3545   PetscFunctionBegin;
3546   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3547   if (mesh->tr) PetscCall(DMPlexTransformRestoreCone(mesh->tr, p, cone, ornt));
3548   PetscFunctionReturn(PETSC_SUCCESS);
3549 }
3550 
3551 /*@
3552   DMPlexGetSupportSize - Return the number of out-edges for this point in the DAG
3553 
3554   Not Collective
3555 
3556   Input Parameters:
3557 + dm - The `DMPLEX`
3558 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3559 
3560   Output Parameter:
3561 . size - The support size for point `p`
3562 
3563   Level: beginner
3564 
3565 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`, `DMPlexGetConeSize()`
3566 @*/
3567 PetscErrorCode DMPlexGetSupportSize(DM dm, PetscInt p, PetscInt *size)
3568 {
3569   DM_Plex *mesh = (DM_Plex *)dm->data;
3570 
3571   PetscFunctionBegin;
3572   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3573   PetscAssertPointer(size, 3);
3574   PetscCall(PetscSectionGetDof(mesh->supportSection, p, size));
3575   PetscFunctionReturn(PETSC_SUCCESS);
3576 }
3577 
3578 /*@
3579   DMPlexSetSupportSize - Set the number of out-edges for this point in the DAG
3580 
3581   Not Collective
3582 
3583   Input Parameters:
3584 + dm   - The `DMPLEX`
3585 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3586 - size - The support size for point `p`
3587 
3588   Level: beginner
3589 
3590   Note:
3591   This should be called after `DMPlexSetChart()`.
3592 
3593 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetSupportSize()`, `DMPlexSetChart()`
3594 @*/
3595 PetscErrorCode DMPlexSetSupportSize(DM dm, PetscInt p, PetscInt size)
3596 {
3597   DM_Plex *mesh = (DM_Plex *)dm->data;
3598 
3599   PetscFunctionBegin;
3600   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3601   PetscCall(PetscSectionSetDof(mesh->supportSection, p, size));
3602   PetscFunctionReturn(PETSC_SUCCESS);
3603 }
3604 
3605 /*@C
3606   DMPlexGetSupport - Return the points on the out-edges for this point in the DAG
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 
3614   Output Parameter:
3615 . support - An array of points which are on the out-edges for point `p`
3616 
3617   Level: beginner
3618 
3619   Fortran Notes:
3620   You must also call `DMPlexRestoreSupport()` after you finish using the returned array.
3621   `DMPlexRestoreSupport()` is not needed/available in C.
3622 
3623 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSupportSize()`, `DMPlexSetSupport()`, `DMPlexGetCone()`, `DMPlexSetChart()`
3624 @*/
3625 PetscErrorCode DMPlexGetSupport(DM dm, PetscInt p, const PetscInt *support[])
3626 {
3627   DM_Plex *mesh = (DM_Plex *)dm->data;
3628   PetscInt off;
3629 
3630   PetscFunctionBegin;
3631   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3632   PetscAssertPointer(support, 3);
3633   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3634   *support = PetscSafePointerPlusOffset(mesh->supports, off);
3635   PetscFunctionReturn(PETSC_SUCCESS);
3636 }
3637 
3638 /*@
3639   DMPlexSetSupport - Set the points on the out-edges for this point in the DAG, that is the list of points that this point covers
3640 
3641   Not Collective
3642 
3643   Input Parameters:
3644 + dm      - The `DMPLEX`
3645 . p       - The point, which must lie in the chart set with `DMPlexSetChart()`
3646 - support - An array of points which are on the out-edges for point `p`
3647 
3648   Level: beginner
3649 
3650   Note:
3651   This should be called after all calls to `DMPlexSetSupportSize()` and `DMSetUp()`.
3652 
3653 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetCone()`, `DMPlexSetConeSize()`, `DMPlexCreate()`, `DMPlexGetSupport()`, `DMPlexSetChart()`, `DMPlexSetSupportSize()`, `DMSetUp()`
3654 @*/
3655 PetscErrorCode DMPlexSetSupport(DM dm, PetscInt p, const PetscInt support[])
3656 {
3657   DM_Plex *mesh = (DM_Plex *)dm->data;
3658   PetscInt pStart, pEnd;
3659   PetscInt dof, off, c;
3660 
3661   PetscFunctionBegin;
3662   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3663   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3664   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3665   if (dof) PetscAssertPointer(support, 3);
3666   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3667   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);
3668   for (c = 0; c < dof; ++c) {
3669     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);
3670     mesh->supports[off + c] = support[c];
3671   }
3672   PetscFunctionReturn(PETSC_SUCCESS);
3673 }
3674 
3675 /*@
3676   DMPlexInsertSupport - Insert a point into the out-edges for the point p in the DAG
3677 
3678   Not Collective
3679 
3680   Input Parameters:
3681 + dm           - The `DMPLEX`
3682 . p            - The point, which must lie in the chart set with `DMPlexSetChart()`
3683 . supportPos   - The local index in the cone where the point should be put
3684 - supportPoint - The mesh point to insert
3685 
3686   Level: beginner
3687 
3688 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3689 @*/
3690 PetscErrorCode DMPlexInsertSupport(DM dm, PetscInt p, PetscInt supportPos, PetscInt supportPoint)
3691 {
3692   DM_Plex *mesh = (DM_Plex *)dm->data;
3693   PetscInt pStart, pEnd;
3694   PetscInt dof, off;
3695 
3696   PetscFunctionBegin;
3697   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3698   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3699   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3700   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3701   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);
3702   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);
3703   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);
3704   mesh->supports[off + supportPos] = supportPoint;
3705   PetscFunctionReturn(PETSC_SUCCESS);
3706 }
3707 
3708 /* Converts an orientation o in the current numbering to the previous scheme used in Plex */
3709 PetscInt DMPolytopeConvertNewOrientation_Internal(DMPolytopeType ct, PetscInt o)
3710 {
3711   switch (ct) {
3712   case DM_POLYTOPE_SEGMENT:
3713     if (o == -1) return -2;
3714     break;
3715   case DM_POLYTOPE_TRIANGLE:
3716     if (o == -3) return -1;
3717     if (o == -2) return -3;
3718     if (o == -1) return -2;
3719     break;
3720   case DM_POLYTOPE_QUADRILATERAL:
3721     if (o == -4) return -2;
3722     if (o == -3) return -1;
3723     if (o == -2) return -4;
3724     if (o == -1) return -3;
3725     break;
3726   default:
3727     return o;
3728   }
3729   return o;
3730 }
3731 
3732 /* Converts an orientation o in the previous scheme used in Plex to the current numbering */
3733 PetscInt DMPolytopeConvertOldOrientation_Internal(DMPolytopeType ct, PetscInt o)
3734 {
3735   switch (ct) {
3736   case DM_POLYTOPE_SEGMENT:
3737     if ((o == -2) || (o == 1)) return -1;
3738     if (o == -1) return 0;
3739     break;
3740   case DM_POLYTOPE_TRIANGLE:
3741     if (o == -3) return -2;
3742     if (o == -2) return -1;
3743     if (o == -1) return -3;
3744     break;
3745   case DM_POLYTOPE_QUADRILATERAL:
3746     if (o == -4) return -2;
3747     if (o == -3) return -1;
3748     if (o == -2) return -4;
3749     if (o == -1) return -3;
3750     break;
3751   default:
3752     return o;
3753   }
3754   return o;
3755 }
3756 
3757 /* Takes in a mesh whose orientations are in the previous scheme and converts them all to the current numbering */
3758 PetscErrorCode DMPlexConvertOldOrientations_Internal(DM dm)
3759 {
3760   PetscInt pStart, pEnd, p;
3761 
3762   PetscFunctionBegin;
3763   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3764   for (p = pStart; p < pEnd; ++p) {
3765     const PetscInt *cone, *ornt;
3766     PetscInt        coneSize, c;
3767 
3768     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
3769     PetscCall(DMPlexGetCone(dm, p, &cone));
3770     PetscCall(DMPlexGetConeOrientation(dm, p, &ornt));
3771     for (c = 0; c < coneSize; ++c) {
3772       DMPolytopeType ct;
3773       const PetscInt o = ornt[c];
3774 
3775       PetscCall(DMPlexGetCellType(dm, cone[c], &ct));
3776       switch (ct) {
3777       case DM_POLYTOPE_SEGMENT:
3778         if ((o == -2) || (o == 1)) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3779         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, 0));
3780         break;
3781       case DM_POLYTOPE_TRIANGLE:
3782         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3783         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3784         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3785         break;
3786       case DM_POLYTOPE_QUADRILATERAL:
3787         if (o == -4) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3788         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3789         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -4));
3790         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3791         break;
3792       default:
3793         break;
3794       }
3795     }
3796   }
3797   PetscFunctionReturn(PETSC_SUCCESS);
3798 }
3799 
3800 static inline PetscErrorCode DMPlexGetTransitiveClosure_Hot_Private(DM dm, PetscInt p, PetscBool useCone, PetscInt *size, const PetscInt *arr[], const PetscInt *ornt[])
3801 {
3802   DM_Plex *mesh = (DM_Plex *)dm->data;
3803 
3804   PetscFunctionBeginHot;
3805   if (PetscDefined(USE_DEBUG) || mesh->tr) {
3806     if (useCone) {
3807       PetscCall(DMPlexGetConeSize(dm, p, size));
3808       PetscCall(DMPlexGetOrientedCone(dm, p, arr, ornt));
3809     } else {
3810       PetscCall(DMPlexGetSupportSize(dm, p, size));
3811       PetscCall(DMPlexGetSupport(dm, p, arr));
3812     }
3813   } else {
3814     if (useCone) {
3815       const PetscSection s   = mesh->coneSection;
3816       const PetscInt     ps  = p - s->pStart;
3817       const PetscInt     off = s->atlasOff[ps];
3818 
3819       *size = s->atlasDof[ps];
3820       *arr  = mesh->cones + off;
3821       *ornt = mesh->coneOrientations + off;
3822     } else {
3823       const PetscSection s   = mesh->supportSection;
3824       const PetscInt     ps  = p - s->pStart;
3825       const PetscInt     off = s->atlasOff[ps];
3826 
3827       *size = s->atlasDof[ps];
3828       *arr  = mesh->supports + off;
3829     }
3830   }
3831   PetscFunctionReturn(PETSC_SUCCESS);
3832 }
3833 
3834 static inline PetscErrorCode DMPlexRestoreTransitiveClosure_Hot_Private(DM dm, PetscInt p, PetscBool useCone, PetscInt *size, const PetscInt *arr[], const PetscInt *ornt[])
3835 {
3836   DM_Plex *mesh = (DM_Plex *)dm->data;
3837 
3838   PetscFunctionBeginHot;
3839   if (PetscDefined(USE_DEBUG) || mesh->tr) {
3840     if (useCone) PetscCall(DMPlexRestoreOrientedCone(dm, p, arr, ornt));
3841   }
3842   PetscFunctionReturn(PETSC_SUCCESS);
3843 }
3844 
3845 static PetscErrorCode DMPlexGetTransitiveClosure_Depth1_Private(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3846 {
3847   DMPolytopeType  ct = DM_POLYTOPE_UNKNOWN;
3848   PetscInt       *closure;
3849   const PetscInt *tmp = NULL, *tmpO = NULL;
3850   PetscInt        off = 0, tmpSize, t;
3851 
3852   PetscFunctionBeginHot;
3853   if (ornt) {
3854     PetscCall(DMPlexGetCellType(dm, p, &ct));
3855     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;
3856   }
3857   if (*points) {
3858     closure = *points;
3859   } else {
3860     PetscInt maxConeSize, maxSupportSize;
3861     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3862     PetscCall(DMGetWorkArray(dm, 2 * (PetscMax(maxConeSize, maxSupportSize) + 1), MPIU_INT, &closure));
3863   }
3864   PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, p, useCone, &tmpSize, &tmp, &tmpO));
3865   if (ct == DM_POLYTOPE_UNKNOWN) {
3866     closure[off++] = p;
3867     closure[off++] = 0;
3868     for (t = 0; t < tmpSize; ++t) {
3869       closure[off++] = tmp[t];
3870       closure[off++] = tmpO ? tmpO[t] : 0;
3871     }
3872   } else {
3873     const PetscInt *arr = DMPolytopeTypeGetArrangement(ct, ornt);
3874 
3875     /* We assume that cells with a valid type have faces with a valid type */
3876     closure[off++] = p;
3877     closure[off++] = ornt;
3878     for (t = 0; t < tmpSize; ++t) {
3879       DMPolytopeType ft;
3880 
3881       PetscCall(DMPlexGetCellType(dm, tmp[t], &ft));
3882       closure[off++] = tmp[arr[t]];
3883       closure[off++] = tmpO ? DMPolytopeTypeComposeOrientation(ft, ornt, tmpO[t]) : 0;
3884     }
3885   }
3886   PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, p, useCone, &tmpSize, &tmp, &tmpO));
3887   if (numPoints) *numPoints = tmpSize + 1;
3888   if (points) *points = closure;
3889   PetscFunctionReturn(PETSC_SUCCESS);
3890 }
3891 
3892 /* We need a special tensor version because we want to allow duplicate points in the endcaps for hybrid cells */
3893 static PetscErrorCode DMPlexTransitiveClosure_Tensor_Internal(DM dm, PetscInt point, DMPolytopeType ct, PetscInt o, PetscBool useCone, PetscInt *numPoints, PetscInt **points)
3894 {
3895   const PetscInt *arr = DMPolytopeTypeGetArrangement(ct, o);
3896   const PetscInt *cone, *ornt;
3897   PetscInt       *pts, *closure = NULL;
3898   DMPolytopeType  ft;
3899   PetscInt        maxConeSize, maxSupportSize, coneSeries, supportSeries, maxSize;
3900   PetscInt        dim, coneSize, c, d, clSize, cl;
3901 
3902   PetscFunctionBeginHot;
3903   PetscCall(DMGetDimension(dm, &dim));
3904   PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, point, PETSC_TRUE, &coneSize, &cone, &ornt));
3905   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3906   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, dim + 1) - 1) / (maxConeSize - 1)) : dim + 1;
3907   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, dim + 1) - 1) / (maxSupportSize - 1)) : dim + 1;
3908   maxSize       = PetscMax(coneSeries, supportSeries);
3909   if (*points) {
3910     pts = *points;
3911   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &pts));
3912   c        = 0;
3913   pts[c++] = point;
3914   pts[c++] = o;
3915   PetscCall(DMPlexGetCellType(dm, cone[arr[0 * 2 + 0]], &ft));
3916   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[0 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[0 * 2 + 1], ornt[0]), useCone, &clSize, &closure));
3917   for (cl = 0; cl < clSize * 2; cl += 2) {
3918     pts[c++] = closure[cl];
3919     pts[c++] = closure[cl + 1];
3920   }
3921   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[1 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[1 * 2 + 1], ornt[1]), useCone, &clSize, &closure));
3922   for (cl = 0; cl < clSize * 2; cl += 2) {
3923     pts[c++] = closure[cl];
3924     pts[c++] = closure[cl + 1];
3925   }
3926   PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[0], useCone, &clSize, &closure));
3927   for (d = 2; d < coneSize; ++d) {
3928     PetscCall(DMPlexGetCellType(dm, cone[arr[d * 2 + 0]], &ft));
3929     pts[c++] = cone[arr[d * 2 + 0]];
3930     pts[c++] = DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]);
3931   }
3932   PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, point, PETSC_TRUE, &coneSize, &cone, &ornt));
3933   if (dim >= 3) {
3934     for (d = 2; d < coneSize; ++d) {
3935       const PetscInt  fpoint = cone[arr[d * 2 + 0]];
3936       const PetscInt *fcone, *fornt;
3937       PetscInt        fconeSize, fc, i;
3938 
3939       PetscCall(DMPlexGetCellType(dm, fpoint, &ft));
3940       const PetscInt *farr = DMPolytopeTypeGetArrangement(ft, DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]));
3941       PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, fpoint, PETSC_TRUE, &fconeSize, &fcone, &fornt));
3942       for (fc = 0; fc < fconeSize; ++fc) {
3943         const PetscInt cp = fcone[farr[fc * 2 + 0]];
3944         const PetscInt co = farr[fc * 2 + 1];
3945 
3946         for (i = 0; i < c; i += 2)
3947           if (pts[i] == cp) break;
3948         if (i == c) {
3949           PetscCall(DMPlexGetCellType(dm, cp, &ft));
3950           pts[c++] = cp;
3951           pts[c++] = DMPolytopeTypeComposeOrientation(ft, co, fornt[farr[fc * 2 + 0]]);
3952         }
3953       }
3954       PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, fpoint, PETSC_TRUE, &fconeSize, &fcone, &fornt));
3955     }
3956   }
3957   *numPoints = c / 2;
3958   *points    = pts;
3959   PetscFunctionReturn(PETSC_SUCCESS);
3960 }
3961 
3962 PetscErrorCode DMPlexGetTransitiveClosure_Internal(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3963 {
3964   DMPolytopeType ct;
3965   PetscInt      *closure, *fifo;
3966   PetscInt       closureSize = 0, fifoStart = 0, fifoSize = 0;
3967   PetscInt       maxConeSize, maxSupportSize, coneSeries, supportSeries;
3968   PetscInt       depth, maxSize;
3969 
3970   PetscFunctionBeginHot;
3971   PetscCall(DMPlexGetDepth(dm, &depth));
3972   if (depth == 1) {
3973     PetscCall(DMPlexGetTransitiveClosure_Depth1_Private(dm, p, ornt, useCone, numPoints, points));
3974     PetscFunctionReturn(PETSC_SUCCESS);
3975   }
3976   PetscCall(DMPlexGetCellType(dm, p, &ct));
3977   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;
3978   if (DMPolytopeTypeIsHybrid(ct) && ct != DM_POLYTOPE_POINT_PRISM_TENSOR) {
3979     PetscCall(DMPlexTransitiveClosure_Tensor_Internal(dm, p, ct, ornt, useCone, numPoints, points));
3980     PetscFunctionReturn(PETSC_SUCCESS);
3981   }
3982   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3983   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, depth + 1) - 1) / (maxConeSize - 1)) : depth + 1;
3984   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, depth + 1) - 1) / (maxSupportSize - 1)) : depth + 1;
3985   maxSize       = PetscMax(coneSeries, supportSeries);
3986   PetscCall(DMGetWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
3987   if (*points) {
3988     closure = *points;
3989   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &closure));
3990   closure[closureSize++] = p;
3991   closure[closureSize++] = ornt;
3992   fifo[fifoSize++]       = p;
3993   fifo[fifoSize++]       = ornt;
3994   fifo[fifoSize++]       = ct;
3995   /* Should kick out early when depth is reached, rather than checking all vertices for empty cones */
3996   while (fifoSize - fifoStart) {
3997     const PetscInt       q    = fifo[fifoStart++];
3998     const PetscInt       o    = fifo[fifoStart++];
3999     const DMPolytopeType qt   = (DMPolytopeType)fifo[fifoStart++];
4000     const PetscInt      *qarr = DMPolytopeTypeGetArrangement(qt, o);
4001     const PetscInt      *tmp, *tmpO = NULL;
4002     PetscInt             tmpSize, t;
4003 
4004     if (PetscDefined(USE_DEBUG)) {
4005       PetscInt nO = DMPolytopeTypeGetNumArrangements(qt) / 2;
4006       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);
4007     }
4008     PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, q, useCone, &tmpSize, &tmp, &tmpO));
4009     for (t = 0; t < tmpSize; ++t) {
4010       const PetscInt ip = useCone && qarr ? qarr[t * 2] : t;
4011       const PetscInt io = useCone && qarr ? qarr[t * 2 + 1] : 0;
4012       const PetscInt cp = tmp[ip];
4013       PetscCall(DMPlexGetCellType(dm, cp, &ct));
4014       const PetscInt co = tmpO ? DMPolytopeTypeComposeOrientation(ct, io, tmpO[ip]) : 0;
4015       PetscInt       c;
4016 
4017       /* Check for duplicate */
4018       for (c = 0; c < closureSize; c += 2) {
4019         if (closure[c] == cp) break;
4020       }
4021       if (c == closureSize) {
4022         closure[closureSize++] = cp;
4023         closure[closureSize++] = co;
4024         fifo[fifoSize++]       = cp;
4025         fifo[fifoSize++]       = co;
4026         fifo[fifoSize++]       = ct;
4027       }
4028     }
4029     PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, q, useCone, &tmpSize, &tmp, &tmpO));
4030   }
4031   PetscCall(DMRestoreWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
4032   if (numPoints) *numPoints = closureSize / 2;
4033   if (points) *points = closure;
4034   PetscFunctionReturn(PETSC_SUCCESS);
4035 }
4036 
4037 /*@C
4038   DMPlexGetTransitiveClosure - Return the points on the transitive closure of the in-edges or out-edges for this point in the DAG
4039 
4040   Not Collective
4041 
4042   Input Parameters:
4043 + dm      - The `DMPLEX`
4044 . p       - The mesh point
4045 - useCone - `PETSC_TRUE` for the closure, otherwise return the star
4046 
4047   Input/Output Parameter:
4048 . points - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...];
4049            if `NULL` on input, internal storage will be returned, otherwise the provided array is used
4050 
4051   Output Parameter:
4052 . numPoints - The number of points in the closure, so points[] is of size 2*`numPoints`
4053 
4054   Level: beginner
4055 
4056   Note:
4057   If using internal storage (points is `NULL` on input), each call overwrites the last output.
4058 
4059   Fortran Notes:
4060   The `numPoints` argument is not present in the Fortran binding since it is internal to the array.
4061 
4062 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
4063 @*/
4064 PetscErrorCode DMPlexGetTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
4065 {
4066   PetscFunctionBeginHot;
4067   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4068   if (numPoints) PetscAssertPointer(numPoints, 4);
4069   if (points) PetscAssertPointer(points, 5);
4070   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, p, 0, useCone, numPoints, points));
4071   PetscFunctionReturn(PETSC_SUCCESS);
4072 }
4073 
4074 /*@C
4075   DMPlexRestoreTransitiveClosure - Restore the array of points on the transitive closure of the in-edges or out-edges for this point in the DAG
4076 
4077   Not Collective
4078 
4079   Input Parameters:
4080 + dm        - The `DMPLEX`
4081 . p         - The mesh point
4082 . useCone   - `PETSC_TRUE` for the closure, otherwise return the star
4083 . numPoints - The number of points in the closure, so points[] is of size 2*`numPoints`
4084 - points    - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...]
4085 
4086   Level: beginner
4087 
4088   Note:
4089   If not using internal storage (points is not `NULL` on input), this call is unnecessary
4090 
4091 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
4092 @*/
4093 PetscErrorCode DMPlexRestoreTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
4094 {
4095   PetscFunctionBeginHot;
4096   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4097   if (numPoints) *numPoints = 0;
4098   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, points));
4099   PetscFunctionReturn(PETSC_SUCCESS);
4100 }
4101 
4102 /*@
4103   DMPlexGetMaxSizes - Return the maximum number of in-edges (cone) and out-edges (support) for any point in the DAG
4104 
4105   Not Collective
4106 
4107   Input Parameter:
4108 . dm - The `DMPLEX`
4109 
4110   Output Parameters:
4111 + maxConeSize    - The maximum number of in-edges
4112 - maxSupportSize - The maximum number of out-edges
4113 
4114   Level: beginner
4115 
4116 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
4117 @*/
4118 PetscErrorCode DMPlexGetMaxSizes(DM dm, PetscInt *maxConeSize, PetscInt *maxSupportSize)
4119 {
4120   DM_Plex *mesh = (DM_Plex *)dm->data;
4121 
4122   PetscFunctionBegin;
4123   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4124   if (maxConeSize) PetscCall(PetscSectionGetMaxDof(mesh->coneSection, maxConeSize));
4125   if (maxSupportSize) PetscCall(PetscSectionGetMaxDof(mesh->supportSection, maxSupportSize));
4126   PetscFunctionReturn(PETSC_SUCCESS);
4127 }
4128 
4129 PetscErrorCode DMSetUp_Plex(DM dm)
4130 {
4131   DM_Plex *mesh = (DM_Plex *)dm->data;
4132   PetscInt size, maxSupportSize;
4133 
4134   PetscFunctionBegin;
4135   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4136   PetscCall(PetscSectionSetUp(mesh->coneSection));
4137   PetscCall(PetscSectionGetStorageSize(mesh->coneSection, &size));
4138   PetscCall(PetscMalloc1(size, &mesh->cones));
4139   PetscCall(PetscCalloc1(size, &mesh->coneOrientations));
4140   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
4141   if (maxSupportSize) {
4142     PetscCall(PetscSectionSetUp(mesh->supportSection));
4143     PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &size));
4144     PetscCall(PetscMalloc1(size, &mesh->supports));
4145   }
4146   PetscFunctionReturn(PETSC_SUCCESS);
4147 }
4148 
4149 PetscErrorCode DMCreateSubDM_Plex(DM dm, PetscInt numFields, const PetscInt fields[], IS *is, DM *subdm)
4150 {
4151   PetscFunctionBegin;
4152   if (subdm) PetscCall(DMClone(dm, subdm));
4153   PetscCall(DMCreateSectionSubDM(dm, numFields, fields, NULL, NULL, is, subdm));
4154   if (subdm) (*subdm)->useNatural = dm->useNatural;
4155   if (dm->useNatural && dm->sfMigration) {
4156     PetscSF sfNatural;
4157 
4158     (*subdm)->sfMigration = dm->sfMigration;
4159     PetscCall(PetscObjectReference((PetscObject)dm->sfMigration));
4160     PetscCall(DMPlexCreateGlobalToNaturalSF(*subdm, NULL, (*subdm)->sfMigration, &sfNatural));
4161     (*subdm)->sfNatural = sfNatural;
4162   }
4163   PetscFunctionReturn(PETSC_SUCCESS);
4164 }
4165 
4166 PetscErrorCode DMCreateSuperDM_Plex(DM dms[], PetscInt len, IS **is, DM *superdm)
4167 {
4168   PetscInt i = 0;
4169 
4170   PetscFunctionBegin;
4171   PetscCall(DMClone(dms[0], superdm));
4172   PetscCall(DMCreateSectionSuperDM(dms, len, is, superdm));
4173   (*superdm)->useNatural = PETSC_FALSE;
4174   for (i = 0; i < len; i++) {
4175     if (dms[i]->useNatural && dms[i]->sfMigration) {
4176       PetscSF sfNatural;
4177 
4178       (*superdm)->sfMigration = dms[i]->sfMigration;
4179       PetscCall(PetscObjectReference((PetscObject)dms[i]->sfMigration));
4180       (*superdm)->useNatural = PETSC_TRUE;
4181       PetscCall(DMPlexCreateGlobalToNaturalSF(*superdm, NULL, (*superdm)->sfMigration, &sfNatural));
4182       (*superdm)->sfNatural = sfNatural;
4183       break;
4184     }
4185   }
4186   PetscFunctionReturn(PETSC_SUCCESS);
4187 }
4188 
4189 /*@
4190   DMPlexSymmetrize - Create support (out-edge) information from cone (in-edge) information
4191 
4192   Not Collective
4193 
4194   Input Parameter:
4195 . dm - The `DMPLEX`
4196 
4197   Level: beginner
4198 
4199   Note:
4200   This should be called after all calls to `DMPlexSetCone()`
4201 
4202 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMPlexSetCone()`
4203 @*/
4204 PetscErrorCode DMPlexSymmetrize(DM dm)
4205 {
4206   DM_Plex  *mesh = (DM_Plex *)dm->data;
4207   PetscInt *offsets;
4208   PetscInt  supportSize;
4209   PetscInt  pStart, pEnd, p;
4210 
4211   PetscFunctionBegin;
4212   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4213   PetscCheck(!mesh->supports, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONGSTATE, "Supports were already setup in this DMPlex");
4214   PetscCall(PetscLogEventBegin(DMPLEX_Symmetrize, dm, 0, 0, 0));
4215   /* Calculate support sizes */
4216   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4217   for (p = pStart; p < pEnd; ++p) {
4218     PetscInt dof, off, c;
4219 
4220     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
4221     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
4222     for (c = off; c < off + dof; ++c) PetscCall(PetscSectionAddDof(mesh->supportSection, mesh->cones[c], 1));
4223   }
4224   PetscCall(PetscSectionSetUp(mesh->supportSection));
4225   /* Calculate supports */
4226   PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &supportSize));
4227   PetscCall(PetscMalloc1(supportSize, &mesh->supports));
4228   PetscCall(PetscCalloc1(pEnd - pStart, &offsets));
4229   for (p = pStart; p < pEnd; ++p) {
4230     PetscInt dof, off, c;
4231 
4232     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
4233     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
4234     for (c = off; c < off + dof; ++c) {
4235       const PetscInt q = mesh->cones[c];
4236       PetscInt       offS;
4237 
4238       PetscCall(PetscSectionGetOffset(mesh->supportSection, q, &offS));
4239 
4240       mesh->supports[offS + offsets[q]] = p;
4241       ++offsets[q];
4242     }
4243   }
4244   PetscCall(PetscFree(offsets));
4245   PetscCall(PetscLogEventEnd(DMPLEX_Symmetrize, dm, 0, 0, 0));
4246   PetscFunctionReturn(PETSC_SUCCESS);
4247 }
4248 
4249 static PetscErrorCode DMPlexCreateDepthStratum(DM dm, DMLabel label, PetscInt depth, PetscInt pStart, PetscInt pEnd)
4250 {
4251   IS stratumIS;
4252 
4253   PetscFunctionBegin;
4254   if (pStart >= pEnd) PetscFunctionReturn(PETSC_SUCCESS);
4255   if (PetscDefined(USE_DEBUG)) {
4256     PetscInt  qStart, qEnd, numLevels, level;
4257     PetscBool overlap = PETSC_FALSE;
4258     PetscCall(DMLabelGetNumValues(label, &numLevels));
4259     for (level = 0; level < numLevels; level++) {
4260       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4261       if ((pStart >= qStart && pStart < qEnd) || (pEnd > qStart && pEnd <= qEnd)) {
4262         overlap = PETSC_TRUE;
4263         break;
4264       }
4265     }
4266     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);
4267   }
4268   PetscCall(ISCreateStride(PETSC_COMM_SELF, pEnd - pStart, pStart, 1, &stratumIS));
4269   PetscCall(DMLabelSetStratumIS(label, depth, stratumIS));
4270   PetscCall(ISDestroy(&stratumIS));
4271   PetscFunctionReturn(PETSC_SUCCESS);
4272 }
4273 
4274 static PetscErrorCode DMPlexStratify_CellType_Private(DM dm, DMLabel label)
4275 {
4276   PetscInt *pMin, *pMax;
4277   PetscInt  pStart, pEnd;
4278   PetscInt  dmin = PETSC_MAX_INT, dmax = PETSC_MIN_INT;
4279 
4280   PetscFunctionBegin;
4281   {
4282     DMLabel label2;
4283 
4284     PetscCall(DMPlexGetCellTypeLabel(dm, &label2));
4285     PetscCall(PetscObjectViewFromOptions((PetscObject)label2, NULL, "-ct_view"));
4286   }
4287   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4288   for (PetscInt p = pStart; p < pEnd; ++p) {
4289     DMPolytopeType ct;
4290 
4291     PetscCall(DMPlexGetCellType(dm, p, &ct));
4292     dmin = PetscMin(DMPolytopeTypeGetDim(ct), dmin);
4293     dmax = PetscMax(DMPolytopeTypeGetDim(ct), dmax);
4294   }
4295   PetscCall(PetscMalloc2(dmax + 1, &pMin, dmax + 1, &pMax));
4296   for (PetscInt d = dmin; d <= dmax; ++d) {
4297     pMin[d] = PETSC_MAX_INT;
4298     pMax[d] = PETSC_MIN_INT;
4299   }
4300   for (PetscInt p = pStart; p < pEnd; ++p) {
4301     DMPolytopeType ct;
4302     PetscInt       d;
4303 
4304     PetscCall(DMPlexGetCellType(dm, p, &ct));
4305     d       = DMPolytopeTypeGetDim(ct);
4306     pMin[d] = PetscMin(p, pMin[d]);
4307     pMax[d] = PetscMax(p, pMax[d]);
4308   }
4309   for (PetscInt d = dmin; d <= dmax; ++d) {
4310     if (pMin[d] > pMax[d]) continue;
4311     PetscCall(DMPlexCreateDepthStratum(dm, label, d, pMin[d], pMax[d] + 1));
4312   }
4313   PetscCall(PetscFree2(pMin, pMax));
4314   PetscFunctionReturn(PETSC_SUCCESS);
4315 }
4316 
4317 static PetscErrorCode DMPlexStratify_Topological_Private(DM dm, DMLabel label)
4318 {
4319   PetscInt pStart, pEnd;
4320   PetscInt numRoots = 0, numLeaves = 0;
4321 
4322   PetscFunctionBegin;
4323   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4324   {
4325     /* Initialize roots and count leaves */
4326     PetscInt sMin = PETSC_MAX_INT;
4327     PetscInt sMax = PETSC_MIN_INT;
4328     PetscInt coneSize, supportSize;
4329 
4330     for (PetscInt p = pStart; p < pEnd; ++p) {
4331       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4332       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
4333       if (!coneSize && supportSize) {
4334         sMin = PetscMin(p, sMin);
4335         sMax = PetscMax(p, sMax);
4336         ++numRoots;
4337       } else if (!supportSize && coneSize) {
4338         ++numLeaves;
4339       } else if (!supportSize && !coneSize) {
4340         /* Isolated points */
4341         sMin = PetscMin(p, sMin);
4342         sMax = PetscMax(p, sMax);
4343       }
4344     }
4345     PetscCall(DMPlexCreateDepthStratum(dm, label, 0, sMin, sMax + 1));
4346   }
4347 
4348   if (numRoots + numLeaves == (pEnd - pStart)) {
4349     PetscInt sMin = PETSC_MAX_INT;
4350     PetscInt sMax = PETSC_MIN_INT;
4351     PetscInt coneSize, supportSize;
4352 
4353     for (PetscInt p = pStart; p < pEnd; ++p) {
4354       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4355       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
4356       if (!supportSize && coneSize) {
4357         sMin = PetscMin(p, sMin);
4358         sMax = PetscMax(p, sMax);
4359       }
4360     }
4361     PetscCall(DMPlexCreateDepthStratum(dm, label, 1, sMin, sMax + 1));
4362   } else {
4363     PetscInt level = 0;
4364     PetscInt qStart, qEnd;
4365 
4366     PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4367     while (qEnd > qStart) {
4368       PetscInt sMin = PETSC_MAX_INT;
4369       PetscInt sMax = PETSC_MIN_INT;
4370 
4371       for (PetscInt q = qStart; q < qEnd; ++q) {
4372         const PetscInt *support;
4373         PetscInt        supportSize;
4374 
4375         PetscCall(DMPlexGetSupportSize(dm, q, &supportSize));
4376         PetscCall(DMPlexGetSupport(dm, q, &support));
4377         for (PetscInt s = 0; s < supportSize; ++s) {
4378           sMin = PetscMin(support[s], sMin);
4379           sMax = PetscMax(support[s], sMax);
4380         }
4381       }
4382       PetscCall(DMLabelGetNumValues(label, &level));
4383       PetscCall(DMPlexCreateDepthStratum(dm, label, level, sMin, sMax + 1));
4384       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4385     }
4386   }
4387   PetscFunctionReturn(PETSC_SUCCESS);
4388 }
4389 
4390 /*@
4391   DMPlexStratify - Computes the strata for all points in the `DMPLEX`
4392 
4393   Collective
4394 
4395   Input Parameter:
4396 . dm - The `DMPLEX`
4397 
4398   Level: beginner
4399 
4400   Notes:
4401   The strata group all points of the same grade, and this function calculates the strata. This
4402   grade can be seen as the height (or depth) of the point in the DAG.
4403 
4404   The DAG for most topologies is a graded poset (https://en.wikipedia.org/wiki/Graded_poset), and
4405   can be illustrated by a Hasse Diagram (https://en.wikipedia.org/wiki/Hasse_diagram).
4406   Concretely, `DMPlexStratify()` creates a new label named "depth" containing the depth in the DAG of each point. For cell-vertex
4407   meshes, vertices are depth 0 and cells are depth 1. For fully interpolated meshes, depth 0 for vertices, 1 for edges, and so on
4408   until cells have depth equal to the dimension of the mesh. The depth label can be accessed through `DMPlexGetDepthLabel()` or `DMPlexGetDepthStratum()`, or
4409   manually via `DMGetLabel()`.  The height is defined implicitly by height = maxDimension - depth, and can be accessed
4410   via `DMPlexGetHeightStratum()`.  For example, cells have height 0 and faces have height 1.
4411 
4412   The depth of a point is calculated by executing a breadth-first search (BFS) on the DAG. This could produce surprising results
4413   if run on a partially interpolated mesh, meaning one that had some edges and faces, but not others. For example, suppose that
4414   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
4415   to interpolate only that one (e0), so that
4416 .vb
4417   cone(c0) = {e0, v2}
4418   cone(e0) = {v0, v1}
4419 .ve
4420   If `DMPlexStratify()` is run on this mesh, it will give depths
4421 .vb
4422    depth 0 = {v0, v1, v2}
4423    depth 1 = {e0, c0}
4424 .ve
4425   where the triangle has been given depth 1, instead of 2, because it is reachable from vertex v2.
4426 
4427   `DMPlexStratify()` should be called after all calls to `DMPlexSymmetrize()`
4428 
4429 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexComputeCellTypes()`
4430 @*/
4431 PetscErrorCode DMPlexStratify(DM dm)
4432 {
4433   DM_Plex  *mesh = (DM_Plex *)dm->data;
4434   DMLabel   label;
4435   PetscBool flg = PETSC_FALSE;
4436 
4437   PetscFunctionBegin;
4438   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4439   PetscCall(PetscLogEventBegin(DMPLEX_Stratify, dm, 0, 0, 0));
4440 
4441   // Create depth label
4442   PetscCall(DMCreateLabel(dm, "depth"));
4443   PetscCall(DMPlexGetDepthLabel(dm, &label));
4444 
4445   PetscCall(PetscOptionsGetBool(NULL, dm->hdr.prefix, "-dm_plex_stratify_celltype", &flg, NULL));
4446   if (flg) PetscCall(DMPlexStratify_CellType_Private(dm, label));
4447   else PetscCall(DMPlexStratify_Topological_Private(dm, label));
4448 
4449   { /* just in case there is an empty process */
4450     PetscInt numValues, maxValues = 0, v;
4451 
4452     PetscCall(DMLabelGetNumValues(label, &numValues));
4453     PetscCall(MPIU_Allreduce(&numValues, &maxValues, 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
4454     for (v = numValues; v < maxValues; v++) PetscCall(DMLabelAddStratum(label, v));
4455   }
4456   PetscCall(PetscObjectStateGet((PetscObject)label, &mesh->depthState));
4457   PetscCall(PetscLogEventEnd(DMPLEX_Stratify, dm, 0, 0, 0));
4458   PetscFunctionReturn(PETSC_SUCCESS);
4459 }
4460 
4461 PetscErrorCode DMPlexComputeCellType_Internal(DM dm, PetscInt p, PetscInt pdepth, DMPolytopeType *pt)
4462 {
4463   DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4464   PetscInt       dim, depth, pheight, coneSize;
4465 
4466   PetscFunctionBeginHot;
4467   PetscCall(DMGetDimension(dm, &dim));
4468   PetscCall(DMPlexGetDepth(dm, &depth));
4469   PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4470   pheight = depth - pdepth;
4471   if (depth <= 1) {
4472     switch (pdepth) {
4473     case 0:
4474       ct = DM_POLYTOPE_POINT;
4475       break;
4476     case 1:
4477       switch (coneSize) {
4478       case 2:
4479         ct = DM_POLYTOPE_SEGMENT;
4480         break;
4481       case 3:
4482         ct = DM_POLYTOPE_TRIANGLE;
4483         break;
4484       case 4:
4485         switch (dim) {
4486         case 2:
4487           ct = DM_POLYTOPE_QUADRILATERAL;
4488           break;
4489         case 3:
4490           ct = DM_POLYTOPE_TETRAHEDRON;
4491           break;
4492         default:
4493           break;
4494         }
4495         break;
4496       case 5:
4497         ct = DM_POLYTOPE_PYRAMID;
4498         break;
4499       case 6:
4500         ct = DM_POLYTOPE_TRI_PRISM_TENSOR;
4501         break;
4502       case 8:
4503         ct = DM_POLYTOPE_HEXAHEDRON;
4504         break;
4505       default:
4506         break;
4507       }
4508     }
4509   } else {
4510     if (pdepth == 0) {
4511       ct = DM_POLYTOPE_POINT;
4512     } else if (pheight == 0) {
4513       switch (dim) {
4514       case 1:
4515         switch (coneSize) {
4516         case 2:
4517           ct = DM_POLYTOPE_SEGMENT;
4518           break;
4519         default:
4520           break;
4521         }
4522         break;
4523       case 2:
4524         switch (coneSize) {
4525         case 3:
4526           ct = DM_POLYTOPE_TRIANGLE;
4527           break;
4528         case 4:
4529           ct = DM_POLYTOPE_QUADRILATERAL;
4530           break;
4531         default:
4532           break;
4533         }
4534         break;
4535       case 3:
4536         switch (coneSize) {
4537         case 4:
4538           ct = DM_POLYTOPE_TETRAHEDRON;
4539           break;
4540         case 5: {
4541           const PetscInt *cone;
4542           PetscInt        faceConeSize;
4543 
4544           PetscCall(DMPlexGetCone(dm, p, &cone));
4545           PetscCall(DMPlexGetConeSize(dm, cone[0], &faceConeSize));
4546           switch (faceConeSize) {
4547           case 3:
4548             ct = DM_POLYTOPE_TRI_PRISM_TENSOR;
4549             break;
4550           case 4:
4551             ct = DM_POLYTOPE_PYRAMID;
4552             break;
4553           }
4554         } break;
4555         case 6:
4556           ct = DM_POLYTOPE_HEXAHEDRON;
4557           break;
4558         default:
4559           break;
4560         }
4561         break;
4562       default:
4563         break;
4564       }
4565     } else if (pheight > 0) {
4566       switch (coneSize) {
4567       case 2:
4568         ct = DM_POLYTOPE_SEGMENT;
4569         break;
4570       case 3:
4571         ct = DM_POLYTOPE_TRIANGLE;
4572         break;
4573       case 4:
4574         ct = DM_POLYTOPE_QUADRILATERAL;
4575         break;
4576       default:
4577         break;
4578       }
4579     }
4580   }
4581   *pt = ct;
4582   PetscFunctionReturn(PETSC_SUCCESS);
4583 }
4584 
4585 /*@
4586   DMPlexComputeCellTypes - Infer the polytope type of every cell using its dimension and cone size.
4587 
4588   Collective
4589 
4590   Input Parameter:
4591 . dm - The `DMPLEX`
4592 
4593   Level: developer
4594 
4595   Note:
4596   This function is normally called automatically when a cell type is requested. It creates an
4597   internal `DMLabel` named "celltype" which can be directly accessed using `DMGetLabel()`. A user may disable
4598   automatic creation by creating the label manually, using `DMCreateLabel`(dm, "celltype").
4599 
4600   `DMPlexComputeCellTypes()` should be called after all calls to `DMPlexSymmetrize()` and `DMPlexStratify()`
4601 
4602 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexStratify()`, `DMGetLabel()`, `DMCreateLabel()`
4603 @*/
4604 PetscErrorCode DMPlexComputeCellTypes(DM dm)
4605 {
4606   DM_Plex *mesh;
4607   DMLabel  ctLabel;
4608   PetscInt pStart, pEnd, p;
4609 
4610   PetscFunctionBegin;
4611   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4612   mesh = (DM_Plex *)dm->data;
4613   PetscCall(DMCreateLabel(dm, "celltype"));
4614   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
4615   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4616   PetscCall(PetscFree(mesh->cellTypes));
4617   PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
4618   for (p = pStart; p < pEnd; ++p) {
4619     DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4620     PetscInt       pdepth;
4621 
4622     PetscCall(DMPlexGetPointDepth(dm, p, &pdepth));
4623     PetscCall(DMPlexComputeCellType_Internal(dm, p, pdepth, &ct));
4624     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);
4625     PetscCall(DMLabelSetValue(ctLabel, p, ct));
4626     mesh->cellTypes[p - pStart].value_as_uint8 = ct;
4627   }
4628   PetscCall(PetscObjectStateGet((PetscObject)ctLabel, &mesh->celltypeState));
4629   PetscCall(PetscObjectViewFromOptions((PetscObject)ctLabel, NULL, "-dm_plex_celltypes_view"));
4630   PetscFunctionReturn(PETSC_SUCCESS);
4631 }
4632 
4633 /*@C
4634   DMPlexGetJoin - Get an array for the join of the set of points
4635 
4636   Not Collective
4637 
4638   Input Parameters:
4639 + dm        - The `DMPLEX` object
4640 . numPoints - The number of input points for the join
4641 - points    - The input points
4642 
4643   Output Parameters:
4644 + numCoveredPoints - The number of points in the join
4645 - coveredPoints    - The points in the join
4646 
4647   Level: intermediate
4648 
4649   Note:
4650   Currently, this is restricted to a single level join
4651 
4652   Fortran Notes:
4653   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4654 
4655 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4656 @*/
4657 PetscErrorCode DMPlexGetJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4658 {
4659   DM_Plex  *mesh = (DM_Plex *)dm->data;
4660   PetscInt *join[2];
4661   PetscInt  joinSize, i = 0;
4662   PetscInt  dof, off, p, c, m;
4663   PetscInt  maxSupportSize;
4664 
4665   PetscFunctionBegin;
4666   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4667   PetscAssertPointer(points, 3);
4668   PetscAssertPointer(numCoveredPoints, 4);
4669   PetscAssertPointer(coveredPoints, 5);
4670   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
4671   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[0]));
4672   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[1]));
4673   /* Copy in support of first point */
4674   PetscCall(PetscSectionGetDof(mesh->supportSection, points[0], &dof));
4675   PetscCall(PetscSectionGetOffset(mesh->supportSection, points[0], &off));
4676   for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = mesh->supports[off + joinSize];
4677   /* Check each successive support */
4678   for (p = 1; p < numPoints; ++p) {
4679     PetscInt newJoinSize = 0;
4680 
4681     PetscCall(PetscSectionGetDof(mesh->supportSection, points[p], &dof));
4682     PetscCall(PetscSectionGetOffset(mesh->supportSection, points[p], &off));
4683     for (c = 0; c < dof; ++c) {
4684       const PetscInt point = mesh->supports[off + c];
4685 
4686       for (m = 0; m < joinSize; ++m) {
4687         if (point == join[i][m]) {
4688           join[1 - i][newJoinSize++] = point;
4689           break;
4690         }
4691       }
4692     }
4693     joinSize = newJoinSize;
4694     i        = 1 - i;
4695   }
4696   *numCoveredPoints = joinSize;
4697   *coveredPoints    = join[i];
4698   PetscCall(DMRestoreWorkArray(dm, maxSupportSize, MPIU_INT, &join[1 - i]));
4699   PetscFunctionReturn(PETSC_SUCCESS);
4700 }
4701 
4702 /*@C
4703   DMPlexRestoreJoin - Restore 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()`, `DMPlexGetFullJoin()`, `DMPlexGetMeet()`
4722 @*/
4723 PetscErrorCode DMPlexRestoreJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4724 {
4725   PetscFunctionBegin;
4726   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4727   if (points) PetscAssertPointer(points, 3);
4728   if (numCoveredPoints) PetscAssertPointer(numCoveredPoints, 4);
4729   PetscAssertPointer(coveredPoints, 5);
4730   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
4731   if (numCoveredPoints) *numCoveredPoints = 0;
4732   PetscFunctionReturn(PETSC_SUCCESS);
4733 }
4734 
4735 /*@C
4736   DMPlexGetFullJoin - Get an array for the join of the set of points
4737 
4738   Not Collective
4739 
4740   Input Parameters:
4741 + dm        - The `DMPLEX` object
4742 . numPoints - The number of input points for the join
4743 - points    - The input points
4744 
4745   Output Parameters:
4746 + numCoveredPoints - The number of points in the join
4747 - coveredPoints    - The points in the join
4748 
4749   Level: intermediate
4750 
4751   Fortran Notes:
4752   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4753 
4754 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetJoin()`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4755 @*/
4756 PetscErrorCode DMPlexGetFullJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4757 {
4758   PetscInt *offsets, **closures;
4759   PetscInt *join[2];
4760   PetscInt  depth = 0, maxSize, joinSize = 0, i = 0;
4761   PetscInt  p, d, c, m, ms;
4762 
4763   PetscFunctionBegin;
4764   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4765   PetscAssertPointer(points, 3);
4766   PetscAssertPointer(numCoveredPoints, 4);
4767   PetscAssertPointer(coveredPoints, 5);
4768 
4769   PetscCall(DMPlexGetDepth(dm, &depth));
4770   PetscCall(PetscCalloc1(numPoints, &closures));
4771   PetscCall(DMGetWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
4772   PetscCall(DMPlexGetMaxSizes(dm, NULL, &ms));
4773   maxSize = (ms > 1) ? ((PetscPowInt(ms, depth + 1) - 1) / (ms - 1)) : depth + 1;
4774   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[0]));
4775   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[1]));
4776 
4777   for (p = 0; p < numPoints; ++p) {
4778     PetscInt closureSize;
4779 
4780     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_FALSE, &closureSize, &closures[p]));
4781 
4782     offsets[p * (depth + 2) + 0] = 0;
4783     for (d = 0; d < depth + 1; ++d) {
4784       PetscInt pStart, pEnd, i;
4785 
4786       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
4787       for (i = offsets[p * (depth + 2) + d]; i < closureSize; ++i) {
4788         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
4789           offsets[p * (depth + 2) + d + 1] = i;
4790           break;
4791         }
4792       }
4793       if (i == closureSize) offsets[p * (depth + 2) + d + 1] = i;
4794     }
4795     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);
4796   }
4797   for (d = 0; d < depth + 1; ++d) {
4798     PetscInt dof;
4799 
4800     /* Copy in support of first point */
4801     dof = offsets[d + 1] - offsets[d];
4802     for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = closures[0][(offsets[d] + joinSize) * 2];
4803     /* Check each successive cone */
4804     for (p = 1; p < numPoints && joinSize; ++p) {
4805       PetscInt newJoinSize = 0;
4806 
4807       dof = offsets[p * (depth + 2) + d + 1] - offsets[p * (depth + 2) + d];
4808       for (c = 0; c < dof; ++c) {
4809         const PetscInt point = closures[p][(offsets[p * (depth + 2) + d] + c) * 2];
4810 
4811         for (m = 0; m < joinSize; ++m) {
4812           if (point == join[i][m]) {
4813             join[1 - i][newJoinSize++] = point;
4814             break;
4815           }
4816         }
4817       }
4818       joinSize = newJoinSize;
4819       i        = 1 - i;
4820     }
4821     if (joinSize) break;
4822   }
4823   *numCoveredPoints = joinSize;
4824   *coveredPoints    = join[i];
4825   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_FALSE, NULL, &closures[p]));
4826   PetscCall(PetscFree(closures));
4827   PetscCall(DMRestoreWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
4828   PetscCall(DMRestoreWorkArray(dm, ms, MPIU_INT, &join[1 - i]));
4829   PetscFunctionReturn(PETSC_SUCCESS);
4830 }
4831 
4832 /*@C
4833   DMPlexGetMeet - Get an array for the meet of the set of points
4834 
4835   Not Collective
4836 
4837   Input Parameters:
4838 + dm        - The `DMPLEX` object
4839 . numPoints - The number of input points for the meet
4840 - points    - The input points
4841 
4842   Output Parameters:
4843 + numCoveringPoints - The number of points in the meet
4844 - coveringPoints    - The points in the meet
4845 
4846   Level: intermediate
4847 
4848   Note:
4849   Currently, this is restricted to a single level meet
4850 
4851   Fortran Notes:
4852   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4853 
4854 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
4855 @*/
4856 PetscErrorCode DMPlexGetMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveringPoints, const PetscInt **coveringPoints)
4857 {
4858   DM_Plex  *mesh = (DM_Plex *)dm->data;
4859   PetscInt *meet[2];
4860   PetscInt  meetSize, i = 0;
4861   PetscInt  dof, off, p, c, m;
4862   PetscInt  maxConeSize;
4863 
4864   PetscFunctionBegin;
4865   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4866   PetscAssertPointer(points, 3);
4867   PetscAssertPointer(numCoveringPoints, 4);
4868   PetscAssertPointer(coveringPoints, 5);
4869   PetscCall(PetscSectionGetMaxDof(mesh->coneSection, &maxConeSize));
4870   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[0]));
4871   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[1]));
4872   /* Copy in cone of first point */
4873   PetscCall(PetscSectionGetDof(mesh->coneSection, points[0], &dof));
4874   PetscCall(PetscSectionGetOffset(mesh->coneSection, points[0], &off));
4875   for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = mesh->cones[off + meetSize];
4876   /* Check each successive cone */
4877   for (p = 1; p < numPoints; ++p) {
4878     PetscInt newMeetSize = 0;
4879 
4880     PetscCall(PetscSectionGetDof(mesh->coneSection, points[p], &dof));
4881     PetscCall(PetscSectionGetOffset(mesh->coneSection, points[p], &off));
4882     for (c = 0; c < dof; ++c) {
4883       const PetscInt point = mesh->cones[off + c];
4884 
4885       for (m = 0; m < meetSize; ++m) {
4886         if (point == meet[i][m]) {
4887           meet[1 - i][newMeetSize++] = point;
4888           break;
4889         }
4890       }
4891     }
4892     meetSize = newMeetSize;
4893     i        = 1 - i;
4894   }
4895   *numCoveringPoints = meetSize;
4896   *coveringPoints    = meet[i];
4897   PetscCall(DMRestoreWorkArray(dm, maxConeSize, MPIU_INT, &meet[1 - i]));
4898   PetscFunctionReturn(PETSC_SUCCESS);
4899 }
4900 
4901 /*@C
4902   DMPlexRestoreMeet - Restore 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()`, `DMPlexGetFullMeet()`, `DMPlexGetJoin()`
4921 @*/
4922 PetscErrorCode DMPlexRestoreMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4923 {
4924   PetscFunctionBegin;
4925   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4926   if (points) PetscAssertPointer(points, 3);
4927   if (numCoveredPoints) PetscAssertPointer(numCoveredPoints, 4);
4928   PetscAssertPointer(coveredPoints, 5);
4929   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
4930   if (numCoveredPoints) *numCoveredPoints = 0;
4931   PetscFunctionReturn(PETSC_SUCCESS);
4932 }
4933 
4934 /*@C
4935   DMPlexGetFullMeet - Get an array for the meet of the set of points
4936 
4937   Not Collective
4938 
4939   Input Parameters:
4940 + dm        - The `DMPLEX` object
4941 . numPoints - The number of input points for the meet
4942 - points    - The input points
4943 
4944   Output Parameters:
4945 + numCoveredPoints - The number of points in the meet
4946 - coveredPoints    - The points in the meet
4947 
4948   Level: intermediate
4949 
4950   Fortran Notes:
4951   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4952 
4953 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetMeet()`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
4954 @*/
4955 PetscErrorCode DMPlexGetFullMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4956 {
4957   PetscInt *offsets, **closures;
4958   PetscInt *meet[2];
4959   PetscInt  height = 0, maxSize, meetSize = 0, i = 0;
4960   PetscInt  p, h, c, m, mc;
4961 
4962   PetscFunctionBegin;
4963   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4964   PetscAssertPointer(points, 3);
4965   PetscAssertPointer(numCoveredPoints, 4);
4966   PetscAssertPointer(coveredPoints, 5);
4967 
4968   PetscCall(DMPlexGetDepth(dm, &height));
4969   PetscCall(PetscMalloc1(numPoints, &closures));
4970   PetscCall(DMGetWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
4971   PetscCall(DMPlexGetMaxSizes(dm, &mc, NULL));
4972   maxSize = (mc > 1) ? ((PetscPowInt(mc, height + 1) - 1) / (mc - 1)) : height + 1;
4973   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[0]));
4974   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[1]));
4975 
4976   for (p = 0; p < numPoints; ++p) {
4977     PetscInt closureSize;
4978 
4979     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_TRUE, &closureSize, &closures[p]));
4980 
4981     offsets[p * (height + 2) + 0] = 0;
4982     for (h = 0; h < height + 1; ++h) {
4983       PetscInt pStart, pEnd, i;
4984 
4985       PetscCall(DMPlexGetHeightStratum(dm, h, &pStart, &pEnd));
4986       for (i = offsets[p * (height + 2) + h]; i < closureSize; ++i) {
4987         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
4988           offsets[p * (height + 2) + h + 1] = i;
4989           break;
4990         }
4991       }
4992       if (i == closureSize) offsets[p * (height + 2) + h + 1] = i;
4993     }
4994     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);
4995   }
4996   for (h = 0; h < height + 1; ++h) {
4997     PetscInt dof;
4998 
4999     /* Copy in cone of first point */
5000     dof = offsets[h + 1] - offsets[h];
5001     for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = closures[0][(offsets[h] + meetSize) * 2];
5002     /* Check each successive cone */
5003     for (p = 1; p < numPoints && meetSize; ++p) {
5004       PetscInt newMeetSize = 0;
5005 
5006       dof = offsets[p * (height + 2) + h + 1] - offsets[p * (height + 2) + h];
5007       for (c = 0; c < dof; ++c) {
5008         const PetscInt point = closures[p][(offsets[p * (height + 2) + h] + c) * 2];
5009 
5010         for (m = 0; m < meetSize; ++m) {
5011           if (point == meet[i][m]) {
5012             meet[1 - i][newMeetSize++] = point;
5013             break;
5014           }
5015         }
5016       }
5017       meetSize = newMeetSize;
5018       i        = 1 - i;
5019     }
5020     if (meetSize) break;
5021   }
5022   *numCoveredPoints = meetSize;
5023   *coveredPoints    = meet[i];
5024   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_TRUE, NULL, &closures[p]));
5025   PetscCall(PetscFree(closures));
5026   PetscCall(DMRestoreWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
5027   PetscCall(DMRestoreWorkArray(dm, mc, MPIU_INT, &meet[1 - i]));
5028   PetscFunctionReturn(PETSC_SUCCESS);
5029 }
5030 
5031 /*@C
5032   DMPlexEqual - Determine if two `DM` have the same topology
5033 
5034   Not Collective
5035 
5036   Input Parameters:
5037 + dmA - A `DMPLEX` object
5038 - dmB - A `DMPLEX` object
5039 
5040   Output Parameter:
5041 . equal - `PETSC_TRUE` if the topologies are identical
5042 
5043   Level: intermediate
5044 
5045   Note:
5046   We are not solving graph isomorphism, so we do not permute.
5047 
5048 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCone()`
5049 @*/
5050 PetscErrorCode DMPlexEqual(DM dmA, DM dmB, PetscBool *equal)
5051 {
5052   PetscInt depth, depthB, pStart, pEnd, pStartB, pEndB, p;
5053 
5054   PetscFunctionBegin;
5055   PetscValidHeaderSpecific(dmA, DM_CLASSID, 1);
5056   PetscValidHeaderSpecific(dmB, DM_CLASSID, 2);
5057   PetscAssertPointer(equal, 3);
5058 
5059   *equal = PETSC_FALSE;
5060   PetscCall(DMPlexGetDepth(dmA, &depth));
5061   PetscCall(DMPlexGetDepth(dmB, &depthB));
5062   if (depth != depthB) PetscFunctionReturn(PETSC_SUCCESS);
5063   PetscCall(DMPlexGetChart(dmA, &pStart, &pEnd));
5064   PetscCall(DMPlexGetChart(dmB, &pStartB, &pEndB));
5065   if ((pStart != pStartB) || (pEnd != pEndB)) PetscFunctionReturn(PETSC_SUCCESS);
5066   for (p = pStart; p < pEnd; ++p) {
5067     const PetscInt *cone, *coneB, *ornt, *orntB, *support, *supportB;
5068     PetscInt        coneSize, coneSizeB, c, supportSize, supportSizeB, s;
5069 
5070     PetscCall(DMPlexGetConeSize(dmA, p, &coneSize));
5071     PetscCall(DMPlexGetCone(dmA, p, &cone));
5072     PetscCall(DMPlexGetConeOrientation(dmA, p, &ornt));
5073     PetscCall(DMPlexGetConeSize(dmB, p, &coneSizeB));
5074     PetscCall(DMPlexGetCone(dmB, p, &coneB));
5075     PetscCall(DMPlexGetConeOrientation(dmB, p, &orntB));
5076     if (coneSize != coneSizeB) PetscFunctionReturn(PETSC_SUCCESS);
5077     for (c = 0; c < coneSize; ++c) {
5078       if (cone[c] != coneB[c]) PetscFunctionReturn(PETSC_SUCCESS);
5079       if (ornt[c] != orntB[c]) PetscFunctionReturn(PETSC_SUCCESS);
5080     }
5081     PetscCall(DMPlexGetSupportSize(dmA, p, &supportSize));
5082     PetscCall(DMPlexGetSupport(dmA, p, &support));
5083     PetscCall(DMPlexGetSupportSize(dmB, p, &supportSizeB));
5084     PetscCall(DMPlexGetSupport(dmB, p, &supportB));
5085     if (supportSize != supportSizeB) PetscFunctionReturn(PETSC_SUCCESS);
5086     for (s = 0; s < supportSize; ++s) {
5087       if (support[s] != supportB[s]) PetscFunctionReturn(PETSC_SUCCESS);
5088     }
5089   }
5090   *equal = PETSC_TRUE;
5091   PetscFunctionReturn(PETSC_SUCCESS);
5092 }
5093 
5094 /*@C
5095   DMPlexGetNumFaceVertices - Returns the number of vertices on a face
5096 
5097   Not Collective
5098 
5099   Input Parameters:
5100 + dm         - The `DMPLEX`
5101 . cellDim    - The cell dimension
5102 - numCorners - The number of vertices on a cell
5103 
5104   Output Parameter:
5105 . numFaceVertices - The number of vertices on a face
5106 
5107   Level: developer
5108 
5109   Note:
5110   Of course this can only work for a restricted set of symmetric shapes
5111 
5112 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCone()`
5113 @*/
5114 PetscErrorCode DMPlexGetNumFaceVertices(DM dm, PetscInt cellDim, PetscInt numCorners, PetscInt *numFaceVertices)
5115 {
5116   MPI_Comm comm;
5117 
5118   PetscFunctionBegin;
5119   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
5120   PetscAssertPointer(numFaceVertices, 4);
5121   switch (cellDim) {
5122   case 0:
5123     *numFaceVertices = 0;
5124     break;
5125   case 1:
5126     *numFaceVertices = 1;
5127     break;
5128   case 2:
5129     switch (numCorners) {
5130     case 3:                 /* triangle */
5131       *numFaceVertices = 2; /* Edge has 2 vertices */
5132       break;
5133     case 4:                 /* quadrilateral */
5134       *numFaceVertices = 2; /* Edge has 2 vertices */
5135       break;
5136     case 6:                 /* quadratic triangle, tri and quad cohesive Lagrange cells */
5137       *numFaceVertices = 3; /* Edge has 3 vertices */
5138       break;
5139     case 9:                 /* quadratic quadrilateral, quadratic quad cohesive Lagrange cells */
5140       *numFaceVertices = 3; /* Edge has 3 vertices */
5141       break;
5142     default:
5143       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
5144     }
5145     break;
5146   case 3:
5147     switch (numCorners) {
5148     case 4:                 /* tetradehdron */
5149       *numFaceVertices = 3; /* Face has 3 vertices */
5150       break;
5151     case 6:                 /* tet cohesive cells */
5152       *numFaceVertices = 4; /* Face has 4 vertices */
5153       break;
5154     case 8:                 /* hexahedron */
5155       *numFaceVertices = 4; /* Face has 4 vertices */
5156       break;
5157     case 9:                 /* tet cohesive Lagrange cells */
5158       *numFaceVertices = 6; /* Face has 6 vertices */
5159       break;
5160     case 10:                /* quadratic tetrahedron */
5161       *numFaceVertices = 6; /* Face has 6 vertices */
5162       break;
5163     case 12:                /* hex cohesive Lagrange cells */
5164       *numFaceVertices = 6; /* Face has 6 vertices */
5165       break;
5166     case 18:                /* quadratic tet cohesive Lagrange cells */
5167       *numFaceVertices = 6; /* Face has 6 vertices */
5168       break;
5169     case 27:                /* quadratic hexahedron, quadratic hex cohesive Lagrange cells */
5170       *numFaceVertices = 9; /* Face has 9 vertices */
5171       break;
5172     default:
5173       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
5174     }
5175     break;
5176   default:
5177     SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid cell dimension %" PetscInt_FMT, cellDim);
5178   }
5179   PetscFunctionReturn(PETSC_SUCCESS);
5180 }
5181 
5182 /*@
5183   DMPlexGetDepthLabel - Get the `DMLabel` recording the depth of each point
5184 
5185   Not Collective
5186 
5187   Input Parameter:
5188 . dm - The `DMPLEX` object
5189 
5190   Output Parameter:
5191 . depthLabel - The `DMLabel` recording point depth
5192 
5193   Level: developer
5194 
5195 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepth()`, `DMPlexGetHeightStratum()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`,
5196 @*/
5197 PetscErrorCode DMPlexGetDepthLabel(DM dm, DMLabel *depthLabel)
5198 {
5199   PetscFunctionBegin;
5200   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5201   PetscAssertPointer(depthLabel, 2);
5202   *depthLabel = dm->depthLabel;
5203   PetscFunctionReturn(PETSC_SUCCESS);
5204 }
5205 
5206 /*@
5207   DMPlexGetDepth - Get the depth of the DAG representing this mesh
5208 
5209   Not Collective
5210 
5211   Input Parameter:
5212 . dm - The `DMPLEX` object
5213 
5214   Output Parameter:
5215 . depth - The number of strata (breadth first levels) in the DAG
5216 
5217   Level: developer
5218 
5219   Notes:
5220   This returns maximum of point depths over all points, i.e. maximum value of the label returned by `DMPlexGetDepthLabel()`.
5221 
5222   The point depth is described more in detail in `DMPlexGetDepthStratum()`.
5223 
5224   An empty mesh gives -1.
5225 
5226 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepthLabel()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`
5227 @*/
5228 PetscErrorCode DMPlexGetDepth(DM dm, PetscInt *depth)
5229 {
5230   DM_Plex *mesh = (DM_Plex *)dm->data;
5231   DMLabel  label;
5232   PetscInt d = 0;
5233 
5234   PetscFunctionBegin;
5235   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5236   PetscAssertPointer(depth, 2);
5237   if (mesh->tr) {
5238     PetscCall(DMPlexTransformGetDepth(mesh->tr, depth));
5239   } else {
5240     PetscCall(DMPlexGetDepthLabel(dm, &label));
5241     if (label) PetscCall(DMLabelGetNumValues(label, &d));
5242     *depth = d - 1;
5243   }
5244   PetscFunctionReturn(PETSC_SUCCESS);
5245 }
5246 
5247 /*@
5248   DMPlexGetDepthStratum - Get the bounds [`start`, `end`) for all points at a certain depth.
5249 
5250   Not Collective
5251 
5252   Input Parameters:
5253 + dm    - The `DMPLEX` object
5254 - depth - The requested depth
5255 
5256   Output Parameters:
5257 + start - The first point at this `depth`
5258 - end   - One beyond the last point at this `depth`
5259 
5260   Level: developer
5261 
5262   Notes:
5263   Depth indexing is related to topological dimension.  Depth stratum 0 contains the lowest topological dimension points,
5264   often "vertices".  If the mesh is "interpolated" (see `DMPlexInterpolate()`), then depth stratum 1 contains the next
5265   higher dimension, e.g., "edges".
5266 
5267 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetHeightStratum()`, `DMPlexGetCellTypeStratum()`, `DMPlexGetDepth()`, `DMPlexGetDepthLabel()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`, `DMPlexInterpolate()`
5268 @*/
5269 PetscErrorCode DMPlexGetDepthStratum(DM dm, PetscInt depth, PetscInt *start, PetscInt *end)
5270 {
5271   DM_Plex *mesh = (DM_Plex *)dm->data;
5272   DMLabel  label;
5273   PetscInt pStart, pEnd;
5274 
5275   PetscFunctionBegin;
5276   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5277   if (start) {
5278     PetscAssertPointer(start, 3);
5279     *start = 0;
5280   }
5281   if (end) {
5282     PetscAssertPointer(end, 4);
5283     *end = 0;
5284   }
5285   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
5286   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
5287   if (depth < 0) {
5288     if (start) *start = pStart;
5289     if (end) *end = pEnd;
5290     PetscFunctionReturn(PETSC_SUCCESS);
5291   }
5292   if (mesh->tr) {
5293     PetscCall(DMPlexTransformGetDepthStratum(mesh->tr, depth, start, end));
5294   } else {
5295     PetscCall(DMPlexGetDepthLabel(dm, &label));
5296     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named depth was found");
5297     PetscCall(DMLabelGetStratumBounds(label, depth, start, end));
5298   }
5299   PetscFunctionReturn(PETSC_SUCCESS);
5300 }
5301 
5302 /*@
5303   DMPlexGetHeightStratum - Get the bounds [`start`, `end`) for all points at a certain height.
5304 
5305   Not Collective
5306 
5307   Input Parameters:
5308 + dm     - The `DMPLEX` object
5309 - height - The requested height
5310 
5311   Output Parameters:
5312 + start - The first point at this `height`
5313 - end   - One beyond the last point at this `height`
5314 
5315   Level: developer
5316 
5317   Notes:
5318   Height indexing is related to topological codimension.  Height stratum 0 contains the highest topological dimension
5319   points, often called "cells" or "elements".  If the mesh is "interpolated" (see `DMPlexInterpolate()`), then height
5320   stratum 1 contains the boundary of these "cells", often called "faces" or "facets".
5321 
5322 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepthStratum()`, `DMPlexGetCellTypeStratum()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
5323 @*/
5324 PetscErrorCode DMPlexGetHeightStratum(DM dm, PetscInt height, PetscInt *start, PetscInt *end)
5325 {
5326   DMLabel  label;
5327   PetscInt depth, pStart, pEnd;
5328 
5329   PetscFunctionBegin;
5330   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5331   if (start) {
5332     PetscAssertPointer(start, 3);
5333     *start = 0;
5334   }
5335   if (end) {
5336     PetscAssertPointer(end, 4);
5337     *end = 0;
5338   }
5339   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
5340   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
5341   if (height < 0) {
5342     if (start) *start = pStart;
5343     if (end) *end = pEnd;
5344     PetscFunctionReturn(PETSC_SUCCESS);
5345   }
5346   PetscCall(DMPlexGetDepthLabel(dm, &label));
5347   if (label) PetscCall(DMLabelGetNumValues(label, &depth));
5348   else PetscCall(DMGetDimension(dm, &depth));
5349   PetscCheck(depth >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Depth not yet computed");
5350   PetscCall(DMPlexGetDepthStratum(dm, depth - 1 - height, start, end));
5351   PetscFunctionReturn(PETSC_SUCCESS);
5352 }
5353 
5354 /*@
5355   DMPlexGetPointDepth - Get the `depth` of a given point
5356 
5357   Not Collective
5358 
5359   Input Parameters:
5360 + dm    - The `DMPLEX` object
5361 - point - The point
5362 
5363   Output Parameter:
5364 . depth - The depth of the `point`
5365 
5366   Level: intermediate
5367 
5368 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
5369 @*/
5370 PetscErrorCode DMPlexGetPointDepth(DM dm, PetscInt point, PetscInt *depth)
5371 {
5372   PetscFunctionBegin;
5373   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5374   PetscAssertPointer(depth, 3);
5375   PetscCall(DMLabelGetValue(dm->depthLabel, point, depth));
5376   PetscFunctionReturn(PETSC_SUCCESS);
5377 }
5378 
5379 /*@
5380   DMPlexGetPointHeight - Get the `height` of a given point
5381 
5382   Not Collective
5383 
5384   Input Parameters:
5385 + dm    - The `DMPLEX` object
5386 - point - The point
5387 
5388   Output Parameter:
5389 . height - The height of the `point`
5390 
5391   Level: intermediate
5392 
5393 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointDepth()`
5394 @*/
5395 PetscErrorCode DMPlexGetPointHeight(DM dm, PetscInt point, PetscInt *height)
5396 {
5397   PetscInt n, pDepth;
5398 
5399   PetscFunctionBegin;
5400   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5401   PetscAssertPointer(height, 3);
5402   PetscCall(DMLabelGetNumValues(dm->depthLabel, &n));
5403   PetscCall(DMLabelGetValue(dm->depthLabel, point, &pDepth));
5404   *height = n - 1 - pDepth; /* DAG depth is n-1 */
5405   PetscFunctionReturn(PETSC_SUCCESS);
5406 }
5407 
5408 /*@
5409   DMPlexGetCellTypeLabel - Get the `DMLabel` recording the polytope type of each cell
5410 
5411   Not Collective
5412 
5413   Input Parameter:
5414 . dm - The `DMPLEX` object
5415 
5416   Output Parameter:
5417 . celltypeLabel - The `DMLabel` recording cell polytope type
5418 
5419   Level: developer
5420 
5421   Note:
5422   This function will trigger automatica computation of cell types. This can be disabled by calling
5423   `DMCreateLabel`(dm, "celltype") beforehand.
5424 
5425 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMCreateLabel()`
5426 @*/
5427 PetscErrorCode DMPlexGetCellTypeLabel(DM dm, DMLabel *celltypeLabel)
5428 {
5429   PetscFunctionBegin;
5430   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5431   PetscAssertPointer(celltypeLabel, 2);
5432   if (!dm->celltypeLabel) PetscCall(DMPlexComputeCellTypes(dm));
5433   *celltypeLabel = dm->celltypeLabel;
5434   PetscFunctionReturn(PETSC_SUCCESS);
5435 }
5436 
5437 /*@
5438   DMPlexGetCellType - Get the polytope type of a given cell
5439 
5440   Not Collective
5441 
5442   Input Parameters:
5443 + dm   - The `DMPLEX` object
5444 - cell - The cell
5445 
5446   Output Parameter:
5447 . celltype - The polytope type of the cell
5448 
5449   Level: intermediate
5450 
5451 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPolytopeType`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`
5452 @*/
5453 PetscErrorCode DMPlexGetCellType(DM dm, PetscInt cell, DMPolytopeType *celltype)
5454 {
5455   DM_Plex *mesh = (DM_Plex *)dm->data;
5456   DMLabel  label;
5457   PetscInt ct;
5458 
5459   PetscFunctionBegin;
5460   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5461   PetscAssertPointer(celltype, 3);
5462   if (mesh->tr) {
5463     PetscCall(DMPlexTransformGetCellType(mesh->tr, cell, celltype));
5464   } else {
5465     PetscInt pStart, pEnd;
5466 
5467     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, NULL));
5468     if (!mesh->cellTypes) { /* XXX remove? optimize? */
5469       PetscCall(PetscSectionGetChart(mesh->coneSection, NULL, &pEnd));
5470       PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
5471       PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5472       for (PetscInt p = pStart; p < pEnd; p++) {
5473         PetscCall(DMLabelGetValue(label, p, &ct));
5474         mesh->cellTypes[p - pStart].value_as_uint8 = (DMPolytopeType)ct;
5475       }
5476     }
5477     *celltype = (DMPolytopeType)mesh->cellTypes[cell - pStart].value_as_uint8;
5478     if (PetscDefined(USE_DEBUG)) {
5479       PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5480       PetscCall(DMLabelGetValue(label, cell, &ct));
5481       PetscCheck(ct >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Cell %" PetscInt_FMT " has not been assigned a cell type", cell);
5482       PetscCheck(ct == (PetscInt)*celltype, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid cellType for %" PetscInt_FMT ": %d != %" PetscInt_FMT, cell, (int)*celltype, ct);
5483     }
5484   }
5485   PetscFunctionReturn(PETSC_SUCCESS);
5486 }
5487 
5488 /*@
5489   DMPlexSetCellType - Set the polytope type of a given cell
5490 
5491   Not Collective
5492 
5493   Input Parameters:
5494 + dm       - The `DMPLEX` object
5495 . cell     - The cell
5496 - celltype - The polytope type of the cell
5497 
5498   Level: advanced
5499 
5500   Note:
5501   By default, cell types will be automatically computed using `DMPlexComputeCellTypes()` before this function
5502   is executed. This function will override the computed type. However, if automatic classification will not succeed
5503   and a user wants to manually specify all types, the classification must be disabled by calling
5504   DMCreateLabel(dm, "celltype") before getting or setting any cell types.
5505 
5506 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexComputeCellTypes()`, `DMCreateLabel()`
5507 @*/
5508 PetscErrorCode DMPlexSetCellType(DM dm, PetscInt cell, DMPolytopeType celltype)
5509 {
5510   DM_Plex *mesh = (DM_Plex *)dm->data;
5511   DMLabel  label;
5512   PetscInt pStart, pEnd;
5513 
5514   PetscFunctionBegin;
5515   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5516   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
5517   PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5518   PetscCall(DMLabelSetValue(label, cell, celltype));
5519   if (!mesh->cellTypes) PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
5520   mesh->cellTypes[cell - pStart].value_as_uint8 = celltype;
5521   PetscFunctionReturn(PETSC_SUCCESS);
5522 }
5523 
5524 PetscErrorCode DMCreateCoordinateDM_Plex(DM dm, DM *cdm)
5525 {
5526   PetscSection section, s;
5527   Mat          m;
5528   PetscInt     maxHeight;
5529   const char  *prefix;
5530 
5531   PetscFunctionBegin;
5532   PetscCall(DMClone(dm, cdm));
5533   PetscCall(PetscObjectGetOptionsPrefix((PetscObject)dm, &prefix));
5534   PetscCall(PetscObjectSetOptionsPrefix((PetscObject)*cdm, prefix));
5535   PetscCall(PetscObjectAppendOptionsPrefix((PetscObject)*cdm, "cdm_"));
5536   PetscCall(DMPlexGetMaxProjectionHeight(dm, &maxHeight));
5537   PetscCall(DMPlexSetMaxProjectionHeight(*cdm, maxHeight));
5538   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
5539   PetscCall(DMSetLocalSection(*cdm, section));
5540   PetscCall(PetscSectionDestroy(&section));
5541   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &s));
5542   PetscCall(MatCreate(PETSC_COMM_SELF, &m));
5543   PetscCall(DMSetDefaultConstraints(*cdm, s, m, NULL));
5544   PetscCall(PetscSectionDestroy(&s));
5545   PetscCall(MatDestroy(&m));
5546 
5547   PetscCall(DMSetNumFields(*cdm, 1));
5548   PetscCall(DMCreateDS(*cdm));
5549   (*cdm)->cloneOpts = PETSC_TRUE;
5550   if (dm->setfromoptionscalled) PetscCall(DMSetFromOptions(*cdm));
5551   PetscFunctionReturn(PETSC_SUCCESS);
5552 }
5553 
5554 PetscErrorCode DMCreateCoordinateField_Plex(DM dm, DMField *field)
5555 {
5556   Vec coordsLocal, cellCoordsLocal;
5557   DM  coordsDM, cellCoordsDM;
5558 
5559   PetscFunctionBegin;
5560   *field = NULL;
5561   PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
5562   PetscCall(DMGetCoordinateDM(dm, &coordsDM));
5563   PetscCall(DMGetCellCoordinatesLocal(dm, &cellCoordsLocal));
5564   PetscCall(DMGetCellCoordinateDM(dm, &cellCoordsDM));
5565   if (coordsLocal && coordsDM) {
5566     if (cellCoordsLocal && cellCoordsDM) PetscCall(DMFieldCreateDSWithDG(coordsDM, cellCoordsDM, 0, coordsLocal, cellCoordsLocal, field));
5567     else PetscCall(DMFieldCreateDS(coordsDM, 0, coordsLocal, field));
5568   }
5569   PetscFunctionReturn(PETSC_SUCCESS);
5570 }
5571 
5572 /*@C
5573   DMPlexGetConeSection - Return a section which describes the layout of cone data
5574 
5575   Not Collective
5576 
5577   Input Parameter:
5578 . dm - The `DMPLEX` object
5579 
5580   Output Parameter:
5581 . section - The `PetscSection` object
5582 
5583   Level: developer
5584 
5585 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSupportSection()`, `DMPlexGetCones()`, `DMPlexGetConeOrientations()`, `PetscSection`
5586 @*/
5587 PetscErrorCode DMPlexGetConeSection(DM dm, PetscSection *section)
5588 {
5589   DM_Plex *mesh = (DM_Plex *)dm->data;
5590 
5591   PetscFunctionBegin;
5592   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5593   if (section) *section = mesh->coneSection;
5594   PetscFunctionReturn(PETSC_SUCCESS);
5595 }
5596 
5597 /*@C
5598   DMPlexGetSupportSection - Return a section which describes the layout of support data
5599 
5600   Not Collective
5601 
5602   Input Parameter:
5603 . dm - The `DMPLEX` object
5604 
5605   Output Parameter:
5606 . section - The `PetscSection` object
5607 
5608   Level: developer
5609 
5610 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `PetscSection`
5611 @*/
5612 PetscErrorCode DMPlexGetSupportSection(DM dm, PetscSection *section)
5613 {
5614   DM_Plex *mesh = (DM_Plex *)dm->data;
5615 
5616   PetscFunctionBegin;
5617   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5618   if (section) *section = mesh->supportSection;
5619   PetscFunctionReturn(PETSC_SUCCESS);
5620 }
5621 
5622 /*@C
5623   DMPlexGetCones - Return cone data
5624 
5625   Not Collective
5626 
5627   Input Parameter:
5628 . dm - The `DMPLEX` object
5629 
5630   Output Parameter:
5631 . cones - The cone for each point
5632 
5633   Level: developer
5634 
5635 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`
5636 @*/
5637 PetscErrorCode DMPlexGetCones(DM dm, PetscInt *cones[])
5638 {
5639   DM_Plex *mesh = (DM_Plex *)dm->data;
5640 
5641   PetscFunctionBegin;
5642   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5643   if (cones) *cones = mesh->cones;
5644   PetscFunctionReturn(PETSC_SUCCESS);
5645 }
5646 
5647 /*@C
5648   DMPlexGetConeOrientations - Return cone orientation data
5649 
5650   Not Collective
5651 
5652   Input Parameter:
5653 . dm - The `DMPLEX` object
5654 
5655   Output Parameter:
5656 . coneOrientations - The array of cone orientations for all points
5657 
5658   Level: developer
5659 
5660   Notes:
5661   The `PetscSection` returned by `DMPlexGetConeSection()` partitions coneOrientations into cone orientations of particular points as returned by `DMPlexGetConeOrientation()`.
5662 
5663   The meaning of coneOrientations values is detailed in `DMPlexGetConeOrientation()`.
5664 
5665 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `DMPlexGetConeOrientation()`, `PetscSection`
5666 @*/
5667 PetscErrorCode DMPlexGetConeOrientations(DM dm, PetscInt *coneOrientations[])
5668 {
5669   DM_Plex *mesh = (DM_Plex *)dm->data;
5670 
5671   PetscFunctionBegin;
5672   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5673   if (coneOrientations) *coneOrientations = mesh->coneOrientations;
5674   PetscFunctionReturn(PETSC_SUCCESS);
5675 }
5676 
5677 /******************************** FEM Support **********************************/
5678 
5679 PetscErrorCode DMPlexGetAllCells_Internal(DM plex, IS *cellIS)
5680 {
5681   PetscInt depth;
5682 
5683   PetscFunctionBegin;
5684   PetscCall(DMPlexGetDepth(plex, &depth));
5685   PetscCall(DMGetStratumIS(plex, "dim", depth, cellIS));
5686   if (!*cellIS) PetscCall(DMGetStratumIS(plex, "depth", depth, cellIS));
5687   PetscFunctionReturn(PETSC_SUCCESS);
5688 }
5689 
5690 PetscErrorCode DMPlexGetAllFaces_Internal(DM plex, IS *faceIS)
5691 {
5692   PetscInt depth;
5693 
5694   PetscFunctionBegin;
5695   PetscCall(DMPlexGetDepth(plex, &depth));
5696   PetscCall(DMGetStratumIS(plex, "dim", depth - 1, faceIS));
5697   if (!*faceIS) PetscCall(DMGetStratumIS(plex, "depth", depth - 1, faceIS));
5698   PetscFunctionReturn(PETSC_SUCCESS);
5699 }
5700 
5701 /*
5702  Returns number of components and tensor degree for the field.  For interpolated meshes, line should be a point
5703  representing a line in the section.
5704 */
5705 static PetscErrorCode PetscSectionFieldGetTensorDegree_Private(DM dm, PetscSection section, PetscInt field, PetscInt line, PetscInt *Nc, PetscInt *k, PetscBool *continuous, PetscBool *tensor)
5706 {
5707   PetscObject  obj;
5708   PetscClassId id;
5709   PetscFE      fe = NULL;
5710 
5711   PetscFunctionBeginHot;
5712   PetscCall(PetscSectionGetFieldComponents(section, field, Nc));
5713   PetscCall(DMGetField(dm, field, NULL, &obj));
5714   PetscCall(PetscObjectGetClassId(obj, &id));
5715   if (id == PETSCFE_CLASSID) fe = (PetscFE)obj;
5716 
5717   if (!fe) {
5718     /* Assume the full interpolated mesh is in the chart; lines in particular */
5719     /* An order k SEM disc has k-1 dofs on an edge */
5720     PetscCall(PetscSectionGetFieldDof(section, line, field, k));
5721     *k = *k / *Nc + 1;
5722   } else {
5723     PetscInt       dual_space_size, dim;
5724     PetscDualSpace dsp;
5725 
5726     PetscCall(DMGetDimension(dm, &dim));
5727     PetscCall(PetscFEGetDualSpace(fe, &dsp));
5728     PetscCall(PetscDualSpaceGetDimension(dsp, &dual_space_size));
5729     *k = (PetscInt)PetscCeilReal(PetscPowReal(dual_space_size / *Nc, 1.0 / dim)) - 1;
5730     PetscCall(PetscDualSpaceLagrangeGetContinuity(dsp, continuous));
5731     PetscCall(PetscDualSpaceLagrangeGetTensor(dsp, tensor));
5732   }
5733   PetscFunctionReturn(PETSC_SUCCESS);
5734 }
5735 
5736 static PetscErrorCode GetFieldSize_Private(PetscInt dim, PetscInt k, PetscBool tensor, PetscInt *dof)
5737 {
5738   PetscFunctionBeginHot;
5739   if (tensor) {
5740     *dof = PetscPowInt(k + 1, dim);
5741   } else {
5742     switch (dim) {
5743     case 1:
5744       *dof = k + 1;
5745       break;
5746     case 2:
5747       *dof = ((k + 1) * (k + 2)) / 2;
5748       break;
5749     case 3:
5750       *dof = ((k + 1) * (k + 2) * (k + 3)) / 6;
5751       break;
5752     default:
5753       *dof = 0;
5754     }
5755   }
5756   PetscFunctionReturn(PETSC_SUCCESS);
5757 }
5758 
5759 /*@
5760 
5761   DMPlexSetClosurePermutationTensor - Create a permutation from the default (BFS) point ordering in the closure, to a
5762   lexicographic ordering over the tensor product cell (i.e., line, quad, hex, etc.), and set this permutation in the
5763   section provided (or the section of the `DM`).
5764 
5765   Input Parameters:
5766 + dm      - The `DM`
5767 . point   - Either a cell (highest dim point) or an edge (dim 1 point), or `PETSC_DETERMINE`
5768 - section - The `PetscSection` to reorder, or `NULL` for the default section
5769 
5770   Example:
5771   A typical interpolated single-quad mesh might order points as
5772 .vb
5773   [c0, v1, v2, v3, v4, e5, e6, e7, e8]
5774 
5775   v4 -- e6 -- v3
5776   |           |
5777   e7    c0    e8
5778   |           |
5779   v1 -- e5 -- v2
5780 .ve
5781 
5782   (There is no significance to the ordering described here.)  The default section for a Q3 quad might typically assign
5783   dofs in the order of points, e.g.,
5784 .vb
5785     c0 -> [0,1,2,3]
5786     v1 -> [4]
5787     ...
5788     e5 -> [8, 9]
5789 .ve
5790 
5791   which corresponds to the dofs
5792 .vb
5793     6   10  11  7
5794     13  2   3   15
5795     12  0   1   14
5796     4   8   9   5
5797 .ve
5798 
5799   The closure in BFS ordering works through height strata (cells, edges, vertices) to produce the ordering
5800 .vb
5801   0 1 2 3 8 9 14 15 11 10 13 12 4 5 7 6
5802 .ve
5803 
5804   After calling DMPlexSetClosurePermutationTensor(), the closure will be ordered lexicographically,
5805 .vb
5806    4 8 9 5 12 0 1 14 13 2 3 15 6 10 11 7
5807 .ve
5808 
5809   Level: developer
5810 
5811   Notes:
5812   The point is used to determine the number of dofs/field on an edge. For SEM, this is related to the polynomial
5813   degree of the basis.
5814 
5815   This is required to run with libCEED.
5816 
5817 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMGetLocalSection()`, `PetscSectionSetClosurePermutation()`, `DMSetGlobalSection()`
5818 @*/
5819 PetscErrorCode DMPlexSetClosurePermutationTensor(DM dm, PetscInt point, PetscSection section)
5820 {
5821   DMLabel   label;
5822   PetscInt  dim, depth = -1, eStart = -1, Nf;
5823   PetscBool continuous = PETSC_TRUE, tensor = PETSC_TRUE;
5824 
5825   PetscFunctionBegin;
5826   PetscCall(DMGetDimension(dm, &dim));
5827   if (dim < 1) PetscFunctionReturn(PETSC_SUCCESS);
5828   if (point < 0) {
5829     PetscInt sStart, sEnd;
5830 
5831     PetscCall(DMPlexGetDepthStratum(dm, 1, &sStart, &sEnd));
5832     point = sEnd - sStart ? sStart : point;
5833   }
5834   PetscCall(DMPlexGetDepthLabel(dm, &label));
5835   if (point >= 0) PetscCall(DMLabelGetValue(label, point, &depth));
5836   if (!section) PetscCall(DMGetLocalSection(dm, &section));
5837   if (depth == 1) {
5838     eStart = point;
5839   } else if (depth == dim) {
5840     const PetscInt *cone;
5841 
5842     PetscCall(DMPlexGetCone(dm, point, &cone));
5843     if (dim == 2) eStart = cone[0];
5844     else if (dim == 3) {
5845       const PetscInt *cone2;
5846       PetscCall(DMPlexGetCone(dm, cone[0], &cone2));
5847       eStart = cone2[0];
5848     } 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);
5849   } 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);
5850 
5851   PetscCall(PetscSectionGetNumFields(section, &Nf));
5852   for (PetscInt d = 1; d <= dim; d++) {
5853     PetscInt  k, f, Nc, c, i, j, size = 0, offset = 0, foffset = 0;
5854     PetscInt *perm;
5855 
5856     for (f = 0; f < Nf; ++f) {
5857       PetscInt dof;
5858 
5859       PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
5860       PetscCheck(dim == 1 || tensor || !continuous, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Continuous field %" PetscInt_FMT " must have a tensor product discretization", f);
5861       if (!continuous && d < dim) continue;
5862       PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
5863       size += dof * Nc;
5864     }
5865     PetscCall(PetscMalloc1(size, &perm));
5866     for (f = 0; f < Nf; ++f) {
5867       switch (d) {
5868       case 1:
5869         PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
5870         if (!continuous && d < dim) continue;
5871         /*
5872          Original ordering is [ edge of length k-1; vtx0; vtx1 ]
5873          We want              [ vtx0; edge of length k-1; vtx1 ]
5874          */
5875         if (continuous) {
5876           for (c = 0; c < Nc; c++, offset++) perm[offset] = (k - 1) * Nc + c + foffset;
5877           for (i = 0; i < k - 1; i++)
5878             for (c = 0; c < Nc; c++, offset++) perm[offset] = i * Nc + c + foffset;
5879           for (c = 0; c < Nc; c++, offset++) perm[offset] = k * Nc + c + foffset;
5880           foffset = offset;
5881         } else {
5882           PetscInt dof;
5883 
5884           PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
5885           for (i = 0; i < dof * Nc; ++i, ++offset) perm[offset] = i + foffset;
5886           foffset = offset;
5887         }
5888         break;
5889       case 2:
5890         /* The original quad closure is oriented clockwise, {f, e_b, e_r, e_t, e_l, v_lb, v_rb, v_tr, v_tl} */
5891         PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
5892         if (!continuous && d < dim) continue;
5893         /* The SEM order is
5894 
5895          v_lb, {e_b}, v_rb,
5896          e^{(k-1)-i}_l, {f^{i*(k-1)}}, e^i_r,
5897          v_lt, reverse {e_t}, v_rt
5898          */
5899         if (continuous) {
5900           const PetscInt of   = 0;
5901           const PetscInt oeb  = of + PetscSqr(k - 1);
5902           const PetscInt oer  = oeb + (k - 1);
5903           const PetscInt oet  = oer + (k - 1);
5904           const PetscInt oel  = oet + (k - 1);
5905           const PetscInt ovlb = oel + (k - 1);
5906           const PetscInt ovrb = ovlb + 1;
5907           const PetscInt ovrt = ovrb + 1;
5908           const PetscInt ovlt = ovrt + 1;
5909           PetscInt       o;
5910 
5911           /* bottom */
5912           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlb * Nc + c + foffset;
5913           for (o = oeb; o < oer; ++o)
5914             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5915           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrb * Nc + c + foffset;
5916           /* middle */
5917           for (i = 0; i < k - 1; ++i) {
5918             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oel + (k - 2) - i) * Nc + c + foffset;
5919             for (o = of + (k - 1) * i; o < of + (k - 1) * (i + 1); ++o)
5920               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5921             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oer + i) * Nc + c + foffset;
5922           }
5923           /* top */
5924           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlt * Nc + c + foffset;
5925           for (o = oel - 1; o >= oet; --o)
5926             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5927           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrt * Nc + c + foffset;
5928           foffset = offset;
5929         } else {
5930           PetscInt dof;
5931 
5932           PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
5933           for (i = 0; i < dof * Nc; ++i, ++offset) perm[offset] = i + foffset;
5934           foffset = offset;
5935         }
5936         break;
5937       case 3:
5938         /* The original hex closure is
5939 
5940          {c,
5941          f_b, f_t, f_f, f_b, f_r, f_l,
5942          e_bl, e_bb, e_br, e_bf,  e_tf, e_tr, e_tb, e_tl,  e_rf, e_lf, e_lb, e_rb,
5943          v_blf, v_blb, v_brb, v_brf, v_tlf, v_trf, v_trb, v_tlb}
5944          */
5945         PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
5946         if (!continuous && d < dim) continue;
5947         /* The SEM order is
5948          Bottom Slice
5949          v_blf, {e^{(k-1)-n}_bf}, v_brf,
5950          e^{i}_bl, f^{n*(k-1)+(k-1)-i}_b, e^{(k-1)-i}_br,
5951          v_blb, {e_bb}, v_brb,
5952 
5953          Middle Slice (j)
5954          {e^{(k-1)-j}_lf}, {f^{j*(k-1)+n}_f}, e^j_rf,
5955          f^{i*(k-1)+j}_l, {c^{(j*(k-1) + i)*(k-1)+n}_t}, f^{j*(k-1)+i}_r,
5956          e^j_lb, {f^{j*(k-1)+(k-1)-n}_b}, e^{(k-1)-j}_rb,
5957 
5958          Top Slice
5959          v_tlf, {e_tf}, v_trf,
5960          e^{(k-1)-i}_tl, {f^{i*(k-1)}_t}, e^{i}_tr,
5961          v_tlb, {e^{(k-1)-n}_tb}, v_trb,
5962          */
5963         if (continuous) {
5964           const PetscInt oc    = 0;
5965           const PetscInt ofb   = oc + PetscSqr(k - 1) * (k - 1);
5966           const PetscInt oft   = ofb + PetscSqr(k - 1);
5967           const PetscInt off   = oft + PetscSqr(k - 1);
5968           const PetscInt ofk   = off + PetscSqr(k - 1);
5969           const PetscInt ofr   = ofk + PetscSqr(k - 1);
5970           const PetscInt ofl   = ofr + PetscSqr(k - 1);
5971           const PetscInt oebl  = ofl + PetscSqr(k - 1);
5972           const PetscInt oebb  = oebl + (k - 1);
5973           const PetscInt oebr  = oebb + (k - 1);
5974           const PetscInt oebf  = oebr + (k - 1);
5975           const PetscInt oetf  = oebf + (k - 1);
5976           const PetscInt oetr  = oetf + (k - 1);
5977           const PetscInt oetb  = oetr + (k - 1);
5978           const PetscInt oetl  = oetb + (k - 1);
5979           const PetscInt oerf  = oetl + (k - 1);
5980           const PetscInt oelf  = oerf + (k - 1);
5981           const PetscInt oelb  = oelf + (k - 1);
5982           const PetscInt oerb  = oelb + (k - 1);
5983           const PetscInt ovblf = oerb + (k - 1);
5984           const PetscInt ovblb = ovblf + 1;
5985           const PetscInt ovbrb = ovblb + 1;
5986           const PetscInt ovbrf = ovbrb + 1;
5987           const PetscInt ovtlf = ovbrf + 1;
5988           const PetscInt ovtrf = ovtlf + 1;
5989           const PetscInt ovtrb = ovtrf + 1;
5990           const PetscInt ovtlb = ovtrb + 1;
5991           PetscInt       o, n;
5992 
5993           /* Bottom Slice */
5994           /*   bottom */
5995           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblf * Nc + c + foffset;
5996           for (o = oetf - 1; o >= oebf; --o)
5997             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5998           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrf * Nc + c + foffset;
5999           /*   middle */
6000           for (i = 0; i < k - 1; ++i) {
6001             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebl + i) * Nc + c + foffset;
6002             for (n = 0; n < k - 1; ++n) {
6003               o = ofb + n * (k - 1) + i;
6004               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6005             }
6006             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebr + (k - 2) - i) * Nc + c + foffset;
6007           }
6008           /*   top */
6009           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblb * Nc + c + foffset;
6010           for (o = oebb; o < oebr; ++o)
6011             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6012           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrb * Nc + c + foffset;
6013 
6014           /* Middle Slice */
6015           for (j = 0; j < k - 1; ++j) {
6016             /*   bottom */
6017             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelf + (k - 2) - j) * Nc + c + foffset;
6018             for (o = off + j * (k - 1); o < off + (j + 1) * (k - 1); ++o)
6019               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6020             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerf + j) * Nc + c + foffset;
6021             /*   middle */
6022             for (i = 0; i < k - 1; ++i) {
6023               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofl + i * (k - 1) + j) * Nc + c + foffset;
6024               for (n = 0; n < k - 1; ++n)
6025                 for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oc + (j * (k - 1) + i) * (k - 1) + n) * Nc + c + foffset;
6026               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofr + j * (k - 1) + i) * Nc + c + foffset;
6027             }
6028             /*   top */
6029             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelb + j) * Nc + c + foffset;
6030             for (o = ofk + j * (k - 1) + (k - 2); o >= ofk + j * (k - 1); --o)
6031               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6032             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerb + (k - 2) - j) * Nc + c + foffset;
6033           }
6034 
6035           /* Top Slice */
6036           /*   bottom */
6037           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlf * Nc + c + foffset;
6038           for (o = oetf; o < oetr; ++o)
6039             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6040           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrf * Nc + c + foffset;
6041           /*   middle */
6042           for (i = 0; i < k - 1; ++i) {
6043             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetl + (k - 2) - i) * Nc + c + foffset;
6044             for (n = 0; n < k - 1; ++n)
6045               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oft + i * (k - 1) + n) * Nc + c + foffset;
6046             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetr + i) * Nc + c + foffset;
6047           }
6048           /*   top */
6049           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlb * Nc + c + foffset;
6050           for (o = oetl - 1; o >= oetb; --o)
6051             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6052           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrb * Nc + c + foffset;
6053 
6054           foffset = offset;
6055         } else {
6056           PetscInt dof;
6057 
6058           PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
6059           for (i = 0; i < dof * Nc; ++i, ++offset) perm[offset] = i + foffset;
6060           foffset = offset;
6061         }
6062         break;
6063       default:
6064         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "No spectral ordering for dimension %" PetscInt_FMT, d);
6065       }
6066     }
6067     PetscCheck(offset == size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Number of permutation entries %" PetscInt_FMT " != %" PetscInt_FMT, offset, size);
6068     /* Check permutation */
6069     {
6070       PetscInt *check;
6071 
6072       PetscCall(PetscMalloc1(size, &check));
6073       for (i = 0; i < size; ++i) {
6074         check[i] = -1;
6075         PetscCheck(perm[i] >= 0 && perm[i] < size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid permutation index p[%" PetscInt_FMT "] = %" PetscInt_FMT, i, perm[i]);
6076       }
6077       for (i = 0; i < size; ++i) check[perm[i]] = i;
6078       for (i = 0; i < size; ++i) PetscCheck(check[i] >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Missing permutation index %" PetscInt_FMT, i);
6079       PetscCall(PetscFree(check));
6080     }
6081     PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size, PETSC_OWN_POINTER, perm));
6082     if (d == dim) { // Add permutation for localized (in case this is a coordinate DM)
6083       PetscInt *loc_perm;
6084       PetscCall(PetscMalloc1(size * 2, &loc_perm));
6085       for (PetscInt i = 0; i < size; i++) {
6086         loc_perm[i]        = perm[i];
6087         loc_perm[size + i] = size + perm[i];
6088       }
6089       PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size * 2, PETSC_OWN_POINTER, loc_perm));
6090     }
6091   }
6092   PetscFunctionReturn(PETSC_SUCCESS);
6093 }
6094 
6095 PetscErrorCode DMPlexGetPointDualSpaceFEM(DM dm, PetscInt point, PetscInt field, PetscDualSpace *dspace)
6096 {
6097   PetscDS  prob;
6098   PetscInt depth, Nf, h;
6099   DMLabel  label;
6100 
6101   PetscFunctionBeginHot;
6102   PetscCall(DMGetDS(dm, &prob));
6103   Nf      = prob->Nf;
6104   label   = dm->depthLabel;
6105   *dspace = NULL;
6106   if (field < Nf) {
6107     PetscObject disc = prob->disc[field];
6108 
6109     if (disc->classid == PETSCFE_CLASSID) {
6110       PetscDualSpace dsp;
6111 
6112       PetscCall(PetscFEGetDualSpace((PetscFE)disc, &dsp));
6113       PetscCall(DMLabelGetNumValues(label, &depth));
6114       PetscCall(DMLabelGetValue(label, point, &h));
6115       h = depth - 1 - h;
6116       if (h) {
6117         PetscCall(PetscDualSpaceGetHeightSubspace(dsp, h, dspace));
6118       } else {
6119         *dspace = dsp;
6120       }
6121     }
6122   }
6123   PetscFunctionReturn(PETSC_SUCCESS);
6124 }
6125 
6126 static inline PetscErrorCode DMPlexVecGetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6127 {
6128   PetscScalar       *array;
6129   const PetscScalar *vArray;
6130   const PetscInt    *cone, *coneO;
6131   PetscInt           pStart, pEnd, p, numPoints, size = 0, offset = 0;
6132 
6133   PetscFunctionBeginHot;
6134   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6135   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
6136   PetscCall(DMPlexGetCone(dm, point, &cone));
6137   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
6138   if (!values || !*values) {
6139     if ((point >= pStart) && (point < pEnd)) {
6140       PetscInt dof;
6141 
6142       PetscCall(PetscSectionGetDof(section, point, &dof));
6143       size += dof;
6144     }
6145     for (p = 0; p < numPoints; ++p) {
6146       const PetscInt cp = cone[p];
6147       PetscInt       dof;
6148 
6149       if ((cp < pStart) || (cp >= pEnd)) continue;
6150       PetscCall(PetscSectionGetDof(section, cp, &dof));
6151       size += dof;
6152     }
6153     if (!values) {
6154       if (csize) *csize = size;
6155       PetscFunctionReturn(PETSC_SUCCESS);
6156     }
6157     PetscCall(DMGetWorkArray(dm, size, MPIU_SCALAR, &array));
6158   } else {
6159     array = *values;
6160   }
6161   size = 0;
6162   PetscCall(VecGetArrayRead(v, &vArray));
6163   if ((point >= pStart) && (point < pEnd)) {
6164     PetscInt           dof, off, d;
6165     const PetscScalar *varr;
6166 
6167     PetscCall(PetscSectionGetDof(section, point, &dof));
6168     PetscCall(PetscSectionGetOffset(section, point, &off));
6169     varr = PetscSafePointerPlusOffset(vArray, off);
6170     for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
6171     size += dof;
6172   }
6173   for (p = 0; p < numPoints; ++p) {
6174     const PetscInt     cp = cone[p];
6175     PetscInt           o  = coneO[p];
6176     PetscInt           dof, off, d;
6177     const PetscScalar *varr;
6178 
6179     if ((cp < pStart) || (cp >= pEnd)) continue;
6180     PetscCall(PetscSectionGetDof(section, cp, &dof));
6181     PetscCall(PetscSectionGetOffset(section, cp, &off));
6182     varr = PetscSafePointerPlusOffset(vArray, off);
6183     if (o >= 0) {
6184       for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
6185     } else {
6186       for (d = dof - 1; d >= 0; --d, ++offset) array[offset] = varr[d];
6187     }
6188     size += dof;
6189   }
6190   PetscCall(VecRestoreArrayRead(v, &vArray));
6191   if (!*values) {
6192     if (csize) *csize = size;
6193     *values = array;
6194   } else {
6195     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
6196     *csize = size;
6197   }
6198   PetscFunctionReturn(PETSC_SUCCESS);
6199 }
6200 
6201 /* Compress out points not in the section */
6202 static inline PetscErrorCode CompressPoints_Private(PetscSection section, PetscInt *numPoints, PetscInt points[])
6203 {
6204   const PetscInt np = *numPoints;
6205   PetscInt       pStart, pEnd, p, q;
6206 
6207   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6208   for (p = 0, q = 0; p < np; ++p) {
6209     const PetscInt r = points[p * 2];
6210     if ((r >= pStart) && (r < pEnd)) {
6211       points[q * 2]     = r;
6212       points[q * 2 + 1] = points[p * 2 + 1];
6213       ++q;
6214     }
6215   }
6216   *numPoints = q;
6217   return PETSC_SUCCESS;
6218 }
6219 
6220 /* Compressed closure does not apply closure permutation */
6221 PetscErrorCode DMPlexGetCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt ornt, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
6222 {
6223   const PetscInt *cla = NULL;
6224   PetscInt        np, *pts = NULL;
6225 
6226   PetscFunctionBeginHot;
6227   PetscCall(PetscSectionGetClosureIndex(section, (PetscObject)dm, clSec, clPoints));
6228   if (!ornt && *clPoints) {
6229     PetscInt dof, off;
6230 
6231     PetscCall(PetscSectionGetDof(*clSec, point, &dof));
6232     PetscCall(PetscSectionGetOffset(*clSec, point, &off));
6233     PetscCall(ISGetIndices(*clPoints, &cla));
6234     np  = dof / 2;
6235     pts = PetscSafePointerPlusOffset((PetscInt *)cla, off);
6236   } else {
6237     PetscCall(DMPlexGetTransitiveClosure_Internal(dm, point, ornt, PETSC_TRUE, &np, &pts));
6238     PetscCall(CompressPoints_Private(section, &np, pts));
6239   }
6240   *numPoints = np;
6241   *points    = pts;
6242   *clp       = cla;
6243   PetscFunctionReturn(PETSC_SUCCESS);
6244 }
6245 
6246 PetscErrorCode DMPlexRestoreCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
6247 {
6248   PetscFunctionBeginHot;
6249   if (!*clPoints) {
6250     PetscCall(DMPlexRestoreTransitiveClosure(dm, point, PETSC_TRUE, numPoints, points));
6251   } else {
6252     PetscCall(ISRestoreIndices(*clPoints, clp));
6253   }
6254   *numPoints = 0;
6255   *points    = NULL;
6256   *clSec     = NULL;
6257   *clPoints  = NULL;
6258   *clp       = NULL;
6259   PetscFunctionReturn(PETSC_SUCCESS);
6260 }
6261 
6262 static inline PetscErrorCode DMPlexVecGetClosure_Static(DM dm, PetscSection section, PetscInt numPoints, const PetscInt points[], const PetscInt clperm[], const PetscScalar vArray[], PetscInt *size, PetscScalar array[])
6263 {
6264   PetscInt            offset = 0, p;
6265   const PetscInt    **perms  = NULL;
6266   const PetscScalar **flips  = NULL;
6267 
6268   PetscFunctionBeginHot;
6269   *size = 0;
6270   PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
6271   for (p = 0; p < numPoints; p++) {
6272     const PetscInt     point = points[2 * p];
6273     const PetscInt    *perm  = perms ? perms[p] : NULL;
6274     const PetscScalar *flip  = flips ? flips[p] : NULL;
6275     PetscInt           dof, off, d;
6276     const PetscScalar *varr;
6277 
6278     PetscCall(PetscSectionGetDof(section, point, &dof));
6279     PetscCall(PetscSectionGetOffset(section, point, &off));
6280     varr = PetscSafePointerPlusOffset(vArray, off);
6281     if (clperm) {
6282       if (perm) {
6283         for (d = 0; d < dof; d++) array[clperm[offset + perm[d]]] = varr[d];
6284       } else {
6285         for (d = 0; d < dof; d++) array[clperm[offset + d]] = varr[d];
6286       }
6287       if (flip) {
6288         for (d = 0; d < dof; d++) array[clperm[offset + d]] *= flip[d];
6289       }
6290     } else {
6291       if (perm) {
6292         for (d = 0; d < dof; d++) array[offset + perm[d]] = varr[d];
6293       } else {
6294         for (d = 0; d < dof; d++) array[offset + d] = varr[d];
6295       }
6296       if (flip) {
6297         for (d = 0; d < dof; d++) array[offset + d] *= flip[d];
6298       }
6299     }
6300     offset += dof;
6301   }
6302   PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
6303   *size = offset;
6304   PetscFunctionReturn(PETSC_SUCCESS);
6305 }
6306 
6307 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[])
6308 {
6309   PetscInt offset = 0, f;
6310 
6311   PetscFunctionBeginHot;
6312   *size = 0;
6313   for (f = 0; f < numFields; ++f) {
6314     PetscInt            p;
6315     const PetscInt    **perms = NULL;
6316     const PetscScalar **flips = NULL;
6317 
6318     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6319     for (p = 0; p < numPoints; p++) {
6320       const PetscInt     point = points[2 * p];
6321       PetscInt           fdof, foff, b;
6322       const PetscScalar *varr;
6323       const PetscInt    *perm = perms ? perms[p] : NULL;
6324       const PetscScalar *flip = flips ? flips[p] : NULL;
6325 
6326       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6327       PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6328       varr = &vArray[foff];
6329       if (clperm) {
6330         if (perm) {
6331           for (b = 0; b < fdof; b++) array[clperm[offset + perm[b]]] = varr[b];
6332         } else {
6333           for (b = 0; b < fdof; b++) array[clperm[offset + b]] = varr[b];
6334         }
6335         if (flip) {
6336           for (b = 0; b < fdof; b++) array[clperm[offset + b]] *= flip[b];
6337         }
6338       } else {
6339         if (perm) {
6340           for (b = 0; b < fdof; b++) array[offset + perm[b]] = varr[b];
6341         } else {
6342           for (b = 0; b < fdof; b++) array[offset + b] = varr[b];
6343         }
6344         if (flip) {
6345           for (b = 0; b < fdof; b++) array[offset + b] *= flip[b];
6346         }
6347       }
6348       offset += fdof;
6349     }
6350     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6351   }
6352   *size = offset;
6353   PetscFunctionReturn(PETSC_SUCCESS);
6354 }
6355 
6356 PetscErrorCode DMPlexVecGetOrientedClosure_Internal(DM dm, PetscSection section, PetscBool useClPerm, Vec v, PetscInt point, PetscInt ornt, PetscInt *csize, PetscScalar *values[])
6357 {
6358   PetscSection    clSection;
6359   IS              clPoints;
6360   PetscInt       *points = NULL;
6361   const PetscInt *clp, *perm = NULL;
6362   PetscInt        depth, numFields, numPoints, asize;
6363 
6364   PetscFunctionBeginHot;
6365   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6366   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6367   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6368   PetscValidHeaderSpecific(v, VEC_CLASSID, 4);
6369   PetscCall(DMPlexGetDepth(dm, &depth));
6370   PetscCall(PetscSectionGetNumFields(section, &numFields));
6371   if (depth == 1 && numFields < 2) {
6372     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6373     PetscFunctionReturn(PETSC_SUCCESS);
6374   }
6375   /* Get points */
6376   PetscCall(DMPlexGetCompressedClosure(dm, section, point, ornt, &numPoints, &points, &clSection, &clPoints, &clp));
6377   /* Get sizes */
6378   asize = 0;
6379   for (PetscInt p = 0; p < numPoints * 2; p += 2) {
6380     PetscInt dof;
6381     PetscCall(PetscSectionGetDof(section, points[p], &dof));
6382     asize += dof;
6383   }
6384   if (values) {
6385     const PetscScalar *vArray;
6386     PetscInt           size;
6387 
6388     if (*values) {
6389       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);
6390     } else PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, values));
6391     if (useClPerm) PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, asize, &perm));
6392     PetscCall(VecGetArrayRead(v, &vArray));
6393     /* Get values */
6394     if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, numPoints, points, numFields, perm, vArray, &size, *values));
6395     else PetscCall(DMPlexVecGetClosure_Static(dm, section, numPoints, points, perm, vArray, &size, *values));
6396     PetscCheck(asize == size, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Section size %" PetscInt_FMT " does not match Vec closure size %" PetscInt_FMT, asize, size);
6397     /* Cleanup array */
6398     PetscCall(VecRestoreArrayRead(v, &vArray));
6399   }
6400   if (csize) *csize = asize;
6401   /* Cleanup points */
6402   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6403   PetscFunctionReturn(PETSC_SUCCESS);
6404 }
6405 
6406 /*@C
6407   DMPlexVecGetClosure - Get an array of the values on the closure of 'point'
6408 
6409   Not collective
6410 
6411   Input Parameters:
6412 + dm      - The `DM`
6413 . section - The section describing the layout in `v`, or `NULL` to use the default section
6414 . v       - The local vector
6415 - point   - The point in the `DM`
6416 
6417   Input/Output Parameters:
6418 + csize  - The size of the input values array, or `NULL`; on output the number of values in the closure
6419 - values - An array to use for the values, or `NULL` to have it allocated automatically;
6420            if the user provided `NULL`, it is a borrowed array and should not be freed
6421 
6422   Level: intermediate
6423 
6424   Notes:
6425   `DMPlexVecGetClosure()`/`DMPlexVecRestoreClosure()` only allocates the values array if it set to `NULL` in the
6426   calling function. This is because `DMPlexVecGetClosure()` is typically called in the inner loop of a `Vec` or `Mat`
6427   assembly function, and a user may already have allocated storage for this operation.
6428 
6429   A typical use could be
6430 .vb
6431    values = NULL;
6432    PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
6433    for (cl = 0; cl < clSize; ++cl) {
6434      <Compute on closure>
6435    }
6436    PetscCall(DMPlexVecRestoreClosure(dm, NULL, v, p, &clSize, &values));
6437 .ve
6438   or
6439 .vb
6440    PetscMalloc1(clMaxSize, &values);
6441    for (p = pStart; p < pEnd; ++p) {
6442      clSize = clMaxSize;
6443      PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
6444      for (cl = 0; cl < clSize; ++cl) {
6445        <Compute on closure>
6446      }
6447    }
6448    PetscFree(values);
6449 .ve
6450 
6451   Fortran Notes:
6452   The `csize` argument is not present in the Fortran binding since it is internal to the array.
6453 
6454 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecRestoreClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6455 @*/
6456 PetscErrorCode DMPlexVecGetClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6457 {
6458   PetscFunctionBeginHot;
6459   PetscCall(DMPlexVecGetOrientedClosure_Internal(dm, section, PETSC_TRUE, v, point, 0, csize, values));
6460   PetscFunctionReturn(PETSC_SUCCESS);
6461 }
6462 
6463 PetscErrorCode DMPlexVecGetClosureAtDepth_Internal(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt depth, PetscInt *csize, PetscScalar *values[])
6464 {
6465   DMLabel            depthLabel;
6466   PetscSection       clSection;
6467   IS                 clPoints;
6468   PetscScalar       *array;
6469   const PetscScalar *vArray;
6470   PetscInt          *points = NULL;
6471   const PetscInt    *clp, *perm = NULL;
6472   PetscInt           mdepth, numFields, numPoints, Np = 0, p, clsize, size;
6473 
6474   PetscFunctionBeginHot;
6475   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6476   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6477   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6478   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6479   PetscCall(DMPlexGetDepth(dm, &mdepth));
6480   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
6481   PetscCall(PetscSectionGetNumFields(section, &numFields));
6482   if (mdepth == 1 && numFields < 2) {
6483     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6484     PetscFunctionReturn(PETSC_SUCCESS);
6485   }
6486   /* Get points */
6487   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
6488   for (clsize = 0, p = 0; p < Np; p++) {
6489     PetscInt dof;
6490     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
6491     clsize += dof;
6492   }
6493   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &perm));
6494   /* Filter points */
6495   for (p = 0; p < numPoints * 2; p += 2) {
6496     PetscInt dep;
6497 
6498     PetscCall(DMLabelGetValue(depthLabel, points[p], &dep));
6499     if (dep != depth) continue;
6500     points[Np * 2 + 0] = points[p];
6501     points[Np * 2 + 1] = points[p + 1];
6502     ++Np;
6503   }
6504   /* Get array */
6505   if (!values || !*values) {
6506     PetscInt asize = 0, dof;
6507 
6508     for (p = 0; p < Np * 2; p += 2) {
6509       PetscCall(PetscSectionGetDof(section, points[p], &dof));
6510       asize += dof;
6511     }
6512     if (!values) {
6513       PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6514       if (csize) *csize = asize;
6515       PetscFunctionReturn(PETSC_SUCCESS);
6516     }
6517     PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, &array));
6518   } else {
6519     array = *values;
6520   }
6521   PetscCall(VecGetArrayRead(v, &vArray));
6522   /* Get values */
6523   if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, Np, points, numFields, perm, vArray, &size, array));
6524   else PetscCall(DMPlexVecGetClosure_Static(dm, section, Np, points, perm, vArray, &size, array));
6525   /* Cleanup points */
6526   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6527   /* Cleanup array */
6528   PetscCall(VecRestoreArrayRead(v, &vArray));
6529   if (!*values) {
6530     if (csize) *csize = size;
6531     *values = array;
6532   } else {
6533     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
6534     *csize = size;
6535   }
6536   PetscFunctionReturn(PETSC_SUCCESS);
6537 }
6538 
6539 /*@C
6540   DMPlexVecRestoreClosure - Restore the array of the values on the closure of 'point'
6541 
6542   Not collective
6543 
6544   Input Parameters:
6545 + dm      - The `DM`
6546 . section - The section describing the layout in `v`, or `NULL` to use the default section
6547 . v       - The local vector
6548 . point   - The point in the `DM`
6549 . csize   - The number of values in the closure, or `NULL`
6550 - values  - The array of values, which is a borrowed array and should not be freed
6551 
6552   Level: intermediate
6553 
6554   Note:
6555   The array values are discarded and not copied back into `v`. In order to copy values back to `v`, use `DMPlexVecSetClosure()`
6556 
6557   Fortran Notes:
6558   The `csize` argument is not present in the Fortran binding since it is internal to the array.
6559 
6560 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6561 @*/
6562 PetscErrorCode DMPlexVecRestoreClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6563 {
6564   PetscInt size = 0;
6565 
6566   PetscFunctionBegin;
6567   /* Should work without recalculating size */
6568   PetscCall(DMRestoreWorkArray(dm, size, MPIU_SCALAR, (void *)values));
6569   *values = NULL;
6570   PetscFunctionReturn(PETSC_SUCCESS);
6571 }
6572 
6573 static inline void add(PetscScalar *x, PetscScalar y)
6574 {
6575   *x += y;
6576 }
6577 static inline void insert(PetscScalar *x, PetscScalar y)
6578 {
6579   *x = y;
6580 }
6581 
6582 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[])
6583 {
6584   PetscInt        cdof;  /* The number of constraints on this point */
6585   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6586   PetscScalar    *a;
6587   PetscInt        off, cind = 0, k;
6588 
6589   PetscFunctionBegin;
6590   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6591   PetscCall(PetscSectionGetOffset(section, point, &off));
6592   a = &array[off];
6593   if (!cdof || setBC) {
6594     if (clperm) {
6595       if (perm) {
6596         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6597       } else {
6598         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6599       }
6600     } else {
6601       if (perm) {
6602         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6603       } else {
6604         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6605       }
6606     }
6607   } else {
6608     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6609     if (clperm) {
6610       if (perm) {
6611         for (k = 0; k < dof; ++k) {
6612           if ((cind < cdof) && (k == cdofs[cind])) {
6613             ++cind;
6614             continue;
6615           }
6616           fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6617         }
6618       } else {
6619         for (k = 0; k < dof; ++k) {
6620           if ((cind < cdof) && (k == cdofs[cind])) {
6621             ++cind;
6622             continue;
6623           }
6624           fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6625         }
6626       }
6627     } else {
6628       if (perm) {
6629         for (k = 0; k < dof; ++k) {
6630           if ((cind < cdof) && (k == cdofs[cind])) {
6631             ++cind;
6632             continue;
6633           }
6634           fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6635         }
6636       } else {
6637         for (k = 0; k < dof; ++k) {
6638           if ((cind < cdof) && (k == cdofs[cind])) {
6639             ++cind;
6640             continue;
6641           }
6642           fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6643         }
6644       }
6645     }
6646   }
6647   PetscFunctionReturn(PETSC_SUCCESS);
6648 }
6649 
6650 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[])
6651 {
6652   PetscInt        cdof;  /* The number of constraints on this point */
6653   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6654   PetscScalar    *a;
6655   PetscInt        off, cind = 0, k;
6656 
6657   PetscFunctionBegin;
6658   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6659   PetscCall(PetscSectionGetOffset(section, point, &off));
6660   a = &array[off];
6661   if (cdof) {
6662     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6663     if (clperm) {
6664       if (perm) {
6665         for (k = 0; k < dof; ++k) {
6666           if ((cind < cdof) && (k == cdofs[cind])) {
6667             fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6668             cind++;
6669           }
6670         }
6671       } else {
6672         for (k = 0; k < dof; ++k) {
6673           if ((cind < cdof) && (k == cdofs[cind])) {
6674             fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6675             cind++;
6676           }
6677         }
6678       }
6679     } else {
6680       if (perm) {
6681         for (k = 0; k < dof; ++k) {
6682           if ((cind < cdof) && (k == cdofs[cind])) {
6683             fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6684             cind++;
6685           }
6686         }
6687       } else {
6688         for (k = 0; k < dof; ++k) {
6689           if ((cind < cdof) && (k == cdofs[cind])) {
6690             fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6691             cind++;
6692           }
6693         }
6694       }
6695     }
6696   }
6697   PetscFunctionReturn(PETSC_SUCCESS);
6698 }
6699 
6700 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[])
6701 {
6702   PetscScalar    *a;
6703   PetscInt        fdof, foff, fcdof, foffset = *offset;
6704   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6705   PetscInt        cind = 0, b;
6706 
6707   PetscFunctionBegin;
6708   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6709   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6710   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6711   a = &array[foff];
6712   if (!fcdof || setBC) {
6713     if (clperm) {
6714       if (perm) {
6715         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6716       } else {
6717         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6718       }
6719     } else {
6720       if (perm) {
6721         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6722       } else {
6723         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6724       }
6725     }
6726   } else {
6727     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6728     if (clperm) {
6729       if (perm) {
6730         for (b = 0; b < fdof; b++) {
6731           if ((cind < fcdof) && (b == fcdofs[cind])) {
6732             ++cind;
6733             continue;
6734           }
6735           fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6736         }
6737       } else {
6738         for (b = 0; b < fdof; b++) {
6739           if ((cind < fcdof) && (b == fcdofs[cind])) {
6740             ++cind;
6741             continue;
6742           }
6743           fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6744         }
6745       }
6746     } else {
6747       if (perm) {
6748         for (b = 0; b < fdof; b++) {
6749           if ((cind < fcdof) && (b == fcdofs[cind])) {
6750             ++cind;
6751             continue;
6752           }
6753           fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6754         }
6755       } else {
6756         for (b = 0; b < fdof; b++) {
6757           if ((cind < fcdof) && (b == fcdofs[cind])) {
6758             ++cind;
6759             continue;
6760           }
6761           fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6762         }
6763       }
6764     }
6765   }
6766   *offset += fdof;
6767   PetscFunctionReturn(PETSC_SUCCESS);
6768 }
6769 
6770 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[])
6771 {
6772   PetscScalar    *a;
6773   PetscInt        fdof, foff, fcdof, foffset = *offset;
6774   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6775   PetscInt        Nc, cind = 0, ncind = 0, b;
6776   PetscBool       ncSet, fcSet;
6777 
6778   PetscFunctionBegin;
6779   PetscCall(PetscSectionGetFieldComponents(section, f, &Nc));
6780   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6781   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6782   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6783   a = &array[foff];
6784   if (fcdof) {
6785     /* We just override fcdof and fcdofs with Ncc and comps */
6786     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6787     if (clperm) {
6788       if (perm) {
6789         if (comps) {
6790           for (b = 0; b < fdof; b++) {
6791             ncSet = fcSet = PETSC_FALSE;
6792             if (b % Nc == comps[ncind]) {
6793               ncind = (ncind + 1) % Ncc;
6794               ncSet = PETSC_TRUE;
6795             }
6796             if ((cind < fcdof) && (b == fcdofs[cind])) {
6797               ++cind;
6798               fcSet = PETSC_TRUE;
6799             }
6800             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6801           }
6802         } else {
6803           for (b = 0; b < fdof; b++) {
6804             if ((cind < fcdof) && (b == fcdofs[cind])) {
6805               fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6806               ++cind;
6807             }
6808           }
6809         }
6810       } else {
6811         if (comps) {
6812           for (b = 0; b < fdof; b++) {
6813             ncSet = fcSet = PETSC_FALSE;
6814             if (b % Nc == comps[ncind]) {
6815               ncind = (ncind + 1) % Ncc;
6816               ncSet = PETSC_TRUE;
6817             }
6818             if ((cind < fcdof) && (b == fcdofs[cind])) {
6819               ++cind;
6820               fcSet = PETSC_TRUE;
6821             }
6822             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6823           }
6824         } else {
6825           for (b = 0; b < fdof; b++) {
6826             if ((cind < fcdof) && (b == fcdofs[cind])) {
6827               fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6828               ++cind;
6829             }
6830           }
6831         }
6832       }
6833     } else {
6834       if (perm) {
6835         if (comps) {
6836           for (b = 0; b < fdof; b++) {
6837             ncSet = fcSet = PETSC_FALSE;
6838             if (b % Nc == comps[ncind]) {
6839               ncind = (ncind + 1) % Ncc;
6840               ncSet = PETSC_TRUE;
6841             }
6842             if ((cind < fcdof) && (b == fcdofs[cind])) {
6843               ++cind;
6844               fcSet = PETSC_TRUE;
6845             }
6846             if (ncSet && fcSet) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6847           }
6848         } else {
6849           for (b = 0; b < fdof; b++) {
6850             if ((cind < fcdof) && (b == fcdofs[cind])) {
6851               fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6852               ++cind;
6853             }
6854           }
6855         }
6856       } else {
6857         if (comps) {
6858           for (b = 0; b < fdof; b++) {
6859             ncSet = fcSet = PETSC_FALSE;
6860             if (b % Nc == comps[ncind]) {
6861               ncind = (ncind + 1) % Ncc;
6862               ncSet = PETSC_TRUE;
6863             }
6864             if ((cind < fcdof) && (b == fcdofs[cind])) {
6865               ++cind;
6866               fcSet = PETSC_TRUE;
6867             }
6868             if (ncSet && fcSet) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6869           }
6870         } else {
6871           for (b = 0; b < fdof; b++) {
6872             if ((cind < fcdof) && (b == fcdofs[cind])) {
6873               fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6874               ++cind;
6875             }
6876           }
6877         }
6878       }
6879     }
6880   }
6881   *offset += fdof;
6882   PetscFunctionReturn(PETSC_SUCCESS);
6883 }
6884 
6885 static inline PetscErrorCode DMPlexVecSetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6886 {
6887   PetscScalar    *array;
6888   const PetscInt *cone, *coneO;
6889   PetscInt        pStart, pEnd, p, numPoints, off, dof;
6890 
6891   PetscFunctionBeginHot;
6892   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6893   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
6894   PetscCall(DMPlexGetCone(dm, point, &cone));
6895   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
6896   PetscCall(VecGetArray(v, &array));
6897   for (p = 0, off = 0; p <= numPoints; ++p, off += dof) {
6898     const PetscInt cp = !p ? point : cone[p - 1];
6899     const PetscInt o  = !p ? 0 : coneO[p - 1];
6900 
6901     if ((cp < pStart) || (cp >= pEnd)) {
6902       dof = 0;
6903       continue;
6904     }
6905     PetscCall(PetscSectionGetDof(section, cp, &dof));
6906     /* ADD_VALUES */
6907     {
6908       const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6909       PetscScalar    *a;
6910       PetscInt        cdof, coff, cind = 0, k;
6911 
6912       PetscCall(PetscSectionGetConstraintDof(section, cp, &cdof));
6913       PetscCall(PetscSectionGetOffset(section, cp, &coff));
6914       a = &array[coff];
6915       if (!cdof) {
6916         if (o >= 0) {
6917           for (k = 0; k < dof; ++k) a[k] += values[off + k];
6918         } else {
6919           for (k = 0; k < dof; ++k) a[k] += values[off + dof - k - 1];
6920         }
6921       } else {
6922         PetscCall(PetscSectionGetConstraintIndices(section, cp, &cdofs));
6923         if (o >= 0) {
6924           for (k = 0; k < dof; ++k) {
6925             if ((cind < cdof) && (k == cdofs[cind])) {
6926               ++cind;
6927               continue;
6928             }
6929             a[k] += values[off + k];
6930           }
6931         } else {
6932           for (k = 0; k < dof; ++k) {
6933             if ((cind < cdof) && (k == cdofs[cind])) {
6934               ++cind;
6935               continue;
6936             }
6937             a[k] += values[off + dof - k - 1];
6938           }
6939         }
6940       }
6941     }
6942   }
6943   PetscCall(VecRestoreArray(v, &array));
6944   PetscFunctionReturn(PETSC_SUCCESS);
6945 }
6946 
6947 /*@C
6948   DMPlexVecSetClosure - Set an array of the values on the closure of `point`
6949 
6950   Not collective
6951 
6952   Input Parameters:
6953 + dm      - The `DM`
6954 . section - The section describing the layout in `v`, or `NULL` to use the default section
6955 . v       - The local vector
6956 . point   - The point in the `DM`
6957 . values  - The array of values
6958 - mode    - The insert mode. One of `INSERT_ALL_VALUES`, `ADD_ALL_VALUES`, `INSERT_VALUES`, `ADD_VALUES`, `INSERT_BC_VALUES`, and `ADD_BC_VALUES`,
6959          where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions.
6960 
6961   Level: intermediate
6962 
6963 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`
6964 @*/
6965 PetscErrorCode DMPlexVecSetClosure(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6966 {
6967   PetscSection    clSection;
6968   IS              clPoints;
6969   PetscScalar    *array;
6970   PetscInt       *points = NULL;
6971   const PetscInt *clp, *clperm = NULL;
6972   PetscInt        depth, numFields, numPoints, p, clsize;
6973 
6974   PetscFunctionBeginHot;
6975   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6976   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6977   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6978   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6979   PetscCall(DMPlexGetDepth(dm, &depth));
6980   PetscCall(PetscSectionGetNumFields(section, &numFields));
6981   if (depth == 1 && numFields < 2 && mode == ADD_VALUES) {
6982     PetscCall(DMPlexVecSetClosure_Depth1_Static(dm, section, v, point, values, mode));
6983     PetscFunctionReturn(PETSC_SUCCESS);
6984   }
6985   /* Get points */
6986   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
6987   for (clsize = 0, p = 0; p < numPoints; p++) {
6988     PetscInt dof;
6989     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
6990     clsize += dof;
6991   }
6992   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
6993   /* Get array */
6994   PetscCall(VecGetArray(v, &array));
6995   /* Get values */
6996   if (numFields > 0) {
6997     PetscInt offset = 0, f;
6998     for (f = 0; f < numFields; ++f) {
6999       const PetscInt    **perms = NULL;
7000       const PetscScalar **flips = NULL;
7001 
7002       PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7003       switch (mode) {
7004       case INSERT_VALUES:
7005         for (p = 0; p < numPoints; p++) {
7006           const PetscInt     point = points[2 * p];
7007           const PetscInt    *perm  = perms ? perms[p] : NULL;
7008           const PetscScalar *flip  = flips ? flips[p] : NULL;
7009           PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, clperm, values, &offset, array));
7010         }
7011         break;
7012       case INSERT_ALL_VALUES:
7013         for (p = 0; p < numPoints; p++) {
7014           const PetscInt     point = points[2 * p];
7015           const PetscInt    *perm  = perms ? perms[p] : NULL;
7016           const PetscScalar *flip  = flips ? flips[p] : NULL;
7017           PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, clperm, values, &offset, array));
7018         }
7019         break;
7020       case INSERT_BC_VALUES:
7021         for (p = 0; p < numPoints; p++) {
7022           const PetscInt     point = points[2 * p];
7023           const PetscInt    *perm  = perms ? perms[p] : NULL;
7024           const PetscScalar *flip  = flips ? flips[p] : NULL;
7025           PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, insert, clperm, values, &offset, array));
7026         }
7027         break;
7028       case ADD_VALUES:
7029         for (p = 0; p < numPoints; p++) {
7030           const PetscInt     point = points[2 * p];
7031           const PetscInt    *perm  = perms ? perms[p] : NULL;
7032           const PetscScalar *flip  = flips ? flips[p] : NULL;
7033           PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, clperm, values, &offset, array));
7034         }
7035         break;
7036       case ADD_ALL_VALUES:
7037         for (p = 0; p < numPoints; p++) {
7038           const PetscInt     point = points[2 * p];
7039           const PetscInt    *perm  = perms ? perms[p] : NULL;
7040           const PetscScalar *flip  = flips ? flips[p] : NULL;
7041           PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, clperm, values, &offset, array));
7042         }
7043         break;
7044       case ADD_BC_VALUES:
7045         for (p = 0; p < numPoints; p++) {
7046           const PetscInt     point = points[2 * p];
7047           const PetscInt    *perm  = perms ? perms[p] : NULL;
7048           const PetscScalar *flip  = flips ? flips[p] : NULL;
7049           PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, add, clperm, values, &offset, array));
7050         }
7051         break;
7052       default:
7053         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
7054       }
7055       PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7056     }
7057   } else {
7058     PetscInt            dof, off;
7059     const PetscInt    **perms = NULL;
7060     const PetscScalar **flips = NULL;
7061 
7062     PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
7063     switch (mode) {
7064     case INSERT_VALUES:
7065       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7066         const PetscInt     point = points[2 * p];
7067         const PetscInt    *perm  = perms ? perms[p] : NULL;
7068         const PetscScalar *flip  = flips ? flips[p] : NULL;
7069         PetscCall(PetscSectionGetDof(section, point, &dof));
7070         PetscCall(updatePoint_private(section, point, dof, insert, PETSC_FALSE, perm, flip, clperm, values, off, array));
7071       }
7072       break;
7073     case INSERT_ALL_VALUES:
7074       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7075         const PetscInt     point = points[2 * p];
7076         const PetscInt    *perm  = perms ? perms[p] : NULL;
7077         const PetscScalar *flip  = flips ? flips[p] : NULL;
7078         PetscCall(PetscSectionGetDof(section, point, &dof));
7079         PetscCall(updatePoint_private(section, point, dof, insert, PETSC_TRUE, perm, flip, clperm, values, off, array));
7080       }
7081       break;
7082     case INSERT_BC_VALUES:
7083       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7084         const PetscInt     point = points[2 * p];
7085         const PetscInt    *perm  = perms ? perms[p] : NULL;
7086         const PetscScalar *flip  = flips ? flips[p] : NULL;
7087         PetscCall(PetscSectionGetDof(section, point, &dof));
7088         PetscCall(updatePointBC_private(section, point, dof, insert, perm, flip, clperm, values, off, array));
7089       }
7090       break;
7091     case ADD_VALUES:
7092       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7093         const PetscInt     point = points[2 * p];
7094         const PetscInt    *perm  = perms ? perms[p] : NULL;
7095         const PetscScalar *flip  = flips ? flips[p] : NULL;
7096         PetscCall(PetscSectionGetDof(section, point, &dof));
7097         PetscCall(updatePoint_private(section, point, dof, add, PETSC_FALSE, perm, flip, clperm, values, off, array));
7098       }
7099       break;
7100     case ADD_ALL_VALUES:
7101       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7102         const PetscInt     point = points[2 * p];
7103         const PetscInt    *perm  = perms ? perms[p] : NULL;
7104         const PetscScalar *flip  = flips ? flips[p] : NULL;
7105         PetscCall(PetscSectionGetDof(section, point, &dof));
7106         PetscCall(updatePoint_private(section, point, dof, add, PETSC_TRUE, perm, flip, clperm, values, off, array));
7107       }
7108       break;
7109     case ADD_BC_VALUES:
7110       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7111         const PetscInt     point = points[2 * p];
7112         const PetscInt    *perm  = perms ? perms[p] : NULL;
7113         const PetscScalar *flip  = flips ? flips[p] : NULL;
7114         PetscCall(PetscSectionGetDof(section, point, &dof));
7115         PetscCall(updatePointBC_private(section, point, dof, add, perm, flip, clperm, values, off, array));
7116       }
7117       break;
7118     default:
7119       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
7120     }
7121     PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
7122   }
7123   /* Cleanup points */
7124   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
7125   /* Cleanup array */
7126   PetscCall(VecRestoreArray(v, &array));
7127   PetscFunctionReturn(PETSC_SUCCESS);
7128 }
7129 
7130 /* Check whether the given point is in the label. If not, update the offset to skip this point */
7131 static inline PetscErrorCode CheckPoint_Private(DMLabel label, PetscInt labelId, PetscSection section, PetscInt point, PetscInt f, PetscInt *offset, PetscBool *contains)
7132 {
7133   PetscFunctionBegin;
7134   *contains = PETSC_TRUE;
7135   if (label) {
7136     PetscInt fdof;
7137 
7138     PetscCall(DMLabelStratumHasPoint(label, labelId, point, contains));
7139     if (!*contains) {
7140       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7141       *offset += fdof;
7142       PetscFunctionReturn(PETSC_SUCCESS);
7143     }
7144   }
7145   PetscFunctionReturn(PETSC_SUCCESS);
7146 }
7147 
7148 /* Unlike DMPlexVecSetClosure(), this uses plex-native closure permutation, not a user-specified permutation such as DMPlexSetClosurePermutationTensor(). */
7149 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)
7150 {
7151   PetscSection    clSection;
7152   IS              clPoints;
7153   PetscScalar    *array;
7154   PetscInt       *points = NULL;
7155   const PetscInt *clp;
7156   PetscInt        numFields, numPoints, p;
7157   PetscInt        offset = 0, f;
7158 
7159   PetscFunctionBeginHot;
7160   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7161   if (!section) PetscCall(DMGetLocalSection(dm, &section));
7162   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7163   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
7164   PetscCall(PetscSectionGetNumFields(section, &numFields));
7165   /* Get points */
7166   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
7167   /* Get array */
7168   PetscCall(VecGetArray(v, &array));
7169   /* Get values */
7170   for (f = 0; f < numFields; ++f) {
7171     const PetscInt    **perms = NULL;
7172     const PetscScalar **flips = NULL;
7173     PetscBool           contains;
7174 
7175     if (!fieldActive[f]) {
7176       for (p = 0; p < numPoints * 2; p += 2) {
7177         PetscInt fdof;
7178         PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
7179         offset += fdof;
7180       }
7181       continue;
7182     }
7183     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7184     switch (mode) {
7185     case INSERT_VALUES:
7186       for (p = 0; p < numPoints; p++) {
7187         const PetscInt     point = points[2 * p];
7188         const PetscInt    *perm  = perms ? perms[p] : NULL;
7189         const PetscScalar *flip  = flips ? flips[p] : NULL;
7190         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7191         if (!contains) continue;
7192         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, NULL, values, &offset, array));
7193       }
7194       break;
7195     case INSERT_ALL_VALUES:
7196       for (p = 0; p < numPoints; p++) {
7197         const PetscInt     point = points[2 * p];
7198         const PetscInt    *perm  = perms ? perms[p] : NULL;
7199         const PetscScalar *flip  = flips ? flips[p] : NULL;
7200         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7201         if (!contains) continue;
7202         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, NULL, values, &offset, array));
7203       }
7204       break;
7205     case INSERT_BC_VALUES:
7206       for (p = 0; p < numPoints; p++) {
7207         const PetscInt     point = points[2 * p];
7208         const PetscInt    *perm  = perms ? perms[p] : NULL;
7209         const PetscScalar *flip  = flips ? flips[p] : NULL;
7210         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7211         if (!contains) continue;
7212         PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, Ncc, comps, insert, NULL, values, &offset, array));
7213       }
7214       break;
7215     case ADD_VALUES:
7216       for (p = 0; p < numPoints; p++) {
7217         const PetscInt     point = points[2 * p];
7218         const PetscInt    *perm  = perms ? perms[p] : NULL;
7219         const PetscScalar *flip  = flips ? flips[p] : NULL;
7220         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7221         if (!contains) continue;
7222         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, NULL, values, &offset, array));
7223       }
7224       break;
7225     case ADD_ALL_VALUES:
7226       for (p = 0; p < numPoints; p++) {
7227         const PetscInt     point = points[2 * p];
7228         const PetscInt    *perm  = perms ? perms[p] : NULL;
7229         const PetscScalar *flip  = flips ? flips[p] : NULL;
7230         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7231         if (!contains) continue;
7232         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, NULL, values, &offset, array));
7233       }
7234       break;
7235     default:
7236       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
7237     }
7238     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7239   }
7240   /* Cleanup points */
7241   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
7242   /* Cleanup array */
7243   PetscCall(VecRestoreArray(v, &array));
7244   PetscFunctionReturn(PETSC_SUCCESS);
7245 }
7246 
7247 static PetscErrorCode DMPlexPrintMatSetValues(PetscViewer viewer, Mat A, PetscInt point, PetscInt numRIndices, const PetscInt rindices[], PetscInt numCIndices, const PetscInt cindices[], const PetscScalar values[])
7248 {
7249   PetscMPIInt rank;
7250   PetscInt    i, j;
7251 
7252   PetscFunctionBegin;
7253   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7254   PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat for point %" PetscInt_FMT "\n", rank, point));
7255   for (i = 0; i < numRIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat row indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, rindices[i]));
7256   for (i = 0; i < numCIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat col indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, cindices[i]));
7257   numCIndices = numCIndices ? numCIndices : numRIndices;
7258   if (!values) PetscFunctionReturn(PETSC_SUCCESS);
7259   for (i = 0; i < numRIndices; i++) {
7260     PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]", rank));
7261     for (j = 0; j < numCIndices; j++) {
7262 #if defined(PETSC_USE_COMPLEX)
7263       PetscCall(PetscViewerASCIIPrintf(viewer, " (%g,%g)", (double)PetscRealPart(values[i * numCIndices + j]), (double)PetscImaginaryPart(values[i * numCIndices + j])));
7264 #else
7265       PetscCall(PetscViewerASCIIPrintf(viewer, " %g", (double)values[i * numCIndices + j]));
7266 #endif
7267     }
7268     PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
7269   }
7270   PetscFunctionReturn(PETSC_SUCCESS);
7271 }
7272 
7273 /*
7274   DMPlexGetIndicesPoint_Internal - Add the indices for dofs on a point to an index array
7275 
7276   Input Parameters:
7277 + section - The section for this data layout
7278 . islocal - Is the section (and thus indices being requested) local or global?
7279 . point   - The point contributing dofs with these indices
7280 . off     - The global offset of this point
7281 . loff    - The local offset of each field
7282 . setBC   - The flag determining whether to include indices of boundary values
7283 . perm    - A permutation of the dofs on this point, or NULL
7284 - indperm - A permutation of the entire indices array, or NULL
7285 
7286   Output Parameter:
7287 . indices - Indices for dofs on this point
7288 
7289   Level: developer
7290 
7291   Note: The indices could be local or global, depending on the value of 'off'.
7292 */
7293 PetscErrorCode DMPlexGetIndicesPoint_Internal(PetscSection section, PetscBool islocal, PetscInt point, PetscInt off, PetscInt *loff, PetscBool setBC, const PetscInt perm[], const PetscInt indperm[], PetscInt indices[])
7294 {
7295   PetscInt        dof;   /* The number of unknowns on this point */
7296   PetscInt        cdof;  /* The number of constraints on this point */
7297   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
7298   PetscInt        cind = 0, k;
7299 
7300   PetscFunctionBegin;
7301   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
7302   PetscCall(PetscSectionGetDof(section, point, &dof));
7303   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
7304   if (!cdof || setBC) {
7305     for (k = 0; k < dof; ++k) {
7306       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
7307       const PetscInt ind    = indperm ? indperm[preind] : preind;
7308 
7309       indices[ind] = off + k;
7310     }
7311   } else {
7312     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
7313     for (k = 0; k < dof; ++k) {
7314       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
7315       const PetscInt ind    = indperm ? indperm[preind] : preind;
7316 
7317       if ((cind < cdof) && (k == cdofs[cind])) {
7318         /* Insert check for returning constrained indices */
7319         indices[ind] = -(off + k + 1);
7320         ++cind;
7321       } else {
7322         indices[ind] = off + k - (islocal ? 0 : cind);
7323       }
7324     }
7325   }
7326   *loff += dof;
7327   PetscFunctionReturn(PETSC_SUCCESS);
7328 }
7329 
7330 /*
7331  DMPlexGetIndicesPointFields_Internal - gets section indices for a point in its canonical ordering.
7332 
7333  Input Parameters:
7334 + section - a section (global or local)
7335 - islocal - `PETSC_TRUE` if requesting local indices (i.e., section is local); `PETSC_FALSE` for global
7336 . point - point within section
7337 . off - The offset of this point in the (local or global) indexed space - should match islocal and (usually) the section
7338 . foffs - array of length numFields containing the offset in canonical point ordering (the location in indices) of each field
7339 . setBC - identify constrained (boundary condition) points via involution.
7340 . perms - perms[f][permsoff][:] is a permutation of dofs within each field
7341 . permsoff - offset
7342 - indperm - index permutation
7343 
7344  Output Parameter:
7345 . foffs - each entry is incremented by the number of (unconstrained if setBC=FALSE) dofs in that field
7346 . indices - array to hold indices (as defined by section) of each dof associated with point
7347 
7348  Notes:
7349  If section is local and setBC=true, there is no distinction between constrained and unconstrained dofs.
7350  If section is local and setBC=false, the indices for constrained points are the involution -(i+1) of their position
7351  in the local vector.
7352 
7353  If section is global and setBC=false, the indices for constrained points are negative (and their value is not
7354  significant).  It is invalid to call with a global section and setBC=true.
7355 
7356  Developer Note:
7357  The section is only used for field layout, so islocal is technically a statement about the offset (off).  At some point
7358  in the future, global sections may have fields set, in which case we could pass the global section and obtain the
7359  offset could be obtained from the section instead of passing it explicitly as we do now.
7360 
7361  Example:
7362  Suppose a point contains one field with three components, and for which the unconstrained indices are {10, 11, 12}.
7363  When the middle component is constrained, we get the array {10, -12, 12} for (islocal=TRUE, setBC=FALSE).
7364  Note that -12 is the involution of 11, so the user can involute negative indices to recover local indices.
7365  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.
7366 
7367  Level: developer
7368 */
7369 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[])
7370 {
7371   PetscInt numFields, foff, f;
7372 
7373   PetscFunctionBegin;
7374   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
7375   PetscCall(PetscSectionGetNumFields(section, &numFields));
7376   for (f = 0, foff = 0; f < numFields; ++f) {
7377     PetscInt        fdof, cfdof;
7378     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7379     PetscInt        cind = 0, b;
7380     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7381 
7382     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7383     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7384     if (!cfdof || setBC) {
7385       for (b = 0; b < fdof; ++b) {
7386         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7387         const PetscInt ind    = indperm ? indperm[preind] : preind;
7388 
7389         indices[ind] = off + foff + b;
7390       }
7391     } else {
7392       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7393       for (b = 0; b < fdof; ++b) {
7394         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7395         const PetscInt ind    = indperm ? indperm[preind] : preind;
7396 
7397         if ((cind < cfdof) && (b == fcdofs[cind])) {
7398           indices[ind] = -(off + foff + b + 1);
7399           ++cind;
7400         } else {
7401           indices[ind] = off + foff + b - (islocal ? 0 : cind);
7402         }
7403       }
7404     }
7405     foff += (setBC || islocal ? fdof : (fdof - cfdof));
7406     foffs[f] += fdof;
7407   }
7408   PetscFunctionReturn(PETSC_SUCCESS);
7409 }
7410 
7411 /*
7412   This version believes the globalSection offsets for each field, rather than just the point offset
7413 
7414  . foffs - The offset into 'indices' for each field, since it is segregated by field
7415 
7416  Notes:
7417  The semantics of this function relate to that of setBC=FALSE in DMPlexGetIndicesPointFields_Internal.
7418  Since this function uses global indices, setBC=TRUE would be invalid, so no such argument exists.
7419 */
7420 static PetscErrorCode DMPlexGetIndicesPointFieldsSplit_Internal(PetscSection section, PetscSection globalSection, PetscInt point, PetscInt foffs[], const PetscInt ***perms, PetscInt permsoff, const PetscInt indperm[], PetscInt indices[])
7421 {
7422   PetscInt numFields, foff, f;
7423 
7424   PetscFunctionBegin;
7425   PetscCall(PetscSectionGetNumFields(section, &numFields));
7426   for (f = 0; f < numFields; ++f) {
7427     PetscInt        fdof, cfdof;
7428     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7429     PetscInt        cind = 0, b;
7430     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7431 
7432     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7433     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7434     PetscCall(PetscSectionGetFieldOffset(globalSection, point, f, &foff));
7435     if (!cfdof) {
7436       for (b = 0; b < fdof; ++b) {
7437         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7438         const PetscInt ind    = indperm ? indperm[preind] : preind;
7439 
7440         indices[ind] = foff + b;
7441       }
7442     } else {
7443       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7444       for (b = 0; b < fdof; ++b) {
7445         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7446         const PetscInt ind    = indperm ? indperm[preind] : preind;
7447 
7448         if ((cind < cfdof) && (b == fcdofs[cind])) {
7449           indices[ind] = -(foff + b + 1);
7450           ++cind;
7451         } else {
7452           indices[ind] = foff + b - cind;
7453         }
7454       }
7455     }
7456     foffs[f] += fdof;
7457   }
7458   PetscFunctionReturn(PETSC_SUCCESS);
7459 }
7460 
7461 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)
7462 {
7463   Mat             cMat;
7464   PetscSection    aSec, cSec;
7465   IS              aIS;
7466   PetscInt        aStart = -1, aEnd = -1;
7467   PetscInt        sStart = -1, sEnd = -1;
7468   PetscInt        cStart = -1, cEnd = -1;
7469   const PetscInt *anchors;
7470   PetscInt        numFields, f, p, q, newP = 0;
7471   PetscInt        newNumPoints = 0, newNumIndices = 0;
7472   PetscInt       *newPoints, *indices, *newIndices;
7473   PetscInt        maxAnchor, maxDof;
7474   PetscInt        newOffsets[32];
7475   PetscInt       *pointMatOffsets[32];
7476   PetscInt       *newPointOffsets[32];
7477   PetscScalar    *pointMat[32];
7478   PetscScalar    *newValues      = NULL, *tmpValues;
7479   PetscBool       anyConstrained = PETSC_FALSE;
7480 
7481   PetscFunctionBegin;
7482   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7483   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7484   PetscCall(PetscSectionGetNumFields(section, &numFields));
7485 
7486   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
7487   /* if there are point-to-point constraints */
7488   if (aSec) {
7489     PetscCall(PetscArrayzero(newOffsets, 32));
7490     PetscCall(ISGetIndices(aIS, &anchors));
7491     PetscCall(PetscSectionGetChart(aSec, &aStart, &aEnd));
7492     PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
7493     /* figure out how many points are going to be in the new element matrix
7494      * (we allow double counting, because it's all just going to be summed
7495      * into the global matrix anyway) */
7496     for (p = 0; p < 2 * numPoints; p += 2) {
7497       PetscInt b    = points[p];
7498       PetscInt bDof = 0, bSecDof = 0;
7499 
7500       if (b >= sStart && b < sEnd) PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7501       if (!bSecDof) continue;
7502       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7503       if (bDof) {
7504         /* this point is constrained */
7505         /* it is going to be replaced by its anchors */
7506         PetscInt bOff, q;
7507 
7508         anyConstrained = PETSC_TRUE;
7509         newNumPoints += bDof;
7510         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7511         for (q = 0; q < bDof; q++) {
7512           PetscInt a    = anchors[bOff + q];
7513           PetscInt aDof = 0;
7514 
7515           if (a >= sStart && a < sEnd) PetscCall(PetscSectionGetDof(section, a, &aDof));
7516           newNumIndices += aDof;
7517           for (f = 0; f < numFields; ++f) {
7518             PetscInt fDof = 0;
7519 
7520             if (a >= sStart && a < sEnd) PetscCall(PetscSectionGetFieldDof(section, a, f, &fDof));
7521             newOffsets[f + 1] += fDof;
7522           }
7523         }
7524       } else {
7525         /* this point is not constrained */
7526         newNumPoints++;
7527         newNumIndices += bSecDof;
7528         for (f = 0; f < numFields; ++f) {
7529           PetscInt fDof;
7530 
7531           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7532           newOffsets[f + 1] += fDof;
7533         }
7534       }
7535     }
7536   }
7537   if (!anyConstrained) {
7538     if (outNumPoints) *outNumPoints = 0;
7539     if (outNumIndices) *outNumIndices = 0;
7540     if (outPoints) *outPoints = NULL;
7541     if (outValues) *outValues = NULL;
7542     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7543     PetscFunctionReturn(PETSC_SUCCESS);
7544   }
7545 
7546   if (outNumPoints) *outNumPoints = newNumPoints;
7547   if (outNumIndices) *outNumIndices = newNumIndices;
7548 
7549   for (f = 0; f < numFields; ++f) newOffsets[f + 1] += newOffsets[f];
7550 
7551   if (!outPoints && !outValues) {
7552     if (offsets) {
7553       for (f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7554     }
7555     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7556     PetscFunctionReturn(PETSC_SUCCESS);
7557   }
7558 
7559   PetscCheck(!numFields || newOffsets[numFields] == newNumIndices, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, newOffsets[numFields], newNumIndices);
7560 
7561   PetscCall(DMGetDefaultConstraints(dm, &cSec, &cMat, NULL));
7562   PetscCall(PetscSectionGetChart(cSec, &cStart, &cEnd));
7563 
7564   /* workspaces */
7565   if (numFields) {
7566     for (f = 0; f < numFields; f++) {
7567       PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[f]));
7568       PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[f]));
7569     }
7570   } else {
7571     PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[0]));
7572     PetscCall(DMGetWorkArray(dm, numPoints, MPIU_INT, &newPointOffsets[0]));
7573   }
7574 
7575   /* get workspaces for the point-to-point matrices */
7576   if (numFields) {
7577     PetscInt totalOffset, totalMatOffset;
7578 
7579     for (p = 0; p < numPoints; p++) {
7580       PetscInt b    = points[2 * p];
7581       PetscInt bDof = 0, bSecDof = 0;
7582 
7583       if (b >= sStart && b < sEnd) PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7584       if (!bSecDof) {
7585         for (f = 0; f < numFields; f++) {
7586           newPointOffsets[f][p + 1] = 0;
7587           pointMatOffsets[f][p + 1] = 0;
7588         }
7589         continue;
7590       }
7591       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7592       if (bDof) {
7593         for (f = 0; f < numFields; f++) {
7594           PetscInt fDof, q, bOff, allFDof = 0;
7595 
7596           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7597           PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7598           for (q = 0; q < bDof; q++) {
7599             PetscInt a     = anchors[bOff + q];
7600             PetscInt aFDof = 0;
7601 
7602             if (a >= sStart && a < sEnd) PetscCall(PetscSectionGetFieldDof(section, a, f, &aFDof));
7603             allFDof += aFDof;
7604           }
7605           newPointOffsets[f][p + 1] = allFDof;
7606           pointMatOffsets[f][p + 1] = fDof * allFDof;
7607         }
7608       } else {
7609         for (f = 0; f < numFields; f++) {
7610           PetscInt fDof;
7611 
7612           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7613           newPointOffsets[f][p + 1] = fDof;
7614           pointMatOffsets[f][p + 1] = 0;
7615         }
7616       }
7617     }
7618     for (f = 0, totalOffset = 0, totalMatOffset = 0; f < numFields; f++) {
7619       newPointOffsets[f][0] = totalOffset;
7620       pointMatOffsets[f][0] = totalMatOffset;
7621       for (p = 0; p < numPoints; p++) {
7622         newPointOffsets[f][p + 1] += newPointOffsets[f][p];
7623         pointMatOffsets[f][p + 1] += pointMatOffsets[f][p];
7624       }
7625       totalOffset    = newPointOffsets[f][numPoints];
7626       totalMatOffset = pointMatOffsets[f][numPoints];
7627       PetscCall(DMGetWorkArray(dm, pointMatOffsets[f][numPoints], MPIU_SCALAR, &pointMat[f]));
7628     }
7629   } else {
7630     for (p = 0; p < numPoints; p++) {
7631       PetscInt b    = points[2 * p];
7632       PetscInt bDof = 0, bSecDof = 0;
7633 
7634       if (b >= sStart && b < sEnd) PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7635       if (!bSecDof) {
7636         newPointOffsets[0][p + 1] = 0;
7637         pointMatOffsets[0][p + 1] = 0;
7638         continue;
7639       }
7640       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7641       if (bDof) {
7642         PetscInt bOff, q, allDof = 0;
7643 
7644         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7645         for (q = 0; q < bDof; q++) {
7646           PetscInt a = anchors[bOff + q], aDof = 0;
7647 
7648           if (a >= sStart && a < sEnd) PetscCall(PetscSectionGetDof(section, a, &aDof));
7649           allDof += aDof;
7650         }
7651         newPointOffsets[0][p + 1] = allDof;
7652         pointMatOffsets[0][p + 1] = bSecDof * allDof;
7653       } else {
7654         newPointOffsets[0][p + 1] = bSecDof;
7655         pointMatOffsets[0][p + 1] = 0;
7656       }
7657     }
7658     newPointOffsets[0][0] = 0;
7659     pointMatOffsets[0][0] = 0;
7660     for (p = 0; p < numPoints; p++) {
7661       newPointOffsets[0][p + 1] += newPointOffsets[0][p];
7662       pointMatOffsets[0][p + 1] += pointMatOffsets[0][p];
7663     }
7664     PetscCall(DMGetWorkArray(dm, pointMatOffsets[0][numPoints], MPIU_SCALAR, &pointMat[0]));
7665   }
7666 
7667   /* output arrays */
7668   PetscCall(DMGetWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7669 
7670   /* get the point-to-point matrices; construct newPoints */
7671   PetscCall(PetscSectionGetMaxDof(aSec, &maxAnchor));
7672   PetscCall(PetscSectionGetMaxDof(section, &maxDof));
7673   PetscCall(DMGetWorkArray(dm, maxDof, MPIU_INT, &indices));
7674   PetscCall(DMGetWorkArray(dm, maxAnchor * maxDof, MPIU_INT, &newIndices));
7675   if (numFields) {
7676     for (p = 0, newP = 0; p < numPoints; p++) {
7677       PetscInt b    = points[2 * p];
7678       PetscInt o    = points[2 * p + 1];
7679       PetscInt bDof = 0, bSecDof = 0;
7680 
7681       if (b >= sStart && b < sEnd) PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7682       if (!bSecDof) continue;
7683       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7684       if (bDof) {
7685         PetscInt fStart[32], fEnd[32], fAnchorStart[32], fAnchorEnd[32], bOff, q;
7686 
7687         fStart[0] = 0;
7688         fEnd[0]   = 0;
7689         for (f = 0; f < numFields; f++) {
7690           PetscInt fDof = 0;
7691 
7692           if (b >= cStart && b < cEnd) PetscCall(PetscSectionGetFieldDof(cSec, b, f, &fDof));
7693           fStart[f + 1] = fStart[f] + fDof;
7694           fEnd[f + 1]   = fStart[f + 1];
7695         }
7696         if (b >= cStart && b < cEnd) {
7697           PetscCall(PetscSectionGetOffset(cSec, b, &bOff));
7698           PetscCall(DMPlexGetIndicesPointFields_Internal(cSec, PETSC_TRUE, b, bOff, fEnd, PETSC_TRUE, perms, p, NULL, indices));
7699         }
7700 
7701         fAnchorStart[0] = 0;
7702         fAnchorEnd[0]   = 0;
7703         for (f = 0; f < numFields; f++) {
7704           PetscInt fDof = newPointOffsets[f][p + 1] - newPointOffsets[f][p];
7705 
7706           fAnchorStart[f + 1] = fAnchorStart[f] + fDof;
7707           fAnchorEnd[f + 1]   = fAnchorStart[f + 1];
7708         }
7709         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7710         for (q = 0; q < bDof; q++) {
7711           PetscInt a = anchors[bOff + q], aOff = -1;
7712 
7713           /* we take the orientation of ap into account in the order that we constructed the indices above: the newly added points have no orientation */
7714           newPoints[2 * (newP + q)]     = a;
7715           newPoints[2 * (newP + q) + 1] = 0;
7716           if (a >= sStart && a < sEnd) {
7717             PetscCall(PetscSectionGetOffset(section, a, &aOff));
7718             PetscCall(DMPlexGetIndicesPointFields_Internal(section, PETSC_TRUE, a, aOff, fAnchorEnd, PETSC_TRUE, NULL, -1, NULL, newIndices));
7719           }
7720         }
7721         newP += bDof;
7722 
7723         if (outValues) {
7724           /* get the point-to-point submatrix */
7725           for (f = 0; f < numFields; f++) {
7726             if (fEnd[f] - fStart[f] > 0) PetscCall(MatGetValues(cMat, fEnd[f] - fStart[f], indices + fStart[f], fAnchorEnd[f] - fAnchorStart[f], newIndices + fAnchorStart[f], pointMat[f] + pointMatOffsets[f][p]));
7727           }
7728         }
7729       } else {
7730         newPoints[2 * newP]     = b;
7731         newPoints[2 * newP + 1] = o;
7732         newP++;
7733       }
7734     }
7735   } else {
7736     for (p = 0; p < numPoints; p++) {
7737       PetscInt b    = points[2 * p];
7738       PetscInt o    = points[2 * p + 1];
7739       PetscInt bDof = 0, bSecDof = 0;
7740 
7741       if (b >= sStart && b < sEnd) PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7742       if (!bSecDof) continue;
7743       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7744       if (bDof) {
7745         PetscInt bEnd = 0, bAnchorEnd = 0, bOff;
7746 
7747         if (b >= cStart && b < cEnd) {
7748           PetscCall(PetscSectionGetOffset(cSec, b, &bOff));
7749           PetscCall(DMPlexGetIndicesPoint_Internal(cSec, PETSC_TRUE, b, bOff, &bEnd, PETSC_TRUE, (perms && perms[0]) ? perms[0][p] : NULL, NULL, indices));
7750         }
7751 
7752         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7753         for (q = 0; q < bDof; q++) {
7754           PetscInt a = anchors[bOff + q], aOff;
7755 
7756           /* we take the orientation of ap into account in the order that we constructed the indices above: the newly added points have no orientation */
7757 
7758           newPoints[2 * (newP + q)]     = a;
7759           newPoints[2 * (newP + q) + 1] = 0;
7760           if (a >= sStart && a < sEnd) {
7761             PetscCall(PetscSectionGetOffset(section, a, &aOff));
7762             PetscCall(DMPlexGetIndicesPoint_Internal(section, PETSC_TRUE, a, aOff, &bAnchorEnd, PETSC_TRUE, NULL, NULL, newIndices));
7763           }
7764         }
7765         newP += bDof;
7766 
7767         /* get the point-to-point submatrix */
7768         if (outValues) PetscCall(MatGetValues(cMat, bEnd, indices, bAnchorEnd, newIndices, pointMat[0] + pointMatOffsets[0][p]));
7769       } else {
7770         newPoints[2 * newP]     = b;
7771         newPoints[2 * newP + 1] = o;
7772         newP++;
7773       }
7774     }
7775   }
7776 
7777   if (outValues) {
7778     PetscCall(DMGetWorkArray(dm, newNumIndices * numIndices, MPIU_SCALAR, &tmpValues));
7779     PetscCall(PetscArrayzero(tmpValues, newNumIndices * numIndices));
7780     /* multiply constraints on the right */
7781     if (numFields) {
7782       for (f = 0; f < numFields; f++) {
7783         PetscInt oldOff = offsets[f];
7784 
7785         for (p = 0; p < numPoints; p++) {
7786           PetscInt cStart = newPointOffsets[f][p];
7787           PetscInt b      = points[2 * p];
7788           PetscInt c, r, k;
7789           PetscInt dof = 0;
7790 
7791           if (b >= sStart && b < sEnd) PetscCall(PetscSectionGetFieldDof(section, b, f, &dof));
7792           if (!dof) continue;
7793           if (pointMatOffsets[f][p] < pointMatOffsets[f][p + 1]) {
7794             PetscInt           nCols = newPointOffsets[f][p + 1] - cStart;
7795             const PetscScalar *mat   = pointMat[f] + pointMatOffsets[f][p];
7796 
7797             for (r = 0; r < numIndices; r++) {
7798               for (c = 0; c < nCols; c++) {
7799                 for (k = 0; k < dof; k++) tmpValues[r * newNumIndices + cStart + c] += values[r * numIndices + oldOff + k] * mat[k * nCols + c];
7800               }
7801             }
7802           } else {
7803             /* copy this column as is */
7804             for (r = 0; r < numIndices; r++) {
7805               for (c = 0; c < dof; c++) tmpValues[r * newNumIndices + cStart + c] = values[r * numIndices + oldOff + c];
7806             }
7807           }
7808           oldOff += dof;
7809         }
7810       }
7811     } else {
7812       PetscInt oldOff = 0;
7813       for (p = 0; p < numPoints; p++) {
7814         PetscInt cStart = newPointOffsets[0][p];
7815         PetscInt b      = points[2 * p];
7816         PetscInt c, r, k;
7817         PetscInt dof = 0;
7818 
7819         if (b >= sStart && b < sEnd) PetscCall(PetscSectionGetDof(section, b, &dof));
7820         if (!dof) continue;
7821         if (pointMatOffsets[0][p] < pointMatOffsets[0][p + 1]) {
7822           PetscInt           nCols = newPointOffsets[0][p + 1] - cStart;
7823           const PetscScalar *mat   = pointMat[0] + pointMatOffsets[0][p];
7824 
7825           for (r = 0; r < numIndices; r++) {
7826             for (c = 0; c < nCols; c++) {
7827               for (k = 0; k < dof; k++) tmpValues[r * newNumIndices + cStart + c] += mat[k * nCols + c] * values[r * numIndices + oldOff + k];
7828             }
7829           }
7830         } else {
7831           /* copy this column as is */
7832           for (r = 0; r < numIndices; r++) {
7833             for (c = 0; c < dof; c++) tmpValues[r * newNumIndices + cStart + c] = values[r * numIndices + oldOff + c];
7834           }
7835         }
7836         oldOff += dof;
7837       }
7838     }
7839 
7840     if (multiplyLeft) {
7841       PetscCall(DMGetWorkArray(dm, newNumIndices * newNumIndices, MPIU_SCALAR, &newValues));
7842       PetscCall(PetscArrayzero(newValues, newNumIndices * newNumIndices));
7843       /* multiply constraints transpose on the left */
7844       if (numFields) {
7845         for (f = 0; f < numFields; f++) {
7846           PetscInt oldOff = offsets[f];
7847 
7848           for (p = 0; p < numPoints; p++) {
7849             PetscInt rStart = newPointOffsets[f][p];
7850             PetscInt b      = points[2 * p];
7851             PetscInt c, r, k;
7852             PetscInt dof = 0;
7853 
7854             if (b >= sStart && b < sEnd) PetscCall(PetscSectionGetFieldDof(section, b, f, &dof));
7855             if (pointMatOffsets[f][p] < pointMatOffsets[f][p + 1]) {
7856               PetscInt                          nRows = newPointOffsets[f][p + 1] - rStart;
7857               const PetscScalar *PETSC_RESTRICT mat   = pointMat[f] + pointMatOffsets[f][p];
7858 
7859               for (r = 0; r < nRows; r++) {
7860                 for (c = 0; c < newNumIndices; c++) {
7861                   for (k = 0; k < dof; k++) newValues[(rStart + r) * newNumIndices + c] += mat[k * nRows + r] * tmpValues[(oldOff + k) * newNumIndices + c];
7862                 }
7863               }
7864             } else {
7865               /* copy this row as is */
7866               for (r = 0; r < dof; r++) {
7867                 for (c = 0; c < newNumIndices; c++) newValues[(rStart + r) * newNumIndices + c] = tmpValues[(oldOff + r) * newNumIndices + c];
7868               }
7869             }
7870             oldOff += dof;
7871           }
7872         }
7873       } else {
7874         PetscInt oldOff = 0;
7875 
7876         for (p = 0; p < numPoints; p++) {
7877           PetscInt rStart = newPointOffsets[0][p];
7878           PetscInt b      = points[2 * p];
7879           PetscInt c, r, k;
7880           PetscInt dof = 0;
7881 
7882           if (b >= sStart && b < sEnd) PetscCall(PetscSectionGetDof(section, b, &dof));
7883           if (pointMatOffsets[0][p] < pointMatOffsets[0][p + 1]) {
7884             PetscInt                          nRows = newPointOffsets[0][p + 1] - rStart;
7885             const PetscScalar *PETSC_RESTRICT mat   = pointMat[0] + pointMatOffsets[0][p];
7886 
7887             for (r = 0; r < nRows; r++) {
7888               for (c = 0; c < newNumIndices; c++) {
7889                 for (k = 0; k < dof; k++) newValues[(rStart + r) * newNumIndices + c] += mat[k * nRows + r] * tmpValues[(oldOff + k) * newNumIndices + c];
7890               }
7891             }
7892           } else {
7893             /* copy this row as is */
7894             for (r = 0; r < dof; r++) {
7895               for (c = 0; c < newNumIndices; c++) newValues[(rStart + r) * newNumIndices + c] = tmpValues[(oldOff + r) * newNumIndices + c];
7896             }
7897           }
7898           oldOff += dof;
7899         }
7900       }
7901 
7902       PetscCall(DMRestoreWorkArray(dm, newNumIndices * numIndices, MPIU_SCALAR, &tmpValues));
7903     } else {
7904       newValues = tmpValues;
7905     }
7906   }
7907 
7908   /* clean up */
7909   PetscCall(DMRestoreWorkArray(dm, maxDof, MPIU_INT, &indices));
7910   PetscCall(DMRestoreWorkArray(dm, maxAnchor * maxDof, MPIU_INT, &newIndices));
7911 
7912   if (numFields) {
7913     for (f = 0; f < numFields; f++) {
7914       PetscCall(DMRestoreWorkArray(dm, pointMatOffsets[f][numPoints], MPIU_SCALAR, &pointMat[f]));
7915       PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[f]));
7916       PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[f]));
7917     }
7918   } else {
7919     PetscCall(DMRestoreWorkArray(dm, pointMatOffsets[0][numPoints], MPIU_SCALAR, &pointMat[0]));
7920     PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[0]));
7921     PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[0]));
7922   }
7923   PetscCall(ISRestoreIndices(aIS, &anchors));
7924 
7925   /* output */
7926   if (outPoints) {
7927     *outPoints = newPoints;
7928   } else {
7929     PetscCall(DMRestoreWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7930   }
7931   if (outValues) *outValues = newValues;
7932   for (f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7933   PetscFunctionReturn(PETSC_SUCCESS);
7934 }
7935 
7936 /*@C
7937   DMPlexGetClosureIndices - Gets the global dof indices associated with the closure of the given point within the provided sections.
7938 
7939   Not collective
7940 
7941   Input Parameters:
7942 + dm         - The `DM`
7943 . section    - The `PetscSection` describing the points (a local section)
7944 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
7945 . point      - The point defining the closure
7946 - useClPerm  - Use the closure point permutation if available
7947 
7948   Output Parameters:
7949 + numIndices - The number of dof indices in the closure of point with the input sections
7950 . indices    - The dof indices
7951 . outOffsets - Array to write the field offsets into, or `NULL`
7952 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or `NULL`
7953 
7954   Level: advanced
7955 
7956   Notes:
7957   Must call `DMPlexRestoreClosureIndices()` to free allocated memory
7958 
7959   If `idxSection` is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
7960   of those indices is not significant.  If `idxSection` is local, the constrained dofs will yield the involution -(idx+1)
7961   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
7962   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when `idxSection` == section, otherwise global
7963   indices (with the above semantics) are implied.
7964 
7965 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`,
7966           `PetscSection`, `DMGetGlobalSection()`
7967 @*/
7968 PetscErrorCode DMPlexGetClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
7969 {
7970   /* Closure ordering */
7971   PetscSection    clSection;
7972   IS              clPoints;
7973   const PetscInt *clp;
7974   PetscInt       *points;
7975   const PetscInt *clperm = NULL;
7976   /* Dof permutation and sign flips */
7977   const PetscInt    **perms[32] = {NULL};
7978   const PetscScalar **flips[32] = {NULL};
7979   PetscScalar        *valCopy   = NULL;
7980   /* Hanging node constraints */
7981   PetscInt    *pointsC = NULL;
7982   PetscScalar *valuesC = NULL;
7983   PetscInt     NclC, NiC;
7984 
7985   PetscInt *idx;
7986   PetscInt  Nf, Ncl, Ni = 0, offsets[32], p, f;
7987   PetscBool isLocal = (section == idxSection) ? PETSC_TRUE : PETSC_FALSE;
7988   PetscInt  idxStart, idxEnd;
7989 
7990   PetscFunctionBeginHot;
7991   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7992   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7993   PetscValidHeaderSpecific(idxSection, PETSC_SECTION_CLASSID, 3);
7994   if (numIndices) PetscAssertPointer(numIndices, 6);
7995   if (indices) PetscAssertPointer(indices, 7);
7996   if (outOffsets) PetscAssertPointer(outOffsets, 8);
7997   if (values) PetscAssertPointer(values, 9);
7998   PetscCall(PetscSectionGetNumFields(section, &Nf));
7999   PetscCheck(Nf <= 31, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", Nf);
8000   PetscCall(PetscArrayzero(offsets, 32));
8001   /* 1) Get points in closure */
8002   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &Ncl, &points, &clSection, &clPoints, &clp));
8003   if (useClPerm) {
8004     PetscInt depth, clsize;
8005     PetscCall(DMPlexGetPointDepth(dm, point, &depth));
8006     for (clsize = 0, p = 0; p < Ncl; p++) {
8007       PetscInt dof;
8008       PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
8009       clsize += dof;
8010     }
8011     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
8012   }
8013   /* 2) Get number of indices on these points and field offsets from section */
8014   for (p = 0; p < Ncl * 2; p += 2) {
8015     PetscInt dof, fdof;
8016 
8017     PetscCall(PetscSectionGetDof(section, points[p], &dof));
8018     for (f = 0; f < Nf; ++f) {
8019       PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
8020       offsets[f + 1] += fdof;
8021     }
8022     Ni += dof;
8023   }
8024   for (f = 1; f < Nf; ++f) offsets[f + 1] += offsets[f];
8025   PetscCheck(!Nf || offsets[Nf] == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, offsets[Nf], Ni);
8026   /* 3) Get symmetries and sign flips. Apply sign flips to values if passed in (only works for square values matrix) */
8027   for (f = 0; f < PetscMax(1, Nf); ++f) {
8028     if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
8029     else PetscCall(PetscSectionGetPointSyms(section, Ncl, points, &perms[f], &flips[f]));
8030     /* may need to apply sign changes to the element matrix */
8031     if (values && flips[f]) {
8032       PetscInt foffset = offsets[f];
8033 
8034       for (p = 0; p < Ncl; ++p) {
8035         PetscInt           pnt  = points[2 * p], fdof;
8036         const PetscScalar *flip = flips[f] ? flips[f][p] : NULL;
8037 
8038         if (!Nf) PetscCall(PetscSectionGetDof(section, pnt, &fdof));
8039         else PetscCall(PetscSectionGetFieldDof(section, pnt, f, &fdof));
8040         if (flip) {
8041           PetscInt i, j, k;
8042 
8043           if (!valCopy) {
8044             PetscCall(DMGetWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
8045             for (j = 0; j < Ni * Ni; ++j) valCopy[j] = (*values)[j];
8046             *values = valCopy;
8047           }
8048           for (i = 0; i < fdof; ++i) {
8049             PetscScalar fval = flip[i];
8050 
8051             for (k = 0; k < Ni; ++k) {
8052               valCopy[Ni * (foffset + i) + k] *= fval;
8053               valCopy[Ni * k + (foffset + i)] *= fval;
8054             }
8055           }
8056         }
8057         foffset += fdof;
8058       }
8059     }
8060   }
8061   /* 4) Apply hanging node constraints. Get new symmetries and replace all storage with constrained storage */
8062   PetscCall(DMPlexAnchorsModifyMat(dm, section, Ncl, Ni, points, perms, values ? *values : NULL, &NclC, &NiC, &pointsC, values ? &valuesC : NULL, offsets, PETSC_TRUE));
8063   if (NclC) {
8064     if (valCopy) PetscCall(DMRestoreWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
8065     for (f = 0; f < PetscMax(1, Nf); ++f) {
8066       if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
8067       else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
8068     }
8069     for (f = 0; f < PetscMax(1, Nf); ++f) {
8070       if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, NclC, pointsC, &perms[f], &flips[f]));
8071       else PetscCall(PetscSectionGetPointSyms(section, NclC, pointsC, &perms[f], &flips[f]));
8072     }
8073     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
8074     Ncl    = NclC;
8075     Ni     = NiC;
8076     points = pointsC;
8077     if (values) *values = valuesC;
8078   }
8079   /* 5) Calculate indices */
8080   PetscCall(DMGetWorkArray(dm, Ni, MPIU_INT, &idx));
8081   PetscCall(PetscSectionGetChart(idxSection, &idxStart, &idxEnd));
8082   if (Nf) {
8083     PetscInt  idxOff;
8084     PetscBool useFieldOffsets;
8085 
8086     if (outOffsets) {
8087       for (f = 0; f <= Nf; f++) outOffsets[f] = offsets[f];
8088     }
8089     PetscCall(PetscSectionGetUseFieldOffsets(idxSection, &useFieldOffsets));
8090     if (useFieldOffsets) {
8091       for (p = 0; p < Ncl; ++p) {
8092         const PetscInt pnt = points[p * 2];
8093 
8094         PetscCall(DMPlexGetIndicesPointFieldsSplit_Internal(section, idxSection, pnt, offsets, perms, p, clperm, idx));
8095       }
8096     } else {
8097       for (p = 0; p < Ncl; ++p) {
8098         const PetscInt pnt = points[p * 2];
8099 
8100         if (pnt < idxStart || pnt >= idxEnd) continue;
8101         PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
8102         /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
8103          * not (at the time of this writing) have fields set. They probably should, in which case we would pass the
8104          * global section. */
8105         PetscCall(DMPlexGetIndicesPointFields_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, offsets, PETSC_FALSE, perms, p, clperm, idx));
8106       }
8107     }
8108   } else {
8109     PetscInt off = 0, idxOff;
8110 
8111     for (p = 0; p < Ncl; ++p) {
8112       const PetscInt  pnt  = points[p * 2];
8113       const PetscInt *perm = perms[0] ? perms[0][p] : NULL;
8114 
8115       if (pnt < idxStart || pnt >= idxEnd) continue;
8116       PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
8117       /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
8118        * not (at the time of this writing) have fields set. They probably should, in which case we would pass the global section. */
8119       PetscCall(DMPlexGetIndicesPoint_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, &off, PETSC_FALSE, perm, clperm, idx));
8120     }
8121   }
8122   /* 6) Cleanup */
8123   for (f = 0; f < PetscMax(1, Nf); ++f) {
8124     if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
8125     else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
8126   }
8127   if (NclC) {
8128     PetscCall(DMRestoreWorkArray(dm, NclC * 2, MPIU_INT, &pointsC));
8129   } else {
8130     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
8131   }
8132 
8133   if (numIndices) *numIndices = Ni;
8134   if (indices) *indices = idx;
8135   PetscFunctionReturn(PETSC_SUCCESS);
8136 }
8137 
8138 /*@C
8139   DMPlexRestoreClosureIndices - Restores the global dof indices associated with the closure of the given point within the provided sections.
8140 
8141   Not collective
8142 
8143   Input Parameters:
8144 + dm         - The `DM`
8145 . section    - The `PetscSection` describing the points (a local section)
8146 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
8147 . point      - The point defining the closure
8148 - useClPerm  - Use the closure point permutation if available
8149 
8150   Output Parameters:
8151 + numIndices - The number of dof indices in the closure of point with the input sections
8152 . indices    - The dof indices
8153 . outOffsets - Array to write the field offsets into, or `NULL`
8154 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or `NULL`
8155 
8156   Level: advanced
8157 
8158   Notes:
8159   If values were modified, the user is responsible for calling `DMRestoreWorkArray`(dm, 0, `MPIU_SCALAR`, &values).
8160 
8161   If idxSection is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
8162   of those indices is not significant.  If idxSection is local, the constrained dofs will yield the involution -(idx+1)
8163   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
8164   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when idxSection == section, otherwise global
8165   indices (with the above semantics) are implied.
8166 
8167 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`, `DMGetGlobalSection()`
8168 @*/
8169 PetscErrorCode DMPlexRestoreClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
8170 {
8171   PetscFunctionBegin;
8172   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8173   PetscAssertPointer(indices, 7);
8174   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, indices));
8175   PetscFunctionReturn(PETSC_SUCCESS);
8176 }
8177 
8178 PetscErrorCode DMPlexMatSetClosure_Internal(DM dm, PetscSection section, PetscSection globalSection, PetscBool useClPerm, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8179 {
8180   DM_Plex           *mesh = (DM_Plex *)dm->data;
8181   PetscInt          *indices;
8182   PetscInt           numIndices;
8183   const PetscScalar *valuesOrig = values;
8184   PetscErrorCode     ierr;
8185 
8186   PetscFunctionBegin;
8187   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8188   if (!section) PetscCall(DMGetLocalSection(dm, &section));
8189   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
8190   if (!globalSection) PetscCall(DMGetGlobalSection(dm, &globalSection));
8191   PetscValidHeaderSpecific(globalSection, PETSC_SECTION_CLASSID, 3);
8192   PetscValidHeaderSpecific(A, MAT_CLASSID, 5);
8193 
8194   PetscCall(DMPlexGetClosureIndices(dm, section, globalSection, point, useClPerm, &numIndices, &indices, NULL, (PetscScalar **)&values));
8195 
8196   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndices, indices, 0, NULL, values));
8197   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8198   ierr = MatSetValues(A, numIndices, indices, numIndices, indices, values, mode);
8199   if (ierr) {
8200     PetscMPIInt rank;
8201 
8202     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8203     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8204     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndices, indices, 0, NULL, values));
8205     PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
8206     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
8207     SETERRQ(PetscObjectComm((PetscObject)dm), ierr, "Not possible to set matrix values");
8208   }
8209   if (mesh->printFEM > 1) {
8210     PetscInt i;
8211     PetscCall(PetscPrintf(PETSC_COMM_SELF, "  Indices:"));
8212     for (i = 0; i < numIndices; ++i) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, indices[i]));
8213     PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8214   }
8215 
8216   PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
8217   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
8218   PetscFunctionReturn(PETSC_SUCCESS);
8219 }
8220 
8221 /*@C
8222   DMPlexMatSetClosure - Set an array of the values on the closure of 'point'
8223 
8224   Not collective
8225 
8226   Input Parameters:
8227 + dm            - The `DM`
8228 . section       - The section describing the layout in `v`, or `NULL` to use the default section
8229 . globalSection - The section describing the layout in `v`, or `NULL` to use the default global section
8230 . A             - The matrix
8231 . point         - The point in the `DM`
8232 . values        - The array of values
8233 - mode          - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
8234 
8235   Level: intermediate
8236 
8237 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosureGeneral()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
8238 @*/
8239 PetscErrorCode DMPlexMatSetClosure(DM dm, PetscSection section, PetscSection globalSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8240 {
8241   PetscFunctionBegin;
8242   PetscCall(DMPlexMatSetClosure_Internal(dm, section, globalSection, PETSC_TRUE, A, point, values, mode));
8243   PetscFunctionReturn(PETSC_SUCCESS);
8244 }
8245 
8246 /*@C
8247   DMPlexMatSetClosureGeneral - Set an array of the values on the closure of 'point' using a different row and column section
8248 
8249   Not collective
8250 
8251   Input Parameters:
8252 + dmRow            - The `DM` for the row fields
8253 . sectionRow       - The section describing the layout, or `NULL` to use the default section in `dmRow`
8254 . useRowPerm       - The flag to use the closure permutation of the `dmRow` if available
8255 . globalSectionRow - The section describing the layout, or `NULL` to use the default global section in `dmRow`
8256 . dmCol            - The `DM` for the column fields
8257 . sectionCol       - The section describing the layout, or `NULL` to use the default section in `dmCol`
8258 . useColPerm       - The flag to use the closure permutation of the `dmCol` if available
8259 . globalSectionCol - The section describing the layout, or `NULL` to use the default global section in `dmCol`
8260 . A                - The matrix
8261 . point            - The point in the `DM`
8262 . values           - The array of values
8263 - mode             - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
8264 
8265   Level: intermediate
8266 
8267 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosure()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
8268 @*/
8269 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)
8270 {
8271   DM_Plex           *mesh = (DM_Plex *)dmRow->data;
8272   PetscInt          *indicesRow, *indicesCol;
8273   PetscInt           numIndicesRow, numIndicesCol;
8274   const PetscScalar *valuesV0 = values, *valuesV1, *valuesV2;
8275   PetscErrorCode     ierr;
8276 
8277   PetscFunctionBegin;
8278   PetscValidHeaderSpecific(dmRow, DM_CLASSID, 1);
8279   if (!sectionRow) PetscCall(DMGetLocalSection(dmRow, &sectionRow));
8280   PetscValidHeaderSpecific(sectionRow, PETSC_SECTION_CLASSID, 2);
8281   if (!globalSectionRow) PetscCall(DMGetGlobalSection(dmRow, &globalSectionRow));
8282   PetscValidHeaderSpecific(globalSectionRow, PETSC_SECTION_CLASSID, 3);
8283   PetscValidHeaderSpecific(dmCol, DM_CLASSID, 5);
8284   if (!sectionCol) PetscCall(DMGetLocalSection(dmCol, &sectionCol));
8285   PetscValidHeaderSpecific(sectionCol, PETSC_SECTION_CLASSID, 6);
8286   if (!globalSectionCol) PetscCall(DMGetGlobalSection(dmCol, &globalSectionCol));
8287   PetscValidHeaderSpecific(globalSectionCol, PETSC_SECTION_CLASSID, 7);
8288   PetscValidHeaderSpecific(A, MAT_CLASSID, 9);
8289 
8290   valuesV1 = valuesV0;
8291   PetscCall(DMPlexGetClosureIndices(dmRow, sectionRow, globalSectionRow, point, useRowPerm, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&valuesV1));
8292   valuesV2 = valuesV1;
8293   PetscCall(DMPlexGetClosureIndices(dmCol, sectionCol, globalSectionCol, point, useColPerm, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&valuesV2));
8294 
8295   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
8296   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8297   ierr = MatSetValues(A, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values, mode);
8298   if (ierr) {
8299     PetscMPIInt rank;
8300 
8301     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8302     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8303     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
8304     PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesRow, NULL, (PetscScalar **)&valuesV2));
8305     PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&valuesV1));
8306     if (valuesV2 != valuesV1) PetscCall(DMRestoreWorkArray(dmCol, 0, MPIU_SCALAR, &valuesV2));
8307     if (valuesV1 != valuesV0) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &valuesV1));
8308   }
8309 
8310   PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, useColPerm, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&valuesV2));
8311   PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, useRowPerm, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&valuesV1));
8312   if (valuesV2 != valuesV1) PetscCall(DMRestoreWorkArray(dmCol, 0, MPIU_SCALAR, &valuesV2));
8313   if (valuesV1 != valuesV0) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &valuesV1));
8314   PetscFunctionReturn(PETSC_SUCCESS);
8315 }
8316 
8317 PetscErrorCode DMPlexMatSetClosureRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8318 {
8319   DM_Plex        *mesh    = (DM_Plex *)dmf->data;
8320   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
8321   PetscInt       *cpoints = NULL;
8322   PetscInt       *findices, *cindices;
8323   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
8324   PetscInt        foffsets[32], coffsets[32];
8325   DMPolytopeType  ct;
8326   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
8327   PetscErrorCode  ierr;
8328 
8329   PetscFunctionBegin;
8330   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
8331   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
8332   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
8333   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
8334   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
8335   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
8336   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
8337   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
8338   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
8339   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
8340   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
8341   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
8342   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
8343   PetscCall(PetscArrayzero(foffsets, 32));
8344   PetscCall(PetscArrayzero(coffsets, 32));
8345   /* Column indices */
8346   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8347   maxFPoints = numCPoints;
8348   /* Compress out points not in the section */
8349   /*   TODO: Squeeze out points with 0 dof as well */
8350   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
8351   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
8352     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
8353       cpoints[q * 2]     = cpoints[p];
8354       cpoints[q * 2 + 1] = cpoints[p + 1];
8355       ++q;
8356     }
8357   }
8358   numCPoints = q;
8359   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
8360     PetscInt fdof;
8361 
8362     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
8363     if (!dof) continue;
8364     for (f = 0; f < numFields; ++f) {
8365       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
8366       coffsets[f + 1] += fdof;
8367     }
8368     numCIndices += dof;
8369   }
8370   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
8371   /* Row indices */
8372   PetscCall(DMPlexGetCellType(dmc, point, &ct));
8373   {
8374     DMPlexTransform tr;
8375     DMPolytopeType *rct;
8376     PetscInt       *rsize, *rcone, *rornt, Nt;
8377 
8378     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8379     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8380     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8381     numSubcells = rsize[Nt - 1];
8382     PetscCall(DMPlexTransformDestroy(&tr));
8383   }
8384   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8385   for (r = 0, q = 0; r < numSubcells; ++r) {
8386     /* TODO Map from coarse to fine cells */
8387     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8388     /* Compress out points not in the section */
8389     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8390     for (p = 0; p < numFPoints * 2; p += 2) {
8391       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8392         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8393         if (!dof) continue;
8394         for (s = 0; s < q; ++s)
8395           if (fpoints[p] == ftotpoints[s * 2]) break;
8396         if (s < q) continue;
8397         ftotpoints[q * 2]     = fpoints[p];
8398         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8399         ++q;
8400       }
8401     }
8402     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8403   }
8404   numFPoints = q;
8405   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8406     PetscInt fdof;
8407 
8408     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8409     if (!dof) continue;
8410     for (f = 0; f < numFields; ++f) {
8411       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8412       foffsets[f + 1] += fdof;
8413     }
8414     numFIndices += dof;
8415   }
8416   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8417 
8418   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8419   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8420   PetscCall(DMGetWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8421   PetscCall(DMGetWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8422   if (numFields) {
8423     const PetscInt **permsF[32] = {NULL};
8424     const PetscInt **permsC[32] = {NULL};
8425 
8426     for (f = 0; f < numFields; f++) {
8427       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8428       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8429     }
8430     for (p = 0; p < numFPoints; p++) {
8431       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8432       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8433     }
8434     for (p = 0; p < numCPoints; p++) {
8435       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8436       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8437     }
8438     for (f = 0; f < numFields; f++) {
8439       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8440       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8441     }
8442   } else {
8443     const PetscInt **permsF = NULL;
8444     const PetscInt **permsC = NULL;
8445 
8446     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8447     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8448     for (p = 0, off = 0; p < numFPoints; p++) {
8449       const PetscInt *perm = permsF ? permsF[p] : NULL;
8450 
8451       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8452       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8453     }
8454     for (p = 0, off = 0; p < numCPoints; p++) {
8455       const PetscInt *perm = permsC ? permsC[p] : NULL;
8456 
8457       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8458       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8459     }
8460     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8461     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8462   }
8463   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8464   /* TODO: flips */
8465   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8466   ierr = MatSetValues(A, numFIndices, findices, numCIndices, cindices, values, mode);
8467   if (ierr) {
8468     PetscMPIInt rank;
8469 
8470     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8471     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8472     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8473     PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8474     PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8475   }
8476   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8477   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8478   PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8479   PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8480   PetscFunctionReturn(PETSC_SUCCESS);
8481 }
8482 
8483 PetscErrorCode DMPlexMatGetClosureIndicesRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, PetscInt point, PetscInt cindices[], PetscInt findices[])
8484 {
8485   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
8486   PetscInt       *cpoints      = NULL;
8487   PetscInt        foffsets[32] = {0}, coffsets[32] = {0};
8488   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
8489   DMPolytopeType  ct;
8490   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
8491 
8492   PetscFunctionBegin;
8493   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
8494   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
8495   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
8496   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
8497   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
8498   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
8499   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
8500   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
8501   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
8502   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
8503   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
8504   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
8505   /* Column indices */
8506   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8507   maxFPoints = numCPoints;
8508   /* Compress out points not in the section */
8509   /*   TODO: Squeeze out points with 0 dof as well */
8510   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
8511   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
8512     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
8513       cpoints[q * 2]     = cpoints[p];
8514       cpoints[q * 2 + 1] = cpoints[p + 1];
8515       ++q;
8516     }
8517   }
8518   numCPoints = q;
8519   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
8520     PetscInt fdof;
8521 
8522     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
8523     if (!dof) continue;
8524     for (f = 0; f < numFields; ++f) {
8525       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
8526       coffsets[f + 1] += fdof;
8527     }
8528     numCIndices += dof;
8529   }
8530   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
8531   /* Row indices */
8532   PetscCall(DMPlexGetCellType(dmc, point, &ct));
8533   {
8534     DMPlexTransform tr;
8535     DMPolytopeType *rct;
8536     PetscInt       *rsize, *rcone, *rornt, Nt;
8537 
8538     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8539     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8540     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8541     numSubcells = rsize[Nt - 1];
8542     PetscCall(DMPlexTransformDestroy(&tr));
8543   }
8544   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8545   for (r = 0, q = 0; r < numSubcells; ++r) {
8546     /* TODO Map from coarse to fine cells */
8547     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8548     /* Compress out points not in the section */
8549     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8550     for (p = 0; p < numFPoints * 2; p += 2) {
8551       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8552         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8553         if (!dof) continue;
8554         for (s = 0; s < q; ++s)
8555           if (fpoints[p] == ftotpoints[s * 2]) break;
8556         if (s < q) continue;
8557         ftotpoints[q * 2]     = fpoints[p];
8558         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8559         ++q;
8560       }
8561     }
8562     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8563   }
8564   numFPoints = q;
8565   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8566     PetscInt fdof;
8567 
8568     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8569     if (!dof) continue;
8570     for (f = 0; f < numFields; ++f) {
8571       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8572       foffsets[f + 1] += fdof;
8573     }
8574     numFIndices += dof;
8575   }
8576   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8577 
8578   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8579   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8580   if (numFields) {
8581     const PetscInt **permsF[32] = {NULL};
8582     const PetscInt **permsC[32] = {NULL};
8583 
8584     for (f = 0; f < numFields; f++) {
8585       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8586       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8587     }
8588     for (p = 0; p < numFPoints; p++) {
8589       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8590       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8591     }
8592     for (p = 0; p < numCPoints; p++) {
8593       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8594       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8595     }
8596     for (f = 0; f < numFields; f++) {
8597       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8598       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8599     }
8600   } else {
8601     const PetscInt **permsF = NULL;
8602     const PetscInt **permsC = NULL;
8603 
8604     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8605     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8606     for (p = 0, off = 0; p < numFPoints; p++) {
8607       const PetscInt *perm = permsF ? permsF[p] : NULL;
8608 
8609       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8610       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8611     }
8612     for (p = 0, off = 0; p < numCPoints; p++) {
8613       const PetscInt *perm = permsC ? permsC[p] : NULL;
8614 
8615       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8616       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8617     }
8618     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8619     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8620   }
8621   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8622   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8623   PetscFunctionReturn(PETSC_SUCCESS);
8624 }
8625 
8626 /*@C
8627   DMPlexGetVTKCellHeight - Returns the height in the DAG used to determine which points are cells (normally 0)
8628 
8629   Input Parameter:
8630 . dm - The `DMPLEX` object
8631 
8632   Output Parameter:
8633 . cellHeight - The height of a cell
8634 
8635   Level: developer
8636 
8637 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetVTKCellHeight()`
8638 @*/
8639 PetscErrorCode DMPlexGetVTKCellHeight(DM dm, PetscInt *cellHeight)
8640 {
8641   DM_Plex *mesh = (DM_Plex *)dm->data;
8642 
8643   PetscFunctionBegin;
8644   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8645   PetscAssertPointer(cellHeight, 2);
8646   *cellHeight = mesh->vtkCellHeight;
8647   PetscFunctionReturn(PETSC_SUCCESS);
8648 }
8649 
8650 /*@C
8651   DMPlexSetVTKCellHeight - Sets the height in the DAG used to determine which points are cells (normally 0)
8652 
8653   Input Parameters:
8654 + dm         - The `DMPLEX` object
8655 - cellHeight - The height of a cell
8656 
8657   Level: developer
8658 
8659 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetVTKCellHeight()`
8660 @*/
8661 PetscErrorCode DMPlexSetVTKCellHeight(DM dm, PetscInt cellHeight)
8662 {
8663   DM_Plex *mesh = (DM_Plex *)dm->data;
8664 
8665   PetscFunctionBegin;
8666   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8667   mesh->vtkCellHeight = cellHeight;
8668   PetscFunctionReturn(PETSC_SUCCESS);
8669 }
8670 
8671 /*@
8672   DMPlexGetCellTypeStratum - Get the range of cells of a given celltype
8673 
8674   Input Parameters:
8675 + dm - The `DMPLEX` object
8676 - ct - The `DMPolytopeType` of the cell
8677 
8678   Output Parameters:
8679 + start - The first cell of this type, or `NULL`
8680 - end   - The upper bound on this celltype, or `NULL`
8681 
8682   Level: advanced
8683 
8684 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexConstructGhostCells()`, `DMPlexGetDepthStratum()`, `DMPlexGetHeightStratum()`
8685 @*/
8686 PetscErrorCode DMPlexGetCellTypeStratum(DM dm, DMPolytopeType ct, PetscInt *start, PetscInt *end)
8687 {
8688   DM_Plex *mesh = (DM_Plex *)dm->data;
8689   DMLabel  label;
8690   PetscInt pStart, pEnd;
8691 
8692   PetscFunctionBegin;
8693   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8694   if (start) {
8695     PetscAssertPointer(start, 3);
8696     *start = 0;
8697   }
8698   if (end) {
8699     PetscAssertPointer(end, 4);
8700     *end = 0;
8701   }
8702   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8703   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
8704   if (mesh->tr) {
8705     PetscCall(DMPlexTransformGetCellTypeStratum(mesh->tr, ct, start, end));
8706   } else {
8707     PetscCall(DMPlexGetCellTypeLabel(dm, &label));
8708     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named celltype was found");
8709     PetscCall(DMLabelGetStratumBounds(label, ct, start, end));
8710   }
8711   PetscFunctionReturn(PETSC_SUCCESS);
8712 }
8713 
8714 PetscErrorCode DMPlexCreateNumbering_Plex(DM dm, PetscInt pStart, PetscInt pEnd, PetscInt shift, PetscInt *globalSize, PetscSF sf, IS *numbering)
8715 {
8716   PetscSection section, globalSection;
8717   PetscInt    *numbers, p;
8718 
8719   PetscFunctionBegin;
8720   if (PetscDefined(USE_DEBUG)) PetscCall(DMPlexCheckPointSF(dm, sf, PETSC_TRUE));
8721   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
8722   PetscCall(PetscSectionSetChart(section, pStart, pEnd));
8723   for (p = pStart; p < pEnd; ++p) PetscCall(PetscSectionSetDof(section, p, 1));
8724   PetscCall(PetscSectionSetUp(section));
8725   PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &globalSection));
8726   PetscCall(PetscMalloc1(pEnd - pStart, &numbers));
8727   for (p = pStart; p < pEnd; ++p) {
8728     PetscCall(PetscSectionGetOffset(globalSection, p, &numbers[p - pStart]));
8729     if (numbers[p - pStart] < 0) numbers[p - pStart] -= shift;
8730     else numbers[p - pStart] += shift;
8731   }
8732   PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), pEnd - pStart, numbers, PETSC_OWN_POINTER, numbering));
8733   if (globalSize) {
8734     PetscLayout layout;
8735     PetscCall(PetscSectionGetPointLayout(PetscObjectComm((PetscObject)dm), globalSection, &layout));
8736     PetscCall(PetscLayoutGetSize(layout, globalSize));
8737     PetscCall(PetscLayoutDestroy(&layout));
8738   }
8739   PetscCall(PetscSectionDestroy(&section));
8740   PetscCall(PetscSectionDestroy(&globalSection));
8741   PetscFunctionReturn(PETSC_SUCCESS);
8742 }
8743 
8744 PetscErrorCode DMPlexCreateCellNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalCellNumbers)
8745 {
8746   PetscInt cellHeight, cStart, cEnd;
8747 
8748   PetscFunctionBegin;
8749   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8750   if (includeHybrid) PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8751   else PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd));
8752   PetscCall(DMPlexCreateNumbering_Plex(dm, cStart, cEnd, 0, NULL, dm->sf, globalCellNumbers));
8753   PetscFunctionReturn(PETSC_SUCCESS);
8754 }
8755 
8756 /*@
8757   DMPlexGetCellNumbering - Get a global cell numbering for all cells on this process
8758 
8759   Input Parameter:
8760 . dm - The `DMPLEX` object
8761 
8762   Output Parameter:
8763 . globalCellNumbers - Global cell numbers for all cells on this process
8764 
8765   Level: developer
8766 
8767 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetVertexNumbering()`
8768 @*/
8769 PetscErrorCode DMPlexGetCellNumbering(DM dm, IS *globalCellNumbers)
8770 {
8771   DM_Plex *mesh = (DM_Plex *)dm->data;
8772 
8773   PetscFunctionBegin;
8774   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8775   if (!mesh->globalCellNumbers) PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_FALSE, &mesh->globalCellNumbers));
8776   *globalCellNumbers = mesh->globalCellNumbers;
8777   PetscFunctionReturn(PETSC_SUCCESS);
8778 }
8779 
8780 PetscErrorCode DMPlexCreateVertexNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalVertexNumbers)
8781 {
8782   PetscInt vStart, vEnd;
8783 
8784   PetscFunctionBegin;
8785   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8786   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8787   PetscCall(DMPlexCreateNumbering_Plex(dm, vStart, vEnd, 0, NULL, dm->sf, globalVertexNumbers));
8788   PetscFunctionReturn(PETSC_SUCCESS);
8789 }
8790 
8791 /*@
8792   DMPlexGetVertexNumbering - Get a global vertex numbering for all vertices on this process
8793 
8794   Input Parameter:
8795 . dm - The `DMPLEX` object
8796 
8797   Output Parameter:
8798 . globalVertexNumbers - Global vertex numbers for all vertices on this process
8799 
8800   Level: developer
8801 
8802 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
8803 @*/
8804 PetscErrorCode DMPlexGetVertexNumbering(DM dm, IS *globalVertexNumbers)
8805 {
8806   DM_Plex *mesh = (DM_Plex *)dm->data;
8807 
8808   PetscFunctionBegin;
8809   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8810   if (!mesh->globalVertexNumbers) PetscCall(DMPlexCreateVertexNumbering_Internal(dm, PETSC_FALSE, &mesh->globalVertexNumbers));
8811   *globalVertexNumbers = mesh->globalVertexNumbers;
8812   PetscFunctionReturn(PETSC_SUCCESS);
8813 }
8814 
8815 /*@
8816   DMPlexCreatePointNumbering - Create a global numbering for all points.
8817 
8818   Collective
8819 
8820   Input Parameter:
8821 . dm - The `DMPLEX` object
8822 
8823   Output Parameter:
8824 . globalPointNumbers - Global numbers for all points on this process
8825 
8826   Level: developer
8827 
8828   Notes:
8829   The point numbering `IS` is parallel, with local portion indexed by local points (see `DMGetLocalSection()`). The global
8830   points are taken as stratified, with each MPI rank owning a contiguous subset of each stratum. In the IS, owned points
8831   will have their non-negative value while points owned by different ranks will be involuted -(idx+1). As an example,
8832   consider a parallel mesh in which the first two elements and first two vertices are owned by rank 0.
8833 
8834   The partitioned mesh is
8835   ```
8836   (2)--0--(3)--1--(4)    (1)--0--(2)
8837   ```
8838   and its global numbering is
8839   ```
8840   (3)--0--(4)--1--(5)--2--(6)
8841   ```
8842   Then the global numbering is provided as
8843   ```
8844   [0] Number of indices in set 5
8845   [0] 0 0
8846   [0] 1 1
8847   [0] 2 3
8848   [0] 3 4
8849   [0] 4 -6
8850   [1] Number of indices in set 3
8851   [1] 0 2
8852   [1] 1 5
8853   [1] 2 6
8854   ```
8855 
8856 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
8857 @*/
8858 PetscErrorCode DMPlexCreatePointNumbering(DM dm, IS *globalPointNumbers)
8859 {
8860   IS        nums[4];
8861   PetscInt  depths[4], gdepths[4], starts[4];
8862   PetscInt  depth, d, shift = 0;
8863   PetscBool empty = PETSC_FALSE;
8864 
8865   PetscFunctionBegin;
8866   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8867   PetscCall(DMPlexGetDepth(dm, &depth));
8868   // For unstratified meshes use dim instead of depth
8869   if (depth < 0) PetscCall(DMGetDimension(dm, &depth));
8870   // If any stratum is empty, we must mark all empty
8871   for (d = 0; d <= depth; ++d) {
8872     PetscInt end;
8873 
8874     depths[d] = depth - d;
8875     PetscCall(DMPlexGetDepthStratum(dm, depths[d], &starts[d], &end));
8876     if (!(starts[d] - end)) empty = PETSC_TRUE;
8877   }
8878   if (empty)
8879     for (d = 0; d <= depth; ++d) {
8880       depths[d] = -1;
8881       starts[d] = -1;
8882     }
8883   else PetscCall(PetscSortIntWithArray(depth + 1, starts, depths));
8884   PetscCall(MPIU_Allreduce(depths, gdepths, depth + 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
8885   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]);
8886   // Note here that 'shift' is collective, so that the numbering is stratified by depth
8887   for (d = 0; d <= depth; ++d) {
8888     PetscInt pStart, pEnd, gsize;
8889 
8890     PetscCall(DMPlexGetDepthStratum(dm, gdepths[d], &pStart, &pEnd));
8891     PetscCall(DMPlexCreateNumbering_Plex(dm, pStart, pEnd, shift, &gsize, dm->sf, &nums[d]));
8892     shift += gsize;
8893   }
8894   PetscCall(ISConcatenate(PETSC_COMM_SELF, depth + 1, nums, globalPointNumbers));
8895   for (d = 0; d <= depth; ++d) PetscCall(ISDestroy(&nums[d]));
8896   PetscFunctionReturn(PETSC_SUCCESS);
8897 }
8898 
8899 /*@
8900   DMPlexCreateRankField - Create a cell field whose value is the rank of the owner
8901 
8902   Input Parameter:
8903 . dm - The `DMPLEX` object
8904 
8905   Output Parameter:
8906 . ranks - The rank field
8907 
8908   Options Database Key:
8909 . -dm_partition_view - Adds the rank field into the `DM` output from `-dm_view` using the same viewer
8910 
8911   Level: intermediate
8912 
8913 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`
8914 @*/
8915 PetscErrorCode DMPlexCreateRankField(DM dm, Vec *ranks)
8916 {
8917   DM             rdm;
8918   PetscFE        fe;
8919   PetscScalar   *r;
8920   PetscMPIInt    rank;
8921   DMPolytopeType ct;
8922   PetscInt       dim, cStart, cEnd, c;
8923   PetscBool      simplex;
8924 
8925   PetscFunctionBeginUser;
8926   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8927   PetscAssertPointer(ranks, 2);
8928   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
8929   PetscCall(DMClone(dm, &rdm));
8930   PetscCall(DMGetDimension(rdm, &dim));
8931   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
8932   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
8933   simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct) + 1 ? PETSC_TRUE : PETSC_FALSE;
8934   PetscCall(PetscFECreateDefault(PETSC_COMM_SELF, dim, 1, simplex, "PETSc___rank_", -1, &fe));
8935   PetscCall(PetscObjectSetName((PetscObject)fe, "rank"));
8936   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
8937   PetscCall(PetscFEDestroy(&fe));
8938   PetscCall(DMCreateDS(rdm));
8939   PetscCall(DMCreateGlobalVector(rdm, ranks));
8940   PetscCall(PetscObjectSetName((PetscObject)*ranks, "partition"));
8941   PetscCall(VecGetArray(*ranks, &r));
8942   for (c = cStart; c < cEnd; ++c) {
8943     PetscScalar *lr;
8944 
8945     PetscCall(DMPlexPointGlobalRef(rdm, c, r, &lr));
8946     if (lr) *lr = rank;
8947   }
8948   PetscCall(VecRestoreArray(*ranks, &r));
8949   PetscCall(DMDestroy(&rdm));
8950   PetscFunctionReturn(PETSC_SUCCESS);
8951 }
8952 
8953 /*@
8954   DMPlexCreateLabelField - Create a field whose value is the label value for that point
8955 
8956   Input Parameters:
8957 + dm    - The `DMPLEX`
8958 - label - The `DMLabel`
8959 
8960   Output Parameter:
8961 . val - The label value field
8962 
8963   Options Database Key:
8964 . -dm_label_view - Adds the label value field into the `DM` output from `-dm_view` using the same viewer
8965 
8966   Level: intermediate
8967 
8968 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`
8969 @*/
8970 PetscErrorCode DMPlexCreateLabelField(DM dm, DMLabel label, Vec *val)
8971 {
8972   DM             rdm, plex;
8973   Vec            lval;
8974   PetscSection   section;
8975   PetscFE        fe;
8976   PetscScalar   *v;
8977   PetscInt       dim, pStart, pEnd, p, cStart;
8978   DMPolytopeType ct;
8979   char           name[PETSC_MAX_PATH_LEN];
8980   const char    *lname, *prefix;
8981 
8982   PetscFunctionBeginUser;
8983   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8984   PetscAssertPointer(label, 2);
8985   PetscAssertPointer(val, 3);
8986   PetscCall(DMClone(dm, &rdm));
8987   PetscCall(DMConvert(rdm, DMPLEX, &plex));
8988   PetscCall(DMPlexGetHeightStratum(plex, 0, &cStart, NULL));
8989   PetscCall(DMPlexGetCellType(plex, cStart, &ct));
8990   PetscCall(DMDestroy(&plex));
8991   PetscCall(DMGetDimension(rdm, &dim));
8992   PetscCall(DMGetOptionsPrefix(dm, &prefix));
8993   PetscCall(PetscObjectGetName((PetscObject)label, &lname));
8994   PetscCall(PetscSNPrintf(name, sizeof(name), "%s%s_", prefix ? prefix : "", lname));
8995   PetscCall(PetscFECreateByCell(PETSC_COMM_SELF, dim, 1, ct, name, -1, &fe));
8996   PetscCall(PetscObjectSetName((PetscObject)fe, ""));
8997   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
8998   PetscCall(PetscFEDestroy(&fe));
8999   PetscCall(DMCreateDS(rdm));
9000   PetscCall(DMCreateGlobalVector(rdm, val));
9001   PetscCall(DMCreateLocalVector(rdm, &lval));
9002   PetscCall(PetscObjectSetName((PetscObject)*val, lname));
9003   PetscCall(DMGetLocalSection(rdm, &section));
9004   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
9005   PetscCall(VecGetArray(lval, &v));
9006   for (p = pStart; p < pEnd; ++p) {
9007     PetscInt cval, dof, off;
9008 
9009     PetscCall(PetscSectionGetDof(section, p, &dof));
9010     if (!dof) continue;
9011     PetscCall(DMLabelGetValue(label, p, &cval));
9012     PetscCall(PetscSectionGetOffset(section, p, &off));
9013     for (PetscInt d = 0; d < dof; d++) v[off + d] = cval;
9014   }
9015   PetscCall(VecRestoreArray(lval, &v));
9016   PetscCall(DMLocalToGlobal(rdm, lval, INSERT_VALUES, *val));
9017   PetscCall(VecDestroy(&lval));
9018   PetscCall(DMDestroy(&rdm));
9019   PetscFunctionReturn(PETSC_SUCCESS);
9020 }
9021 
9022 /*@
9023   DMPlexCheckSymmetry - Check that the adjacency information in the mesh is symmetric.
9024 
9025   Input Parameter:
9026 . dm - The `DMPLEX` object
9027 
9028   Level: developer
9029 
9030   Notes:
9031   This is a useful diagnostic when creating meshes programmatically.
9032 
9033   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9034 
9035 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9036 @*/
9037 PetscErrorCode DMPlexCheckSymmetry(DM dm)
9038 {
9039   PetscSection    coneSection, supportSection;
9040   const PetscInt *cone, *support;
9041   PetscInt        coneSize, c, supportSize, s;
9042   PetscInt        pStart, pEnd, p, pp, csize, ssize;
9043   PetscBool       storagecheck = PETSC_TRUE;
9044 
9045   PetscFunctionBegin;
9046   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9047   PetscCall(DMViewFromOptions(dm, NULL, "-sym_dm_view"));
9048   PetscCall(DMPlexGetConeSection(dm, &coneSection));
9049   PetscCall(DMPlexGetSupportSection(dm, &supportSection));
9050   /* Check that point p is found in the support of its cone points, and vice versa */
9051   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
9052   for (p = pStart; p < pEnd; ++p) {
9053     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
9054     PetscCall(DMPlexGetCone(dm, p, &cone));
9055     for (c = 0; c < coneSize; ++c) {
9056       PetscBool dup = PETSC_FALSE;
9057       PetscInt  d;
9058       for (d = c - 1; d >= 0; --d) {
9059         if (cone[c] == cone[d]) {
9060           dup = PETSC_TRUE;
9061           break;
9062         }
9063       }
9064       PetscCall(DMPlexGetSupportSize(dm, cone[c], &supportSize));
9065       PetscCall(DMPlexGetSupport(dm, cone[c], &support));
9066       for (s = 0; s < supportSize; ++s) {
9067         if (support[s] == p) break;
9068       }
9069       if ((s >= supportSize) || (dup && (support[s + 1] != p))) {
9070         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", p));
9071         for (s = 0; s < coneSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[s]));
9072         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9073         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", cone[c]));
9074         for (s = 0; s < supportSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[s]));
9075         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9076         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]);
9077         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in support of cone point %" PetscInt_FMT, p, cone[c]);
9078       }
9079     }
9080     PetscCall(DMPlexGetTreeParent(dm, p, &pp, NULL));
9081     if (p != pp) {
9082       storagecheck = PETSC_FALSE;
9083       continue;
9084     }
9085     PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
9086     PetscCall(DMPlexGetSupport(dm, p, &support));
9087     for (s = 0; s < supportSize; ++s) {
9088       PetscCall(DMPlexGetConeSize(dm, support[s], &coneSize));
9089       PetscCall(DMPlexGetCone(dm, support[s], &cone));
9090       for (c = 0; c < coneSize; ++c) {
9091         PetscCall(DMPlexGetTreeParent(dm, cone[c], &pp, NULL));
9092         if (cone[c] != pp) {
9093           c = 0;
9094           break;
9095         }
9096         if (cone[c] == p) break;
9097       }
9098       if (c >= coneSize) {
9099         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", p));
9100         for (c = 0; c < supportSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[c]));
9101         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9102         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", support[s]));
9103         for (c = 0; c < coneSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[c]));
9104         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9105         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in cone of support point %" PetscInt_FMT, p, support[s]);
9106       }
9107     }
9108   }
9109   if (storagecheck) {
9110     PetscCall(PetscSectionGetStorageSize(coneSection, &csize));
9111     PetscCall(PetscSectionGetStorageSize(supportSection, &ssize));
9112     PetscCheck(csize == ssize, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Total cone size %" PetscInt_FMT " != Total support size %" PetscInt_FMT, csize, ssize);
9113   }
9114   PetscFunctionReturn(PETSC_SUCCESS);
9115 }
9116 
9117 /*
9118   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.
9119 */
9120 static PetscErrorCode DMPlexCellUnsplitVertices_Private(DM dm, PetscInt c, DMPolytopeType ct, PetscInt *unsplit)
9121 {
9122   DMPolytopeType  cct;
9123   PetscInt        ptpoints[4];
9124   const PetscInt *cone, *ccone, *ptcone;
9125   PetscInt        coneSize, cp, cconeSize, ccp, npt = 0, pt;
9126 
9127   PetscFunctionBegin;
9128   *unsplit = 0;
9129   switch (ct) {
9130   case DM_POLYTOPE_POINT_PRISM_TENSOR:
9131     ptpoints[npt++] = c;
9132     break;
9133   case DM_POLYTOPE_SEG_PRISM_TENSOR:
9134     PetscCall(DMPlexGetCone(dm, c, &cone));
9135     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9136     for (cp = 0; cp < coneSize; ++cp) {
9137       PetscCall(DMPlexGetCellType(dm, cone[cp], &cct));
9138       if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) ptpoints[npt++] = cone[cp];
9139     }
9140     break;
9141   case DM_POLYTOPE_TRI_PRISM_TENSOR:
9142   case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9143     PetscCall(DMPlexGetCone(dm, c, &cone));
9144     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9145     for (cp = 0; cp < coneSize; ++cp) {
9146       PetscCall(DMPlexGetCone(dm, cone[cp], &ccone));
9147       PetscCall(DMPlexGetConeSize(dm, cone[cp], &cconeSize));
9148       for (ccp = 0; ccp < cconeSize; ++ccp) {
9149         PetscCall(DMPlexGetCellType(dm, ccone[ccp], &cct));
9150         if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) {
9151           PetscInt p;
9152           for (p = 0; p < npt; ++p)
9153             if (ptpoints[p] == ccone[ccp]) break;
9154           if (p == npt) ptpoints[npt++] = ccone[ccp];
9155         }
9156       }
9157     }
9158     break;
9159   default:
9160     break;
9161   }
9162   for (pt = 0; pt < npt; ++pt) {
9163     PetscCall(DMPlexGetCone(dm, ptpoints[pt], &ptcone));
9164     if (ptcone[0] == ptcone[1]) ++(*unsplit);
9165   }
9166   PetscFunctionReturn(PETSC_SUCCESS);
9167 }
9168 
9169 /*@
9170   DMPlexCheckSkeleton - Check that each cell has the correct number of vertices
9171 
9172   Input Parameters:
9173 + dm         - The `DMPLEX` object
9174 - cellHeight - Normally 0
9175 
9176   Level: developer
9177 
9178   Notes:
9179   This is a useful diagnostic when creating meshes programmatically.
9180   Currently applicable only to homogeneous simplex or tensor meshes.
9181 
9182   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9183 
9184 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9185 @*/
9186 PetscErrorCode DMPlexCheckSkeleton(DM dm, PetscInt cellHeight)
9187 {
9188   DMPlexInterpolatedFlag interp;
9189   DMPolytopeType         ct;
9190   PetscInt               vStart, vEnd, cStart, cEnd, c;
9191 
9192   PetscFunctionBegin;
9193   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9194   PetscCall(DMPlexIsInterpolated(dm, &interp));
9195   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9196   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
9197   for (c = cStart; c < cEnd; ++c) {
9198     PetscInt *closure = NULL;
9199     PetscInt  coneSize, closureSize, cl, Nv = 0;
9200 
9201     PetscCall(DMPlexGetCellType(dm, c, &ct));
9202     PetscCheck((PetscInt)ct >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell %" PetscInt_FMT " has no cell type", c);
9203     if (ct == DM_POLYTOPE_UNKNOWN) continue;
9204     if (interp == DMPLEX_INTERPOLATED_FULL) {
9205       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9206       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));
9207     }
9208     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9209     for (cl = 0; cl < closureSize * 2; cl += 2) {
9210       const PetscInt p = closure[cl];
9211       if ((p >= vStart) && (p < vEnd)) ++Nv;
9212     }
9213     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9214     /* Special Case: Tensor faces with identified vertices */
9215     if (Nv < DMPolytopeTypeGetNumVertices(ct)) {
9216       PetscInt unsplit;
9217 
9218       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9219       if (Nv + unsplit == DMPolytopeTypeGetNumVertices(ct)) continue;
9220     }
9221     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));
9222   }
9223   PetscFunctionReturn(PETSC_SUCCESS);
9224 }
9225 
9226 /*@
9227   DMPlexCheckFaces - Check that the faces of each cell give a vertex order this is consistent with what we expect from the cell type
9228 
9229   Collective
9230 
9231   Input Parameters:
9232 + dm         - The `DMPLEX` object
9233 - cellHeight - Normally 0
9234 
9235   Level: developer
9236 
9237   Notes:
9238   This is a useful diagnostic when creating meshes programmatically.
9239   This routine is only relevant for meshes that are fully interpolated across all ranks.
9240   It will error out if a partially interpolated mesh is given on some rank.
9241   It will do nothing for locally uninterpolated mesh (as there is nothing to check).
9242 
9243   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9244 
9245 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMPlexGetVTKCellHeight()`, `DMSetFromOptions()`
9246 @*/
9247 PetscErrorCode DMPlexCheckFaces(DM dm, PetscInt cellHeight)
9248 {
9249   PetscInt               dim, depth, vStart, vEnd, cStart, cEnd, c, h;
9250   DMPlexInterpolatedFlag interpEnum;
9251 
9252   PetscFunctionBegin;
9253   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9254   PetscCall(DMPlexIsInterpolatedCollective(dm, &interpEnum));
9255   if (interpEnum == DMPLEX_INTERPOLATED_NONE) PetscFunctionReturn(PETSC_SUCCESS);
9256   if (interpEnum != DMPLEX_INTERPOLATED_FULL) {
9257     PetscCall(PetscPrintf(PetscObjectComm((PetscObject)dm), "DMPlexCheckFaces() warning: Mesh is only partially interpolated, this is currently not supported"));
9258     PetscFunctionReturn(PETSC_SUCCESS);
9259   }
9260 
9261   PetscCall(DMGetDimension(dm, &dim));
9262   PetscCall(DMPlexGetDepth(dm, &depth));
9263   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
9264   for (h = cellHeight; h < PetscMin(depth, dim); ++h) {
9265     PetscCall(DMPlexGetHeightStratum(dm, h, &cStart, &cEnd));
9266     for (c = cStart; c < cEnd; ++c) {
9267       const PetscInt       *cone, *ornt, *faceSizes, *faces;
9268       const DMPolytopeType *faceTypes;
9269       DMPolytopeType        ct;
9270       PetscInt              numFaces, coneSize, f;
9271       PetscInt             *closure = NULL, closureSize, cl, numCorners = 0, fOff = 0, unsplit;
9272 
9273       PetscCall(DMPlexGetCellType(dm, c, &ct));
9274       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9275       if (unsplit) continue;
9276       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9277       PetscCall(DMPlexGetCone(dm, c, &cone));
9278       PetscCall(DMPlexGetConeOrientation(dm, c, &ornt));
9279       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9280       for (cl = 0; cl < closureSize * 2; cl += 2) {
9281         const PetscInt p = closure[cl];
9282         if ((p >= vStart) && (p < vEnd)) closure[numCorners++] = p;
9283       }
9284       PetscCall(DMPlexGetRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
9285       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);
9286       for (f = 0; f < numFaces; ++f) {
9287         DMPolytopeType fct;
9288         PetscInt      *fclosure = NULL, fclosureSize, cl, fnumCorners = 0, v;
9289 
9290         PetscCall(DMPlexGetCellType(dm, cone[f], &fct));
9291         PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[f], ornt[f], PETSC_TRUE, &fclosureSize, &fclosure));
9292         for (cl = 0; cl < fclosureSize * 2; cl += 2) {
9293           const PetscInt p = fclosure[cl];
9294           if ((p >= vStart) && (p < vEnd)) fclosure[fnumCorners++] = p;
9295         }
9296         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]);
9297         for (v = 0; v < fnumCorners; ++v) {
9298           if (fclosure[v] != faces[fOff + v]) {
9299             PetscInt v1;
9300 
9301             PetscCall(PetscPrintf(PETSC_COMM_SELF, "face closure:"));
9302             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, fclosure[v1]));
9303             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\ncell face:"));
9304             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, faces[fOff + v1]));
9305             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9306             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]);
9307           }
9308         }
9309         PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[f], PETSC_TRUE, &fclosureSize, &fclosure));
9310         fOff += faceSizes[f];
9311       }
9312       PetscCall(DMPlexRestoreRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
9313       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9314     }
9315   }
9316   PetscFunctionReturn(PETSC_SUCCESS);
9317 }
9318 
9319 /*@
9320   DMPlexCheckGeometry - Check the geometry of mesh cells
9321 
9322   Input Parameter:
9323 . dm - The `DMPLEX` object
9324 
9325   Level: developer
9326 
9327   Notes:
9328   This is a useful diagnostic when creating meshes programmatically.
9329 
9330   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9331 
9332 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9333 @*/
9334 PetscErrorCode DMPlexCheckGeometry(DM dm)
9335 {
9336   Vec       coordinates;
9337   PetscReal detJ, J[9], refVol = 1.0;
9338   PetscReal vol;
9339   PetscInt  dim, depth, dE, d, cStart, cEnd, c;
9340 
9341   PetscFunctionBegin;
9342   PetscCall(DMGetDimension(dm, &dim));
9343   PetscCall(DMGetCoordinateDim(dm, &dE));
9344   if (dim != dE) PetscFunctionReturn(PETSC_SUCCESS);
9345   PetscCall(DMPlexGetDepth(dm, &depth));
9346   for (d = 0; d < dim; ++d) refVol *= 2.0;
9347   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
9348   /* Make sure local coordinates are created, because that step is collective */
9349   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
9350   if (!coordinates) PetscFunctionReturn(PETSC_SUCCESS);
9351   for (c = cStart; c < cEnd; ++c) {
9352     DMPolytopeType ct;
9353     PetscInt       unsplit;
9354     PetscBool      ignoreZeroVol = PETSC_FALSE;
9355 
9356     PetscCall(DMPlexGetCellType(dm, c, &ct));
9357     switch (ct) {
9358     case DM_POLYTOPE_SEG_PRISM_TENSOR:
9359     case DM_POLYTOPE_TRI_PRISM_TENSOR:
9360     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9361       ignoreZeroVol = PETSC_TRUE;
9362       break;
9363     default:
9364       break;
9365     }
9366     switch (ct) {
9367     case DM_POLYTOPE_TRI_PRISM:
9368     case DM_POLYTOPE_TRI_PRISM_TENSOR:
9369     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9370     case DM_POLYTOPE_PYRAMID:
9371       continue;
9372     default:
9373       break;
9374     }
9375     PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9376     if (unsplit) continue;
9377     PetscCall(DMPlexComputeCellGeometryFEM(dm, c, NULL, NULL, J, NULL, &detJ));
9378     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);
9379     PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FEM Volume %g\n", c, (double)(detJ * refVol)));
9380     /* This should work with periodicity since DG coordinates should be used */
9381     if (depth > 1) {
9382       PetscCall(DMPlexComputeCellGeometryFVM(dm, c, &vol, NULL, NULL));
9383       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);
9384       PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FVM Volume %g\n", c, (double)vol));
9385     }
9386   }
9387   PetscFunctionReturn(PETSC_SUCCESS);
9388 }
9389 
9390 /*@
9391   DMPlexCheckPointSF - Check that several necessary conditions are met for the point `PetscSF` of this plex.
9392 
9393   Collective
9394 
9395   Input Parameters:
9396 + dm              - The `DMPLEX` object
9397 . pointSF         - The `PetscSF`, or `NULL` for `PointSF` attached to `DM`
9398 - allowExtraRoots - Flag to allow extra points not present in the `DM`
9399 
9400   Level: developer
9401 
9402   Notes:
9403   This is mainly intended for debugging/testing purposes.
9404 
9405   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9406 
9407   Extra roots can come from periodic cuts, where additional points appear on the boundary
9408 
9409 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMGetPointSF()`, `DMSetFromOptions()`
9410 @*/
9411 PetscErrorCode DMPlexCheckPointSF(DM dm, PetscSF pointSF, PetscBool allowExtraRoots)
9412 {
9413   PetscInt           l, nleaves, nroots, overlap;
9414   const PetscInt    *locals;
9415   const PetscSFNode *remotes;
9416   PetscBool          distributed;
9417   MPI_Comm           comm;
9418   PetscMPIInt        rank;
9419 
9420   PetscFunctionBegin;
9421   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9422   if (pointSF) PetscValidHeaderSpecific(pointSF, PETSCSF_CLASSID, 2);
9423   else pointSF = dm->sf;
9424   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9425   PetscCheck(pointSF, comm, PETSC_ERR_ARG_WRONGSTATE, "DMPlex must have Point SF attached");
9426   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9427   {
9428     PetscMPIInt mpiFlag;
9429 
9430     PetscCallMPI(MPI_Comm_compare(comm, PetscObjectComm((PetscObject)pointSF), &mpiFlag));
9431     PetscCheck(mpiFlag == MPI_CONGRUENT || mpiFlag == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "DM and Point SF have different communicators (flag %d)", mpiFlag);
9432   }
9433   PetscCall(PetscSFGetGraph(pointSF, &nroots, &nleaves, &locals, &remotes));
9434   PetscCall(DMPlexIsDistributed(dm, &distributed));
9435   if (!distributed) {
9436     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);
9437     PetscFunctionReturn(PETSC_SUCCESS);
9438   }
9439   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);
9440   PetscCall(DMPlexGetOverlap(dm, &overlap));
9441 
9442   /* Check SF graph is compatible with DMPlex chart */
9443   {
9444     PetscInt pStart, pEnd, maxLeaf;
9445 
9446     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
9447     PetscCall(PetscSFGetLeafRange(pointSF, NULL, &maxLeaf));
9448     PetscCheck(allowExtraRoots || pEnd - pStart == nroots, PETSC_COMM_SELF, PETSC_ERR_PLIB, "pEnd - pStart = %" PetscInt_FMT " != nroots = %" PetscInt_FMT, pEnd - pStart, nroots);
9449     PetscCheck(maxLeaf < pEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "maxLeaf = %" PetscInt_FMT " >= pEnd = %" PetscInt_FMT, maxLeaf, pEnd);
9450   }
9451 
9452   /* Check Point SF has no local points referenced */
9453   for (l = 0; l < nleaves; l++) {
9454     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);
9455   }
9456 
9457   /* Check there are no cells in interface */
9458   if (!overlap) {
9459     PetscInt cellHeight, cStart, cEnd;
9460 
9461     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9462     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9463     for (l = 0; l < nleaves; ++l) {
9464       const PetscInt point = locals ? locals[l] : l;
9465 
9466       PetscCheck(point < cStart || point >= cEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " which is a cell", point);
9467     }
9468   }
9469 
9470   /* If some point is in interface, then all its cone points must be also in interface (either as leaves or roots) */
9471   {
9472     const PetscInt *rootdegree;
9473 
9474     PetscCall(PetscSFComputeDegreeBegin(pointSF, &rootdegree));
9475     PetscCall(PetscSFComputeDegreeEnd(pointSF, &rootdegree));
9476     for (l = 0; l < nleaves; ++l) {
9477       const PetscInt  point = locals ? locals[l] : l;
9478       const PetscInt *cone;
9479       PetscInt        coneSize, c, idx;
9480 
9481       PetscCall(DMPlexGetConeSize(dm, point, &coneSize));
9482       PetscCall(DMPlexGetCone(dm, point, &cone));
9483       for (c = 0; c < coneSize; ++c) {
9484         if (!rootdegree[cone[c]]) {
9485           if (locals) {
9486             PetscCall(PetscFindInt(cone[c], nleaves, locals, &idx));
9487           } else {
9488             idx = (cone[c] < nleaves) ? cone[c] : -1;
9489           }
9490           PetscCheck(idx >= 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " but not %" PetscInt_FMT " from its cone", point, cone[c]);
9491         }
9492       }
9493     }
9494   }
9495   PetscFunctionReturn(PETSC_SUCCESS);
9496 }
9497 
9498 /*@
9499   DMPlexCheck - Perform various checks of `DMPLEX` sanity
9500 
9501   Input Parameter:
9502 . dm - The `DMPLEX` object
9503 
9504   Level: developer
9505 
9506   Notes:
9507   This is a useful diagnostic when creating meshes programmatically.
9508 
9509   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9510 
9511   Currently does not include `DMPlexCheckCellShape()`.
9512 
9513 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9514 @*/
9515 PetscErrorCode DMPlexCheck(DM dm)
9516 {
9517   PetscInt cellHeight;
9518 
9519   PetscFunctionBegin;
9520   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9521   PetscCall(DMPlexCheckSymmetry(dm));
9522   PetscCall(DMPlexCheckSkeleton(dm, cellHeight));
9523   PetscCall(DMPlexCheckFaces(dm, cellHeight));
9524   PetscCall(DMPlexCheckGeometry(dm));
9525   PetscCall(DMPlexCheckPointSF(dm, NULL, PETSC_FALSE));
9526   PetscCall(DMPlexCheckInterfaceCones(dm));
9527   PetscFunctionReturn(PETSC_SUCCESS);
9528 }
9529 
9530 typedef struct cell_stats {
9531   PetscReal min, max, sum, squaresum;
9532   PetscInt  count;
9533 } cell_stats_t;
9534 
9535 static void MPIAPI cell_stats_reduce(void *a, void *b, int *len, MPI_Datatype *datatype)
9536 {
9537   PetscInt i, N = *len;
9538 
9539   for (i = 0; i < N; i++) {
9540     cell_stats_t *A = (cell_stats_t *)a;
9541     cell_stats_t *B = (cell_stats_t *)b;
9542 
9543     B->min = PetscMin(A->min, B->min);
9544     B->max = PetscMax(A->max, B->max);
9545     B->sum += A->sum;
9546     B->squaresum += A->squaresum;
9547     B->count += A->count;
9548   }
9549 }
9550 
9551 /*@
9552   DMPlexCheckCellShape - Checks the Jacobian of the mapping from reference to real cells and computes some minimal statistics.
9553 
9554   Collective
9555 
9556   Input Parameters:
9557 + dm        - The `DMPLEX` object
9558 . output    - If true, statistics will be displayed on `stdout`
9559 - condLimit - Display all cells above this condition number, or `PETSC_DETERMINE` for no cell output
9560 
9561   Level: developer
9562 
9563   Notes:
9564   This is mainly intended for debugging/testing purposes.
9565 
9566   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9567 
9568 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexComputeOrthogonalQuality()`
9569 @*/
9570 PetscErrorCode DMPlexCheckCellShape(DM dm, PetscBool output, PetscReal condLimit)
9571 {
9572   DM           dmCoarse;
9573   cell_stats_t stats, globalStats;
9574   MPI_Comm     comm = PetscObjectComm((PetscObject)dm);
9575   PetscReal   *J, *invJ, min = 0, max = 0, mean = 0, stdev = 0;
9576   PetscReal    limit = condLimit > 0 ? condLimit : PETSC_MAX_REAL;
9577   PetscInt     cdim, cStart, cEnd, c, eStart, eEnd, count = 0;
9578   PetscMPIInt  rank, size;
9579 
9580   PetscFunctionBegin;
9581   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9582   stats.min = PETSC_MAX_REAL;
9583   stats.max = PETSC_MIN_REAL;
9584   stats.sum = stats.squaresum = 0.;
9585   stats.count                 = 0;
9586 
9587   PetscCallMPI(MPI_Comm_size(comm, &size));
9588   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9589   PetscCall(DMGetCoordinateDim(dm, &cdim));
9590   PetscCall(PetscMalloc2(PetscSqr(cdim), &J, PetscSqr(cdim), &invJ));
9591   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
9592   PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
9593   for (c = cStart; c < cEnd; c++) {
9594     PetscInt  i;
9595     PetscReal frobJ = 0., frobInvJ = 0., cond2, cond, detJ;
9596 
9597     PetscCall(DMPlexComputeCellGeometryAffineFEM(dm, c, NULL, J, invJ, &detJ));
9598     PetscCheck(detJ >= 0.0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Mesh cell %" PetscInt_FMT " is inverted", c);
9599     for (i = 0; i < PetscSqr(cdim); ++i) {
9600       frobJ += J[i] * J[i];
9601       frobInvJ += invJ[i] * invJ[i];
9602     }
9603     cond2 = frobJ * frobInvJ;
9604     cond  = PetscSqrtReal(cond2);
9605 
9606     stats.min = PetscMin(stats.min, cond);
9607     stats.max = PetscMax(stats.max, cond);
9608     stats.sum += cond;
9609     stats.squaresum += cond2;
9610     stats.count++;
9611     if (output && cond > limit) {
9612       PetscSection coordSection;
9613       Vec          coordsLocal;
9614       PetscScalar *coords = NULL;
9615       PetscInt     Nv, d, clSize, cl, *closure = NULL;
9616 
9617       PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
9618       PetscCall(DMGetCoordinateSection(dm, &coordSection));
9619       PetscCall(DMPlexVecGetClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9620       PetscCall(PetscSynchronizedPrintf(comm, "[%d] Cell %" PetscInt_FMT " cond %g\n", rank, c, (double)cond));
9621       for (i = 0; i < Nv / cdim; ++i) {
9622         PetscCall(PetscSynchronizedPrintf(comm, "  Vertex %" PetscInt_FMT ": (", i));
9623         for (d = 0; d < cdim; ++d) {
9624           if (d > 0) PetscCall(PetscSynchronizedPrintf(comm, ", "));
9625           PetscCall(PetscSynchronizedPrintf(comm, "%g", (double)PetscRealPart(coords[i * cdim + d])));
9626         }
9627         PetscCall(PetscSynchronizedPrintf(comm, ")\n"));
9628       }
9629       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9630       for (cl = 0; cl < clSize * 2; cl += 2) {
9631         const PetscInt edge = closure[cl];
9632 
9633         if ((edge >= eStart) && (edge < eEnd)) {
9634           PetscReal len;
9635 
9636           PetscCall(DMPlexComputeCellGeometryFVM(dm, edge, &len, NULL, NULL));
9637           PetscCall(PetscSynchronizedPrintf(comm, "  Edge %" PetscInt_FMT ": length %g\n", edge, (double)len));
9638         }
9639       }
9640       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9641       PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9642     }
9643   }
9644   if (output) PetscCall(PetscSynchronizedFlush(comm, NULL));
9645 
9646   if (size > 1) {
9647     PetscMPIInt  blockLengths[2] = {4, 1};
9648     MPI_Aint     blockOffsets[2] = {offsetof(cell_stats_t, min), offsetof(cell_stats_t, count)};
9649     MPI_Datatype blockTypes[2]   = {MPIU_REAL, MPIU_INT}, statType;
9650     MPI_Op       statReduce;
9651 
9652     PetscCallMPI(MPI_Type_create_struct(2, blockLengths, blockOffsets, blockTypes, &statType));
9653     PetscCallMPI(MPI_Type_commit(&statType));
9654     PetscCallMPI(MPI_Op_create(cell_stats_reduce, PETSC_TRUE, &statReduce));
9655     PetscCallMPI(MPI_Reduce(&stats, &globalStats, 1, statType, statReduce, 0, comm));
9656     PetscCallMPI(MPI_Op_free(&statReduce));
9657     PetscCallMPI(MPI_Type_free(&statType));
9658   } else {
9659     PetscCall(PetscArraycpy(&globalStats, &stats, 1));
9660   }
9661   if (rank == 0) {
9662     count = globalStats.count;
9663     min   = globalStats.min;
9664     max   = globalStats.max;
9665     mean  = globalStats.sum / globalStats.count;
9666     stdev = globalStats.count > 1 ? PetscSqrtReal(PetscMax((globalStats.squaresum - globalStats.count * mean * mean) / (globalStats.count - 1), 0)) : 0.0;
9667   }
9668 
9669   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));
9670   PetscCall(PetscFree2(J, invJ));
9671 
9672   PetscCall(DMGetCoarseDM(dm, &dmCoarse));
9673   if (dmCoarse) {
9674     PetscBool isplex;
9675 
9676     PetscCall(PetscObjectTypeCompare((PetscObject)dmCoarse, DMPLEX, &isplex));
9677     if (isplex) PetscCall(DMPlexCheckCellShape(dmCoarse, output, condLimit));
9678   }
9679   PetscFunctionReturn(PETSC_SUCCESS);
9680 }
9681 
9682 /*@
9683   DMPlexComputeOrthogonalQuality - Compute cell-wise orthogonal quality mesh statistic. Optionally tags all cells with
9684   orthogonal quality below given tolerance.
9685 
9686   Collective
9687 
9688   Input Parameters:
9689 + dm   - The `DMPLEX` object
9690 . fv   - Optional `PetscFV` object for pre-computed cell/face centroid information
9691 - atol - [0, 1] Absolute tolerance for tagging cells.
9692 
9693   Output Parameters:
9694 + OrthQual      - `Vec` containing orthogonal quality per cell
9695 - OrthQualLabel - `DMLabel` tagging cells below atol with `DM_ADAPT_REFINE`
9696 
9697   Options Database Keys:
9698 + -dm_plex_orthogonal_quality_label_view - view OrthQualLabel if label is requested. Currently only `PETSCVIEWERASCII` is supported.
9699 - -dm_plex_orthogonal_quality_vec_view   - view OrthQual vector.
9700 
9701   Level: intermediate
9702 
9703   Notes:
9704   Orthogonal quality is given by the following formula\:
9705 
9706   $ \min \left[ \frac{A_i \cdot f_i}{\|A_i\| \|f_i\|} , \frac{A_i \cdot c_i}{\|A_i\| \|c_i\|} \right]$
9707 
9708   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
9709   is the vector from the current cells centroid to the centroid of its i'th neighbor (which shares a face with the
9710   current cell). This computes the vector similarity between each cell face and its corresponding neighbor centroid by
9711   calculating the cosine of the angle between these vectors.
9712 
9713   Orthogonal quality ranges from 1 (best) to 0 (worst).
9714 
9715   This routine is mainly useful for FVM, however is not restricted to only FVM. The `PetscFV` object is optionally used to check for
9716   pre-computed FVM cell data, but if it is not passed in then this data will be computed.
9717 
9718   Cells are tagged if they have an orthogonal quality less than or equal to the absolute tolerance.
9719 
9720 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCheckCellShape()`, `DMCreateLabel()`, `PetscFV`, `DMLabel`, `Vec`
9721 @*/
9722 PetscErrorCode DMPlexComputeOrthogonalQuality(DM dm, PetscFV fv, PetscReal atol, Vec *OrthQual, DMLabel *OrthQualLabel)
9723 {
9724   PetscInt               nc, cellHeight, cStart, cEnd, cell, cellIter = 0;
9725   PetscInt              *idx;
9726   PetscScalar           *oqVals;
9727   const PetscScalar     *cellGeomArr, *faceGeomArr;
9728   PetscReal             *ci, *fi, *Ai;
9729   MPI_Comm               comm;
9730   Vec                    cellgeom, facegeom;
9731   DM                     dmFace, dmCell;
9732   IS                     glob;
9733   ISLocalToGlobalMapping ltog;
9734   PetscViewer            vwr;
9735 
9736   PetscFunctionBegin;
9737   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9738   if (fv) PetscValidHeaderSpecific(fv, PETSCFV_CLASSID, 2);
9739   PetscAssertPointer(OrthQual, 4);
9740   PetscCheck(atol >= 0.0 && atol <= 1.0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Absolute tolerance %g not in [0,1]", (double)atol);
9741   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9742   PetscCall(DMGetDimension(dm, &nc));
9743   PetscCheck(nc >= 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must have dimension >= 2 (current %" PetscInt_FMT ")", nc);
9744   {
9745     DMPlexInterpolatedFlag interpFlag;
9746 
9747     PetscCall(DMPlexIsInterpolated(dm, &interpFlag));
9748     if (interpFlag != DMPLEX_INTERPOLATED_FULL) {
9749       PetscMPIInt rank;
9750 
9751       PetscCallMPI(MPI_Comm_rank(comm, &rank));
9752       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must be fully interpolated, DM on rank %d is not fully interpolated", rank);
9753     }
9754   }
9755   if (OrthQualLabel) {
9756     PetscAssertPointer(OrthQualLabel, 5);
9757     PetscCall(DMCreateLabel(dm, "Orthogonal_Quality"));
9758     PetscCall(DMGetLabel(dm, "Orthogonal_Quality", OrthQualLabel));
9759   } else {
9760     *OrthQualLabel = NULL;
9761   }
9762   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9763   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9764   PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_TRUE, &glob));
9765   PetscCall(ISLocalToGlobalMappingCreateIS(glob, &ltog));
9766   PetscCall(ISLocalToGlobalMappingSetType(ltog, ISLOCALTOGLOBALMAPPINGHASH));
9767   PetscCall(VecCreate(comm, OrthQual));
9768   PetscCall(VecSetType(*OrthQual, VECSTANDARD));
9769   PetscCall(VecSetSizes(*OrthQual, cEnd - cStart, PETSC_DETERMINE));
9770   PetscCall(VecSetLocalToGlobalMapping(*OrthQual, ltog));
9771   PetscCall(VecSetUp(*OrthQual));
9772   PetscCall(ISDestroy(&glob));
9773   PetscCall(ISLocalToGlobalMappingDestroy(&ltog));
9774   PetscCall(DMPlexGetDataFVM(dm, fv, &cellgeom, &facegeom, NULL));
9775   PetscCall(VecGetArrayRead(cellgeom, &cellGeomArr));
9776   PetscCall(VecGetArrayRead(facegeom, &faceGeomArr));
9777   PetscCall(VecGetDM(cellgeom, &dmCell));
9778   PetscCall(VecGetDM(facegeom, &dmFace));
9779   PetscCall(PetscMalloc5(cEnd - cStart, &idx, cEnd - cStart, &oqVals, nc, &ci, nc, &fi, nc, &Ai));
9780   for (cell = cStart; cell < cEnd; cellIter++, cell++) {
9781     PetscInt         cellneigh, cellneighiter = 0, adjSize = PETSC_DETERMINE;
9782     PetscInt         cellarr[2], *adj = NULL;
9783     PetscScalar     *cArr, *fArr;
9784     PetscReal        minvalc = 1.0, minvalf = 1.0;
9785     PetscFVCellGeom *cg;
9786 
9787     idx[cellIter] = cell - cStart;
9788     cellarr[0]    = cell;
9789     /* Make indexing into cellGeom easier */
9790     PetscCall(DMPlexPointLocalRead(dmCell, cell, cellGeomArr, &cg));
9791     PetscCall(DMPlexGetAdjacency_Internal(dm, cell, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &adjSize, &adj));
9792     /* Technically 1 too big, but easier than fiddling with empty adjacency array */
9793     PetscCall(PetscCalloc2(adjSize, &cArr, adjSize, &fArr));
9794     for (cellneigh = 0; cellneigh < adjSize; cellneighiter++, cellneigh++) {
9795       PetscInt         i;
9796       const PetscInt   neigh  = adj[cellneigh];
9797       PetscReal        normci = 0, normfi = 0, normai = 0;
9798       PetscFVCellGeom *cgneigh;
9799       PetscFVFaceGeom *fg;
9800 
9801       /* Don't count ourselves in the neighbor list */
9802       if (neigh == cell) continue;
9803       PetscCall(DMPlexPointLocalRead(dmCell, neigh, cellGeomArr, &cgneigh));
9804       cellarr[1] = neigh;
9805       {
9806         PetscInt        numcovpts;
9807         const PetscInt *covpts;
9808 
9809         PetscCall(DMPlexGetMeet(dm, 2, cellarr, &numcovpts, &covpts));
9810         PetscCall(DMPlexPointLocalRead(dmFace, covpts[0], faceGeomArr, &fg));
9811         PetscCall(DMPlexRestoreMeet(dm, 2, cellarr, &numcovpts, &covpts));
9812       }
9813 
9814       /* Compute c_i, f_i and their norms */
9815       for (i = 0; i < nc; i++) {
9816         ci[i] = cgneigh->centroid[i] - cg->centroid[i];
9817         fi[i] = fg->centroid[i] - cg->centroid[i];
9818         Ai[i] = fg->normal[i];
9819         normci += PetscPowReal(ci[i], 2);
9820         normfi += PetscPowReal(fi[i], 2);
9821         normai += PetscPowReal(Ai[i], 2);
9822       }
9823       normci = PetscSqrtReal(normci);
9824       normfi = PetscSqrtReal(normfi);
9825       normai = PetscSqrtReal(normai);
9826 
9827       /* Normalize and compute for each face-cell-normal pair */
9828       for (i = 0; i < nc; i++) {
9829         ci[i] = ci[i] / normci;
9830         fi[i] = fi[i] / normfi;
9831         Ai[i] = Ai[i] / normai;
9832         /* PetscAbs because I don't know if normals are guaranteed to point out */
9833         cArr[cellneighiter] += PetscAbs(Ai[i] * ci[i]);
9834         fArr[cellneighiter] += PetscAbs(Ai[i] * fi[i]);
9835       }
9836       if (PetscRealPart(cArr[cellneighiter]) < minvalc) minvalc = PetscRealPart(cArr[cellneighiter]);
9837       if (PetscRealPart(fArr[cellneighiter]) < minvalf) minvalf = PetscRealPart(fArr[cellneighiter]);
9838     }
9839     PetscCall(PetscFree(adj));
9840     PetscCall(PetscFree2(cArr, fArr));
9841     /* Defer to cell if they're equal */
9842     oqVals[cellIter] = PetscMin(minvalf, minvalc);
9843     if (OrthQualLabel) {
9844       if (PetscRealPart(oqVals[cellIter]) <= atol) PetscCall(DMLabelSetValue(*OrthQualLabel, cell, DM_ADAPT_REFINE));
9845     }
9846   }
9847   PetscCall(VecSetValuesLocal(*OrthQual, cEnd - cStart, idx, oqVals, INSERT_VALUES));
9848   PetscCall(VecAssemblyBegin(*OrthQual));
9849   PetscCall(VecAssemblyEnd(*OrthQual));
9850   PetscCall(VecRestoreArrayRead(cellgeom, &cellGeomArr));
9851   PetscCall(VecRestoreArrayRead(facegeom, &faceGeomArr));
9852   PetscCall(PetscOptionsGetViewer(comm, NULL, NULL, "-dm_plex_orthogonal_quality_label_view", &vwr, NULL, NULL));
9853   if (OrthQualLabel) {
9854     if (vwr) PetscCall(DMLabelView(*OrthQualLabel, vwr));
9855   }
9856   PetscCall(PetscFree5(idx, oqVals, ci, fi, Ai));
9857   PetscCall(PetscOptionsRestoreViewer(&vwr));
9858   PetscCall(VecViewFromOptions(*OrthQual, NULL, "-dm_plex_orthogonal_quality_vec_view"));
9859   PetscFunctionReturn(PETSC_SUCCESS);
9860 }
9861 
9862 /* this is here instead of DMGetOutputDM because output DM still has constraints in the local indices that affect
9863  * interpolator construction */
9864 static PetscErrorCode DMGetFullDM(DM dm, DM *odm)
9865 {
9866   PetscSection section, newSection, gsection;
9867   PetscSF      sf;
9868   PetscBool    hasConstraints, ghasConstraints;
9869 
9870   PetscFunctionBegin;
9871   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9872   PetscAssertPointer(odm, 2);
9873   PetscCall(DMGetLocalSection(dm, &section));
9874   PetscCall(PetscSectionHasConstraints(section, &hasConstraints));
9875   PetscCall(MPIU_Allreduce(&hasConstraints, &ghasConstraints, 1, MPIU_BOOL, MPI_LOR, PetscObjectComm((PetscObject)dm)));
9876   if (!ghasConstraints) {
9877     PetscCall(PetscObjectReference((PetscObject)dm));
9878     *odm = dm;
9879     PetscFunctionReturn(PETSC_SUCCESS);
9880   }
9881   PetscCall(DMClone(dm, odm));
9882   PetscCall(DMCopyFields(dm, *odm));
9883   PetscCall(DMGetLocalSection(*odm, &newSection));
9884   PetscCall(DMGetPointSF(*odm, &sf));
9885   PetscCall(PetscSectionCreateGlobalSection(newSection, sf, PETSC_TRUE, PETSC_TRUE, PETSC_FALSE, &gsection));
9886   PetscCall(DMSetGlobalSection(*odm, gsection));
9887   PetscCall(PetscSectionDestroy(&gsection));
9888   PetscFunctionReturn(PETSC_SUCCESS);
9889 }
9890 
9891 static PetscErrorCode DMCreateAffineInterpolationCorrection_Plex(DM dmc, DM dmf, Vec *shift)
9892 {
9893   DM        dmco, dmfo;
9894   Mat       interpo;
9895   Vec       rscale;
9896   Vec       cglobalo, clocal;
9897   Vec       fglobal, fglobalo, flocal;
9898   PetscBool regular;
9899 
9900   PetscFunctionBegin;
9901   PetscCall(DMGetFullDM(dmc, &dmco));
9902   PetscCall(DMGetFullDM(dmf, &dmfo));
9903   PetscCall(DMSetCoarseDM(dmfo, dmco));
9904   PetscCall(DMPlexGetRegularRefinement(dmf, &regular));
9905   PetscCall(DMPlexSetRegularRefinement(dmfo, regular));
9906   PetscCall(DMCreateInterpolation(dmco, dmfo, &interpo, &rscale));
9907   PetscCall(DMCreateGlobalVector(dmco, &cglobalo));
9908   PetscCall(DMCreateLocalVector(dmc, &clocal));
9909   PetscCall(VecSet(cglobalo, 0.));
9910   PetscCall(VecSet(clocal, 0.));
9911   PetscCall(DMCreateGlobalVector(dmf, &fglobal));
9912   PetscCall(DMCreateGlobalVector(dmfo, &fglobalo));
9913   PetscCall(DMCreateLocalVector(dmf, &flocal));
9914   PetscCall(VecSet(fglobal, 0.));
9915   PetscCall(VecSet(fglobalo, 0.));
9916   PetscCall(VecSet(flocal, 0.));
9917   PetscCall(DMPlexInsertBoundaryValues(dmc, PETSC_TRUE, clocal, 0., NULL, NULL, NULL));
9918   PetscCall(DMLocalToGlobalBegin(dmco, clocal, INSERT_VALUES, cglobalo));
9919   PetscCall(DMLocalToGlobalEnd(dmco, clocal, INSERT_VALUES, cglobalo));
9920   PetscCall(MatMult(interpo, cglobalo, fglobalo));
9921   PetscCall(DMGlobalToLocalBegin(dmfo, fglobalo, INSERT_VALUES, flocal));
9922   PetscCall(DMGlobalToLocalEnd(dmfo, fglobalo, INSERT_VALUES, flocal));
9923   PetscCall(DMLocalToGlobalBegin(dmf, flocal, INSERT_VALUES, fglobal));
9924   PetscCall(DMLocalToGlobalEnd(dmf, flocal, INSERT_VALUES, fglobal));
9925   *shift = fglobal;
9926   PetscCall(VecDestroy(&flocal));
9927   PetscCall(VecDestroy(&fglobalo));
9928   PetscCall(VecDestroy(&clocal));
9929   PetscCall(VecDestroy(&cglobalo));
9930   PetscCall(VecDestroy(&rscale));
9931   PetscCall(MatDestroy(&interpo));
9932   PetscCall(DMDestroy(&dmfo));
9933   PetscCall(DMDestroy(&dmco));
9934   PetscFunctionReturn(PETSC_SUCCESS);
9935 }
9936 
9937 PETSC_INTERN PetscErrorCode DMInterpolateSolution_Plex(DM coarse, DM fine, Mat interp, Vec coarseSol, Vec fineSol)
9938 {
9939   PetscObject shifto;
9940   Vec         shift;
9941 
9942   PetscFunctionBegin;
9943   if (!interp) {
9944     Vec rscale;
9945 
9946     PetscCall(DMCreateInterpolation(coarse, fine, &interp, &rscale));
9947     PetscCall(VecDestroy(&rscale));
9948   } else {
9949     PetscCall(PetscObjectReference((PetscObject)interp));
9950   }
9951   PetscCall(PetscObjectQuery((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", &shifto));
9952   if (!shifto) {
9953     PetscCall(DMCreateAffineInterpolationCorrection_Plex(coarse, fine, &shift));
9954     PetscCall(PetscObjectCompose((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", (PetscObject)shift));
9955     shifto = (PetscObject)shift;
9956     PetscCall(VecDestroy(&shift));
9957   }
9958   shift = (Vec)shifto;
9959   PetscCall(MatInterpolate(interp, coarseSol, fineSol));
9960   PetscCall(VecAXPY(fineSol, 1.0, shift));
9961   PetscCall(MatDestroy(&interp));
9962   PetscFunctionReturn(PETSC_SUCCESS);
9963 }
9964 
9965 /* Pointwise interpolation
9966      Just code FEM for now
9967      u^f = I u^c
9968      sum_k u^f_k phi^f_k = I sum_j u^c_j phi^c_j
9969      u^f_i = sum_j psi^f_i I phi^c_j u^c_j
9970      I_{ij} = psi^f_i phi^c_j
9971 */
9972 PetscErrorCode DMCreateInterpolation_Plex(DM dmCoarse, DM dmFine, Mat *interpolation, Vec *scaling)
9973 {
9974   PetscSection gsc, gsf;
9975   PetscInt     m, n;
9976   void        *ctx;
9977   DM           cdm;
9978   PetscBool    regular, ismatis, isRefined = dmCoarse->data == dmFine->data ? PETSC_FALSE : PETSC_TRUE;
9979 
9980   PetscFunctionBegin;
9981   PetscCall(DMGetGlobalSection(dmFine, &gsf));
9982   PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
9983   PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
9984   PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
9985 
9986   PetscCall(PetscStrcmp(dmCoarse->mattype, MATIS, &ismatis));
9987   PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), interpolation));
9988   PetscCall(MatSetSizes(*interpolation, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
9989   PetscCall(MatSetType(*interpolation, ismatis ? MATAIJ : dmCoarse->mattype));
9990   PetscCall(DMGetApplicationContext(dmFine, &ctx));
9991 
9992   PetscCall(DMGetCoarseDM(dmFine, &cdm));
9993   PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
9994   if (!isRefined || (regular && cdm == dmCoarse)) PetscCall(DMPlexComputeInterpolatorNested(dmCoarse, dmFine, isRefined, *interpolation, ctx));
9995   else PetscCall(DMPlexComputeInterpolatorGeneral(dmCoarse, dmFine, *interpolation, ctx));
9996   PetscCall(MatViewFromOptions(*interpolation, NULL, "-interp_mat_view"));
9997   if (scaling) {
9998     /* Use naive scaling */
9999     PetscCall(DMCreateInterpolationScale(dmCoarse, dmFine, *interpolation, scaling));
10000   }
10001   PetscFunctionReturn(PETSC_SUCCESS);
10002 }
10003 
10004 PetscErrorCode DMCreateInjection_Plex(DM dmCoarse, DM dmFine, Mat *mat)
10005 {
10006   VecScatter ctx;
10007 
10008   PetscFunctionBegin;
10009   PetscCall(DMPlexComputeInjectorFEM(dmCoarse, dmFine, &ctx, NULL));
10010   PetscCall(MatCreateScatter(PetscObjectComm((PetscObject)ctx), ctx, mat));
10011   PetscCall(VecScatterDestroy(&ctx));
10012   PetscFunctionReturn(PETSC_SUCCESS);
10013 }
10014 
10015 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[])
10016 {
10017   const PetscInt Nc = uOff[1] - uOff[0];
10018   PetscInt       c;
10019   for (c = 0; c < Nc; ++c) g0[c * Nc + c] = 1.0;
10020 }
10021 
10022 PetscErrorCode DMCreateMassMatrixLumped_Plex(DM dm, Vec *mass)
10023 {
10024   DM           dmc;
10025   PetscDS      ds;
10026   Vec          ones, locmass;
10027   IS           cellIS;
10028   PetscFormKey key;
10029   PetscInt     depth;
10030 
10031   PetscFunctionBegin;
10032   PetscCall(DMClone(dm, &dmc));
10033   PetscCall(DMCopyDisc(dm, dmc));
10034   PetscCall(DMGetDS(dmc, &ds));
10035   PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
10036   PetscCall(DMCreateGlobalVector(dmc, mass));
10037   PetscCall(DMGetLocalVector(dmc, &ones));
10038   PetscCall(DMGetLocalVector(dmc, &locmass));
10039   PetscCall(DMPlexGetDepth(dmc, &depth));
10040   PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
10041   PetscCall(VecSet(locmass, 0.0));
10042   PetscCall(VecSet(ones, 1.0));
10043   key.label = NULL;
10044   key.value = 0;
10045   key.field = 0;
10046   key.part  = 0;
10047   PetscCall(DMPlexComputeJacobian_Action_Internal(dmc, key, cellIS, 0.0, 0.0, ones, NULL, ones, locmass, NULL));
10048   PetscCall(ISDestroy(&cellIS));
10049   PetscCall(VecSet(*mass, 0.0));
10050   PetscCall(DMLocalToGlobalBegin(dmc, locmass, ADD_VALUES, *mass));
10051   PetscCall(DMLocalToGlobalEnd(dmc, locmass, ADD_VALUES, *mass));
10052   PetscCall(DMRestoreLocalVector(dmc, &ones));
10053   PetscCall(DMRestoreLocalVector(dmc, &locmass));
10054   PetscCall(DMDestroy(&dmc));
10055   PetscFunctionReturn(PETSC_SUCCESS);
10056 }
10057 
10058 PetscErrorCode DMCreateMassMatrix_Plex(DM dmCoarse, DM dmFine, Mat *mass)
10059 {
10060   PetscSection gsc, gsf;
10061   PetscInt     m, n;
10062   void        *ctx;
10063   DM           cdm;
10064   PetscBool    regular;
10065 
10066   PetscFunctionBegin;
10067   if (dmFine == dmCoarse) {
10068     DM            dmc;
10069     PetscDS       ds;
10070     PetscWeakForm wf;
10071     Vec           u;
10072     IS            cellIS;
10073     PetscFormKey  key;
10074     PetscInt      depth;
10075 
10076     PetscCall(DMClone(dmFine, &dmc));
10077     PetscCall(DMCopyDisc(dmFine, dmc));
10078     PetscCall(DMGetDS(dmc, &ds));
10079     PetscCall(PetscDSGetWeakForm(ds, &wf));
10080     PetscCall(PetscWeakFormClear(wf));
10081     PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
10082     PetscCall(DMCreateMatrix(dmc, mass));
10083     PetscCall(DMGetLocalVector(dmc, &u));
10084     PetscCall(DMPlexGetDepth(dmc, &depth));
10085     PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
10086     PetscCall(MatZeroEntries(*mass));
10087     key.label = NULL;
10088     key.value = 0;
10089     key.field = 0;
10090     key.part  = 0;
10091     PetscCall(DMPlexComputeJacobian_Internal(dmc, key, cellIS, 0.0, 0.0, u, NULL, *mass, *mass, NULL));
10092     PetscCall(ISDestroy(&cellIS));
10093     PetscCall(DMRestoreLocalVector(dmc, &u));
10094     PetscCall(DMDestroy(&dmc));
10095   } else {
10096     PetscCall(DMGetGlobalSection(dmFine, &gsf));
10097     PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
10098     PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
10099     PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
10100 
10101     PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), mass));
10102     PetscCall(MatSetSizes(*mass, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
10103     PetscCall(MatSetType(*mass, dmCoarse->mattype));
10104     PetscCall(DMGetApplicationContext(dmFine, &ctx));
10105 
10106     PetscCall(DMGetCoarseDM(dmFine, &cdm));
10107     PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
10108     if (regular && cdm == dmCoarse) PetscCall(DMPlexComputeMassMatrixNested(dmCoarse, dmFine, *mass, ctx));
10109     else PetscCall(DMPlexComputeMassMatrixGeneral(dmCoarse, dmFine, *mass, ctx));
10110   }
10111   PetscCall(MatViewFromOptions(*mass, NULL, "-mass_mat_view"));
10112   PetscFunctionReturn(PETSC_SUCCESS);
10113 }
10114 
10115 /*@
10116   DMPlexGetRegularRefinement - Get the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
10117 
10118   Input Parameter:
10119 . dm - The `DMPLEX` object
10120 
10121   Output Parameter:
10122 . regular - The flag
10123 
10124   Level: intermediate
10125 
10126 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetRegularRefinement()`
10127 @*/
10128 PetscErrorCode DMPlexGetRegularRefinement(DM dm, PetscBool *regular)
10129 {
10130   PetscFunctionBegin;
10131   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10132   PetscAssertPointer(regular, 2);
10133   *regular = ((DM_Plex *)dm->data)->regularRefinement;
10134   PetscFunctionReturn(PETSC_SUCCESS);
10135 }
10136 
10137 /*@
10138   DMPlexSetRegularRefinement - Set the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
10139 
10140   Input Parameters:
10141 + dm      - The `DMPLEX` object
10142 - regular - The flag
10143 
10144   Level: intermediate
10145 
10146 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetRegularRefinement()`
10147 @*/
10148 PetscErrorCode DMPlexSetRegularRefinement(DM dm, PetscBool regular)
10149 {
10150   PetscFunctionBegin;
10151   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10152   ((DM_Plex *)dm->data)->regularRefinement = regular;
10153   PetscFunctionReturn(PETSC_SUCCESS);
10154 }
10155 
10156 /*@
10157   DMPlexGetAnchors - Get the layout of the anchor (point-to-point) constraints.  Typically, the user will not have to
10158   call DMPlexGetAnchors() directly: if there are anchors, then `DMPlexGetAnchors()` is called during `DMGetDefaultConstraints()`.
10159 
10160   Not Collective
10161 
10162   Input Parameter:
10163 . dm - The `DMPLEX` object
10164 
10165   Output Parameters:
10166 + anchorSection - If not `NULL`, set to the section describing which points anchor the constrained points.
10167 - anchorIS      - If not `NULL`, set to the list of anchors indexed by `anchorSection`
10168 
10169   Level: intermediate
10170 
10171 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`, `IS`, `PetscSection`
10172 @*/
10173 PetscErrorCode DMPlexGetAnchors(DM dm, PetscSection *anchorSection, IS *anchorIS)
10174 {
10175   DM_Plex *plex = (DM_Plex *)dm->data;
10176 
10177   PetscFunctionBegin;
10178   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10179   if (!plex->anchorSection && !plex->anchorIS && plex->createanchors) PetscCall((*plex->createanchors)(dm));
10180   if (anchorSection) *anchorSection = plex->anchorSection;
10181   if (anchorIS) *anchorIS = plex->anchorIS;
10182   PetscFunctionReturn(PETSC_SUCCESS);
10183 }
10184 
10185 /*@
10186   DMPlexSetAnchors - Set the layout of the local anchor (point-to-point) constraints.
10187 
10188   Collective
10189 
10190   Input Parameters:
10191 + dm            - The `DMPLEX` object
10192 . anchorSection - The section that describes the mapping from constrained points to the anchor points listed in anchorIS.
10193                   Must have a local communicator (`PETSC_COMM_SELF` or derivative).
10194 - anchorIS      - The list of all anchor points.  Must have a local communicator (`PETSC_COMM_SELF` or derivative).
10195 
10196   Level: intermediate
10197 
10198   Notes:
10199   Unlike boundary conditions, when a point's degrees of freedom in a section are constrained to
10200   an outside value, the anchor constraints set a point's degrees of freedom to be a linear
10201   combination of other points' degrees of freedom.
10202 
10203   After specifying the layout of constraints with `DMPlexSetAnchors()`, one specifies the constraints by calling
10204   `DMGetDefaultConstraints()` and filling in the entries in the constraint matrix.
10205 
10206   The reference counts of `anchorSection` and `anchorIS` are incremented.
10207 
10208 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`
10209 @*/
10210 PetscErrorCode DMPlexSetAnchors(DM dm, PetscSection anchorSection, IS anchorIS)
10211 {
10212   DM_Plex    *plex = (DM_Plex *)dm->data;
10213   PetscMPIInt result;
10214 
10215   PetscFunctionBegin;
10216   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10217   if (anchorSection) {
10218     PetscValidHeaderSpecific(anchorSection, PETSC_SECTION_CLASSID, 2);
10219     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorSection), &result));
10220     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor section must have local communicator");
10221   }
10222   if (anchorIS) {
10223     PetscValidHeaderSpecific(anchorIS, IS_CLASSID, 3);
10224     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorIS), &result));
10225     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor IS must have local communicator");
10226   }
10227 
10228   PetscCall(PetscObjectReference((PetscObject)anchorSection));
10229   PetscCall(PetscSectionDestroy(&plex->anchorSection));
10230   plex->anchorSection = anchorSection;
10231 
10232   PetscCall(PetscObjectReference((PetscObject)anchorIS));
10233   PetscCall(ISDestroy(&plex->anchorIS));
10234   plex->anchorIS = anchorIS;
10235 
10236   if (PetscUnlikelyDebug(anchorIS && anchorSection)) {
10237     PetscInt        size, a, pStart, pEnd;
10238     const PetscInt *anchors;
10239 
10240     PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
10241     PetscCall(ISGetLocalSize(anchorIS, &size));
10242     PetscCall(ISGetIndices(anchorIS, &anchors));
10243     for (a = 0; a < size; a++) {
10244       PetscInt p;
10245 
10246       p = anchors[a];
10247       if (p >= pStart && p < pEnd) {
10248         PetscInt dof;
10249 
10250         PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
10251         if (dof) {
10252           PetscCall(ISRestoreIndices(anchorIS, &anchors));
10253           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Point %" PetscInt_FMT " cannot be constrained and an anchor", p);
10254         }
10255       }
10256     }
10257     PetscCall(ISRestoreIndices(anchorIS, &anchors));
10258   }
10259   /* reset the generic constraints */
10260   PetscCall(DMSetDefaultConstraints(dm, NULL, NULL, NULL));
10261   PetscFunctionReturn(PETSC_SUCCESS);
10262 }
10263 
10264 static PetscErrorCode DMPlexCreateConstraintSection_Anchors(DM dm, PetscSection section, PetscSection *cSec)
10265 {
10266   PetscSection anchorSection;
10267   PetscInt     pStart, pEnd, sStart, sEnd, p, dof, numFields, f;
10268 
10269   PetscFunctionBegin;
10270   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10271   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
10272   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, cSec));
10273   PetscCall(PetscSectionGetNumFields(section, &numFields));
10274   if (numFields) {
10275     PetscInt f;
10276     PetscCall(PetscSectionSetNumFields(*cSec, numFields));
10277 
10278     for (f = 0; f < numFields; f++) {
10279       PetscInt numComp;
10280 
10281       PetscCall(PetscSectionGetFieldComponents(section, f, &numComp));
10282       PetscCall(PetscSectionSetFieldComponents(*cSec, f, numComp));
10283     }
10284   }
10285   PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
10286   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
10287   pStart = PetscMax(pStart, sStart);
10288   pEnd   = PetscMin(pEnd, sEnd);
10289   pEnd   = PetscMax(pStart, pEnd);
10290   PetscCall(PetscSectionSetChart(*cSec, pStart, pEnd));
10291   for (p = pStart; p < pEnd; p++) {
10292     PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
10293     if (dof) {
10294       PetscCall(PetscSectionGetDof(section, p, &dof));
10295       PetscCall(PetscSectionSetDof(*cSec, p, dof));
10296       for (f = 0; f < numFields; f++) {
10297         PetscCall(PetscSectionGetFieldDof(section, p, f, &dof));
10298         PetscCall(PetscSectionSetFieldDof(*cSec, p, f, dof));
10299       }
10300     }
10301   }
10302   PetscCall(PetscSectionSetUp(*cSec));
10303   PetscCall(PetscObjectSetName((PetscObject)*cSec, "Constraint Section"));
10304   PetscFunctionReturn(PETSC_SUCCESS);
10305 }
10306 
10307 static PetscErrorCode DMPlexCreateConstraintMatrix_Anchors(DM dm, PetscSection section, PetscSection cSec, Mat *cMat)
10308 {
10309   PetscSection    aSec;
10310   PetscInt        pStart, pEnd, p, sStart, sEnd, dof, aDof, aOff, off, nnz, annz, m, n, q, a, offset, *i, *j;
10311   const PetscInt *anchors;
10312   PetscInt        numFields, f;
10313   IS              aIS;
10314   MatType         mtype;
10315   PetscBool       iscuda, iskokkos;
10316 
10317   PetscFunctionBegin;
10318   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10319   PetscCall(PetscSectionGetStorageSize(cSec, &m));
10320   PetscCall(PetscSectionGetStorageSize(section, &n));
10321   PetscCall(MatCreate(PETSC_COMM_SELF, cMat));
10322   PetscCall(MatSetSizes(*cMat, m, n, m, n));
10323   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJCUSPARSE, &iscuda));
10324   if (!iscuda) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJCUSPARSE, &iscuda));
10325   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJKOKKOS, &iskokkos));
10326   if (!iskokkos) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJKOKKOS, &iskokkos));
10327   if (iscuda) mtype = MATSEQAIJCUSPARSE;
10328   else if (iskokkos) mtype = MATSEQAIJKOKKOS;
10329   else mtype = MATSEQAIJ;
10330   PetscCall(MatSetType(*cMat, mtype));
10331   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
10332   PetscCall(ISGetIndices(aIS, &anchors));
10333   /* cSec will be a subset of aSec and section */
10334   PetscCall(PetscSectionGetChart(cSec, &pStart, &pEnd));
10335   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
10336   PetscCall(PetscMalloc1(m + 1, &i));
10337   i[0] = 0;
10338   PetscCall(PetscSectionGetNumFields(section, &numFields));
10339   for (p = pStart; p < pEnd; p++) {
10340     PetscInt rDof, rOff, r;
10341 
10342     PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10343     if (!rDof) continue;
10344     PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10345     if (numFields) {
10346       for (f = 0; f < numFields; f++) {
10347         annz = 0;
10348         for (r = 0; r < rDof; r++) {
10349           a = anchors[rOff + r];
10350           if (a < sStart || a >= sEnd) continue;
10351           PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
10352           annz += aDof;
10353         }
10354         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
10355         PetscCall(PetscSectionGetFieldOffset(cSec, p, f, &off));
10356         for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
10357       }
10358     } else {
10359       annz = 0;
10360       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10361       for (q = 0; q < dof; q++) {
10362         a = anchors[rOff + q];
10363         if (a < sStart || a >= sEnd) continue;
10364         PetscCall(PetscSectionGetDof(section, a, &aDof));
10365         annz += aDof;
10366       }
10367       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10368       PetscCall(PetscSectionGetOffset(cSec, p, &off));
10369       for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
10370     }
10371   }
10372   nnz = i[m];
10373   PetscCall(PetscMalloc1(nnz, &j));
10374   offset = 0;
10375   for (p = pStart; p < pEnd; p++) {
10376     if (numFields) {
10377       for (f = 0; f < numFields; f++) {
10378         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
10379         for (q = 0; q < dof; q++) {
10380           PetscInt rDof, rOff, r;
10381           PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10382           PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10383           for (r = 0; r < rDof; r++) {
10384             PetscInt s;
10385 
10386             a = anchors[rOff + r];
10387             if (a < sStart || a >= sEnd) continue;
10388             PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
10389             PetscCall(PetscSectionGetFieldOffset(section, a, f, &aOff));
10390             for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
10391           }
10392         }
10393       }
10394     } else {
10395       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10396       for (q = 0; q < dof; q++) {
10397         PetscInt rDof, rOff, r;
10398         PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10399         PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10400         for (r = 0; r < rDof; r++) {
10401           PetscInt s;
10402 
10403           a = anchors[rOff + r];
10404           if (a < sStart || a >= sEnd) continue;
10405           PetscCall(PetscSectionGetDof(section, a, &aDof));
10406           PetscCall(PetscSectionGetOffset(section, a, &aOff));
10407           for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
10408         }
10409       }
10410     }
10411   }
10412   PetscCall(MatSeqAIJSetPreallocationCSR(*cMat, i, j, NULL));
10413   PetscCall(PetscFree(i));
10414   PetscCall(PetscFree(j));
10415   PetscCall(ISRestoreIndices(aIS, &anchors));
10416   PetscFunctionReturn(PETSC_SUCCESS);
10417 }
10418 
10419 PetscErrorCode DMCreateDefaultConstraints_Plex(DM dm)
10420 {
10421   DM_Plex     *plex = (DM_Plex *)dm->data;
10422   PetscSection anchorSection, section, cSec;
10423   Mat          cMat;
10424 
10425   PetscFunctionBegin;
10426   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10427   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
10428   if (anchorSection) {
10429     PetscInt Nf;
10430 
10431     PetscCall(DMGetLocalSection(dm, &section));
10432     PetscCall(DMPlexCreateConstraintSection_Anchors(dm, section, &cSec));
10433     PetscCall(DMPlexCreateConstraintMatrix_Anchors(dm, section, cSec, &cMat));
10434     PetscCall(DMGetNumFields(dm, &Nf));
10435     if (Nf && plex->computeanchormatrix) PetscCall((*plex->computeanchormatrix)(dm, section, cSec, cMat));
10436     PetscCall(DMSetDefaultConstraints(dm, cSec, cMat, NULL));
10437     PetscCall(PetscSectionDestroy(&cSec));
10438     PetscCall(MatDestroy(&cMat));
10439   }
10440   PetscFunctionReturn(PETSC_SUCCESS);
10441 }
10442 
10443 PetscErrorCode DMCreateSubDomainDM_Plex(DM dm, DMLabel label, PetscInt value, IS *is, DM *subdm)
10444 {
10445   IS           subis;
10446   PetscSection section, subsection;
10447 
10448   PetscFunctionBegin;
10449   PetscCall(DMGetLocalSection(dm, &section));
10450   PetscCheck(section, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set default section for DM before splitting subdomain");
10451   PetscCheck(subdm, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set output subDM for splitting subdomain");
10452   /* Create subdomain */
10453   PetscCall(DMPlexFilter(dm, label, value, subdm));
10454   /* Create submodel */
10455   PetscCall(DMPlexGetSubpointIS(*subdm, &subis));
10456   PetscCall(PetscSectionCreateSubmeshSection(section, subis, &subsection));
10457   PetscCall(DMSetLocalSection(*subdm, subsection));
10458   PetscCall(PetscSectionDestroy(&subsection));
10459   PetscCall(DMCopyDisc(dm, *subdm));
10460   /* Create map from submodel to global model */
10461   if (is) {
10462     PetscSection    sectionGlobal, subsectionGlobal;
10463     IS              spIS;
10464     const PetscInt *spmap;
10465     PetscInt       *subIndices;
10466     PetscInt        subSize = 0, subOff = 0, pStart, pEnd, p;
10467     PetscInt        Nf, f, bs = -1, bsLocal[2], bsMinMax[2];
10468 
10469     PetscCall(DMPlexGetSubpointIS(*subdm, &spIS));
10470     PetscCall(ISGetIndices(spIS, &spmap));
10471     PetscCall(PetscSectionGetNumFields(section, &Nf));
10472     PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
10473     PetscCall(DMGetGlobalSection(*subdm, &subsectionGlobal));
10474     PetscCall(PetscSectionGetChart(subsection, &pStart, &pEnd));
10475     for (p = pStart; p < pEnd; ++p) {
10476       PetscInt gdof, pSubSize = 0;
10477 
10478       PetscCall(PetscSectionGetDof(sectionGlobal, p, &gdof));
10479       if (gdof > 0) {
10480         for (f = 0; f < Nf; ++f) {
10481           PetscInt fdof, fcdof;
10482 
10483           PetscCall(PetscSectionGetFieldDof(subsection, p, f, &fdof));
10484           PetscCall(PetscSectionGetFieldConstraintDof(subsection, p, f, &fcdof));
10485           pSubSize += fdof - fcdof;
10486         }
10487         subSize += pSubSize;
10488         if (pSubSize) {
10489           if (bs < 0) {
10490             bs = pSubSize;
10491           } else if (bs != pSubSize) {
10492             /* Layout does not admit a pointwise block size */
10493             bs = 1;
10494           }
10495         }
10496       }
10497     }
10498     /* Must have same blocksize on all procs (some might have no points) */
10499     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs;
10500     bsLocal[1] = bs;
10501     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
10502     if (bsMinMax[0] != bsMinMax[1]) {
10503       bs = 1;
10504     } else {
10505       bs = bsMinMax[0];
10506     }
10507     PetscCall(PetscMalloc1(subSize, &subIndices));
10508     for (p = pStart; p < pEnd; ++p) {
10509       PetscInt gdof, goff;
10510 
10511       PetscCall(PetscSectionGetDof(subsectionGlobal, p, &gdof));
10512       if (gdof > 0) {
10513         const PetscInt point = spmap[p];
10514 
10515         PetscCall(PetscSectionGetOffset(sectionGlobal, point, &goff));
10516         for (f = 0; f < Nf; ++f) {
10517           PetscInt fdof, fcdof, fc, f2, poff = 0;
10518 
10519           /* Can get rid of this loop by storing field information in the global section */
10520           for (f2 = 0; f2 < f; ++f2) {
10521             PetscCall(PetscSectionGetFieldDof(section, p, f2, &fdof));
10522             PetscCall(PetscSectionGetFieldConstraintDof(section, p, f2, &fcdof));
10523             poff += fdof - fcdof;
10524           }
10525           PetscCall(PetscSectionGetFieldDof(section, p, f, &fdof));
10526           PetscCall(PetscSectionGetFieldConstraintDof(section, p, f, &fcdof));
10527           for (fc = 0; fc < fdof - fcdof; ++fc, ++subOff) subIndices[subOff] = goff + poff + fc;
10528         }
10529       }
10530     }
10531     PetscCall(ISRestoreIndices(spIS, &spmap));
10532     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), subSize, subIndices, PETSC_OWN_POINTER, is));
10533     if (bs > 1) {
10534       /* We need to check that the block size does not come from non-contiguous fields */
10535       PetscInt i, j, set = 1;
10536       for (i = 0; i < subSize; i += bs) {
10537         for (j = 0; j < bs; ++j) {
10538           if (subIndices[i + j] != subIndices[i] + j) {
10539             set = 0;
10540             break;
10541           }
10542         }
10543       }
10544       if (set) PetscCall(ISSetBlockSize(*is, bs));
10545     }
10546     /* Attach nullspace */
10547     for (f = 0; f < Nf; ++f) {
10548       (*subdm)->nullspaceConstructors[f] = dm->nullspaceConstructors[f];
10549       if ((*subdm)->nullspaceConstructors[f]) break;
10550     }
10551     if (f < Nf) {
10552       MatNullSpace nullSpace;
10553       PetscCall((*(*subdm)->nullspaceConstructors[f])(*subdm, f, f, &nullSpace));
10554 
10555       PetscCall(PetscObjectCompose((PetscObject)*is, "nullspace", (PetscObject)nullSpace));
10556       PetscCall(MatNullSpaceDestroy(&nullSpace));
10557     }
10558   }
10559   PetscFunctionReturn(PETSC_SUCCESS);
10560 }
10561 
10562 /*@
10563   DMPlexMonitorThroughput - Report the cell throughput of FE integration
10564 
10565   Input Parameters:
10566 + dm    - The `DM`
10567 - dummy - unused argument
10568 
10569   Options Database Key:
10570 . -dm_plex_monitor_throughput - Activate the monitor
10571 
10572   Level: developer
10573 
10574 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexCreate()`
10575 @*/
10576 PetscErrorCode DMPlexMonitorThroughput(DM dm, void *dummy)
10577 {
10578   PetscLogHandler default_handler;
10579 
10580   PetscFunctionBegin;
10581   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10582   PetscCall(PetscLogGetDefaultHandler(&default_handler));
10583   if (default_handler) {
10584     PetscLogEvent      event;
10585     PetscEventPerfInfo eventInfo;
10586     PetscReal          cellRate, flopRate;
10587     PetscInt           cStart, cEnd, Nf, N;
10588     const char        *name;
10589 
10590     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
10591     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
10592     PetscCall(DMGetNumFields(dm, &Nf));
10593     PetscCall(PetscLogEventGetId("DMPlexResidualFE", &event));
10594     PetscCall(PetscLogEventGetPerfInfo(PETSC_DEFAULT, event, &eventInfo));
10595     N        = (cEnd - cStart) * Nf * eventInfo.count;
10596     flopRate = eventInfo.flops / eventInfo.time;
10597     cellRate = N / eventInfo.time;
10598     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)));
10599   } else {
10600     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.");
10601   }
10602   PetscFunctionReturn(PETSC_SUCCESS);
10603 }
10604