xref: /petsc/src/dm/impls/plex/plex.c (revision 47bfdf3fa30783d319821566bb96996bc8fc6067)
1 #include <petsc/private/dmpleximpl.h> /*I      "petscdmplex.h"   I*/
2 #include <petsc/private/dmlabelimpl.h>
3 #include <petsc/private/isimpl.h>
4 #include <petsc/private/vecimpl.h>
5 #include <petsc/private/glvisvecimpl.h>
6 #include <petscsf.h>
7 #include <petscds.h>
8 #include <petscdraw.h>
9 #include <petscdmfield.h>
10 #include <petscdmplextransform.h>
11 #include <petscblaslapack.h>
12 
13 /* Logging support */
14 PetscLogEvent DMPLEX_Interpolate, DMPLEX_Partition, DMPLEX_Distribute, DMPLEX_DistributeCones, DMPLEX_DistributeLabels, DMPLEX_DistributeSF, DMPLEX_DistributeOverlap, DMPLEX_DistributeField, DMPLEX_DistributeData, DMPLEX_Migrate, DMPLEX_InterpolateSF, DMPLEX_GlobalToNaturalBegin, DMPLEX_GlobalToNaturalEnd, DMPLEX_NaturalToGlobalBegin, DMPLEX_NaturalToGlobalEnd, DMPLEX_Stratify, DMPLEX_Symmetrize, DMPLEX_Preallocate, DMPLEX_ResidualFEM, DMPLEX_JacobianFEM, DMPLEX_InterpolatorFEM, DMPLEX_InjectorFEM, DMPLEX_IntegralFEM, DMPLEX_CreateGmsh, DMPLEX_RebalanceSharedPoints, DMPLEX_PartSelf, DMPLEX_PartLabelInvert, DMPLEX_PartLabelCreateSF, DMPLEX_PartStratSF, DMPLEX_CreatePointSF, DMPLEX_LocatePoints, DMPLEX_TopologyView, DMPLEX_LabelsView, DMPLEX_CoordinatesView, DMPLEX_SectionView, DMPLEX_GlobalVectorView, DMPLEX_LocalVectorView, DMPLEX_TopologyLoad, DMPLEX_LabelsLoad, DMPLEX_CoordinatesLoad, DMPLEX_SectionLoad, DMPLEX_GlobalVectorLoad, DMPLEX_LocalVectorLoad;
15 PetscLogEvent DMPLEX_RebalBuildGraph, DMPLEX_RebalRewriteSF, DMPLEX_RebalGatherGraph, DMPLEX_RebalPartition, DMPLEX_RebalScatterPart, DMPLEX_Generate, DMPLEX_Transform, DMPLEX_GetLocalOffsets, DMPLEX_Uninterpolate;
16 
17 PetscBool  Plexcite       = PETSC_FALSE;
18 const char PlexCitation[] = "@article{LangeMitchellKnepleyGorman2015,\n"
19                             "title     = {Efficient mesh management in {Firedrake} using {PETSc-DMPlex}},\n"
20                             "author    = {Michael Lange and Lawrence Mitchell and Matthew G. Knepley and Gerard J. Gorman},\n"
21                             "journal   = {SIAM Journal on Scientific Computing},\n"
22                             "volume    = {38},\n"
23                             "number    = {5},\n"
24                             "pages     = {S143--S155},\n"
25                             "eprint    = {http://arxiv.org/abs/1506.07749},\n"
26                             "doi       = {10.1137/15M1026092},\n"
27                             "year      = {2016},\n"
28                             "petsc_uses={DMPlex},\n}\n";
29 
30 PETSC_EXTERN PetscErrorCode VecView_MPI(Vec, PetscViewer);
31 
32 /*@
33   DMPlexIsSimplex - Is the first cell in this mesh a simplex?
34 
35   Input Parameter:
36 . dm - The `DMPLEX` object
37 
38   Output Parameter:
39 . simplex - Flag checking for a simplex
40 
41   Level: intermediate
42 
43   Note:
44   This just gives the first range of cells found. If the mesh has several cell types, it will only give the first.
45   If the mesh has no cells, this returns `PETSC_FALSE`.
46 
47 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSimplexOrBoxCells()`, `DMPlexGetCellType()`, `DMPlexGetHeightStratum()`, `DMPolytopeTypeGetNumVertices()`
48 @*/
49 PetscErrorCode DMPlexIsSimplex(DM dm, PetscBool *simplex)
50 {
51   DMPolytopeType ct;
52   PetscInt       cStart, cEnd;
53 
54   PetscFunctionBegin;
55   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
56   if (cEnd <= cStart) {
57     *simplex = PETSC_FALSE;
58     PetscFunctionReturn(PETSC_SUCCESS);
59   }
60   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
61   *simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct) + 1 ? PETSC_TRUE : PETSC_FALSE;
62   PetscFunctionReturn(PETSC_SUCCESS);
63 }
64 
65 /*@
66   DMPlexGetSimplexOrBoxCells - Get the range of cells which are neither prisms nor ghost FV cells
67 
68   Input Parameters:
69 + dm     - The `DMPLEX` object
70 - height - The cell height in the Plex, 0 is the default
71 
72   Output Parameters:
73 + cStart - The first "normal" cell
74 - cEnd   - The upper bound on "normal" cells
75 
76   Level: developer
77 
78   Note:
79   This function requires that tensor cells are ordered last.
80 
81 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexConstructGhostCells()`, `DMPlexGetCellTypeStratum()`
82 @*/
83 PetscErrorCode DMPlexGetSimplexOrBoxCells(DM dm, PetscInt height, PetscInt *cStart, PetscInt *cEnd)
84 {
85   DMLabel         ctLabel;
86   IS              valueIS;
87   const PetscInt *ctypes;
88   PetscInt        Nct, cS = PETSC_MAX_INT, cE = 0;
89 
90   PetscFunctionBegin;
91   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
92   PetscCall(DMLabelGetValueIS(ctLabel, &valueIS));
93   PetscCall(ISGetLocalSize(valueIS, &Nct));
94   PetscCall(ISGetIndices(valueIS, &ctypes));
95   if (!Nct) cS = cE = 0;
96   for (PetscInt t = 0; t < Nct; ++t) {
97     const DMPolytopeType ct = (DMPolytopeType)ctypes[t];
98     PetscInt             ctS, ctE, ht;
99 
100     if (ct == DM_POLYTOPE_UNKNOWN) {
101       // If any cells are not typed, just use all cells
102       PetscCall(DMPlexGetHeightStratum(dm, PetscMax(height, 0), cStart, cEnd));
103       break;
104     }
105     if (DMPolytopeTypeIsHybrid(ct) || ct == DM_POLYTOPE_FV_GHOST) continue;
106     PetscCall(DMLabelGetStratumBounds(ctLabel, ct, &ctS, &ctE));
107     if (ctS >= ctE) continue;
108     // Check that a point has the right height
109     PetscCall(DMPlexGetPointHeight(dm, ctS, &ht));
110     if (ht != height) continue;
111     cS = PetscMin(cS, ctS);
112     cE = PetscMax(cE, ctE);
113   }
114   PetscCall(ISDestroy(&valueIS));
115   // Reset label for fast lookup
116   PetscCall(DMLabelMakeAllInvalid_Internal(ctLabel));
117   if (cStart) *cStart = cS;
118   if (cEnd) *cEnd = cE;
119   PetscFunctionReturn(PETSC_SUCCESS);
120 }
121 
122 PetscErrorCode DMPlexGetFieldTypes_Internal(DM dm, PetscSection section, PetscInt field, PetscInt *types, PetscInt **ssStart, PetscInt **ssEnd, PetscViewerVTKFieldType **sft)
123 {
124   PetscInt                 cdim, pStart, pEnd, vStart, vEnd, cStart, cEnd, c, depth, cellHeight, t;
125   PetscInt                *sStart, *sEnd;
126   PetscViewerVTKFieldType *ft;
127   PetscInt                 vcdof[DM_NUM_POLYTOPES + 1], globalvcdof[DM_NUM_POLYTOPES + 1];
128   DMLabel                  depthLabel, ctLabel;
129 
130   PetscFunctionBegin;
131   /* the vcdof and globalvcdof are sized to allow every polytope type and simple vertex at DM_NUM_POLYTOPES */
132   PetscCall(PetscArrayzero(vcdof, DM_NUM_POLYTOPES + 1));
133   PetscCall(DMGetCoordinateDim(dm, &cdim));
134   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
135   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
136   if (field >= 0) {
137     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, vStart, field, &vcdof[DM_NUM_POLYTOPES]));
138   } else {
139     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetDof(section, vStart, &vcdof[DM_NUM_POLYTOPES]));
140   }
141 
142   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
143   PetscCall(DMPlexGetDepth(dm, &depth));
144   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
145   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
146   for (c = 0; c < DM_NUM_POLYTOPES; ++c) {
147     const DMPolytopeType ict = (DMPolytopeType)c;
148     PetscInt             dep;
149 
150     if (ict == DM_POLYTOPE_FV_GHOST) continue;
151     PetscCall(DMLabelGetStratumBounds(ctLabel, ict, &cStart, &cEnd));
152     if (pStart >= 0) {
153       PetscCall(DMLabelGetValue(depthLabel, cStart, &dep));
154       if (dep != depth - cellHeight) continue;
155     }
156     if (field >= 0) {
157       if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, cStart, field, &vcdof[c]));
158     } else {
159       if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetDof(section, cStart, &vcdof[c]));
160     }
161   }
162 
163   PetscCall(MPIU_Allreduce(vcdof, globalvcdof, DM_NUM_POLYTOPES + 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
164   *types = 0;
165 
166   for (c = 0; c < DM_NUM_POLYTOPES + 1; ++c) {
167     if (globalvcdof[c]) ++(*types);
168   }
169 
170   PetscCall(PetscMalloc3(*types, &sStart, *types, &sEnd, *types, &ft));
171   t = 0;
172   if (globalvcdof[DM_NUM_POLYTOPES]) {
173     sStart[t] = vStart;
174     sEnd[t]   = vEnd;
175     ft[t]     = (globalvcdof[t] == cdim) ? PETSC_VTK_POINT_VECTOR_FIELD : PETSC_VTK_POINT_FIELD;
176     ++t;
177   }
178 
179   for (c = 0; c < DM_NUM_POLYTOPES; ++c) {
180     if (globalvcdof[c]) {
181       const DMPolytopeType ict = (DMPolytopeType)c;
182 
183       PetscCall(DMLabelGetStratumBounds(ctLabel, ict, &cStart, &cEnd));
184       sStart[t] = cStart;
185       sEnd[t]   = cEnd;
186       ft[t]     = (globalvcdof[c] == cdim) ? PETSC_VTK_CELL_VECTOR_FIELD : PETSC_VTK_CELL_FIELD;
187       ++t;
188     }
189   }
190 
191   if (!*types) {
192     if (field >= 0) {
193       const char *fieldname;
194 
195       PetscCall(PetscSectionGetFieldName(section, field, &fieldname));
196       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section field %" PetscInt_FMT " \"%s\"\n", field, fieldname));
197     } else {
198       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section\n"));
199     }
200   }
201 
202   *ssStart = sStart;
203   *ssEnd   = sEnd;
204   *sft     = ft;
205   PetscFunctionReturn(PETSC_SUCCESS);
206 }
207 
208 PetscErrorCode DMPlexRestoreFieldTypes_Internal(DM dm, PetscSection section, PetscInt field, PetscInt *types, PetscInt **sStart, PetscInt **sEnd, PetscViewerVTKFieldType **ft)
209 {
210   PetscFunctionBegin;
211   PetscCall(PetscFree3(*sStart, *sEnd, *ft));
212   PetscFunctionReturn(PETSC_SUCCESS);
213 }
214 
215 PetscErrorCode DMPlexGetFieldType_Internal(DM dm, PetscSection section, PetscInt field, PetscInt *sStart, PetscInt *sEnd, PetscViewerVTKFieldType *ft)
216 {
217   PetscInt cdim, pStart, pEnd, vStart, vEnd, cStart, cEnd;
218   PetscInt vcdof[2] = {0, 0}, globalvcdof[2];
219 
220   PetscFunctionBegin;
221   *ft = PETSC_VTK_INVALID;
222   PetscCall(DMGetCoordinateDim(dm, &cdim));
223   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
224   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
225   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
226   if (field >= 0) {
227     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, vStart, field, &vcdof[0]));
228     if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, cStart, field, &vcdof[1]));
229   } else {
230     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetDof(section, vStart, &vcdof[0]));
231     if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetDof(section, cStart, &vcdof[1]));
232   }
233   PetscCall(MPIU_Allreduce(vcdof, globalvcdof, 2, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
234   if (globalvcdof[0]) {
235     *sStart = vStart;
236     *sEnd   = vEnd;
237     if (globalvcdof[0] == cdim) *ft = PETSC_VTK_POINT_VECTOR_FIELD;
238     else *ft = PETSC_VTK_POINT_FIELD;
239   } else if (globalvcdof[1]) {
240     *sStart = cStart;
241     *sEnd   = cEnd;
242     if (globalvcdof[1] == cdim) *ft = PETSC_VTK_CELL_VECTOR_FIELD;
243     else *ft = PETSC_VTK_CELL_FIELD;
244   } else {
245     if (field >= 0) {
246       const char *fieldname;
247 
248       PetscCall(PetscSectionGetFieldName(section, field, &fieldname));
249       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section field %" PetscInt_FMT " \"%s\"\n", field, fieldname));
250     } else {
251       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section\n"));
252     }
253   }
254   PetscFunctionReturn(PETSC_SUCCESS);
255 }
256 
257 /*@
258   DMPlexVecView1D - Plot many 1D solutions on the same line graph
259 
260   Collective
261 
262   Input Parameters:
263 + dm     - The `DMPLEX` object
264 . n      - The number of vectors
265 . u      - The array of local vectors
266 - viewer - The `PetscViewer`
267 
268   Level: advanced
269 
270 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `VecViewFromOptions()`, `VecView()`
271 @*/
272 PetscErrorCode DMPlexVecView1D(DM dm, PetscInt n, Vec u[], PetscViewer viewer)
273 {
274   PetscDS            ds;
275   PetscDraw          draw = NULL;
276   PetscDrawLG        lg;
277   Vec                coordinates;
278   const PetscScalar *coords, **sol;
279   PetscReal         *vals;
280   PetscInt          *Nc;
281   PetscInt           Nf, f, c, Nl, l, i, vStart, vEnd, v;
282   char             **names;
283 
284   PetscFunctionBegin;
285   PetscCall(DMGetDS(dm, &ds));
286   PetscCall(PetscDSGetNumFields(ds, &Nf));
287   PetscCall(PetscDSGetTotalComponents(ds, &Nl));
288   PetscCall(PetscDSGetComponents(ds, &Nc));
289 
290   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
291   if (!draw) PetscFunctionReturn(PETSC_SUCCESS);
292   PetscCall(PetscDrawLGCreate(draw, n * Nl, &lg));
293 
294   PetscCall(PetscMalloc3(n, &sol, n * Nl, &names, n * Nl, &vals));
295   for (i = 0, l = 0; i < n; ++i) {
296     const char *vname;
297 
298     PetscCall(PetscObjectGetName((PetscObject)u[i], &vname));
299     for (f = 0; f < Nf; ++f) {
300       PetscObject disc;
301       const char *fname;
302       char        tmpname[PETSC_MAX_PATH_LEN];
303 
304       PetscCall(PetscDSGetDiscretization(ds, f, &disc));
305       /* TODO Create names for components */
306       for (c = 0; c < Nc[f]; ++c, ++l) {
307         PetscCall(PetscObjectGetName(disc, &fname));
308         PetscCall(PetscStrncpy(tmpname, vname, sizeof(tmpname)));
309         PetscCall(PetscStrlcat(tmpname, ":", sizeof(tmpname)));
310         PetscCall(PetscStrlcat(tmpname, fname, sizeof(tmpname)));
311         PetscCall(PetscStrallocpy(tmpname, &names[l]));
312       }
313     }
314   }
315   PetscCall(PetscDrawLGSetLegend(lg, (const char *const *)names));
316   /* Just add P_1 support for now */
317   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
318   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
319   PetscCall(VecGetArrayRead(coordinates, &coords));
320   for (i = 0; i < n; ++i) PetscCall(VecGetArrayRead(u[i], &sol[i]));
321   for (v = vStart; v < vEnd; ++v) {
322     PetscScalar *x, *svals;
323 
324     PetscCall(DMPlexPointLocalRead(dm, v, coords, &x));
325     for (i = 0; i < n; ++i) {
326       PetscCall(DMPlexPointLocalRead(dm, v, sol[i], &svals));
327       for (l = 0; l < Nl; ++l) vals[i * Nl + l] = PetscRealPart(svals[l]);
328     }
329     PetscCall(PetscDrawLGAddCommonPoint(lg, PetscRealPart(x[0]), vals));
330   }
331   PetscCall(VecRestoreArrayRead(coordinates, &coords));
332   for (i = 0; i < n; ++i) PetscCall(VecRestoreArrayRead(u[i], &sol[i]));
333   for (l = 0; l < n * Nl; ++l) PetscCall(PetscFree(names[l]));
334   PetscCall(PetscFree3(sol, names, vals));
335 
336   PetscCall(PetscDrawLGDraw(lg));
337   PetscCall(PetscDrawLGDestroy(&lg));
338   PetscFunctionReturn(PETSC_SUCCESS);
339 }
340 
341 static PetscErrorCode VecView_Plex_Local_Draw_1D(Vec u, PetscViewer viewer)
342 {
343   DM dm;
344 
345   PetscFunctionBegin;
346   PetscCall(VecGetDM(u, &dm));
347   PetscCall(DMPlexVecView1D(dm, 1, &u, viewer));
348   PetscFunctionReturn(PETSC_SUCCESS);
349 }
350 
351 static PetscErrorCode VecView_Plex_Local_Draw_2D(Vec v, PetscViewer viewer)
352 {
353   DM                 dm;
354   PetscSection       s;
355   PetscDraw          draw, popup;
356   DM                 cdm;
357   PetscSection       coordSection;
358   Vec                coordinates;
359   const PetscScalar *array;
360   PetscReal          lbound[3], ubound[3];
361   PetscReal          vbound[2], time;
362   PetscBool          flg;
363   PetscInt           dim, Nf, f, Nc, comp, vStart, vEnd, cStart, cEnd, c, N, level, step, w = 0;
364   const char        *name;
365   char               title[PETSC_MAX_PATH_LEN];
366 
367   PetscFunctionBegin;
368   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
369   PetscCall(VecGetDM(v, &dm));
370   PetscCall(DMGetCoordinateDim(dm, &dim));
371   PetscCall(DMGetLocalSection(dm, &s));
372   PetscCall(PetscSectionGetNumFields(s, &Nf));
373   PetscCall(DMGetCoarsenLevel(dm, &level));
374   PetscCall(DMGetCoordinateDM(dm, &cdm));
375   PetscCall(DMGetLocalSection(cdm, &coordSection));
376   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
377   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
378   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
379 
380   PetscCall(PetscObjectGetName((PetscObject)v, &name));
381   PetscCall(DMGetOutputSequenceNumber(dm, &step, &time));
382 
383   PetscCall(VecGetLocalSize(coordinates, &N));
384   PetscCall(DMGetBoundingBox(dm, lbound, ubound));
385   PetscCall(PetscDrawClear(draw));
386 
387   /* Could implement something like DMDASelectFields() */
388   for (f = 0; f < Nf; ++f) {
389     DM          fdm = dm;
390     Vec         fv  = v;
391     IS          fis;
392     char        prefix[PETSC_MAX_PATH_LEN];
393     const char *fname;
394 
395     PetscCall(PetscSectionGetFieldComponents(s, f, &Nc));
396     PetscCall(PetscSectionGetFieldName(s, f, &fname));
397 
398     if (v->hdr.prefix) PetscCall(PetscStrncpy(prefix, v->hdr.prefix, sizeof(prefix)));
399     else prefix[0] = '\0';
400     if (Nf > 1) {
401       PetscCall(DMCreateSubDM(dm, 1, &f, &fis, &fdm));
402       PetscCall(VecGetSubVector(v, fis, &fv));
403       PetscCall(PetscStrlcat(prefix, fname, sizeof(prefix)));
404       PetscCall(PetscStrlcat(prefix, "_", sizeof(prefix)));
405     }
406     for (comp = 0; comp < Nc; ++comp, ++w) {
407       PetscInt nmax = 2;
408 
409       PetscCall(PetscViewerDrawGetDraw(viewer, w, &draw));
410       if (Nc > 1) PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s_%" PetscInt_FMT " Step: %" PetscInt_FMT " Time: %.4g", name, fname, comp, step, (double)time));
411       else PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s Step: %" PetscInt_FMT " Time: %.4g", name, fname, step, (double)time));
412       PetscCall(PetscDrawSetTitle(draw, title));
413 
414       /* TODO Get max and min only for this component */
415       PetscCall(PetscOptionsGetRealArray(NULL, prefix, "-vec_view_bounds", vbound, &nmax, &flg));
416       if (!flg) {
417         PetscCall(VecMin(fv, NULL, &vbound[0]));
418         PetscCall(VecMax(fv, NULL, &vbound[1]));
419         if (vbound[1] <= vbound[0]) vbound[1] = vbound[0] + 1.0;
420       }
421 
422       PetscCall(PetscDrawGetPopup(draw, &popup));
423       PetscCall(PetscDrawScalePopup(popup, vbound[0], vbound[1]));
424       PetscCall(PetscDrawSetCoordinates(draw, lbound[0], lbound[1], ubound[0], ubound[1]));
425       PetscCall(VecGetArrayRead(fv, &array));
426       for (c = cStart; c < cEnd; ++c) {
427         PetscScalar       *coords = NULL, *a = NULL;
428         const PetscScalar *coords_arr;
429         PetscBool          isDG;
430         PetscInt           numCoords, color[4] = {-1, -1, -1, -1};
431 
432         PetscCall(DMPlexPointLocalRead(fdm, c, array, &a));
433         if (a) {
434           color[0] = PetscDrawRealToColor(PetscRealPart(a[comp]), vbound[0], vbound[1]);
435           color[1] = color[2] = color[3] = color[0];
436         } else {
437           PetscScalar *vals = NULL;
438           PetscInt     numVals, va;
439 
440           PetscCall(DMPlexVecGetClosure(fdm, NULL, fv, c, &numVals, &vals));
441           PetscCheck(numVals % Nc == 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "The number of components %" PetscInt_FMT " does not divide the number of values in the closure %" PetscInt_FMT, Nc, numVals);
442           switch (numVals / Nc) {
443           case 3: /* P1 Triangle */
444           case 4: /* P1 Quadrangle */
445             for (va = 0; va < numVals / Nc; ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp]), vbound[0], vbound[1]);
446             break;
447           case 6: /* P2 Triangle */
448           case 8: /* P2 Quadrangle */
449             for (va = 0; va < numVals / (Nc * 2); ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp + numVals / (Nc * 2)]), vbound[0], vbound[1]);
450             break;
451           default:
452             SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of values for cell closure %" PetscInt_FMT " cannot be handled", numVals / Nc);
453           }
454           PetscCall(DMPlexVecRestoreClosure(fdm, NULL, fv, c, &numVals, &vals));
455         }
456         PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
457         switch (numCoords) {
458         case 6:
459         case 12: /* Localized triangle */
460           PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), color[0], color[1], color[2]));
461           break;
462         case 8:
463         case 16: /* Localized quadrilateral */
464           PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), color[0], color[1], color[2]));
465           PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), color[2], color[3], color[0]));
466           break;
467         default:
468           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells with %" PetscInt_FMT " coordinates", numCoords);
469         }
470         PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
471       }
472       PetscCall(VecRestoreArrayRead(fv, &array));
473       PetscCall(PetscDrawFlush(draw));
474       PetscCall(PetscDrawPause(draw));
475       PetscCall(PetscDrawSave(draw));
476     }
477     if (Nf > 1) {
478       PetscCall(VecRestoreSubVector(v, fis, &fv));
479       PetscCall(ISDestroy(&fis));
480       PetscCall(DMDestroy(&fdm));
481     }
482   }
483   PetscFunctionReturn(PETSC_SUCCESS);
484 }
485 
486 static PetscErrorCode VecView_Plex_Local_Draw(Vec v, PetscViewer viewer)
487 {
488   DM        dm;
489   PetscDraw draw;
490   PetscInt  dim;
491   PetscBool isnull;
492 
493   PetscFunctionBegin;
494   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
495   PetscCall(PetscDrawIsNull(draw, &isnull));
496   if (isnull) PetscFunctionReturn(PETSC_SUCCESS);
497 
498   PetscCall(VecGetDM(v, &dm));
499   PetscCall(DMGetCoordinateDim(dm, &dim));
500   switch (dim) {
501   case 1:
502     PetscCall(VecView_Plex_Local_Draw_1D(v, viewer));
503     break;
504   case 2:
505     PetscCall(VecView_Plex_Local_Draw_2D(v, viewer));
506     break;
507   default:
508     SETERRQ(PetscObjectComm((PetscObject)v), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT ". Try PETSCVIEWERGLVIS", dim);
509   }
510   PetscFunctionReturn(PETSC_SUCCESS);
511 }
512 
513 static PetscErrorCode VecView_Plex_Local_VTK(Vec v, PetscViewer viewer)
514 {
515   DM                      dm;
516   Vec                     locv;
517   const char             *name;
518   PetscSection            section;
519   PetscInt                pStart, pEnd;
520   PetscInt                numFields;
521   PetscViewerVTKFieldType ft;
522 
523   PetscFunctionBegin;
524   PetscCall(VecGetDM(v, &dm));
525   PetscCall(DMCreateLocalVector(dm, &locv)); /* VTK viewer requires exclusive ownership of the vector */
526   PetscCall(PetscObjectGetName((PetscObject)v, &name));
527   PetscCall(PetscObjectSetName((PetscObject)locv, name));
528   PetscCall(VecCopy(v, locv));
529   PetscCall(DMGetLocalSection(dm, &section));
530   PetscCall(PetscSectionGetNumFields(section, &numFields));
531   if (!numFields) {
532     PetscCall(DMPlexGetFieldType_Internal(dm, section, PETSC_DETERMINE, &pStart, &pEnd, &ft));
533     PetscCall(PetscViewerVTKAddField(viewer, (PetscObject)dm, DMPlexVTKWriteAll, PETSC_DEFAULT, ft, PETSC_TRUE, (PetscObject)locv));
534   } else {
535     PetscInt f;
536 
537     for (f = 0; f < numFields; f++) {
538       PetscCall(DMPlexGetFieldType_Internal(dm, section, f, &pStart, &pEnd, &ft));
539       if (ft == PETSC_VTK_INVALID) continue;
540       PetscCall(PetscObjectReference((PetscObject)locv));
541       PetscCall(PetscViewerVTKAddField(viewer, (PetscObject)dm, DMPlexVTKWriteAll, f, ft, PETSC_TRUE, (PetscObject)locv));
542     }
543     PetscCall(VecDestroy(&locv));
544   }
545   PetscFunctionReturn(PETSC_SUCCESS);
546 }
547 
548 PetscErrorCode VecView_Plex_Local(Vec v, PetscViewer viewer)
549 {
550   DM        dm;
551   PetscBool isvtk, ishdf5, isdraw, isglvis, iscgns;
552 
553   PetscFunctionBegin;
554   PetscCall(VecGetDM(v, &dm));
555   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
556   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
557   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
558   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
559   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
560   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
561   if (isvtk || ishdf5 || isdraw || isglvis || iscgns) {
562     PetscInt    i, numFields;
563     PetscObject fe;
564     PetscBool   fem  = PETSC_FALSE;
565     Vec         locv = v;
566     const char *name;
567     PetscInt    step;
568     PetscReal   time;
569 
570     PetscCall(DMGetNumFields(dm, &numFields));
571     for (i = 0; i < numFields; i++) {
572       PetscCall(DMGetField(dm, i, NULL, &fe));
573       if (fe->classid == PETSCFE_CLASSID) {
574         fem = PETSC_TRUE;
575         break;
576       }
577     }
578     if (fem) {
579       PetscObject isZero;
580 
581       PetscCall(DMGetLocalVector(dm, &locv));
582       PetscCall(PetscObjectGetName((PetscObject)v, &name));
583       PetscCall(PetscObjectSetName((PetscObject)locv, name));
584       PetscCall(PetscObjectQuery((PetscObject)v, "__Vec_bc_zero__", &isZero));
585       PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", isZero));
586       PetscCall(VecCopy(v, locv));
587       PetscCall(DMGetOutputSequenceNumber(dm, NULL, &time));
588       PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_TRUE, locv, time, NULL, NULL, NULL));
589     }
590     if (isvtk) {
591       PetscCall(VecView_Plex_Local_VTK(locv, viewer));
592     } else if (ishdf5) {
593 #if defined(PETSC_HAVE_HDF5)
594       PetscCall(VecView_Plex_Local_HDF5_Internal(locv, viewer));
595 #else
596       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
597 #endif
598     } else if (isdraw) {
599       PetscCall(VecView_Plex_Local_Draw(locv, viewer));
600     } else if (isglvis) {
601       PetscCall(DMGetOutputSequenceNumber(dm, &step, NULL));
602       PetscCall(PetscViewerGLVisSetSnapId(viewer, step));
603       PetscCall(VecView_GLVis(locv, viewer));
604     } else if (iscgns) {
605 #if defined(PETSC_HAVE_CGNS)
606       PetscCall(VecView_Plex_Local_CGNS(locv, viewer));
607 #else
608       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "CGNS not supported in this build.\nPlease reconfigure using --download-cgns");
609 #endif
610     }
611     if (fem) {
612       PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", NULL));
613       PetscCall(DMRestoreLocalVector(dm, &locv));
614     }
615   } else {
616     PetscBool isseq;
617 
618     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
619     if (isseq) PetscCall(VecView_Seq(v, viewer));
620     else PetscCall(VecView_MPI(v, viewer));
621   }
622   PetscFunctionReturn(PETSC_SUCCESS);
623 }
624 
625 PetscErrorCode VecView_Plex(Vec v, PetscViewer viewer)
626 {
627   DM        dm;
628   PetscBool isvtk, ishdf5, isdraw, isglvis, isexodusii, iscgns;
629 
630   PetscFunctionBegin;
631   PetscCall(VecGetDM(v, &dm));
632   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
633   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
634   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
635   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
636   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
637   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
638   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodusii));
639   if (isvtk || isdraw || isglvis || iscgns) {
640     Vec         locv;
641     PetscObject isZero;
642     const char *name;
643 
644     PetscCall(DMGetLocalVector(dm, &locv));
645     PetscCall(PetscObjectGetName((PetscObject)v, &name));
646     PetscCall(PetscObjectSetName((PetscObject)locv, name));
647     PetscCall(DMGlobalToLocalBegin(dm, v, INSERT_VALUES, locv));
648     PetscCall(DMGlobalToLocalEnd(dm, v, INSERT_VALUES, locv));
649     PetscCall(PetscObjectQuery((PetscObject)v, "__Vec_bc_zero__", &isZero));
650     PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", isZero));
651     PetscCall(VecView_Plex_Local(locv, viewer));
652     PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", NULL));
653     PetscCall(DMRestoreLocalVector(dm, &locv));
654   } else if (ishdf5) {
655 #if defined(PETSC_HAVE_HDF5)
656     PetscCall(VecView_Plex_HDF5_Internal(v, viewer));
657 #else
658     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
659 #endif
660   } else if (isexodusii) {
661 #if defined(PETSC_HAVE_EXODUSII)
662     PetscCall(VecView_PlexExodusII_Internal(v, viewer));
663 #else
664     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
665 #endif
666   } else {
667     PetscBool isseq;
668 
669     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
670     if (isseq) PetscCall(VecView_Seq(v, viewer));
671     else PetscCall(VecView_MPI(v, viewer));
672   }
673   PetscFunctionReturn(PETSC_SUCCESS);
674 }
675 
676 PetscErrorCode VecView_Plex_Native(Vec originalv, PetscViewer viewer)
677 {
678   DM                dm;
679   MPI_Comm          comm;
680   PetscViewerFormat format;
681   Vec               v;
682   PetscBool         isvtk, ishdf5;
683 
684   PetscFunctionBegin;
685   PetscCall(VecGetDM(originalv, &dm));
686   PetscCall(PetscObjectGetComm((PetscObject)originalv, &comm));
687   PetscCheck(dm, comm, PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
688   PetscCall(PetscViewerGetFormat(viewer, &format));
689   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
690   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
691   if (format == PETSC_VIEWER_NATIVE) {
692     /* Natural ordering is the common case for DMDA, NATIVE means plain vector, for PLEX is the opposite */
693     /* this need a better fix */
694     if (dm->useNatural) {
695       if (dm->sfNatural) {
696         const char *vecname;
697         PetscInt    n, nroots;
698 
699         PetscCall(VecGetLocalSize(originalv, &n));
700         PetscCall(PetscSFGetGraph(dm->sfNatural, &nroots, NULL, NULL, NULL));
701         if (n == nroots) {
702           PetscCall(DMPlexCreateNaturalVector(dm, &v));
703           PetscCall(DMPlexGlobalToNaturalBegin(dm, originalv, v));
704           PetscCall(DMPlexGlobalToNaturalEnd(dm, originalv, v));
705           PetscCall(PetscObjectGetName((PetscObject)originalv, &vecname));
706           PetscCall(PetscObjectSetName((PetscObject)v, vecname));
707         } else SETERRQ(comm, PETSC_ERR_ARG_WRONG, "DM global to natural SF only handles global vectors");
708       } else SETERRQ(comm, PETSC_ERR_ARG_WRONGSTATE, "DM global to natural SF was not created");
709     } else v = originalv;
710   } else v = originalv;
711 
712   if (ishdf5) {
713 #if defined(PETSC_HAVE_HDF5)
714     PetscCall(VecView_Plex_HDF5_Native_Internal(v, viewer));
715 #else
716     SETERRQ(comm, PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
717 #endif
718   } else if (isvtk) {
719     SETERRQ(comm, PETSC_ERR_SUP, "VTK format does not support viewing in natural order. Please switch to HDF5.");
720   } else {
721     PetscBool isseq;
722 
723     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
724     if (isseq) PetscCall(VecView_Seq(v, viewer));
725     else PetscCall(VecView_MPI(v, viewer));
726   }
727   if (v != originalv) PetscCall(VecDestroy(&v));
728   PetscFunctionReturn(PETSC_SUCCESS);
729 }
730 
731 PetscErrorCode VecLoad_Plex_Local(Vec v, PetscViewer viewer)
732 {
733   DM        dm;
734   PetscBool ishdf5;
735 
736   PetscFunctionBegin;
737   PetscCall(VecGetDM(v, &dm));
738   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
739   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
740   if (ishdf5) {
741     DM          dmBC;
742     Vec         gv;
743     const char *name;
744 
745     PetscCall(DMGetOutputDM(dm, &dmBC));
746     PetscCall(DMGetGlobalVector(dmBC, &gv));
747     PetscCall(PetscObjectGetName((PetscObject)v, &name));
748     PetscCall(PetscObjectSetName((PetscObject)gv, name));
749     PetscCall(VecLoad_Default(gv, viewer));
750     PetscCall(DMGlobalToLocalBegin(dmBC, gv, INSERT_VALUES, v));
751     PetscCall(DMGlobalToLocalEnd(dmBC, gv, INSERT_VALUES, v));
752     PetscCall(DMRestoreGlobalVector(dmBC, &gv));
753   } else PetscCall(VecLoad_Default(v, viewer));
754   PetscFunctionReturn(PETSC_SUCCESS);
755 }
756 
757 PetscErrorCode VecLoad_Plex(Vec v, PetscViewer viewer)
758 {
759   DM        dm;
760   PetscBool ishdf5, isexodusii;
761 
762   PetscFunctionBegin;
763   PetscCall(VecGetDM(v, &dm));
764   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
765   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
766   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodusii));
767   if (ishdf5) {
768 #if defined(PETSC_HAVE_HDF5)
769     PetscCall(VecLoad_Plex_HDF5_Internal(v, viewer));
770 #else
771     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
772 #endif
773   } else if (isexodusii) {
774 #if defined(PETSC_HAVE_EXODUSII)
775     PetscCall(VecLoad_PlexExodusII_Internal(v, viewer));
776 #else
777     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
778 #endif
779   } else PetscCall(VecLoad_Default(v, viewer));
780   PetscFunctionReturn(PETSC_SUCCESS);
781 }
782 
783 PetscErrorCode VecLoad_Plex_Native(Vec originalv, PetscViewer viewer)
784 {
785   DM                dm;
786   PetscViewerFormat format;
787   PetscBool         ishdf5;
788 
789   PetscFunctionBegin;
790   PetscCall(VecGetDM(originalv, &dm));
791   PetscCheck(dm, PetscObjectComm((PetscObject)originalv), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
792   PetscCall(PetscViewerGetFormat(viewer, &format));
793   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
794   if (format == PETSC_VIEWER_NATIVE) {
795     if (dm->useNatural) {
796       if (dm->sfNatural) {
797         if (ishdf5) {
798 #if defined(PETSC_HAVE_HDF5)
799           Vec         v;
800           const char *vecname;
801 
802           PetscCall(DMPlexCreateNaturalVector(dm, &v));
803           PetscCall(PetscObjectGetName((PetscObject)originalv, &vecname));
804           PetscCall(PetscObjectSetName((PetscObject)v, vecname));
805           PetscCall(VecLoad_Plex_HDF5_Native_Internal(v, viewer));
806           PetscCall(DMPlexNaturalToGlobalBegin(dm, v, originalv));
807           PetscCall(DMPlexNaturalToGlobalEnd(dm, v, originalv));
808           PetscCall(VecDestroy(&v));
809 #else
810           SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
811 #endif
812         } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Reading in natural order is not supported for anything but HDF5.");
813       }
814     } else PetscCall(VecLoad_Default(originalv, viewer));
815   }
816   PetscFunctionReturn(PETSC_SUCCESS);
817 }
818 
819 PETSC_UNUSED static PetscErrorCode DMPlexView_Ascii_Geometry(DM dm, PetscViewer viewer)
820 {
821   PetscSection       coordSection;
822   Vec                coordinates;
823   DMLabel            depthLabel, celltypeLabel;
824   const char        *name[4];
825   const PetscScalar *a;
826   PetscInt           dim, pStart, pEnd, cStart, cEnd, c;
827 
828   PetscFunctionBegin;
829   PetscCall(DMGetDimension(dm, &dim));
830   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
831   PetscCall(DMGetCoordinateSection(dm, &coordSection));
832   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
833   PetscCall(DMPlexGetCellTypeLabel(dm, &celltypeLabel));
834   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
835   PetscCall(PetscSectionGetChart(coordSection, &pStart, &pEnd));
836   PetscCall(VecGetArrayRead(coordinates, &a));
837   name[0]       = "vertex";
838   name[1]       = "edge";
839   name[dim - 1] = "face";
840   name[dim]     = "cell";
841   for (c = cStart; c < cEnd; ++c) {
842     PetscInt *closure = NULL;
843     PetscInt  closureSize, cl, ct;
844 
845     PetscCall(DMLabelGetValue(celltypeLabel, c, &ct));
846     PetscCall(PetscViewerASCIIPrintf(viewer, "Geometry for cell %" PetscInt_FMT " polytope type %s:\n", c, DMPolytopeTypes[ct]));
847     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
848     PetscCall(PetscViewerASCIIPushTab(viewer));
849     for (cl = 0; cl < closureSize * 2; cl += 2) {
850       PetscInt point = closure[cl], depth, dof, off, d, p;
851 
852       if ((point < pStart) || (point >= pEnd)) continue;
853       PetscCall(PetscSectionGetDof(coordSection, point, &dof));
854       if (!dof) continue;
855       PetscCall(DMLabelGetValue(depthLabel, point, &depth));
856       PetscCall(PetscSectionGetOffset(coordSection, point, &off));
857       PetscCall(PetscViewerASCIIPrintf(viewer, "%s %" PetscInt_FMT " coords:", name[depth], point));
858       for (p = 0; p < dof / dim; ++p) {
859         PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
860         for (d = 0; d < dim; ++d) {
861           if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
862           PetscCall(PetscViewerASCIIPrintf(viewer, "%g", (double)PetscRealPart(a[off + p * dim + d])));
863         }
864         PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
865       }
866       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
867     }
868     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
869     PetscCall(PetscViewerASCIIPopTab(viewer));
870   }
871   PetscCall(VecRestoreArrayRead(coordinates, &a));
872   PetscFunctionReturn(PETSC_SUCCESS);
873 }
874 
875 typedef enum {
876   CS_CARTESIAN,
877   CS_POLAR,
878   CS_CYLINDRICAL,
879   CS_SPHERICAL
880 } CoordSystem;
881 const char *CoordSystems[] = {"cartesian", "polar", "cylindrical", "spherical", "CoordSystem", "CS_", NULL};
882 
883 static PetscErrorCode DMPlexView_Ascii_Coordinates(PetscViewer viewer, CoordSystem cs, PetscInt dim, const PetscScalar x[])
884 {
885   PetscInt i;
886 
887   PetscFunctionBegin;
888   if (dim > 3) {
889     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double)PetscRealPart(x[i])));
890   } else {
891     PetscReal coords[3], trcoords[3] = {0., 0., 0.};
892 
893     for (i = 0; i < dim; ++i) coords[i] = PetscRealPart(x[i]);
894     switch (cs) {
895     case CS_CARTESIAN:
896       for (i = 0; i < dim; ++i) trcoords[i] = coords[i];
897       break;
898     case CS_POLAR:
899       PetscCheck(dim == 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Polar coordinates are for 2 dimension, not %" PetscInt_FMT, dim);
900       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
901       trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
902       break;
903     case CS_CYLINDRICAL:
904       PetscCheck(dim == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cylindrical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
905       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
906       trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
907       trcoords[2] = coords[2];
908       break;
909     case CS_SPHERICAL:
910       PetscCheck(dim == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Spherical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
911       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]) + PetscSqr(coords[2]));
912       trcoords[1] = PetscAtan2Real(PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1])), coords[2]);
913       trcoords[2] = PetscAtan2Real(coords[1], coords[0]);
914       break;
915     }
916     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double)trcoords[i]));
917   }
918   PetscFunctionReturn(PETSC_SUCCESS);
919 }
920 
921 static PetscErrorCode DMPlexView_Ascii(DM dm, PetscViewer viewer)
922 {
923   DM_Plex          *mesh = (DM_Plex *)dm->data;
924   DM                cdm, cdmCell;
925   PetscSection      coordSection, coordSectionCell;
926   Vec               coordinates, coordinatesCell;
927   PetscViewerFormat format;
928 
929   PetscFunctionBegin;
930   PetscCall(PetscViewerGetFormat(viewer, &format));
931   if (format == PETSC_VIEWER_ASCII_INFO_DETAIL) {
932     const char *name;
933     PetscInt    dim, cellHeight, maxConeSize, maxSupportSize;
934     PetscInt    pStart, pEnd, p, numLabels, l;
935     PetscMPIInt rank, size;
936 
937     PetscCall(DMGetCoordinateDM(dm, &cdm));
938     PetscCall(DMGetCoordinateSection(dm, &coordSection));
939     PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
940     PetscCall(DMGetCellCoordinateDM(dm, &cdmCell));
941     PetscCall(DMGetCellCoordinateSection(dm, &coordSectionCell));
942     PetscCall(DMGetCellCoordinatesLocal(dm, &coordinatesCell));
943     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
944     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
945     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
946     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
947     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
948     PetscCall(DMGetDimension(dm, &dim));
949     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
950     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
951     else PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
952     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
953     PetscCall(PetscViewerASCIIPrintf(viewer, "Supports:\n"));
954     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
955     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max support size: %" PetscInt_FMT "\n", rank, maxSupportSize));
956     for (p = pStart; p < pEnd; ++p) {
957       PetscInt dof, off, s;
958 
959       PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
960       PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
961       for (s = off; s < off + dof; ++s) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d]: %" PetscInt_FMT " ----> %" PetscInt_FMT "\n", rank, p, mesh->supports[s]));
962     }
963     PetscCall(PetscViewerFlush(viewer));
964     PetscCall(PetscViewerASCIIPrintf(viewer, "Cones:\n"));
965     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max cone size: %" PetscInt_FMT "\n", rank, maxConeSize));
966     for (p = pStart; p < pEnd; ++p) {
967       PetscInt dof, off, c;
968 
969       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
970       PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
971       for (c = off; c < off + dof; ++c) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d]: %" PetscInt_FMT " <---- %" PetscInt_FMT " (%" PetscInt_FMT ")\n", rank, p, mesh->cones[c], mesh->coneOrientations[c]));
972     }
973     PetscCall(PetscViewerFlush(viewer));
974     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
975     if (coordSection && coordinates) {
976       CoordSystem        cs = CS_CARTESIAN;
977       const PetscScalar *array, *arrayCell = NULL;
978       PetscInt           Nf, Nc, pvStart, pvEnd, pcStart = PETSC_MAX_INT, pcEnd = PETSC_MIN_INT, pStart, pEnd, p;
979       PetscMPIInt        rank;
980       const char        *name;
981 
982       PetscCall(PetscOptionsGetEnum(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_coord_system", CoordSystems, (PetscEnum *)&cs, NULL));
983       PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)viewer), &rank));
984       PetscCall(PetscSectionGetNumFields(coordSection, &Nf));
985       PetscCheck(Nf == 1, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Coordinate section should have 1 field, not %" PetscInt_FMT, Nf);
986       PetscCall(PetscSectionGetFieldComponents(coordSection, 0, &Nc));
987       PetscCall(PetscSectionGetChart(coordSection, &pvStart, &pvEnd));
988       if (coordSectionCell) PetscCall(PetscSectionGetChart(coordSectionCell, &pcStart, &pcEnd));
989       pStart = PetscMin(pvStart, pcStart);
990       pEnd   = PetscMax(pvEnd, pcEnd);
991       PetscCall(PetscObjectGetName((PetscObject)coordinates, &name));
992       PetscCall(PetscViewerASCIIPrintf(viewer, "%s with %" PetscInt_FMT " fields\n", name, Nf));
993       PetscCall(PetscViewerASCIIPrintf(viewer, "  field 0 with %" PetscInt_FMT " components\n", Nc));
994       if (cs != CS_CARTESIAN) PetscCall(PetscViewerASCIIPrintf(viewer, "  output coordinate system: %s\n", CoordSystems[cs]));
995 
996       PetscCall(VecGetArrayRead(coordinates, &array));
997       if (coordinatesCell) PetscCall(VecGetArrayRead(coordinatesCell, &arrayCell));
998       PetscCall(PetscViewerASCIIPushSynchronized(viewer));
999       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "Process %d:\n", rank));
1000       for (p = pStart; p < pEnd; ++p) {
1001         PetscInt dof, off;
1002 
1003         if (p >= pvStart && p < pvEnd) {
1004           PetscCall(PetscSectionGetDof(coordSection, p, &dof));
1005           PetscCall(PetscSectionGetOffset(coordSection, p, &off));
1006           if (dof) {
1007             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dim %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
1008             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &array[off]));
1009             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
1010           }
1011         }
1012         if (cdmCell && p >= pcStart && p < pcEnd) {
1013           PetscCall(PetscSectionGetDof(coordSectionCell, p, &dof));
1014           PetscCall(PetscSectionGetOffset(coordSectionCell, p, &off));
1015           if (dof) {
1016             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dim %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
1017             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &arrayCell[off]));
1018             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
1019           }
1020         }
1021       }
1022       PetscCall(PetscViewerFlush(viewer));
1023       PetscCall(PetscViewerASCIIPopSynchronized(viewer));
1024       PetscCall(VecRestoreArrayRead(coordinates, &array));
1025       if (coordinatesCell) PetscCall(VecRestoreArrayRead(coordinatesCell, &arrayCell));
1026     }
1027     PetscCall(DMGetNumLabels(dm, &numLabels));
1028     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
1029     for (l = 0; l < numLabels; ++l) {
1030       DMLabel     label;
1031       PetscBool   isdepth;
1032       const char *name;
1033 
1034       PetscCall(DMGetLabelName(dm, l, &name));
1035       PetscCall(PetscStrcmp(name, "depth", &isdepth));
1036       if (isdepth) continue;
1037       PetscCall(DMGetLabel(dm, name, &label));
1038       PetscCall(DMLabelView(label, viewer));
1039     }
1040     if (size > 1) {
1041       PetscSF sf;
1042 
1043       PetscCall(DMGetPointSF(dm, &sf));
1044       PetscCall(PetscSFView(sf, viewer));
1045     }
1046     if (mesh->periodic.face_sfs)
1047       for (PetscInt i = 0; i < mesh->periodic.num_face_sfs; i++) PetscCall(PetscSFView(mesh->periodic.face_sfs[i], viewer));
1048     PetscCall(PetscViewerFlush(viewer));
1049   } else if (format == PETSC_VIEWER_ASCII_LATEX) {
1050     const char  *name, *color;
1051     const char  *defcolors[3]  = {"gray", "orange", "green"};
1052     const char  *deflcolors[4] = {"blue", "cyan", "red", "magenta"};
1053     char         lname[PETSC_MAX_PATH_LEN];
1054     PetscReal    scale      = 2.0;
1055     PetscReal    tikzscale  = 1.0;
1056     PetscBool    useNumbers = PETSC_TRUE, drawNumbers[4], drawColors[4], useLabels, useColors, plotEdges, drawHasse = PETSC_FALSE;
1057     double       tcoords[3];
1058     PetscScalar *coords;
1059     PetscInt     numLabels, l, numColors, numLColors, dim, d, depth, cStart, cEnd, c, vStart, vEnd, v, eStart = 0, eEnd = 0, fStart = 0, fEnd = 0, e, p, n;
1060     PetscMPIInt  rank, size;
1061     char       **names, **colors, **lcolors;
1062     PetscBool    flg, lflg;
1063     PetscBT      wp = NULL;
1064     PetscInt     pEnd, pStart;
1065 
1066     PetscCall(DMGetCoordinateDM(dm, &cdm));
1067     PetscCall(DMGetCoordinateSection(dm, &coordSection));
1068     PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1069     PetscCall(DMGetCellCoordinateDM(dm, &cdmCell));
1070     PetscCall(DMGetCellCoordinateSection(dm, &coordSectionCell));
1071     PetscCall(DMGetCellCoordinatesLocal(dm, &coordinatesCell));
1072     PetscCall(DMGetDimension(dm, &dim));
1073     PetscCall(DMPlexGetDepth(dm, &depth));
1074     PetscCall(DMGetNumLabels(dm, &numLabels));
1075     numLabels  = PetscMax(numLabels, 10);
1076     numColors  = 10;
1077     numLColors = 10;
1078     PetscCall(PetscCalloc3(numLabels, &names, numColors, &colors, numLColors, &lcolors));
1079     PetscCall(PetscOptionsGetReal(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_scale", &scale, NULL));
1080     PetscCall(PetscOptionsGetReal(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_tikzscale", &tikzscale, NULL));
1081     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_numbers", &useNumbers, NULL));
1082     for (d = 0; d < 4; ++d) drawNumbers[d] = useNumbers;
1083     for (d = 0; d < 4; ++d) drawColors[d] = PETSC_TRUE;
1084     n = 4;
1085     PetscCall(PetscOptionsGetBoolArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_numbers_depth", drawNumbers, &n, &flg));
1086     PetscCheck(!flg || n == dim + 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim + 1);
1087     n = 4;
1088     PetscCall(PetscOptionsGetBoolArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_colors_depth", drawColors, &n, &flg));
1089     PetscCheck(!flg || n == dim + 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim + 1);
1090     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_labels", names, &numLabels, &useLabels));
1091     if (!useLabels) numLabels = 0;
1092     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_colors", colors, &numColors, &useColors));
1093     if (!useColors) {
1094       numColors = 3;
1095       for (c = 0; c < numColors; ++c) PetscCall(PetscStrallocpy(defcolors[c], &colors[c]));
1096     }
1097     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_lcolors", lcolors, &numLColors, &useColors));
1098     if (!useColors) {
1099       numLColors = 4;
1100       for (c = 0; c < numLColors; ++c) PetscCall(PetscStrallocpy(deflcolors[c], &lcolors[c]));
1101     }
1102     PetscCall(PetscOptionsGetString(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_label_filter", lname, sizeof(lname), &lflg));
1103     plotEdges = (PetscBool)(depth > 1 && drawNumbers[1] && dim < 3);
1104     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_edges", &plotEdges, &flg));
1105     PetscCheck(!flg || !plotEdges || depth >= dim, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Mesh must be interpolated");
1106     if (depth < dim) plotEdges = PETSC_FALSE;
1107     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_hasse", &drawHasse, NULL));
1108 
1109     /* filter points with labelvalue != labeldefaultvalue */
1110     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
1111     PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1112     PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
1113     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1114     PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd));
1115     if (lflg) {
1116       DMLabel lbl;
1117 
1118       PetscCall(DMGetLabel(dm, lname, &lbl));
1119       if (lbl) {
1120         PetscInt val, defval;
1121 
1122         PetscCall(DMLabelGetDefaultValue(lbl, &defval));
1123         PetscCall(PetscBTCreate(pEnd - pStart, &wp));
1124         for (c = pStart; c < pEnd; c++) {
1125           PetscInt *closure = NULL;
1126           PetscInt  closureSize;
1127 
1128           PetscCall(DMLabelGetValue(lbl, c, &val));
1129           if (val == defval) continue;
1130 
1131           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1132           for (p = 0; p < closureSize * 2; p += 2) PetscCall(PetscBTSet(wp, closure[p] - pStart));
1133           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1134         }
1135       }
1136     }
1137 
1138     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1139     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
1140     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1141     PetscCall(PetscViewerASCIIPrintf(viewer, "\
1142 \\documentclass[tikz]{standalone}\n\n\
1143 \\usepackage{pgflibraryshapes}\n\
1144 \\usetikzlibrary{backgrounds}\n\
1145 \\usetikzlibrary{arrows}\n\
1146 \\begin{document}\n"));
1147     if (size > 1) {
1148       PetscCall(PetscViewerASCIIPrintf(viewer, "%s for process ", name));
1149       for (p = 0; p < size; ++p) {
1150         if (p) PetscCall(PetscViewerASCIIPrintf(viewer, (p == size - 1) ? ", and " : ", "));
1151         PetscCall(PetscViewerASCIIPrintf(viewer, "{\\textcolor{%s}%" PetscInt_FMT "}", colors[p % numColors], p));
1152       }
1153       PetscCall(PetscViewerASCIIPrintf(viewer, ".\n\n\n"));
1154     }
1155     if (drawHasse) {
1156       PetscInt maxStratum = PetscMax(vEnd - vStart, PetscMax(eEnd - eStart, PetscMax(fEnd - fStart, cEnd - cStart)));
1157 
1158       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vStart}{%" PetscInt_FMT "}\n", vStart));
1159       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vEnd}{%" PetscInt_FMT "}\n", vEnd - 1));
1160       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numVertices}{%" PetscInt_FMT "}\n", vEnd - vStart));
1161       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vShift}{%.2f}\n", 3 + (maxStratum - (vEnd - vStart)) / 2.));
1162       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eStart}{%" PetscInt_FMT "}\n", eStart));
1163       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eEnd}{%" PetscInt_FMT "}\n", eEnd - 1));
1164       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eShift}{%.2f}\n", 3 + (maxStratum - (eEnd - eStart)) / 2.));
1165       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numEdges}{%" PetscInt_FMT "}\n", eEnd - eStart));
1166       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\fStart}{%" PetscInt_FMT "}\n", fStart));
1167       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\fEnd}{%" PetscInt_FMT "}\n", fEnd - 1));
1168       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\fShift}{%.2f}\n", 3 + (maxStratum - (fEnd - fStart)) / 2.));
1169       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numFaces}{%" PetscInt_FMT "}\n", fEnd - fStart));
1170       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cStart}{%" PetscInt_FMT "}\n", cStart));
1171       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cEnd}{%" PetscInt_FMT "}\n", cEnd - 1));
1172       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numCells}{%" PetscInt_FMT "}\n", cEnd - cStart));
1173       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cShift}{%.2f}\n", 3 + (maxStratum - (cEnd - cStart)) / 2.));
1174     }
1175     PetscCall(PetscViewerASCIIPrintf(viewer, "\\begin{tikzpicture}[scale = %g,font=\\fontsize{8}{8}\\selectfont]\n", (double)tikzscale));
1176 
1177     /* Plot vertices */
1178     PetscCall(VecGetArray(coordinates, &coords));
1179     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
1180     for (v = vStart; v < vEnd; ++v) {
1181       PetscInt  off, dof, d;
1182       PetscBool isLabeled = PETSC_FALSE;
1183 
1184       if (wp && !PetscBTLookup(wp, v - pStart)) continue;
1185       PetscCall(PetscSectionGetDof(coordSection, v, &dof));
1186       PetscCall(PetscSectionGetOffset(coordSection, v, &off));
1187       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1188       PetscCheck(dof <= 3, PETSC_COMM_SELF, PETSC_ERR_PLIB, "coordSection vertex %" PetscInt_FMT " has dof %" PetscInt_FMT " > 3", v, dof);
1189       for (d = 0; d < dof; ++d) {
1190         tcoords[d] = (double)(scale * PetscRealPart(coords[off + d]));
1191         tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1192       }
1193       /* Rotate coordinates since PGF makes z point out of the page instead of up */
1194       if (dim == 3) {
1195         PetscReal tmp = tcoords[1];
1196         tcoords[1]    = tcoords[2];
1197         tcoords[2]    = -tmp;
1198       }
1199       for (d = 0; d < dof; ++d) {
1200         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1201         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)tcoords[d]));
1202       }
1203       if (drawHasse) color = colors[0 % numColors];
1204       else color = colors[rank % numColors];
1205       for (l = 0; l < numLabels; ++l) {
1206         PetscInt val;
1207         PetscCall(DMGetLabelValue(dm, names[l], v, &val));
1208         if (val >= 0) {
1209           color     = lcolors[l % numLColors];
1210           isLabeled = PETSC_TRUE;
1211           break;
1212         }
1213       }
1214       if (drawNumbers[0]) {
1215         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", v, rank, color, v));
1216       } else if (drawColors[0]) {
1217         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", v, rank, !isLabeled ? 1 : 2, color));
1218       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", v, rank));
1219     }
1220     PetscCall(VecRestoreArray(coordinates, &coords));
1221     PetscCall(PetscViewerFlush(viewer));
1222     /* Plot edges */
1223     if (plotEdges) {
1224       PetscCall(VecGetArray(coordinates, &coords));
1225       PetscCall(PetscViewerASCIIPrintf(viewer, "\\path\n"));
1226       for (e = eStart; e < eEnd; ++e) {
1227         const PetscInt *cone;
1228         PetscInt        coneSize, offA, offB, dof, d;
1229 
1230         if (wp && !PetscBTLookup(wp, e - pStart)) continue;
1231         PetscCall(DMPlexGetConeSize(dm, e, &coneSize));
1232         PetscCheck(coneSize == 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Edge %" PetscInt_FMT " cone should have two vertices, not %" PetscInt_FMT, e, coneSize);
1233         PetscCall(DMPlexGetCone(dm, e, &cone));
1234         PetscCall(PetscSectionGetDof(coordSection, cone[0], &dof));
1235         PetscCall(PetscSectionGetOffset(coordSection, cone[0], &offA));
1236         PetscCall(PetscSectionGetOffset(coordSection, cone[1], &offB));
1237         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "("));
1238         for (d = 0; d < dof; ++d) {
1239           tcoords[d] = (double)(0.5 * scale * PetscRealPart(coords[offA + d] + coords[offB + d]));
1240           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1241         }
1242         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1243         if (dim == 3) {
1244           PetscReal tmp = tcoords[1];
1245           tcoords[1]    = tcoords[2];
1246           tcoords[2]    = -tmp;
1247         }
1248         for (d = 0; d < dof; ++d) {
1249           if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1250           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)tcoords[d]));
1251         }
1252         if (drawHasse) color = colors[1 % numColors];
1253         else color = colors[rank % numColors];
1254         for (l = 0; l < numLabels; ++l) {
1255           PetscInt val;
1256           PetscCall(DMGetLabelValue(dm, names[l], e, &val));
1257           if (val >= 0) {
1258             color = lcolors[l % numLColors];
1259             break;
1260           }
1261         }
1262         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "} --\n", e, rank, color, e));
1263       }
1264       PetscCall(VecRestoreArray(coordinates, &coords));
1265       PetscCall(PetscViewerFlush(viewer));
1266       PetscCall(PetscViewerASCIIPrintf(viewer, "(0,0);\n"));
1267     }
1268     /* Plot cells */
1269     if (dim == 3 || !drawNumbers[1]) {
1270       for (e = eStart; e < eEnd; ++e) {
1271         const PetscInt *cone;
1272 
1273         if (wp && !PetscBTLookup(wp, e - pStart)) continue;
1274         color = colors[rank % numColors];
1275         for (l = 0; l < numLabels; ++l) {
1276           PetscInt val;
1277           PetscCall(DMGetLabelValue(dm, names[l], e, &val));
1278           if (val >= 0) {
1279             color = lcolors[l % numLColors];
1280             break;
1281           }
1282         }
1283         PetscCall(DMPlexGetCone(dm, e, &cone));
1284         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", color, cone[0], rank, cone[1], rank));
1285       }
1286     } else {
1287       DMPolytopeType ct;
1288 
1289       /* Drawing a 2D polygon */
1290       for (c = cStart; c < cEnd; ++c) {
1291         if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1292         PetscCall(DMPlexGetCellType(dm, c, &ct));
1293         if (DMPolytopeTypeIsHybrid(ct)) {
1294           const PetscInt *cone;
1295           PetscInt        coneSize, e;
1296 
1297           PetscCall(DMPlexGetCone(dm, c, &cone));
1298           PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
1299           for (e = 0; e < coneSize; ++e) {
1300             const PetscInt *econe;
1301 
1302             PetscCall(DMPlexGetCone(dm, cone[e], &econe));
1303             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));
1304           }
1305         } else {
1306           PetscInt *closure = NULL;
1307           PetscInt  closureSize, Nv = 0, v;
1308 
1309           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1310           for (p = 0; p < closureSize * 2; p += 2) {
1311             const PetscInt point = closure[p];
1312 
1313             if ((point >= vStart) && (point < vEnd)) closure[Nv++] = point;
1314           }
1315           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] ", colors[rank % numColors]));
1316           for (v = 0; v <= Nv; ++v) {
1317             const PetscInt vertex = closure[v % Nv];
1318 
1319             if (v > 0) {
1320               if (plotEdges) {
1321                 const PetscInt *edge;
1322                 PetscInt        endpoints[2], ne;
1323 
1324                 endpoints[0] = closure[v - 1];
1325                 endpoints[1] = vertex;
1326                 PetscCall(DMPlexGetJoin(dm, 2, endpoints, &ne, &edge));
1327                 PetscCheck(ne == 1, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Could not find edge for vertices %" PetscInt_FMT ", %" PetscInt_FMT, endpoints[0], endpoints[1]);
1328                 PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- (%" PetscInt_FMT "_%d) -- ", edge[0], rank));
1329                 PetscCall(DMPlexRestoreJoin(dm, 2, endpoints, &ne, &edge));
1330               } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- "));
1331             }
1332             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "(%" PetscInt_FMT "_%d)", vertex, rank));
1333           }
1334           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ";\n"));
1335           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1336         }
1337       }
1338     }
1339     for (c = cStart; c < cEnd; ++c) {
1340       double             ccoords[3] = {0.0, 0.0, 0.0};
1341       PetscBool          isLabeled  = PETSC_FALSE;
1342       PetscScalar       *cellCoords = NULL;
1343       const PetscScalar *array;
1344       PetscInt           numCoords, cdim, d;
1345       PetscBool          isDG;
1346 
1347       if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1348       PetscCall(DMGetCoordinateDim(dm, &cdim));
1349       PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1350       PetscCheck(!(numCoords % cdim), PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "coordinate dim %" PetscInt_FMT " does not divide numCoords %" PetscInt_FMT, cdim, numCoords);
1351       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1352       for (p = 0; p < numCoords / cdim; ++p) {
1353         for (d = 0; d < cdim; ++d) {
1354           tcoords[d] = (double)(scale * PetscRealPart(cellCoords[p * cdim + d]));
1355           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1356         }
1357         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1358         if (cdim == 3) {
1359           PetscReal tmp = tcoords[1];
1360           tcoords[1]    = tcoords[2];
1361           tcoords[2]    = -tmp;
1362         }
1363         for (d = 0; d < dim; ++d) ccoords[d] += tcoords[d];
1364       }
1365       for (d = 0; d < cdim; ++d) ccoords[d] /= (numCoords / cdim);
1366       PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1367       for (d = 0; d < cdim; ++d) {
1368         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1369         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)ccoords[d]));
1370       }
1371       if (drawHasse) color = colors[depth % numColors];
1372       else color = colors[rank % numColors];
1373       for (l = 0; l < numLabels; ++l) {
1374         PetscInt val;
1375         PetscCall(DMGetLabelValue(dm, names[l], c, &val));
1376         if (val >= 0) {
1377           color     = lcolors[l % numLColors];
1378           isLabeled = PETSC_TRUE;
1379           break;
1380         }
1381       }
1382       if (drawNumbers[dim]) {
1383         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", c, rank, color, c));
1384       } else if (drawColors[dim]) {
1385         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", c, rank, !isLabeled ? 1 : 2, color));
1386       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", c, rank));
1387     }
1388     if (drawHasse) {
1389       int height = 0;
1390 
1391       color = colors[depth % numColors];
1392       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Cells\n"));
1393       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\c in {\\cStart,...,\\cEnd}\n"));
1394       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1395       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\c_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\cShift+\\c-\\cStart,%d) {\\c};\n", rank, color, height++));
1396       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1397 
1398       if (depth > 2) {
1399         color = colors[1 % numColors];
1400         PetscCall(PetscViewerASCIIPrintf(viewer, "%% Faces\n"));
1401         PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\f in {\\fStart,...,\\fEnd}\n"));
1402         PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1403         PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\f_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\fShift+\\f-\\fStart,%d) {\\f};\n", rank, color, height++));
1404         PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1405       }
1406 
1407       color = colors[1 % numColors];
1408       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Edges\n"));
1409       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\e in {\\eStart,...,\\eEnd}\n"));
1410       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1411       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\e_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\eShift+\\e-\\eStart,%d) {\\e};\n", rank, color, height++));
1412       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1413 
1414       color = colors[0 % numColors];
1415       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Vertices\n"));
1416       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\v in {\\vStart,...,\\vEnd}\n"));
1417       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1418       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\v_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\vShift+\\v-\\vStart,%d) {\\v};\n", rank, color, height++));
1419       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1420 
1421       for (p = pStart; p < pEnd; ++p) {
1422         const PetscInt *cone;
1423         PetscInt        coneSize, cp;
1424 
1425         PetscCall(DMPlexGetCone(dm, p, &cone));
1426         PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
1427         for (cp = 0; cp < coneSize; ++cp) PetscCall(PetscViewerASCIIPrintf(viewer, "\\draw[->, shorten >=1pt] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", cone[cp], rank, p, rank));
1428       }
1429     }
1430     PetscCall(PetscViewerFlush(viewer));
1431     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
1432     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{tikzpicture}\n"));
1433     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{document}\n"));
1434     for (l = 0; l < numLabels; ++l) PetscCall(PetscFree(names[l]));
1435     for (c = 0; c < numColors; ++c) PetscCall(PetscFree(colors[c]));
1436     for (c = 0; c < numLColors; ++c) PetscCall(PetscFree(lcolors[c]));
1437     PetscCall(PetscFree3(names, colors, lcolors));
1438     PetscCall(PetscBTDestroy(&wp));
1439   } else if (format == PETSC_VIEWER_LOAD_BALANCE) {
1440     Vec                    cown, acown;
1441     VecScatter             sct;
1442     ISLocalToGlobalMapping g2l;
1443     IS                     gid, acis;
1444     MPI_Comm               comm, ncomm = MPI_COMM_NULL;
1445     MPI_Group              ggroup, ngroup;
1446     PetscScalar           *array, nid;
1447     const PetscInt        *idxs;
1448     PetscInt              *idxs2, *start, *adjacency, *work;
1449     PetscInt64             lm[3], gm[3];
1450     PetscInt               i, c, cStart, cEnd, cum, numVertices, ect, ectn, cellHeight;
1451     PetscMPIInt            d1, d2, rank;
1452 
1453     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1454     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1455 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1456     PetscCallMPI(MPI_Comm_split_type(comm, MPI_COMM_TYPE_SHARED, rank, MPI_INFO_NULL, &ncomm));
1457 #endif
1458     if (ncomm != MPI_COMM_NULL) {
1459       PetscCallMPI(MPI_Comm_group(comm, &ggroup));
1460       PetscCallMPI(MPI_Comm_group(ncomm, &ngroup));
1461       d1 = 0;
1462       PetscCallMPI(MPI_Group_translate_ranks(ngroup, 1, &d1, ggroup, &d2));
1463       nid = d2;
1464       PetscCallMPI(MPI_Group_free(&ggroup));
1465       PetscCallMPI(MPI_Group_free(&ngroup));
1466       PetscCallMPI(MPI_Comm_free(&ncomm));
1467     } else nid = 0.0;
1468 
1469     /* Get connectivity */
1470     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1471     PetscCall(DMPlexCreatePartitionerGraph(dm, cellHeight, &numVertices, &start, &adjacency, &gid));
1472 
1473     /* filter overlapped local cells */
1474     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
1475     PetscCall(ISGetIndices(gid, &idxs));
1476     PetscCall(ISGetLocalSize(gid, &cum));
1477     PetscCall(PetscMalloc1(cum, &idxs2));
1478     for (c = cStart, cum = 0; c < cEnd; c++) {
1479       if (idxs[c - cStart] < 0) continue;
1480       idxs2[cum++] = idxs[c - cStart];
1481     }
1482     PetscCall(ISRestoreIndices(gid, &idxs));
1483     PetscCheck(numVertices == cum, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Unexpected %" PetscInt_FMT " != %" PetscInt_FMT, numVertices, cum);
1484     PetscCall(ISDestroy(&gid));
1485     PetscCall(ISCreateGeneral(comm, numVertices, idxs2, PETSC_OWN_POINTER, &gid));
1486 
1487     /* support for node-aware cell locality */
1488     PetscCall(ISCreateGeneral(comm, start[numVertices], adjacency, PETSC_USE_POINTER, &acis));
1489     PetscCall(VecCreateSeq(PETSC_COMM_SELF, start[numVertices], &acown));
1490     PetscCall(VecCreateMPI(comm, numVertices, PETSC_DECIDE, &cown));
1491     PetscCall(VecGetArray(cown, &array));
1492     for (c = 0; c < numVertices; c++) array[c] = nid;
1493     PetscCall(VecRestoreArray(cown, &array));
1494     PetscCall(VecScatterCreate(cown, acis, acown, NULL, &sct));
1495     PetscCall(VecScatterBegin(sct, cown, acown, INSERT_VALUES, SCATTER_FORWARD));
1496     PetscCall(VecScatterEnd(sct, cown, acown, INSERT_VALUES, SCATTER_FORWARD));
1497     PetscCall(ISDestroy(&acis));
1498     PetscCall(VecScatterDestroy(&sct));
1499     PetscCall(VecDestroy(&cown));
1500 
1501     /* compute edgeCut */
1502     for (c = 0, cum = 0; c < numVertices; c++) cum = PetscMax(cum, start[c + 1] - start[c]);
1503     PetscCall(PetscMalloc1(cum, &work));
1504     PetscCall(ISLocalToGlobalMappingCreateIS(gid, &g2l));
1505     PetscCall(ISLocalToGlobalMappingSetType(g2l, ISLOCALTOGLOBALMAPPINGHASH));
1506     PetscCall(ISDestroy(&gid));
1507     PetscCall(VecGetArray(acown, &array));
1508     for (c = 0, ect = 0, ectn = 0; c < numVertices; c++) {
1509       PetscInt totl;
1510 
1511       totl = start[c + 1] - start[c];
1512       PetscCall(ISGlobalToLocalMappingApply(g2l, IS_GTOLM_MASK, totl, adjacency + start[c], NULL, work));
1513       for (i = 0; i < totl; i++) {
1514         if (work[i] < 0) {
1515           ect += 1;
1516           ectn += (array[i + start[c]] != nid) ? 0 : 1;
1517         }
1518       }
1519     }
1520     PetscCall(PetscFree(work));
1521     PetscCall(VecRestoreArray(acown, &array));
1522     lm[0] = numVertices > 0 ? numVertices : PETSC_MAX_INT;
1523     lm[1] = -numVertices;
1524     PetscCall(MPIU_Allreduce(lm, gm, 2, MPIU_INT64, MPI_MIN, comm));
1525     PetscCall(PetscViewerASCIIPrintf(viewer, "  Cell balance: %.2f (max %" PetscInt_FMT ", min %" PetscInt_FMT, -((double)gm[1]) / ((double)gm[0]), -(PetscInt)gm[1], (PetscInt)gm[0]));
1526     lm[0] = ect;                     /* edgeCut */
1527     lm[1] = ectn;                    /* node-aware edgeCut */
1528     lm[2] = numVertices > 0 ? 0 : 1; /* empty processes */
1529     PetscCall(MPIU_Allreduce(lm, gm, 3, MPIU_INT64, MPI_SUM, comm));
1530     PetscCall(PetscViewerASCIIPrintf(viewer, ", empty %" PetscInt_FMT ")\n", (PetscInt)gm[2]));
1531 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1532     PetscCall(PetscViewerASCIIPrintf(viewer, "  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n", (PetscInt)(gm[0] / 2), gm[0] ? ((double)gm[1]) / ((double)gm[0]) : 1.));
1533 #else
1534     PetscCall(PetscViewerASCIIPrintf(viewer, "  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n", (PetscInt)(gm[0] / 2), 0.0));
1535 #endif
1536     PetscCall(ISLocalToGlobalMappingDestroy(&g2l));
1537     PetscCall(PetscFree(start));
1538     PetscCall(PetscFree(adjacency));
1539     PetscCall(VecDestroy(&acown));
1540   } else {
1541     const char    *name;
1542     PetscInt      *sizes, *hybsizes, *ghostsizes;
1543     PetscInt       locDepth, depth, cellHeight, dim, d;
1544     PetscInt       pStart, pEnd, p, gcStart, gcEnd, gcNum;
1545     PetscInt       numLabels, l, maxSize = 17;
1546     DMPolytopeType ct0 = DM_POLYTOPE_UNKNOWN;
1547     MPI_Comm       comm;
1548     PetscMPIInt    size, rank;
1549 
1550     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1551     PetscCallMPI(MPI_Comm_size(comm, &size));
1552     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1553     PetscCall(DMGetDimension(dm, &dim));
1554     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1555     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1556     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
1557     else PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
1558     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
1559     PetscCall(DMPlexGetDepth(dm, &locDepth));
1560     PetscCall(MPIU_Allreduce(&locDepth, &depth, 1, MPIU_INT, MPI_MAX, comm));
1561     PetscCall(DMPlexGetCellTypeStratum(dm, DM_POLYTOPE_FV_GHOST, &gcStart, &gcEnd));
1562     gcNum = gcEnd - gcStart;
1563     if (size < maxSize) PetscCall(PetscCalloc3(size, &sizes, size, &hybsizes, size, &ghostsizes));
1564     else PetscCall(PetscCalloc3(3, &sizes, 3, &hybsizes, 3, &ghostsizes));
1565     for (d = 0; d <= depth; d++) {
1566       PetscInt Nc[2] = {0, 0}, ict;
1567 
1568       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
1569       if (pStart < pEnd) PetscCall(DMPlexGetCellType(dm, pStart, &ct0));
1570       ict = ct0;
1571       PetscCallMPI(MPI_Bcast(&ict, 1, MPIU_INT, 0, comm));
1572       ct0 = (DMPolytopeType)ict;
1573       for (p = pStart; p < pEnd; ++p) {
1574         DMPolytopeType ct;
1575 
1576         PetscCall(DMPlexGetCellType(dm, p, &ct));
1577         if (ct == ct0) ++Nc[0];
1578         else ++Nc[1];
1579       }
1580       if (size < maxSize) {
1581         PetscCallMPI(MPI_Gather(&Nc[0], 1, MPIU_INT, sizes, 1, MPIU_INT, 0, comm));
1582         PetscCallMPI(MPI_Gather(&Nc[1], 1, MPIU_INT, hybsizes, 1, MPIU_INT, 0, comm));
1583         if (d == depth) PetscCallMPI(MPI_Gather(&gcNum, 1, MPIU_INT, ghostsizes, 1, MPIU_INT, 0, comm));
1584         PetscCall(PetscViewerASCIIPrintf(viewer, "  Number of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1585         for (p = 0; p < size; ++p) {
1586           if (rank == 0) {
1587             PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT, sizes[p] + hybsizes[p]));
1588             if (hybsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT ")", hybsizes[p]));
1589             if (ghostsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "]", ghostsizes[p]));
1590           }
1591         }
1592       } else {
1593         PetscInt locMinMax[2];
1594 
1595         locMinMax[0] = Nc[0] + Nc[1];
1596         locMinMax[1] = Nc[0] + Nc[1];
1597         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, sizes));
1598         locMinMax[0] = Nc[1];
1599         locMinMax[1] = Nc[1];
1600         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, hybsizes));
1601         if (d == depth) {
1602           locMinMax[0] = gcNum;
1603           locMinMax[1] = gcNum;
1604           PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, ghostsizes));
1605         }
1606         PetscCall(PetscViewerASCIIPrintf(viewer, "  Min/Max of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1607         PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT "/%" PetscInt_FMT, sizes[0], sizes[1]));
1608         if (hybsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT "/%" PetscInt_FMT ")", hybsizes[0], hybsizes[1]));
1609         if (ghostsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "/%" PetscInt_FMT "]", ghostsizes[0], ghostsizes[1]));
1610       }
1611       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
1612     }
1613     PetscCall(PetscFree3(sizes, hybsizes, ghostsizes));
1614     {
1615       const PetscReal *maxCell;
1616       const PetscReal *L;
1617       PetscBool        localized;
1618 
1619       PetscCall(DMGetPeriodicity(dm, &maxCell, NULL, &L));
1620       PetscCall(DMGetCoordinatesLocalized(dm, &localized));
1621       if (L || localized) {
1622         PetscCall(PetscViewerASCIIPrintf(viewer, "Periodic mesh"));
1623         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1624         if (L) {
1625           PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
1626           for (d = 0; d < dim; ++d) {
1627             if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1628             PetscCall(PetscViewerASCIIPrintf(viewer, "%s", L[d] > 0.0 ? "PERIODIC" : "NONE"));
1629           }
1630           PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
1631         }
1632         PetscCall(PetscViewerASCIIPrintf(viewer, " coordinates %s\n", localized ? "localized" : "not localized"));
1633         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1634       }
1635     }
1636     PetscCall(DMGetNumLabels(dm, &numLabels));
1637     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
1638     for (l = 0; l < numLabels; ++l) {
1639       DMLabel         label;
1640       const char     *name;
1641       IS              valueIS;
1642       const PetscInt *values;
1643       PetscInt        numValues, v;
1644 
1645       PetscCall(DMGetLabelName(dm, l, &name));
1646       PetscCall(DMGetLabel(dm, name, &label));
1647       PetscCall(DMLabelGetNumValues(label, &numValues));
1648       PetscCall(PetscViewerASCIIPrintf(viewer, "  %s: %" PetscInt_FMT " strata with value/size (", name, numValues));
1649       PetscCall(DMLabelGetValueIS(label, &valueIS));
1650       PetscCall(ISGetIndices(valueIS, &values));
1651       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1652       for (v = 0; v < numValues; ++v) {
1653         PetscInt size;
1654 
1655         PetscCall(DMLabelGetStratumSize(label, values[v], &size));
1656         if (v > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1657         PetscCall(PetscViewerASCIIPrintf(viewer, "%" PetscInt_FMT " (%" PetscInt_FMT ")", values[v], size));
1658       }
1659       PetscCall(PetscViewerASCIIPrintf(viewer, ")\n"));
1660       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1661       PetscCall(ISRestoreIndices(valueIS, &values));
1662       PetscCall(ISDestroy(&valueIS));
1663     }
1664     {
1665       char    **labelNames;
1666       PetscInt  Nl = numLabels;
1667       PetscBool flg;
1668 
1669       PetscCall(PetscMalloc1(Nl, &labelNames));
1670       PetscCall(PetscOptionsGetStringArray(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_plex_view_labels", labelNames, &Nl, &flg));
1671       for (l = 0; l < Nl; ++l) {
1672         DMLabel label;
1673 
1674         PetscCall(DMHasLabel(dm, labelNames[l], &flg));
1675         if (flg) {
1676           PetscCall(DMGetLabel(dm, labelNames[l], &label));
1677           PetscCall(DMLabelView(label, viewer));
1678         }
1679         PetscCall(PetscFree(labelNames[l]));
1680       }
1681       PetscCall(PetscFree(labelNames));
1682     }
1683     /* If no fields are specified, people do not want to see adjacency */
1684     if (dm->Nf) {
1685       PetscInt f;
1686 
1687       for (f = 0; f < dm->Nf; ++f) {
1688         const char *name;
1689 
1690         PetscCall(PetscObjectGetName(dm->fields[f].disc, &name));
1691         if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Field %s:\n", name));
1692         PetscCall(PetscViewerASCIIPushTab(viewer));
1693         if (dm->fields[f].label) PetscCall(DMLabelView(dm->fields[f].label, viewer));
1694         if (dm->fields[f].adjacency[0]) {
1695           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM++\n"));
1696           else PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM\n"));
1697         } else {
1698           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FEM\n"));
1699           else PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FUNKY\n"));
1700         }
1701         PetscCall(PetscViewerASCIIPopTab(viewer));
1702       }
1703     }
1704     PetscCall(DMGetCoarseDM(dm, &cdm));
1705     if (cdm) {
1706       PetscCall(PetscViewerASCIIPushTab(viewer));
1707       PetscCall(PetscViewerASCIIPrintf(viewer, "Defined by transform from:\n"));
1708       PetscCall(DMPlexView_Ascii(cdm, viewer));
1709       PetscCall(PetscViewerASCIIPopTab(viewer));
1710     }
1711   }
1712   PetscFunctionReturn(PETSC_SUCCESS);
1713 }
1714 
1715 static PetscErrorCode DMPlexDrawCell(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[])
1716 {
1717   DMPolytopeType ct;
1718   PetscMPIInt    rank;
1719   PetscInt       cdim;
1720 
1721   PetscFunctionBegin;
1722   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1723   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1724   PetscCall(DMGetCoordinateDim(dm, &cdim));
1725   switch (ct) {
1726   case DM_POLYTOPE_SEGMENT:
1727   case DM_POLYTOPE_POINT_PRISM_TENSOR:
1728     switch (cdim) {
1729     case 1: {
1730       const PetscReal y  = 0.5;  /* TODO Put it in the middle of the viewport */
1731       const PetscReal dy = 0.05; /* TODO Make it a fraction of the total length */
1732 
1733       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y, PetscRealPart(coords[1]), y, PETSC_DRAW_BLACK));
1734       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y + dy, PetscRealPart(coords[0]), y - dy, PETSC_DRAW_BLACK));
1735       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[1]), y + dy, PetscRealPart(coords[1]), y - dy, PETSC_DRAW_BLACK));
1736     } break;
1737     case 2: {
1738       const PetscReal dx = (PetscRealPart(coords[3]) - PetscRealPart(coords[1]));
1739       const PetscReal dy = (PetscRealPart(coords[2]) - PetscRealPart(coords[0]));
1740       const PetscReal l  = 0.1 / PetscSqrtReal(dx * dx + dy * dy);
1741 
1742       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1743       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]) + l * dx, PetscRealPart(coords[1]) + l * dy, PetscRealPart(coords[0]) - l * dx, PetscRealPart(coords[1]) - l * dy, PETSC_DRAW_BLACK));
1744       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));
1745     } break;
1746     default:
1747       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of dimension %" PetscInt_FMT, cdim);
1748     }
1749     break;
1750   case DM_POLYTOPE_TRIANGLE:
1751     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));
1752     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1753     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1754     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1755     break;
1756   case DM_POLYTOPE_QUADRILATERAL:
1757     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));
1758     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));
1759     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1760     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1761     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_BLACK));
1762     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1763     break;
1764   case DM_POLYTOPE_SEG_PRISM_TENSOR:
1765     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));
1766     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));
1767     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1768     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_BLACK));
1769     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1770     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1771     break;
1772   case DM_POLYTOPE_FV_GHOST:
1773     break;
1774   default:
1775     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1776   }
1777   PetscFunctionReturn(PETSC_SUCCESS);
1778 }
1779 
1780 static PetscErrorCode DrawPolygon_Private(DM dm, PetscDraw draw, PetscInt cell, PetscInt Nv, const PetscReal refVertices[], const PetscScalar coords[], PetscInt edgeDiv, PetscReal refCoords[], PetscReal edgeCoords[])
1781 {
1782   PetscReal   centroid[2] = {0., 0.};
1783   PetscMPIInt rank;
1784   PetscInt    fillColor;
1785 
1786   PetscFunctionBegin;
1787   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1788   fillColor = PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2;
1789   for (PetscInt v = 0; v < Nv; ++v) {
1790     centroid[0] += PetscRealPart(coords[v * 2 + 0]) / Nv;
1791     centroid[1] += PetscRealPart(coords[v * 2 + 1]) / Nv;
1792   }
1793   for (PetscInt e = 0; e < Nv; ++e) {
1794     refCoords[0] = refVertices[e * 2 + 0];
1795     refCoords[1] = refVertices[e * 2 + 1];
1796     for (PetscInt d = 1; d <= edgeDiv; ++d) {
1797       refCoords[d * 2 + 0] = refCoords[0] + (refVertices[(e + 1) % Nv * 2 + 0] - refCoords[0]) * d / edgeDiv;
1798       refCoords[d * 2 + 1] = refCoords[1] + (refVertices[(e + 1) % Nv * 2 + 1] - refCoords[1]) * d / edgeDiv;
1799     }
1800     PetscCall(DMPlexReferenceToCoordinates(dm, cell, edgeDiv + 1, refCoords, edgeCoords));
1801     for (PetscInt d = 0; d < edgeDiv; ++d) {
1802       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));
1803       PetscCall(PetscDrawLine(draw, edgeCoords[d * 2 + 0], edgeCoords[d * 2 + 1], edgeCoords[(d + 1) * 2 + 0], edgeCoords[(d + 1) * 2 + 1], PETSC_DRAW_BLACK));
1804     }
1805   }
1806   PetscFunctionReturn(PETSC_SUCCESS);
1807 }
1808 
1809 static PetscErrorCode DMPlexDrawCellHighOrder(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[], PetscInt edgeDiv, PetscReal refCoords[], PetscReal edgeCoords[])
1810 {
1811   DMPolytopeType ct;
1812 
1813   PetscFunctionBegin;
1814   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1815   switch (ct) {
1816   case DM_POLYTOPE_TRIANGLE: {
1817     PetscReal refVertices[6] = {-1., -1., 1., -1., -1., 1.};
1818 
1819     PetscCall(DrawPolygon_Private(dm, draw, cell, 3, refVertices, coords, edgeDiv, refCoords, edgeCoords));
1820   } break;
1821   case DM_POLYTOPE_QUADRILATERAL: {
1822     PetscReal refVertices[8] = {-1., -1., 1., -1., 1., 1., -1., 1.};
1823 
1824     PetscCall(DrawPolygon_Private(dm, draw, cell, 4, refVertices, coords, edgeDiv, refCoords, edgeCoords));
1825   } break;
1826   default:
1827     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1828   }
1829   PetscFunctionReturn(PETSC_SUCCESS);
1830 }
1831 
1832 static PetscErrorCode DMPlexView_Draw(DM dm, PetscViewer viewer)
1833 {
1834   PetscDraw    draw;
1835   DM           cdm;
1836   PetscSection coordSection;
1837   Vec          coordinates;
1838   PetscReal    xyl[3], xyr[3];
1839   PetscReal   *refCoords, *edgeCoords;
1840   PetscBool    isnull, drawAffine;
1841   PetscInt     dim, vStart, vEnd, cStart, cEnd, c, cDegree, edgeDiv;
1842 
1843   PetscFunctionBegin;
1844   PetscCall(DMGetCoordinateDim(dm, &dim));
1845   PetscCheck(dim <= 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT, dim);
1846   PetscCall(DMGetCoordinateDegree_Internal(dm, &cDegree));
1847   drawAffine = cDegree > 1 ? PETSC_FALSE : PETSC_TRUE;
1848   edgeDiv    = cDegree + 1;
1849   PetscCall(PetscOptionsGetBool(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_view_draw_affine", &drawAffine, NULL));
1850   if (!drawAffine) PetscCall(PetscMalloc2((edgeDiv + 1) * dim, &refCoords, (edgeDiv + 1) * dim, &edgeCoords));
1851   PetscCall(DMGetCoordinateDM(dm, &cdm));
1852   PetscCall(DMGetLocalSection(cdm, &coordSection));
1853   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1854   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1855   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1856 
1857   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
1858   PetscCall(PetscDrawIsNull(draw, &isnull));
1859   if (isnull) PetscFunctionReturn(PETSC_SUCCESS);
1860   PetscCall(PetscDrawSetTitle(draw, "Mesh"));
1861 
1862   PetscCall(DMGetBoundingBox(dm, xyl, xyr));
1863   PetscCall(PetscDrawSetCoordinates(draw, xyl[0], xyl[1], xyr[0], xyr[1]));
1864   PetscCall(PetscDrawClear(draw));
1865 
1866   for (c = cStart; c < cEnd; ++c) {
1867     PetscScalar       *coords = NULL;
1868     const PetscScalar *coords_arr;
1869     PetscInt           numCoords;
1870     PetscBool          isDG;
1871 
1872     PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
1873     if (drawAffine) PetscCall(DMPlexDrawCell(dm, draw, c, coords));
1874     else PetscCall(DMPlexDrawCellHighOrder(dm, draw, c, coords, edgeDiv, refCoords, edgeCoords));
1875     PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
1876   }
1877   if (!drawAffine) PetscCall(PetscFree2(refCoords, edgeCoords));
1878   PetscCall(PetscDrawFlush(draw));
1879   PetscCall(PetscDrawPause(draw));
1880   PetscCall(PetscDrawSave(draw));
1881   PetscFunctionReturn(PETSC_SUCCESS);
1882 }
1883 
1884 static PetscErrorCode DMPlexCreateHighOrderSurrogate_Internal(DM dm, DM *hdm)
1885 {
1886   DM           odm = dm, rdm = dm, cdm;
1887   PetscFE      fe;
1888   PetscSpace   sp;
1889   PetscClassId id;
1890   PetscInt     degree;
1891   PetscBool    hoView = PETSC_TRUE;
1892 
1893   PetscFunctionBegin;
1894   PetscObjectOptionsBegin((PetscObject)dm);
1895   PetscCall(PetscOptionsBool("-dm_plex_high_order_view", "Subsample to view meshes with high order coordinates", "DMPlexCreateHighOrderSurrogate_Internal", hoView, &hoView, NULL));
1896   PetscOptionsEnd();
1897   PetscCall(PetscObjectReference((PetscObject)dm));
1898   *hdm = dm;
1899   if (!hoView) PetscFunctionReturn(PETSC_SUCCESS);
1900   PetscCall(DMGetCoordinateDM(dm, &cdm));
1901   PetscCall(DMGetField(cdm, 0, NULL, (PetscObject *)&fe));
1902   PetscCall(PetscObjectGetClassId((PetscObject)fe, &id));
1903   if (id != PETSCFE_CLASSID) PetscFunctionReturn(PETSC_SUCCESS);
1904   PetscCall(PetscFEGetBasisSpace(fe, &sp));
1905   PetscCall(PetscSpaceGetDegree(sp, &degree, NULL));
1906   for (PetscInt r = 0, rd = PetscCeilReal(((PetscReal)degree) / 2.); r < (PetscInt)PetscCeilReal(PetscLog2Real(degree)); ++r, rd = PetscCeilReal(((PetscReal)rd) / 2.)) {
1907     DM  cdm, rcdm;
1908     Mat In;
1909     Vec cl, rcl;
1910 
1911     PetscCall(DMRefine(odm, PetscObjectComm((PetscObject)odm), &rdm));
1912     PetscCall(DMPlexCreateCoordinateSpace(rdm, rd, PETSC_FALSE, NULL));
1913     PetscCall(PetscObjectSetName((PetscObject)rdm, "Refined Mesh with Linear Coordinates"));
1914     PetscCall(DMGetCoordinateDM(odm, &cdm));
1915     PetscCall(DMGetCoordinateDM(rdm, &rcdm));
1916     PetscCall(DMGetCoordinatesLocal(odm, &cl));
1917     PetscCall(DMGetCoordinatesLocal(rdm, &rcl));
1918     PetscCall(DMSetCoarseDM(rcdm, cdm));
1919     PetscCall(DMCreateInterpolation(cdm, rcdm, &In, NULL));
1920     PetscCall(MatMult(In, cl, rcl));
1921     PetscCall(MatDestroy(&In));
1922     PetscCall(DMSetCoordinatesLocal(rdm, rcl));
1923     PetscCall(DMDestroy(&odm));
1924     odm = rdm;
1925   }
1926   *hdm = rdm;
1927   PetscFunctionReturn(PETSC_SUCCESS);
1928 }
1929 
1930 #if defined(PETSC_HAVE_EXODUSII)
1931   #include <exodusII.h>
1932   #include <petscviewerexodusii.h>
1933 #endif
1934 
1935 PetscErrorCode DMView_Plex(DM dm, PetscViewer viewer)
1936 {
1937   PetscBool iascii, ishdf5, isvtk, isdraw, flg, isglvis, isexodus, iscgns;
1938   char      name[PETSC_MAX_PATH_LEN];
1939 
1940   PetscFunctionBegin;
1941   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1942   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1943   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &iascii));
1944   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
1945   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1946   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
1947   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
1948   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodus));
1949   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
1950   if (iascii) {
1951     PetscViewerFormat format;
1952     PetscCall(PetscViewerGetFormat(viewer, &format));
1953     if (format == PETSC_VIEWER_ASCII_GLVIS) PetscCall(DMPlexView_GLVis(dm, viewer));
1954     else PetscCall(DMPlexView_Ascii(dm, viewer));
1955   } else if (ishdf5) {
1956 #if defined(PETSC_HAVE_HDF5)
1957     PetscCall(DMPlexView_HDF5_Internal(dm, viewer));
1958 #else
1959     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1960 #endif
1961   } else if (isvtk) {
1962     PetscCall(DMPlexVTKWriteAll((PetscObject)dm, viewer));
1963   } else if (isdraw) {
1964     DM hdm;
1965 
1966     PetscCall(DMPlexCreateHighOrderSurrogate_Internal(dm, &hdm));
1967     PetscCall(DMPlexView_Draw(hdm, viewer));
1968     PetscCall(DMDestroy(&hdm));
1969   } else if (isglvis) {
1970     PetscCall(DMPlexView_GLVis(dm, viewer));
1971 #if defined(PETSC_HAVE_EXODUSII)
1972   } else if (isexodus) {
1973     /*
1974       exodusII requires that all sets be part of exactly one cell set.
1975       If the dm does not have a "Cell Sets" label defined, we create one
1976       with ID 1, containing all cells.
1977       Note that if the Cell Sets label is defined but does not cover all cells,
1978       we may still have a problem. This should probably be checked here or in the viewer;
1979     */
1980     PetscInt numCS;
1981     PetscCall(DMGetLabelSize(dm, "Cell Sets", &numCS));
1982     if (!numCS) {
1983       PetscInt cStart, cEnd, c;
1984       PetscCall(DMCreateLabel(dm, "Cell Sets"));
1985       PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1986       for (c = cStart; c < cEnd; ++c) PetscCall(DMSetLabelValue(dm, "Cell Sets", c, 1));
1987     }
1988     PetscCall(DMView_PlexExodusII(dm, viewer));
1989 #endif
1990 #if defined(PETSC_HAVE_CGNS)
1991   } else if (iscgns) {
1992     PetscCall(DMView_PlexCGNS(dm, viewer));
1993 #endif
1994   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex writing", ((PetscObject)viewer)->type_name);
1995   /* Optionally view the partition */
1996   PetscCall(PetscOptionsHasName(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_partition_view", &flg));
1997   if (flg) {
1998     Vec ranks;
1999     PetscCall(DMPlexCreateRankField(dm, &ranks));
2000     PetscCall(VecView(ranks, viewer));
2001     PetscCall(VecDestroy(&ranks));
2002   }
2003   /* Optionally view a label */
2004   PetscCall(PetscOptionsGetString(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_label_view", name, sizeof(name), &flg));
2005   if (flg) {
2006     DMLabel label;
2007     Vec     val;
2008 
2009     PetscCall(DMGetLabel(dm, name, &label));
2010     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Label %s provided to -dm_label_view does not exist in this DM", name);
2011     PetscCall(DMPlexCreateLabelField(dm, label, &val));
2012     PetscCall(VecView(val, viewer));
2013     PetscCall(VecDestroy(&val));
2014   }
2015   PetscFunctionReturn(PETSC_SUCCESS);
2016 }
2017 
2018 /*@
2019   DMPlexTopologyView - Saves a `DMPLEX` topology into a file
2020 
2021   Collective
2022 
2023   Input Parameters:
2024 + dm     - The `DM` whose topology is to be saved
2025 - viewer - The `PetscViewer` to save it in
2026 
2027   Level: advanced
2028 
2029 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexTopologyLoad()`, `PetscViewer`
2030 @*/
2031 PetscErrorCode DMPlexTopologyView(DM dm, PetscViewer viewer)
2032 {
2033   PetscBool ishdf5;
2034 
2035   PetscFunctionBegin;
2036   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2037   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2038   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2039   PetscCall(PetscLogEventBegin(DMPLEX_TopologyView, viewer, 0, 0, 0));
2040   if (ishdf5) {
2041 #if defined(PETSC_HAVE_HDF5)
2042     PetscViewerFormat format;
2043     PetscCall(PetscViewerGetFormat(viewer, &format));
2044     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2045       IS globalPointNumbering;
2046 
2047       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
2048       PetscCall(DMPlexTopologyView_HDF5_Internal(dm, globalPointNumbering, viewer));
2049       PetscCall(ISDestroy(&globalPointNumbering));
2050     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
2051 #else
2052     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2053 #endif
2054   }
2055   PetscCall(PetscLogEventEnd(DMPLEX_TopologyView, viewer, 0, 0, 0));
2056   PetscFunctionReturn(PETSC_SUCCESS);
2057 }
2058 
2059 /*@
2060   DMPlexCoordinatesView - Saves `DMPLEX` coordinates into a file
2061 
2062   Collective
2063 
2064   Input Parameters:
2065 + dm     - The `DM` whose coordinates are to be saved
2066 - viewer - The `PetscViewer` for saving
2067 
2068   Level: advanced
2069 
2070 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexLabelsView()`, `DMPlexCoordinatesLoad()`, `PetscViewer`
2071 @*/
2072 PetscErrorCode DMPlexCoordinatesView(DM dm, PetscViewer viewer)
2073 {
2074   PetscBool ishdf5;
2075 
2076   PetscFunctionBegin;
2077   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2078   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2079   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2080   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
2081   if (ishdf5) {
2082 #if defined(PETSC_HAVE_HDF5)
2083     PetscViewerFormat format;
2084     PetscCall(PetscViewerGetFormat(viewer, &format));
2085     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2086       PetscCall(DMPlexCoordinatesView_HDF5_Internal(dm, viewer));
2087     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
2088 #else
2089     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2090 #endif
2091   }
2092   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
2093   PetscFunctionReturn(PETSC_SUCCESS);
2094 }
2095 
2096 /*@
2097   DMPlexLabelsView - Saves `DMPLEX` labels into a file
2098 
2099   Collective
2100 
2101   Input Parameters:
2102 + dm     - The `DM` whose labels are to be saved
2103 - viewer - The `PetscViewer` for saving
2104 
2105   Level: advanced
2106 
2107 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsLoad()`, `PetscViewer`
2108 @*/
2109 PetscErrorCode DMPlexLabelsView(DM dm, PetscViewer viewer)
2110 {
2111   PetscBool ishdf5;
2112 
2113   PetscFunctionBegin;
2114   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2115   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2116   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2117   PetscCall(PetscLogEventBegin(DMPLEX_LabelsView, viewer, 0, 0, 0));
2118   if (ishdf5) {
2119 #if defined(PETSC_HAVE_HDF5)
2120     IS                globalPointNumbering;
2121     PetscViewerFormat format;
2122 
2123     PetscCall(PetscViewerGetFormat(viewer, &format));
2124     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2125       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
2126       PetscCall(DMPlexLabelsView_HDF5_Internal(dm, globalPointNumbering, viewer));
2127       PetscCall(ISDestroy(&globalPointNumbering));
2128     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2129 #else
2130     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2131 #endif
2132   }
2133   PetscCall(PetscLogEventEnd(DMPLEX_LabelsView, viewer, 0, 0, 0));
2134   PetscFunctionReturn(PETSC_SUCCESS);
2135 }
2136 
2137 /*@
2138   DMPlexSectionView - Saves a section associated with a `DMPLEX`
2139 
2140   Collective
2141 
2142   Input Parameters:
2143 + dm        - The `DM` that contains the topology on which the section to be saved is defined
2144 . viewer    - The `PetscViewer` for saving
2145 - sectiondm - The `DM` that contains the section to be saved, can be `NULL`
2146 
2147   Level: advanced
2148 
2149   Notes:
2150   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.
2151 
2152   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.
2153 
2154 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`, `PetscSectionView()`, `DMPlexSectionLoad()`, `PetscViewer`
2155 @*/
2156 PetscErrorCode DMPlexSectionView(DM dm, PetscViewer viewer, DM sectiondm)
2157 {
2158   PetscBool ishdf5;
2159 
2160   PetscFunctionBegin;
2161   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2162   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2163   if (!sectiondm) sectiondm = dm;
2164   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2165   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2166   PetscCall(PetscLogEventBegin(DMPLEX_SectionView, viewer, 0, 0, 0));
2167   if (ishdf5) {
2168 #if defined(PETSC_HAVE_HDF5)
2169     PetscCall(DMPlexSectionView_HDF5_Internal(dm, viewer, sectiondm));
2170 #else
2171     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2172 #endif
2173   }
2174   PetscCall(PetscLogEventEnd(DMPLEX_SectionView, viewer, 0, 0, 0));
2175   PetscFunctionReturn(PETSC_SUCCESS);
2176 }
2177 
2178 /*@
2179   DMPlexGlobalVectorView - Saves a global vector
2180 
2181   Collective
2182 
2183   Input Parameters:
2184 + dm        - The `DM` that represents the topology
2185 . viewer    - The `PetscViewer` to save data with
2186 . sectiondm - The `DM` that contains the global section on which vec is defined, can be `NULL`
2187 - vec       - The global vector to be saved
2188 
2189   Level: advanced
2190 
2191   Notes:
2192   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.
2193 
2194   Calling sequence:
2195 .vb
2196        DMCreate(PETSC_COMM_WORLD, &dm);
2197        DMSetType(dm, DMPLEX);
2198        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2199        DMClone(dm, &sectiondm);
2200        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2201        PetscSectionCreate(PETSC_COMM_WORLD, &section);
2202        DMPlexGetChart(sectiondm, &pStart, &pEnd);
2203        PetscSectionSetChart(section, pStart, pEnd);
2204        PetscSectionSetUp(section);
2205        DMSetLocalSection(sectiondm, section);
2206        PetscSectionDestroy(&section);
2207        DMGetGlobalVector(sectiondm, &vec);
2208        PetscObjectSetName((PetscObject)vec, "vec_name");
2209        DMPlexTopologyView(dm, viewer);
2210        DMPlexSectionView(dm, viewer, sectiondm);
2211        DMPlexGlobalVectorView(dm, viewer, sectiondm, vec);
2212        DMRestoreGlobalVector(sectiondm, &vec);
2213        DMDestroy(&sectiondm);
2214        DMDestroy(&dm);
2215 .ve
2216 
2217 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexLocalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2218 @*/
2219 PetscErrorCode DMPlexGlobalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2220 {
2221   PetscBool ishdf5;
2222 
2223   PetscFunctionBegin;
2224   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2225   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2226   if (!sectiondm) sectiondm = dm;
2227   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2228   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2229   /* Check consistency */
2230   {
2231     PetscSection section;
2232     PetscBool    includesConstraints;
2233     PetscInt     m, m1;
2234 
2235     PetscCall(VecGetLocalSize(vec, &m1));
2236     PetscCall(DMGetGlobalSection(sectiondm, &section));
2237     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2238     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2239     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2240     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2241   }
2242   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2243   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorView, viewer, 0, 0, 0));
2244   if (ishdf5) {
2245 #if defined(PETSC_HAVE_HDF5)
2246     PetscCall(DMPlexGlobalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2247 #else
2248     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2249 #endif
2250   }
2251   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorView, viewer, 0, 0, 0));
2252   PetscFunctionReturn(PETSC_SUCCESS);
2253 }
2254 
2255 /*@
2256   DMPlexLocalVectorView - Saves a local vector
2257 
2258   Collective
2259 
2260   Input Parameters:
2261 + dm        - The `DM` that represents the topology
2262 . viewer    - The `PetscViewer` to save data with
2263 . sectiondm - The `DM` that contains the local section on which `vec` is defined, can be `NULL`
2264 - vec       - The local vector to be saved
2265 
2266   Level: advanced
2267 
2268   Note:
2269   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.
2270 
2271   Calling sequence:
2272 .vb
2273        DMCreate(PETSC_COMM_WORLD, &dm);
2274        DMSetType(dm, DMPLEX);
2275        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2276        DMClone(dm, &sectiondm);
2277        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2278        PetscSectionCreate(PETSC_COMM_WORLD, &section);
2279        DMPlexGetChart(sectiondm, &pStart, &pEnd);
2280        PetscSectionSetChart(section, pStart, pEnd);
2281        PetscSectionSetUp(section);
2282        DMSetLocalSection(sectiondm, section);
2283        DMGetLocalVector(sectiondm, &vec);
2284        PetscObjectSetName((PetscObject)vec, "vec_name");
2285        DMPlexTopologyView(dm, viewer);
2286        DMPlexSectionView(dm, viewer, sectiondm);
2287        DMPlexLocalVectorView(dm, viewer, sectiondm, vec);
2288        DMRestoreLocalVector(sectiondm, &vec);
2289        DMDestroy(&sectiondm);
2290        DMDestroy(&dm);
2291 .ve
2292 
2293 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexGlobalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2294 @*/
2295 PetscErrorCode DMPlexLocalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2296 {
2297   PetscBool ishdf5;
2298 
2299   PetscFunctionBegin;
2300   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2301   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2302   if (!sectiondm) sectiondm = dm;
2303   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2304   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2305   /* Check consistency */
2306   {
2307     PetscSection section;
2308     PetscBool    includesConstraints;
2309     PetscInt     m, m1;
2310 
2311     PetscCall(VecGetLocalSize(vec, &m1));
2312     PetscCall(DMGetLocalSection(sectiondm, &section));
2313     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2314     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2315     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2316     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2317   }
2318   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2319   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorView, viewer, 0, 0, 0));
2320   if (ishdf5) {
2321 #if defined(PETSC_HAVE_HDF5)
2322     PetscCall(DMPlexLocalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2323 #else
2324     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2325 #endif
2326   }
2327   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorView, viewer, 0, 0, 0));
2328   PetscFunctionReturn(PETSC_SUCCESS);
2329 }
2330 
2331 PetscErrorCode DMLoad_Plex(DM dm, PetscViewer viewer)
2332 {
2333   PetscBool ishdf5;
2334 
2335   PetscFunctionBegin;
2336   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2337   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2338   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2339   if (ishdf5) {
2340 #if defined(PETSC_HAVE_HDF5)
2341     PetscViewerFormat format;
2342     PetscCall(PetscViewerGetFormat(viewer, &format));
2343     if (format == PETSC_VIEWER_HDF5_XDMF || format == PETSC_VIEWER_HDF5_VIZ) {
2344       PetscCall(DMPlexLoad_HDF5_Xdmf_Internal(dm, viewer));
2345     } else if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2346       PetscCall(DMPlexLoad_HDF5_Internal(dm, viewer));
2347     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2348     PetscFunctionReturn(PETSC_SUCCESS);
2349 #else
2350     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2351 #endif
2352   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex loading", ((PetscObject)viewer)->type_name);
2353 }
2354 
2355 /*@
2356   DMPlexTopologyLoad - Loads a topology into a `DMPLEX`
2357 
2358   Collective
2359 
2360   Input Parameters:
2361 + dm     - The `DM` into which the topology is loaded
2362 - viewer - The `PetscViewer` for the saved topology
2363 
2364   Output Parameter:
2365 . 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;
2366   `NULL` if unneeded
2367 
2368   Level: advanced
2369 
2370 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2371           `PetscViewer`, `PetscSF`
2372 @*/
2373 PetscErrorCode DMPlexTopologyLoad(DM dm, PetscViewer viewer, PetscSF *globalToLocalPointSF)
2374 {
2375   PetscBool ishdf5;
2376 
2377   PetscFunctionBegin;
2378   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2379   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2380   if (globalToLocalPointSF) PetscAssertPointer(globalToLocalPointSF, 3);
2381   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2382   PetscCall(PetscLogEventBegin(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2383   if (ishdf5) {
2384 #if defined(PETSC_HAVE_HDF5)
2385     PetscViewerFormat format;
2386     PetscCall(PetscViewerGetFormat(viewer, &format));
2387     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2388       PetscCall(DMPlexTopologyLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2389     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2390 #else
2391     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2392 #endif
2393   }
2394   PetscCall(PetscLogEventEnd(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2395   PetscFunctionReturn(PETSC_SUCCESS);
2396 }
2397 
2398 /*@
2399   DMPlexCoordinatesLoad - Loads coordinates into a `DMPLEX`
2400 
2401   Collective
2402 
2403   Input Parameters:
2404 + dm                   - The `DM` into which the coordinates are loaded
2405 . viewer               - The `PetscViewer` for the saved coordinates
2406 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad()` when loading dm from viewer
2407 
2408   Level: advanced
2409 
2410 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2411           `PetscSF`, `PetscViewer`
2412 @*/
2413 PetscErrorCode DMPlexCoordinatesLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2414 {
2415   PetscBool ishdf5;
2416 
2417   PetscFunctionBegin;
2418   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2419   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2420   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2421   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2422   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesLoad, viewer, 0, 0, 0));
2423   if (ishdf5) {
2424 #if defined(PETSC_HAVE_HDF5)
2425     PetscViewerFormat format;
2426     PetscCall(PetscViewerGetFormat(viewer, &format));
2427     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2428       PetscCall(DMPlexCoordinatesLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2429     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2430 #else
2431     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2432 #endif
2433   }
2434   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesLoad, viewer, 0, 0, 0));
2435   PetscFunctionReturn(PETSC_SUCCESS);
2436 }
2437 
2438 /*@
2439   DMPlexLabelsLoad - Loads labels into a `DMPLEX`
2440 
2441   Collective
2442 
2443   Input Parameters:
2444 + dm                   - The `DM` into which the labels are loaded
2445 . viewer               - The `PetscViewer` for the saved labels
2446 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad()` when loading `dm` from viewer
2447 
2448   Level: advanced
2449 
2450   Note:
2451   The `PetscSF` argument must not be `NULL` if the `DM` is distributed, otherwise an error occurs.
2452 
2453 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2454           `PetscSF`, `PetscViewer`
2455 @*/
2456 PetscErrorCode DMPlexLabelsLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2457 {
2458   PetscBool ishdf5;
2459 
2460   PetscFunctionBegin;
2461   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2462   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2463   if (globalToLocalPointSF) PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2464   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2465   PetscCall(PetscLogEventBegin(DMPLEX_LabelsLoad, viewer, 0, 0, 0));
2466   if (ishdf5) {
2467 #if defined(PETSC_HAVE_HDF5)
2468     PetscViewerFormat format;
2469 
2470     PetscCall(PetscViewerGetFormat(viewer, &format));
2471     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2472       PetscCall(DMPlexLabelsLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2473     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2474 #else
2475     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2476 #endif
2477   }
2478   PetscCall(PetscLogEventEnd(DMPLEX_LabelsLoad, viewer, 0, 0, 0));
2479   PetscFunctionReturn(PETSC_SUCCESS);
2480 }
2481 
2482 /*@
2483   DMPlexSectionLoad - Loads section into a `DMPLEX`
2484 
2485   Collective
2486 
2487   Input Parameters:
2488 + dm                   - The `DM` that represents the topology
2489 . viewer               - The `PetscViewer` that represents the on-disk section (sectionA)
2490 . sectiondm            - The `DM` into which the on-disk section (sectionA) is migrated, can be `NULL`
2491 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad(`) when loading dm from viewer
2492 
2493   Output Parameters:
2494 + 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)
2495 - 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)
2496 
2497   Level: advanced
2498 
2499   Notes:
2500   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.
2501 
2502   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.
2503 
2504   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.
2505 
2506   Example using 2 processes:
2507 .vb
2508   NX (number of points on dm): 4
2509   sectionA                   : the on-disk section
2510   vecA                       : a vector associated with sectionA
2511   sectionB                   : sectiondm's local section constructed in this function
2512   vecB (local)               : a vector associated with sectiondm's local section
2513   vecB (global)              : a vector associated with sectiondm's global section
2514 
2515                                      rank 0    rank 1
2516   vecA (global)                  : [.0 .4 .1 | .2 .3]        <- to be loaded in DMPlexGlobalVectorLoad() or DMPlexLocalVectorLoad()
2517   sectionA->atlasOff             :       0 2 | 1             <- loaded in PetscSectionLoad()
2518   sectionA->atlasDof             :       1 3 | 1             <- loaded in PetscSectionLoad()
2519   sectionA's global point numbers:       0 2 | 3             <- loaded in DMPlexSectionLoad()
2520   [0, NX)                        :       0 1 | 2 3           <- conceptual partition used in globalToLocalPointSF
2521   sectionB's global point numbers:     0 1 3 | 3 2           <- associated with [0, NX) by globalToLocalPointSF
2522   sectionB->atlasDof             :     1 0 1 | 1 3
2523   sectionB->atlasOff (no perm)   :     0 1 1 | 0 1
2524   vecB (local)                   :   [.0 .4] | [.4 .1 .2 .3] <- to be constructed by calling DMPlexLocalVectorLoad() with localDofSF
2525   vecB (global)                  :    [.0 .4 | .1 .2 .3]     <- to be constructed by calling DMPlexGlobalVectorLoad() with globalDofSF
2526 .ve
2527   where "|" represents a partition of loaded data, and global point 3 is assumed to be owned by rank 0.
2528 
2529 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`, `PetscSectionLoad()`, `DMPlexSectionView()`, `PetscSF`, `PetscViewer`
2530 @*/
2531 PetscErrorCode DMPlexSectionLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF globalToLocalPointSF, PetscSF *globalDofSF, PetscSF *localDofSF)
2532 {
2533   PetscBool ishdf5;
2534 
2535   PetscFunctionBegin;
2536   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2537   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2538   if (!sectiondm) sectiondm = dm;
2539   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2540   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 4);
2541   if (globalDofSF) PetscAssertPointer(globalDofSF, 5);
2542   if (localDofSF) PetscAssertPointer(localDofSF, 6);
2543   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2544   PetscCall(PetscLogEventBegin(DMPLEX_SectionLoad, viewer, 0, 0, 0));
2545   if (ishdf5) {
2546 #if defined(PETSC_HAVE_HDF5)
2547     PetscCall(DMPlexSectionLoad_HDF5_Internal(dm, viewer, sectiondm, globalToLocalPointSF, globalDofSF, localDofSF));
2548 #else
2549     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2550 #endif
2551   }
2552   PetscCall(PetscLogEventEnd(DMPLEX_SectionLoad, viewer, 0, 0, 0));
2553   PetscFunctionReturn(PETSC_SUCCESS);
2554 }
2555 
2556 /*@
2557   DMPlexGlobalVectorLoad - Loads on-disk vector data into a global vector
2558 
2559   Collective
2560 
2561   Input Parameters:
2562 + dm        - The `DM` that represents the topology
2563 . viewer    - The `PetscViewer` that represents the on-disk vector data
2564 . sectiondm - The `DM` that contains the global section on which vec is defined, can be `NULL`
2565 . sf        - The `PetscSF` that migrates the on-disk vector data into vec
2566 - vec       - The global vector to set values of
2567 
2568   Level: advanced
2569 
2570   Notes:
2571   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.
2572 
2573   Calling sequence:
2574 .vb
2575        DMCreate(PETSC_COMM_WORLD, &dm);
2576        DMSetType(dm, DMPLEX);
2577        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2578        DMPlexTopologyLoad(dm, viewer, &sfX);
2579        DMClone(dm, &sectiondm);
2580        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2581        DMPlexSectionLoad(dm, viewer, sectiondm, sfX, &gsf, NULL);
2582        DMGetGlobalVector(sectiondm, &vec);
2583        PetscObjectSetName((PetscObject)vec, "vec_name");
2584        DMPlexGlobalVectorLoad(dm, viewer, sectiondm, gsf, vec);
2585        DMRestoreGlobalVector(sectiondm, &vec);
2586        PetscSFDestroy(&gsf);
2587        PetscSFDestroy(&sfX);
2588        DMDestroy(&sectiondm);
2589        DMDestroy(&dm);
2590 .ve
2591 
2592 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexLocalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`,
2593           `PetscSF`, `PetscViewer`
2594 @*/
2595 PetscErrorCode DMPlexGlobalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2596 {
2597   PetscBool ishdf5;
2598 
2599   PetscFunctionBegin;
2600   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2601   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2602   if (!sectiondm) sectiondm = dm;
2603   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2604   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2605   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2606   /* Check consistency */
2607   {
2608     PetscSection section;
2609     PetscBool    includesConstraints;
2610     PetscInt     m, m1;
2611 
2612     PetscCall(VecGetLocalSize(vec, &m1));
2613     PetscCall(DMGetGlobalSection(sectiondm, &section));
2614     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2615     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2616     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2617     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2618   }
2619   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2620   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorLoad, viewer, 0, 0, 0));
2621   if (ishdf5) {
2622 #if defined(PETSC_HAVE_HDF5)
2623     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2624 #else
2625     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2626 #endif
2627   }
2628   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorLoad, viewer, 0, 0, 0));
2629   PetscFunctionReturn(PETSC_SUCCESS);
2630 }
2631 
2632 /*@
2633   DMPlexLocalVectorLoad - Loads on-disk vector data into a local vector
2634 
2635   Collective
2636 
2637   Input Parameters:
2638 + dm        - The `DM` that represents the topology
2639 . viewer    - The `PetscViewer` that represents the on-disk vector data
2640 . sectiondm - The `DM` that contains the local section on which vec is defined, can be `NULL`
2641 . sf        - The `PetscSF` that migrates the on-disk vector data into vec
2642 - vec       - The local vector to set values of
2643 
2644   Level: advanced
2645 
2646   Notes:
2647   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.
2648 
2649   Calling sequence:
2650 .vb
2651        DMCreate(PETSC_COMM_WORLD, &dm);
2652        DMSetType(dm, DMPLEX);
2653        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2654        DMPlexTopologyLoad(dm, viewer, &sfX);
2655        DMClone(dm, &sectiondm);
2656        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2657        DMPlexSectionLoad(dm, viewer, sectiondm, sfX, NULL, &lsf);
2658        DMGetLocalVector(sectiondm, &vec);
2659        PetscObjectSetName((PetscObject)vec, "vec_name");
2660        DMPlexLocalVectorLoad(dm, viewer, sectiondm, lsf, vec);
2661        DMRestoreLocalVector(sectiondm, &vec);
2662        PetscSFDestroy(&lsf);
2663        PetscSFDestroy(&sfX);
2664        DMDestroy(&sectiondm);
2665        DMDestroy(&dm);
2666 .ve
2667 
2668 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`,
2669           `PetscSF`, `PetscViewer`
2670 @*/
2671 PetscErrorCode DMPlexLocalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2672 {
2673   PetscBool ishdf5;
2674 
2675   PetscFunctionBegin;
2676   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2677   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2678   if (!sectiondm) sectiondm = dm;
2679   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2680   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2681   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2682   /* Check consistency */
2683   {
2684     PetscSection section;
2685     PetscBool    includesConstraints;
2686     PetscInt     m, m1;
2687 
2688     PetscCall(VecGetLocalSize(vec, &m1));
2689     PetscCall(DMGetLocalSection(sectiondm, &section));
2690     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2691     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2692     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2693     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2694   }
2695   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2696   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorLoad, viewer, 0, 0, 0));
2697   if (ishdf5) {
2698 #if defined(PETSC_HAVE_HDF5)
2699     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2700 #else
2701     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2702 #endif
2703   }
2704   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorLoad, viewer, 0, 0, 0));
2705   PetscFunctionReturn(PETSC_SUCCESS);
2706 }
2707 
2708 PetscErrorCode DMDestroy_Plex(DM dm)
2709 {
2710   DM_Plex *mesh = (DM_Plex *)dm->data;
2711 
2712   PetscFunctionBegin;
2713   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMSetUpGLVisViewer_C", NULL));
2714   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertBoundaryValues_C", NULL));
2715   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMCreateNeumannOverlap_C", NULL));
2716   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMInterpolateSolution_C", NULL));
2717   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertTimeDerivativeBoundaryValues_C", NULL));
2718   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", NULL));
2719   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeGetDefault_C", NULL));
2720   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeSetDefault_C", NULL));
2721   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "MatComputeNeumannOverlap_C", NULL));
2722   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderGetDefault_C", NULL));
2723   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderSetDefault_C", NULL));
2724   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionGetDefault_C", NULL));
2725   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionSetDefault_C", NULL));
2726   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionGetType_C", NULL));
2727   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionSetType_C", NULL));
2728   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", NULL));
2729   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexSetOverlap_C", NULL));
2730   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetUseCeed_C", NULL));
2731   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexSetUseCeed_C", NULL));
2732   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMGetIsoperiodicPointSF_C", NULL));
2733   if (--mesh->refct > 0) PetscFunctionReturn(PETSC_SUCCESS);
2734   PetscCall(PetscSectionDestroy(&mesh->coneSection));
2735   PetscCall(PetscFree(mesh->cones));
2736   PetscCall(PetscFree(mesh->coneOrientations));
2737   PetscCall(PetscSectionDestroy(&mesh->supportSection));
2738   PetscCall(PetscSectionDestroy(&mesh->subdomainSection));
2739   PetscCall(PetscFree(mesh->supports));
2740   PetscCall(PetscFree(mesh->cellTypes));
2741   PetscCall(DMPlexTransformDestroy(&mesh->tr));
2742   PetscCall(PetscFree(mesh->tetgenOpts));
2743   PetscCall(PetscFree(mesh->triangleOpts));
2744   PetscCall(PetscFree(mesh->transformType));
2745   PetscCall(PetscFree(mesh->distributionName));
2746   PetscCall(PetscPartitionerDestroy(&mesh->partitioner));
2747   PetscCall(DMLabelDestroy(&mesh->subpointMap));
2748   PetscCall(ISDestroy(&mesh->subpointIS));
2749   PetscCall(ISDestroy(&mesh->globalVertexNumbers));
2750   PetscCall(ISDestroy(&mesh->globalCellNumbers));
2751   if (mesh->periodic.face_sfs) {
2752     for (PetscInt i = 0; i < mesh->periodic.num_face_sfs; i++) PetscCall(PetscSFDestroy(&mesh->periodic.face_sfs[i]));
2753     PetscCall(PetscFree(mesh->periodic.face_sfs));
2754   }
2755   PetscCall(PetscSFDestroy(&mesh->periodic.composed_sf));
2756   if (mesh->periodic.periodic_points) {
2757     for (PetscInt i = 0; i < mesh->periodic.num_face_sfs; i++) PetscCall(ISDestroy(&mesh->periodic.periodic_points[i]));
2758     PetscCall(PetscFree(mesh->periodic.periodic_points));
2759   }
2760   if (mesh->periodic.transform) PetscCall(PetscFree(mesh->periodic.transform));
2761   PetscCall(PetscSectionDestroy(&mesh->anchorSection));
2762   PetscCall(ISDestroy(&mesh->anchorIS));
2763   PetscCall(PetscSectionDestroy(&mesh->parentSection));
2764   PetscCall(PetscFree(mesh->parents));
2765   PetscCall(PetscFree(mesh->childIDs));
2766   PetscCall(PetscSectionDestroy(&mesh->childSection));
2767   PetscCall(PetscFree(mesh->children));
2768   PetscCall(DMDestroy(&mesh->referenceTree));
2769   PetscCall(PetscGridHashDestroy(&mesh->lbox));
2770   PetscCall(PetscFree(mesh->neighbors));
2771   if (mesh->metricCtx) PetscCall(PetscFree(mesh->metricCtx));
2772   /* This was originally freed in DMDestroy(), but that prevents reference counting of backend objects */
2773   PetscCall(PetscFree(mesh));
2774   PetscFunctionReturn(PETSC_SUCCESS);
2775 }
2776 
2777 PetscErrorCode DMCreateMatrix_Plex(DM dm, Mat *J)
2778 {
2779   PetscSection           sectionGlobal, sectionLocal;
2780   PetscInt               bs = -1, mbs;
2781   PetscInt               localSize, localStart = 0;
2782   PetscBool              isShell, isBlock, isSeqBlock, isMPIBlock, isSymBlock, isSymSeqBlock, isSymMPIBlock, isMatIS;
2783   MatType                mtype;
2784   ISLocalToGlobalMapping ltog;
2785 
2786   PetscFunctionBegin;
2787   PetscCall(MatInitializePackage());
2788   mtype = dm->mattype;
2789   PetscCall(DMGetLocalSection(dm, &sectionLocal));
2790   PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
2791   /* PetscCall(PetscSectionGetStorageSize(sectionGlobal, &localSize)); */
2792   PetscCall(PetscSectionGetConstrainedStorageSize(sectionGlobal, &localSize));
2793   PetscCallMPI(MPI_Exscan(&localSize, &localStart, 1, MPIU_INT, MPI_SUM, PetscObjectComm((PetscObject)dm)));
2794   PetscCall(MatCreate(PetscObjectComm((PetscObject)dm), J));
2795   PetscCall(MatSetSizes(*J, localSize, localSize, PETSC_DETERMINE, PETSC_DETERMINE));
2796   PetscCall(MatSetType(*J, mtype));
2797   PetscCall(MatSetFromOptions(*J));
2798   PetscCall(MatGetBlockSize(*J, &mbs));
2799   if (mbs > 1) bs = mbs;
2800   PetscCall(PetscStrcmp(mtype, MATSHELL, &isShell));
2801   PetscCall(PetscStrcmp(mtype, MATBAIJ, &isBlock));
2802   PetscCall(PetscStrcmp(mtype, MATSEQBAIJ, &isSeqBlock));
2803   PetscCall(PetscStrcmp(mtype, MATMPIBAIJ, &isMPIBlock));
2804   PetscCall(PetscStrcmp(mtype, MATSBAIJ, &isSymBlock));
2805   PetscCall(PetscStrcmp(mtype, MATSEQSBAIJ, &isSymSeqBlock));
2806   PetscCall(PetscStrcmp(mtype, MATMPISBAIJ, &isSymMPIBlock));
2807   PetscCall(PetscStrcmp(mtype, MATIS, &isMatIS));
2808   if (!isShell) {
2809     // There are three states with pblocks, since block starts can have no dofs:
2810     // UNKNOWN) New Block:   An open block has been signalled by pblocks[p] == 1
2811     // TRUE)    Block Start: The first entry in a block has been added
2812     // FALSE)   Block Add:   An additional block entry has been added, since pblocks[p] == 0
2813     PetscBT         blst;
2814     PetscBool3      bstate     = PETSC_BOOL3_UNKNOWN;
2815     PetscBool       fillMatrix = (PetscBool)(!dm->prealloc_only && !isMatIS);
2816     const PetscInt *perm       = NULL;
2817     PetscInt       *dnz, *onz, *dnzu, *onzu, bsLocal[2], bsMinMax[2], *pblocks;
2818     PetscInt        pStart, pEnd, dof, cdof, num_fields;
2819 
2820     PetscCall(DMGetLocalToGlobalMapping(dm, &ltog));
2821     PetscCall(PetscSectionGetBlockStarts(sectionLocal, &blst));
2822     if (sectionLocal->perm) PetscCall(ISGetIndices(sectionLocal->perm, &perm));
2823 
2824     PetscCall(PetscCalloc1(localSize, &pblocks));
2825     PetscCall(PetscSectionGetChart(sectionGlobal, &pStart, &pEnd));
2826     PetscCall(PetscSectionGetNumFields(sectionGlobal, &num_fields));
2827     // We need to process in the permuted order to get block sizes right
2828     for (PetscInt point = pStart; point < pEnd; ++point) {
2829       const PetscInt p = perm ? perm[point] : point;
2830 
2831       switch (dm->blocking_type) {
2832       case DM_BLOCKING_TOPOLOGICAL_POINT: { // One block per topological point
2833         PetscInt bdof, offset;
2834 
2835         PetscCall(PetscSectionGetDof(sectionGlobal, p, &dof));
2836         PetscCall(PetscSectionGetOffset(sectionGlobal, p, &offset));
2837         PetscCall(PetscSectionGetConstraintDof(sectionGlobal, p, &cdof));
2838         if (blst && PetscBTLookup(blst, p)) bstate = PETSC_BOOL3_UNKNOWN;
2839         if (dof > 0) {
2840           // State change
2841           if (bstate == PETSC_BOOL3_UNKNOWN) bstate = PETSC_BOOL3_TRUE;
2842           else if (bstate == PETSC_BOOL3_TRUE && blst && !PetscBTLookup(blst, p)) bstate = PETSC_BOOL3_FALSE;
2843 
2844           for (PetscInt i = 0; i < dof - cdof; ++i) pblocks[offset - localStart + i] = dof - cdof;
2845           // Signal block concatenation
2846           if (bstate == PETSC_BOOL3_FALSE && dof - cdof) pblocks[offset - localStart] = -(dof - cdof);
2847         }
2848         dof  = dof < 0 ? -(dof + 1) : dof;
2849         bdof = cdof && (dof - cdof) ? 1 : dof;
2850         if (dof) {
2851           if (bs < 0) {
2852             bs = bdof;
2853           } else if (bs != bdof) {
2854             bs = 1;
2855           }
2856         }
2857       } break;
2858       case DM_BLOCKING_FIELD_NODE: {
2859         for (PetscInt field = 0; field < num_fields; field++) {
2860           PetscInt num_comp, bdof, offset;
2861           PetscCall(PetscSectionGetFieldComponents(sectionGlobal, field, &num_comp));
2862           PetscCall(PetscSectionGetFieldDof(sectionGlobal, p, field, &dof));
2863           if (dof < 0) continue;
2864           PetscCall(PetscSectionGetFieldOffset(sectionGlobal, p, field, &offset));
2865           PetscCall(PetscSectionGetFieldConstraintDof(sectionGlobal, p, field, &cdof));
2866           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);
2867           PetscInt num_nodes = dof / num_comp;
2868           for (PetscInt i = 0; i < dof - cdof; i++) pblocks[offset - localStart + i] = (dof - cdof) / num_nodes;
2869           // Handle possibly constant block size (unlikely)
2870           bdof = cdof && (dof - cdof) ? 1 : dof;
2871           if (dof) {
2872             if (bs < 0) {
2873               bs = bdof;
2874             } else if (bs != bdof) {
2875               bs = 1;
2876             }
2877           }
2878         }
2879       } break;
2880       }
2881     }
2882     if (sectionLocal->perm) PetscCall(ISRestoreIndices(sectionLocal->perm, &perm));
2883     /* Must have same blocksize on all procs (some might have no points) */
2884     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs;
2885     bsLocal[1] = bs;
2886     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
2887     if (bsMinMax[0] != bsMinMax[1]) bs = 1;
2888     else bs = bsMinMax[0];
2889     bs = PetscMax(1, bs);
2890     PetscCall(MatSetLocalToGlobalMapping(*J, ltog, ltog));
2891     if (dm->prealloc_skip) { // User will likely use MatSetPreallocationCOO(), but still set structural parameters
2892       PetscCall(MatSetBlockSize(*J, bs));
2893       PetscCall(MatSetUp(*J));
2894     } else {
2895       PetscCall(PetscCalloc4(localSize / bs, &dnz, localSize / bs, &onz, localSize / bs, &dnzu, localSize / bs, &onzu));
2896       PetscCall(DMPlexPreallocateOperator(dm, bs, dnz, onz, dnzu, onzu, *J, fillMatrix));
2897       PetscCall(PetscFree4(dnz, onz, dnzu, onzu));
2898     }
2899     if (pblocks) { // Consolidate blocks
2900       PetscInt nblocks = 0;
2901       pblocks[0]       = PetscAbs(pblocks[0]);
2902       for (PetscInt i = 0; i < localSize; i += PetscMax(1, pblocks[i])) {
2903         if (pblocks[i] == 0) continue;
2904         // Negative block size indicates the blocks should be concatenated
2905         if (pblocks[i] < 0) {
2906           pblocks[i] = -pblocks[i];
2907           pblocks[nblocks - 1] += pblocks[i];
2908         } else {
2909           pblocks[nblocks++] = pblocks[i]; // nblocks always <= i
2910         }
2911         for (PetscInt j = 1; j < pblocks[i]; j++)
2912           PetscCheck(pblocks[i + j] == pblocks[i], PETSC_COMM_SELF, PETSC_ERR_PLIB, "Block of size %" PetscInt_FMT " at %" PetscInt_FMT " mismatches entry %" PetscInt_FMT " at %" PetscInt_FMT, pblocks[i], i, pblocks[i + j], i + j);
2913       }
2914       PetscCall(MatSetVariableBlockSizes(*J, nblocks, pblocks));
2915     }
2916     PetscCall(PetscFree(pblocks));
2917   }
2918   PetscCall(MatSetDM(*J, dm));
2919   PetscFunctionReturn(PETSC_SUCCESS);
2920 }
2921 
2922 /*@
2923   DMPlexGetSubdomainSection - Returns the section associated with the subdomain
2924 
2925   Not Collective
2926 
2927   Input Parameter:
2928 . dm - The `DMPLEX`
2929 
2930   Output Parameter:
2931 . subsection - The subdomain section
2932 
2933   Level: developer
2934 
2935 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `PetscSection`
2936 @*/
2937 PetscErrorCode DMPlexGetSubdomainSection(DM dm, PetscSection *subsection)
2938 {
2939   DM_Plex *mesh = (DM_Plex *)dm->data;
2940 
2941   PetscFunctionBegin;
2942   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2943   if (!mesh->subdomainSection) {
2944     PetscSection section;
2945     PetscSF      sf;
2946 
2947     PetscCall(PetscSFCreate(PETSC_COMM_SELF, &sf));
2948     PetscCall(DMGetLocalSection(dm, &section));
2949     PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_TRUE, PETSC_FALSE, PETSC_TRUE, &mesh->subdomainSection));
2950     PetscCall(PetscSFDestroy(&sf));
2951   }
2952   *subsection = mesh->subdomainSection;
2953   PetscFunctionReturn(PETSC_SUCCESS);
2954 }
2955 
2956 /*@
2957   DMPlexGetChart - Return the interval for all mesh points [`pStart`, `pEnd`)
2958 
2959   Not Collective
2960 
2961   Input Parameter:
2962 . dm - The `DMPLEX`
2963 
2964   Output Parameters:
2965 + pStart - The first mesh point
2966 - pEnd   - The upper bound for mesh points
2967 
2968   Level: beginner
2969 
2970 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetChart()`
2971 @*/
2972 PetscErrorCode DMPlexGetChart(DM dm, PetscInt *pStart, PetscInt *pEnd)
2973 {
2974   DM_Plex *mesh = (DM_Plex *)dm->data;
2975 
2976   PetscFunctionBegin;
2977   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2978   if (mesh->tr) PetscCall(DMPlexTransformGetChart(mesh->tr, pStart, pEnd));
2979   else PetscCall(PetscSectionGetChart(mesh->coneSection, pStart, pEnd));
2980   PetscFunctionReturn(PETSC_SUCCESS);
2981 }
2982 
2983 /*@
2984   DMPlexSetChart - Set the interval for all mesh points [`pStart`, `pEnd`)
2985 
2986   Not Collective
2987 
2988   Input Parameters:
2989 + dm     - The `DMPLEX`
2990 . pStart - The first mesh point
2991 - pEnd   - The upper bound for mesh points
2992 
2993   Level: beginner
2994 
2995 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetChart()`
2996 @*/
2997 PetscErrorCode DMPlexSetChart(DM dm, PetscInt pStart, PetscInt pEnd)
2998 {
2999   DM_Plex *mesh = (DM_Plex *)dm->data;
3000 
3001   PetscFunctionBegin;
3002   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3003   PetscCall(PetscSectionSetChart(mesh->coneSection, pStart, pEnd));
3004   PetscCall(PetscSectionSetChart(mesh->supportSection, pStart, pEnd));
3005   PetscCall(PetscFree(mesh->cellTypes));
3006   PetscFunctionReturn(PETSC_SUCCESS);
3007 }
3008 
3009 /*@
3010   DMPlexGetConeSize - Return the number of in-edges for this point in the DAG
3011 
3012   Not Collective
3013 
3014   Input Parameters:
3015 + dm - The `DMPLEX`
3016 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3017 
3018   Output Parameter:
3019 . size - The cone size for point `p`
3020 
3021   Level: beginner
3022 
3023 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
3024 @*/
3025 PetscErrorCode DMPlexGetConeSize(DM dm, PetscInt p, PetscInt *size)
3026 {
3027   DM_Plex *mesh = (DM_Plex *)dm->data;
3028 
3029   PetscFunctionBegin;
3030   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3031   PetscAssertPointer(size, 3);
3032   if (mesh->tr) PetscCall(DMPlexTransformGetConeSize(mesh->tr, p, size));
3033   else PetscCall(PetscSectionGetDof(mesh->coneSection, p, size));
3034   PetscFunctionReturn(PETSC_SUCCESS);
3035 }
3036 
3037 /*@
3038   DMPlexSetConeSize - Set the number of in-edges for this point in the DAG
3039 
3040   Not Collective
3041 
3042   Input Parameters:
3043 + dm   - The `DMPLEX`
3044 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3045 - size - The cone size for point `p`
3046 
3047   Level: beginner
3048 
3049   Note:
3050   This should be called after `DMPlexSetChart()`.
3051 
3052 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetCone()`, `DMPlexCreate()`, `DMPlexGetConeSize()`, `DMPlexSetChart()`
3053 @*/
3054 PetscErrorCode DMPlexSetConeSize(DM dm, PetscInt p, PetscInt size)
3055 {
3056   DM_Plex *mesh = (DM_Plex *)dm->data;
3057 
3058   PetscFunctionBegin;
3059   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3060   PetscCheck(!mesh->tr, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Cannot call DMPlexSetConeSize() on a mesh with a transform defined.");
3061   PetscCall(PetscSectionSetDof(mesh->coneSection, p, size));
3062   PetscFunctionReturn(PETSC_SUCCESS);
3063 }
3064 
3065 /*@C
3066   DMPlexGetCone - Return the points on the in-edges for this point in the DAG
3067 
3068   Not Collective
3069 
3070   Input Parameters:
3071 + dm - The `DMPLEX`
3072 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3073 
3074   Output Parameter:
3075 . cone - An array of points which are on the in-edges for point `p`, the length of `cone` is the result of `DMPlexGetConeSize()`
3076 
3077   Level: beginner
3078 
3079   Fortran Notes:
3080   `cone` must be declared with
3081 .vb
3082   PetscInt, pointer :: cone(:)
3083 .ve
3084 
3085   You must also call `DMPlexRestoreCone()` after you finish using the array.
3086   `DMPlexRestoreCone()` is not needed/available in C.
3087 
3088 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSize()`, `DMPlexSetCone()`, `DMPlexGetConeTuple()`, `DMPlexSetChart()`, `DMPlexRestoreCone()`
3089 @*/
3090 PetscErrorCode DMPlexGetCone(DM dm, PetscInt p, const PetscInt *cone[])
3091 {
3092   DM_Plex *mesh = (DM_Plex *)dm->data;
3093   PetscInt off;
3094 
3095   PetscFunctionBegin;
3096   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3097   PetscAssertPointer(cone, 3);
3098   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3099   *cone = PetscSafePointerPlusOffset(mesh->cones, off);
3100   PetscFunctionReturn(PETSC_SUCCESS);
3101 }
3102 
3103 /*@
3104   DMPlexGetConeTuple - Return the points on the in-edges of several points in the DAG
3105 
3106   Not Collective
3107 
3108   Input Parameters:
3109 + dm - The `DMPLEX`
3110 - p  - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3111 
3112   Output Parameters:
3113 + pConesSection - `PetscSection` describing the layout of `pCones`
3114 - pCones        - An `IS` containing the points which are on the in-edges for the point set `p`
3115 
3116   Level: intermediate
3117 
3118 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeRecursive()`, `DMPlexSetChart()`, `PetscSection`, `IS`
3119 @*/
3120 PetscErrorCode DMPlexGetConeTuple(DM dm, IS p, PetscSection *pConesSection, IS *pCones)
3121 {
3122   PetscSection cs, newcs;
3123   PetscInt    *cones;
3124   PetscInt    *newarr = NULL;
3125   PetscInt     n;
3126 
3127   PetscFunctionBegin;
3128   PetscCall(DMPlexGetCones(dm, &cones));
3129   PetscCall(DMPlexGetConeSection(dm, &cs));
3130   PetscCall(PetscSectionExtractDofsFromArray(cs, MPIU_INT, cones, p, &newcs, pCones ? ((void **)&newarr) : NULL));
3131   if (pConesSection) *pConesSection = newcs;
3132   if (pCones) {
3133     PetscCall(PetscSectionGetStorageSize(newcs, &n));
3134     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)p), n, newarr, PETSC_OWN_POINTER, pCones));
3135   }
3136   PetscFunctionReturn(PETSC_SUCCESS);
3137 }
3138 
3139 /*@
3140   DMPlexGetConeRecursiveVertices - Expand each given point into its cone points and do that recursively until we end up just with vertices.
3141 
3142   Not Collective
3143 
3144   Input Parameters:
3145 + dm     - The `DMPLEX`
3146 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3147 
3148   Output Parameter:
3149 . expandedPoints - An `IS` containing the of vertices recursively expanded from input points
3150 
3151   Level: advanced
3152 
3153   Notes:
3154   Like `DMPlexGetConeRecursive()` but returns only the 0-depth `IS` (i.e. vertices only) and no sections.
3155 
3156   There is no corresponding Restore function, just call `ISDestroy()` on the returned `IS` to deallocate.
3157 
3158 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexRestoreConeRecursive()`,
3159           `DMPlexGetDepth()`, `IS`
3160 @*/
3161 PetscErrorCode DMPlexGetConeRecursiveVertices(DM dm, IS points, IS *expandedPoints)
3162 {
3163   IS      *expandedPointsAll;
3164   PetscInt depth;
3165 
3166   PetscFunctionBegin;
3167   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3168   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
3169   PetscAssertPointer(expandedPoints, 3);
3170   PetscCall(DMPlexGetConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
3171   *expandedPoints = expandedPointsAll[0];
3172   PetscCall(PetscObjectReference((PetscObject)expandedPointsAll[0]));
3173   PetscCall(DMPlexRestoreConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
3174   PetscFunctionReturn(PETSC_SUCCESS);
3175 }
3176 
3177 /*@
3178   DMPlexGetConeRecursive - Expand each given point into its cone points and do that recursively until we end up just with vertices
3179   (DAG points of depth 0, i.e., without cones).
3180 
3181   Not Collective
3182 
3183   Input Parameters:
3184 + dm     - The `DMPLEX`
3185 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3186 
3187   Output Parameters:
3188 + depth          - (optional) Size of the output arrays, equal to `DMPLEX` depth, returned by `DMPlexGetDepth()`
3189 . expandedPoints - (optional) An array of index sets with recursively expanded cones
3190 - sections       - (optional) An array of sections which describe mappings from points to their cone points
3191 
3192   Level: advanced
3193 
3194   Notes:
3195   Like `DMPlexGetConeTuple()` but recursive.
3196 
3197   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.
3198   For example, for d=0 it contains only vertices, for d=1 it can contain vertices and edges, etc.
3199 
3200   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\:
3201   (1) DAG points in `expandedPoints`[d+1] with `depth` d+1 to their cone points in `expandedPoints`[d];
3202   (2) DAG points in `expandedPoints`[d+1] with `depth` in [0,d] to the same points in `expandedPoints`[d].
3203 
3204 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexRestoreConeRecursive()`, `DMPlexGetConeRecursiveVertices()`,
3205           `DMPlexGetDepth()`, `PetscSection`, `IS`
3206 @*/
3207 PetscErrorCode DMPlexGetConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
3208 {
3209   const PetscInt *arr0 = NULL, *cone = NULL;
3210   PetscInt       *arr = NULL, *newarr = NULL;
3211   PetscInt        d, depth_, i, n, newn, cn, co, start, end;
3212   IS             *expandedPoints_;
3213   PetscSection   *sections_;
3214 
3215   PetscFunctionBegin;
3216   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3217   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
3218   if (depth) PetscAssertPointer(depth, 3);
3219   if (expandedPoints) PetscAssertPointer(expandedPoints, 4);
3220   if (sections) PetscAssertPointer(sections, 5);
3221   PetscCall(ISGetLocalSize(points, &n));
3222   PetscCall(ISGetIndices(points, &arr0));
3223   PetscCall(DMPlexGetDepth(dm, &depth_));
3224   PetscCall(PetscCalloc1(depth_, &expandedPoints_));
3225   PetscCall(PetscCalloc1(depth_, &sections_));
3226   arr = (PetscInt *)arr0; /* this is ok because first generation of arr is not modified */
3227   for (d = depth_ - 1; d >= 0; d--) {
3228     PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &sections_[d]));
3229     PetscCall(PetscSectionSetChart(sections_[d], 0, n));
3230     for (i = 0; i < n; i++) {
3231       PetscCall(DMPlexGetDepthStratum(dm, d + 1, &start, &end));
3232       if (arr[i] >= start && arr[i] < end) {
3233         PetscCall(DMPlexGetConeSize(dm, arr[i], &cn));
3234         PetscCall(PetscSectionSetDof(sections_[d], i, cn));
3235       } else {
3236         PetscCall(PetscSectionSetDof(sections_[d], i, 1));
3237       }
3238     }
3239     PetscCall(PetscSectionSetUp(sections_[d]));
3240     PetscCall(PetscSectionGetStorageSize(sections_[d], &newn));
3241     PetscCall(PetscMalloc1(newn, &newarr));
3242     for (i = 0; i < n; i++) {
3243       PetscCall(PetscSectionGetDof(sections_[d], i, &cn));
3244       PetscCall(PetscSectionGetOffset(sections_[d], i, &co));
3245       if (cn > 1) {
3246         PetscCall(DMPlexGetCone(dm, arr[i], &cone));
3247         PetscCall(PetscMemcpy(&newarr[co], cone, cn * sizeof(PetscInt)));
3248       } else {
3249         newarr[co] = arr[i];
3250       }
3251     }
3252     PetscCall(ISCreateGeneral(PETSC_COMM_SELF, newn, newarr, PETSC_OWN_POINTER, &expandedPoints_[d]));
3253     arr = newarr;
3254     n   = newn;
3255   }
3256   PetscCall(ISRestoreIndices(points, &arr0));
3257   *depth = depth_;
3258   if (expandedPoints) *expandedPoints = expandedPoints_;
3259   else {
3260     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&expandedPoints_[d]));
3261     PetscCall(PetscFree(expandedPoints_));
3262   }
3263   if (sections) *sections = sections_;
3264   else {
3265     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&sections_[d]));
3266     PetscCall(PetscFree(sections_));
3267   }
3268   PetscFunctionReturn(PETSC_SUCCESS);
3269 }
3270 
3271 /*@
3272   DMPlexRestoreConeRecursive - Deallocates arrays created by `DMPlexGetConeRecursive()`
3273 
3274   Not Collective
3275 
3276   Input Parameters:
3277 + dm     - The `DMPLEX`
3278 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3279 
3280   Output Parameters:
3281 + depth          - (optional) Size of the output arrays, equal to `DMPLEX` depth, returned by `DMPlexGetDepth()`
3282 . expandedPoints - (optional) An array of recursively expanded cones
3283 - sections       - (optional) An array of sections which describe mappings from points to their cone points
3284 
3285   Level: advanced
3286 
3287   Note:
3288   See `DMPlexGetConeRecursive()`
3289 
3290 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexGetConeRecursiveVertices()`,
3291           `DMPlexGetDepth()`, `IS`, `PetscSection`
3292 @*/
3293 PetscErrorCode DMPlexRestoreConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
3294 {
3295   PetscInt d, depth_;
3296 
3297   PetscFunctionBegin;
3298   PetscCall(DMPlexGetDepth(dm, &depth_));
3299   PetscCheck(!depth || *depth == depth_, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "depth changed since last call to DMPlexGetConeRecursive");
3300   if (depth) *depth = 0;
3301   if (expandedPoints) {
3302     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&((*expandedPoints)[d])));
3303     PetscCall(PetscFree(*expandedPoints));
3304   }
3305   if (sections) {
3306     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&((*sections)[d])));
3307     PetscCall(PetscFree(*sections));
3308   }
3309   PetscFunctionReturn(PETSC_SUCCESS);
3310 }
3311 
3312 /*@
3313   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
3314 
3315   Not Collective
3316 
3317   Input Parameters:
3318 + dm   - The `DMPLEX`
3319 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3320 - cone - An array of points which are on the in-edges for point `p`, its length must have been previously provided with `DMPlexSetConeSize()`
3321 
3322   Level: beginner
3323 
3324   Note:
3325   This should be called after all calls to `DMPlexSetConeSize()` and `DMSetUp()`.
3326 
3327 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`, `DMPlexSetSupport()`, `DMPlexSetSupportSize()`
3328 @*/
3329 PetscErrorCode DMPlexSetCone(DM dm, PetscInt p, const PetscInt cone[])
3330 {
3331   DM_Plex *mesh = (DM_Plex *)dm->data;
3332   PetscInt dof, off, c;
3333 
3334   PetscFunctionBegin;
3335   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3336   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3337   if (dof) PetscAssertPointer(cone, 3);
3338   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3339   if (PetscDefined(USE_DEBUG)) {
3340     PetscInt pStart, pEnd;
3341     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3342     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);
3343     for (c = 0; c < dof; ++c) {
3344       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);
3345       mesh->cones[off + c] = cone[c];
3346     }
3347   } else {
3348     for (c = 0; c < dof; ++c) mesh->cones[off + c] = cone[c];
3349   }
3350   PetscFunctionReturn(PETSC_SUCCESS);
3351 }
3352 
3353 /*@C
3354   DMPlexGetConeOrientation - Return the orientations on the in-edges for this point in the DAG
3355 
3356   Not Collective
3357 
3358   Input Parameters:
3359 + dm - The `DMPLEX`
3360 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3361 
3362   Output Parameter:
3363 . coneOrientation - An array of orientations which are on the in-edges for point `p`. An orientation is an
3364                     integer giving the prescription for cone traversal. Its length is given by the result of `DMPlexSetConeSize()`
3365 
3366   Level: beginner
3367 
3368   Note:
3369   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3370   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3371   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3372   with the identity.
3373 
3374   Fortran Notes:
3375   You must call `DMPlexRestoreConeOrientation()` after you finish using the returned array.
3376   `DMPlexRestoreConeOrientation()` is not needed/available in C.
3377 
3378 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetConeSize()`, `DMPolytopeTypeComposeOrientation()`, `DMPolytopeTypeComposeOrientationInv()`,
3379           `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetCone()`, `DMPlexSetChart()`
3380 @*/
3381 PetscErrorCode DMPlexGetConeOrientation(DM dm, PetscInt p, const PetscInt *coneOrientation[])
3382 {
3383   DM_Plex *mesh = (DM_Plex *)dm->data;
3384   PetscInt off;
3385 
3386   PetscFunctionBegin;
3387   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3388   if (PetscDefined(USE_DEBUG)) {
3389     PetscInt dof;
3390     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3391     if (dof) PetscAssertPointer(coneOrientation, 3);
3392   }
3393   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3394 
3395   *coneOrientation = &mesh->coneOrientations[off];
3396   PetscFunctionReturn(PETSC_SUCCESS);
3397 }
3398 
3399 /*@
3400   DMPlexSetConeOrientation - Set the orientations on the in-edges for this point in the DAG
3401 
3402   Not Collective
3403 
3404   Input Parameters:
3405 + dm              - The `DMPLEX`
3406 . p               - The point, which must lie in the chart set with `DMPlexSetChart()`
3407 - coneOrientation - An array of orientations. Its length is given by the result of `DMPlexSetConeSize()`
3408 
3409   Level: beginner
3410 
3411   Notes:
3412   This should be called after all calls to `DMPlexSetConeSize()` and `DMSetUp()`.
3413 
3414   The meaning of coneOrientation is detailed in `DMPlexGetConeOrientation()`.
3415 
3416 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetConeOrientation()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3417 @*/
3418 PetscErrorCode DMPlexSetConeOrientation(DM dm, PetscInt p, const PetscInt coneOrientation[])
3419 {
3420   DM_Plex *mesh = (DM_Plex *)dm->data;
3421   PetscInt pStart, pEnd;
3422   PetscInt dof, off, c;
3423 
3424   PetscFunctionBegin;
3425   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3426   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3427   if (dof) PetscAssertPointer(coneOrientation, 3);
3428   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3429   if (PetscDefined(USE_DEBUG)) {
3430     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3431     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);
3432     for (c = 0; c < dof; ++c) {
3433       PetscInt cdof, o = coneOrientation[c];
3434 
3435       PetscCall(PetscSectionGetDof(mesh->coneSection, mesh->cones[off + c], &cdof));
3436       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);
3437       mesh->coneOrientations[off + c] = o;
3438     }
3439   } else {
3440     for (c = 0; c < dof; ++c) mesh->coneOrientations[off + c] = coneOrientation[c];
3441   }
3442   PetscFunctionReturn(PETSC_SUCCESS);
3443 }
3444 
3445 /*@
3446   DMPlexInsertCone - Insert a point into the in-edges for the point p in the DAG
3447 
3448   Not Collective
3449 
3450   Input Parameters:
3451 + dm        - The `DMPLEX`
3452 . p         - The point, which must lie in the chart set with `DMPlexSetChart()`
3453 . conePos   - The local index in the cone where the point should be put
3454 - conePoint - The mesh point to insert
3455 
3456   Level: beginner
3457 
3458 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3459 @*/
3460 PetscErrorCode DMPlexInsertCone(DM dm, PetscInt p, PetscInt conePos, PetscInt conePoint)
3461 {
3462   DM_Plex *mesh = (DM_Plex *)dm->data;
3463   PetscInt pStart, pEnd;
3464   PetscInt dof, off;
3465 
3466   PetscFunctionBegin;
3467   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3468   if (PetscDefined(USE_DEBUG)) {
3469     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3470     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);
3471     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);
3472     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3473     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);
3474   }
3475   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3476   mesh->cones[off + conePos] = conePoint;
3477   PetscFunctionReturn(PETSC_SUCCESS);
3478 }
3479 
3480 /*@
3481   DMPlexInsertConeOrientation - Insert a point orientation for the in-edge for the point p in the DAG
3482 
3483   Not Collective
3484 
3485   Input Parameters:
3486 + dm              - The `DMPLEX`
3487 . p               - The point, which must lie in the chart set with `DMPlexSetChart()`
3488 . conePos         - The local index in the cone where the point should be put
3489 - coneOrientation - The point orientation to insert
3490 
3491   Level: beginner
3492 
3493   Note:
3494   The meaning of coneOrientation values is detailed in `DMPlexGetConeOrientation()`.
3495 
3496 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3497 @*/
3498 PetscErrorCode DMPlexInsertConeOrientation(DM dm, PetscInt p, PetscInt conePos, PetscInt coneOrientation)
3499 {
3500   DM_Plex *mesh = (DM_Plex *)dm->data;
3501   PetscInt pStart, pEnd;
3502   PetscInt dof, off;
3503 
3504   PetscFunctionBegin;
3505   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3506   if (PetscDefined(USE_DEBUG)) {
3507     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3508     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);
3509     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3510     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);
3511   }
3512   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3513   mesh->coneOrientations[off + conePos] = coneOrientation;
3514   PetscFunctionReturn(PETSC_SUCCESS);
3515 }
3516 
3517 /*@C
3518   DMPlexGetOrientedCone - Return the points and orientations on the in-edges for this point in the DAG
3519 
3520   Not collective
3521 
3522   Input Parameters:
3523 + dm - The DMPlex
3524 - p  - The point, which must lie in the chart set with DMPlexSetChart()
3525 
3526   Output Parameters:
3527 + cone - An array of points which are on the in-edges for point `p`
3528 - ornt - An array of orientations which are on the in-edges for point `p`. An orientation is an
3529          integer giving the prescription for cone traversal.
3530 
3531   Level: beginner
3532 
3533   Notes:
3534   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3535   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3536   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3537   with the identity.
3538 
3539   You must also call `DMPlexRestoreOrientedCone()` after you finish using the returned array.
3540 
3541   Fortran Notes:
3542   `cone` and `ornt` must be declared with
3543 .vb
3544   PetscInt, pointer :: cone(:)
3545   PetscInt, pointer :: ornt(:)
3546 .ve
3547 
3548 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreOrientedCone()`, `DMPlexGetConeSize()`, `DMPlexGetCone()`, `DMPlexGetChart()`
3549 @*/
3550 PetscErrorCode DMPlexGetOrientedCone(DM dm, PetscInt p, const PetscInt *cone[], const PetscInt *ornt[])
3551 {
3552   DM_Plex *mesh = (DM_Plex *)dm->data;
3553 
3554   PetscFunctionBegin;
3555   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3556   if (mesh->tr) {
3557     PetscCall(DMPlexTransformGetCone(mesh->tr, p, cone, ornt));
3558   } else {
3559     PetscInt off;
3560     if (PetscDefined(USE_DEBUG)) {
3561       PetscInt dof;
3562       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3563       if (dof) {
3564         if (cone) PetscAssertPointer(cone, 3);
3565         if (ornt) PetscAssertPointer(ornt, 4);
3566       }
3567     }
3568     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3569     if (cone) *cone = PetscSafePointerPlusOffset(mesh->cones, off);
3570     if (ornt) *ornt = PetscSafePointerPlusOffset(mesh->coneOrientations, off);
3571   }
3572   PetscFunctionReturn(PETSC_SUCCESS);
3573 }
3574 
3575 /*@C
3576   DMPlexRestoreOrientedCone - Restore the points and orientations on the in-edges for this point in the DAG obtained with `DMPlexGetOrientedCone()`
3577 
3578   Not Collective
3579 
3580   Input Parameters:
3581 + dm   - The DMPlex
3582 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3583 . cone - An array of points which are on the in-edges for point p
3584 - ornt - An array of orientations which are on the in-edges for point `p`. An orientation is an
3585          integer giving the prescription for cone traversal.
3586 
3587   Level: beginner
3588 
3589 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetOrientedCone()`, `DMPlexGetConeSize()`, `DMPlexGetCone()`, `DMPlexGetChart()`
3590 @*/
3591 PetscErrorCode DMPlexRestoreOrientedCone(DM dm, PetscInt p, const PetscInt *cone[], const PetscInt *ornt[])
3592 {
3593   DM_Plex *mesh = (DM_Plex *)dm->data;
3594 
3595   PetscFunctionBegin;
3596   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3597   if (mesh->tr) PetscCall(DMPlexTransformRestoreCone(mesh->tr, p, cone, ornt));
3598   PetscFunctionReturn(PETSC_SUCCESS);
3599 }
3600 
3601 /*@
3602   DMPlexGetSupportSize - Return the number of out-edges for this point in the DAG
3603 
3604   Not Collective
3605 
3606   Input Parameters:
3607 + dm - The `DMPLEX`
3608 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3609 
3610   Output Parameter:
3611 . size - The support size for point `p`
3612 
3613   Level: beginner
3614 
3615 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`, `DMPlexGetConeSize()`
3616 @*/
3617 PetscErrorCode DMPlexGetSupportSize(DM dm, PetscInt p, PetscInt *size)
3618 {
3619   DM_Plex *mesh = (DM_Plex *)dm->data;
3620 
3621   PetscFunctionBegin;
3622   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3623   PetscAssertPointer(size, 3);
3624   PetscCall(PetscSectionGetDof(mesh->supportSection, p, size));
3625   PetscFunctionReturn(PETSC_SUCCESS);
3626 }
3627 
3628 /*@
3629   DMPlexSetSupportSize - Set the number of out-edges for this point in the DAG
3630 
3631   Not Collective
3632 
3633   Input Parameters:
3634 + dm   - The `DMPLEX`
3635 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3636 - size - The support size for point `p`
3637 
3638   Level: beginner
3639 
3640   Note:
3641   This should be called after `DMPlexSetChart()`.
3642 
3643 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetSupportSize()`, `DMPlexSetChart()`
3644 @*/
3645 PetscErrorCode DMPlexSetSupportSize(DM dm, PetscInt p, PetscInt size)
3646 {
3647   DM_Plex *mesh = (DM_Plex *)dm->data;
3648 
3649   PetscFunctionBegin;
3650   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3651   PetscCall(PetscSectionSetDof(mesh->supportSection, p, size));
3652   PetscFunctionReturn(PETSC_SUCCESS);
3653 }
3654 
3655 /*@C
3656   DMPlexGetSupport - Return the points on the out-edges for this point in the DAG
3657 
3658   Not Collective
3659 
3660   Input Parameters:
3661 + dm - The `DMPLEX`
3662 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3663 
3664   Output Parameter:
3665 . support - An array of points which are on the out-edges for point `p`, its length is that obtained from `DMPlexGetSupportSize()`
3666 
3667   Level: beginner
3668 
3669   Fortran Notes:
3670   `support` must be declared with
3671 .vb
3672   PetscInt, pointer :: support(:)
3673 .ve
3674 
3675   You must also call `DMPlexRestoreSupport()` after you finish using the returned array.
3676   `DMPlexRestoreSupport()` is not needed/available in C.
3677 
3678 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSupportSize()`, `DMPlexSetSupport()`, `DMPlexGetCone()`, `DMPlexSetChart()`
3679 @*/
3680 PetscErrorCode DMPlexGetSupport(DM dm, PetscInt p, const PetscInt *support[])
3681 {
3682   DM_Plex *mesh = (DM_Plex *)dm->data;
3683   PetscInt off;
3684 
3685   PetscFunctionBegin;
3686   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3687   PetscAssertPointer(support, 3);
3688   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3689   *support = PetscSafePointerPlusOffset(mesh->supports, off);
3690   PetscFunctionReturn(PETSC_SUCCESS);
3691 }
3692 
3693 /*@
3694   DMPlexSetSupport - Set the points on the out-edges for this point in the DAG, that is the list of points that this point covers
3695 
3696   Not Collective
3697 
3698   Input Parameters:
3699 + dm      - The `DMPLEX`
3700 . p       - The point, which must lie in the chart set with `DMPlexSetChart()`
3701 - support - An array of points which are on the out-edges for point `p`, its length is that obtained from `DMPlexGetSupportSize()`
3702 
3703   Level: beginner
3704 
3705   Note:
3706   This should be called after all calls to `DMPlexSetSupportSize()` and `DMSetUp()`.
3707 
3708 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetCone()`, `DMPlexSetConeSize()`, `DMPlexCreate()`, `DMPlexGetSupport()`, `DMPlexSetChart()`, `DMPlexSetSupportSize()`, `DMSetUp()`
3709 @*/
3710 PetscErrorCode DMPlexSetSupport(DM dm, PetscInt p, const PetscInt support[])
3711 {
3712   DM_Plex *mesh = (DM_Plex *)dm->data;
3713   PetscInt pStart, pEnd;
3714   PetscInt dof, off, c;
3715 
3716   PetscFunctionBegin;
3717   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3718   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3719   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3720   if (dof) PetscAssertPointer(support, 3);
3721   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3722   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);
3723   for (c = 0; c < dof; ++c) {
3724     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);
3725     mesh->supports[off + c] = support[c];
3726   }
3727   PetscFunctionReturn(PETSC_SUCCESS);
3728 }
3729 
3730 /*@
3731   DMPlexInsertSupport - Insert a point into the out-edges for the point p in the DAG
3732 
3733   Not Collective
3734 
3735   Input Parameters:
3736 + dm           - The `DMPLEX`
3737 . p            - The point, which must lie in the chart set with `DMPlexSetChart()`
3738 . supportPos   - The local index in the cone where the point should be put
3739 - supportPoint - The mesh point to insert
3740 
3741   Level: beginner
3742 
3743 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3744 @*/
3745 PetscErrorCode DMPlexInsertSupport(DM dm, PetscInt p, PetscInt supportPos, PetscInt supportPoint)
3746 {
3747   DM_Plex *mesh = (DM_Plex *)dm->data;
3748   PetscInt pStart, pEnd;
3749   PetscInt dof, off;
3750 
3751   PetscFunctionBegin;
3752   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3753   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3754   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3755   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3756   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);
3757   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);
3758   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);
3759   mesh->supports[off + supportPos] = supportPoint;
3760   PetscFunctionReturn(PETSC_SUCCESS);
3761 }
3762 
3763 /* Converts an orientation o in the current numbering to the previous scheme used in Plex */
3764 PetscInt DMPolytopeConvertNewOrientation_Internal(DMPolytopeType ct, PetscInt o)
3765 {
3766   switch (ct) {
3767   case DM_POLYTOPE_SEGMENT:
3768     if (o == -1) return -2;
3769     break;
3770   case DM_POLYTOPE_TRIANGLE:
3771     if (o == -3) return -1;
3772     if (o == -2) return -3;
3773     if (o == -1) return -2;
3774     break;
3775   case DM_POLYTOPE_QUADRILATERAL:
3776     if (o == -4) return -2;
3777     if (o == -3) return -1;
3778     if (o == -2) return -4;
3779     if (o == -1) return -3;
3780     break;
3781   default:
3782     return o;
3783   }
3784   return o;
3785 }
3786 
3787 /* Converts an orientation o in the previous scheme used in Plex to the current numbering */
3788 PetscInt DMPolytopeConvertOldOrientation_Internal(DMPolytopeType ct, PetscInt o)
3789 {
3790   switch (ct) {
3791   case DM_POLYTOPE_SEGMENT:
3792     if ((o == -2) || (o == 1)) return -1;
3793     if (o == -1) return 0;
3794     break;
3795   case DM_POLYTOPE_TRIANGLE:
3796     if (o == -3) return -2;
3797     if (o == -2) return -1;
3798     if (o == -1) return -3;
3799     break;
3800   case DM_POLYTOPE_QUADRILATERAL:
3801     if (o == -4) return -2;
3802     if (o == -3) return -1;
3803     if (o == -2) return -4;
3804     if (o == -1) return -3;
3805     break;
3806   default:
3807     return o;
3808   }
3809   return o;
3810 }
3811 
3812 /* Takes in a mesh whose orientations are in the previous scheme and converts them all to the current numbering */
3813 PetscErrorCode DMPlexConvertOldOrientations_Internal(DM dm)
3814 {
3815   PetscInt pStart, pEnd, p;
3816 
3817   PetscFunctionBegin;
3818   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3819   for (p = pStart; p < pEnd; ++p) {
3820     const PetscInt *cone, *ornt;
3821     PetscInt        coneSize, c;
3822 
3823     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
3824     PetscCall(DMPlexGetCone(dm, p, &cone));
3825     PetscCall(DMPlexGetConeOrientation(dm, p, &ornt));
3826     for (c = 0; c < coneSize; ++c) {
3827       DMPolytopeType ct;
3828       const PetscInt o = ornt[c];
3829 
3830       PetscCall(DMPlexGetCellType(dm, cone[c], &ct));
3831       switch (ct) {
3832       case DM_POLYTOPE_SEGMENT:
3833         if ((o == -2) || (o == 1)) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3834         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, 0));
3835         break;
3836       case DM_POLYTOPE_TRIANGLE:
3837         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3838         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3839         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3840         break;
3841       case DM_POLYTOPE_QUADRILATERAL:
3842         if (o == -4) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3843         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3844         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -4));
3845         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3846         break;
3847       default:
3848         break;
3849       }
3850     }
3851   }
3852   PetscFunctionReturn(PETSC_SUCCESS);
3853 }
3854 
3855 static inline PetscErrorCode DMPlexGetTransitiveClosure_Hot_Private(DM dm, PetscInt p, PetscBool useCone, PetscInt *size, const PetscInt *arr[], const PetscInt *ornt[])
3856 {
3857   DM_Plex *mesh = (DM_Plex *)dm->data;
3858 
3859   PetscFunctionBeginHot;
3860   if (PetscDefined(USE_DEBUG) || mesh->tr) {
3861     if (useCone) {
3862       PetscCall(DMPlexGetConeSize(dm, p, size));
3863       PetscCall(DMPlexGetOrientedCone(dm, p, arr, ornt));
3864     } else {
3865       PetscCall(DMPlexGetSupportSize(dm, p, size));
3866       PetscCall(DMPlexGetSupport(dm, p, arr));
3867     }
3868   } else {
3869     if (useCone) {
3870       const PetscSection s   = mesh->coneSection;
3871       const PetscInt     ps  = p - s->pStart;
3872       const PetscInt     off = s->atlasOff[ps];
3873 
3874       *size = s->atlasDof[ps];
3875       *arr  = mesh->cones + off;
3876       *ornt = mesh->coneOrientations + off;
3877     } else {
3878       const PetscSection s   = mesh->supportSection;
3879       const PetscInt     ps  = p - s->pStart;
3880       const PetscInt     off = s->atlasOff[ps];
3881 
3882       *size = s->atlasDof[ps];
3883       *arr  = mesh->supports + off;
3884     }
3885   }
3886   PetscFunctionReturn(PETSC_SUCCESS);
3887 }
3888 
3889 static inline PetscErrorCode DMPlexRestoreTransitiveClosure_Hot_Private(DM dm, PetscInt p, PetscBool useCone, PetscInt *size, const PetscInt *arr[], const PetscInt *ornt[])
3890 {
3891   DM_Plex *mesh = (DM_Plex *)dm->data;
3892 
3893   PetscFunctionBeginHot;
3894   if (PetscDefined(USE_DEBUG) || mesh->tr) {
3895     if (useCone) PetscCall(DMPlexRestoreOrientedCone(dm, p, arr, ornt));
3896   }
3897   PetscFunctionReturn(PETSC_SUCCESS);
3898 }
3899 
3900 static PetscErrorCode DMPlexGetTransitiveClosure_Depth1_Private(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3901 {
3902   DMPolytopeType  ct = DM_POLYTOPE_UNKNOWN;
3903   PetscInt       *closure;
3904   const PetscInt *tmp = NULL, *tmpO = NULL;
3905   PetscInt        off = 0, tmpSize, t;
3906 
3907   PetscFunctionBeginHot;
3908   if (ornt) {
3909     PetscCall(DMPlexGetCellType(dm, p, &ct));
3910     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;
3911   }
3912   if (*points) {
3913     closure = *points;
3914   } else {
3915     PetscInt maxConeSize, maxSupportSize;
3916     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3917     PetscCall(DMGetWorkArray(dm, 2 * (PetscMax(maxConeSize, maxSupportSize) + 1), MPIU_INT, &closure));
3918   }
3919   PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, p, useCone, &tmpSize, &tmp, &tmpO));
3920   if (ct == DM_POLYTOPE_UNKNOWN) {
3921     closure[off++] = p;
3922     closure[off++] = 0;
3923     for (t = 0; t < tmpSize; ++t) {
3924       closure[off++] = tmp[t];
3925       closure[off++] = tmpO ? tmpO[t] : 0;
3926     }
3927   } else {
3928     const PetscInt *arr = DMPolytopeTypeGetArrangement(ct, ornt);
3929 
3930     /* We assume that cells with a valid type have faces with a valid type */
3931     closure[off++] = p;
3932     closure[off++] = ornt;
3933     for (t = 0; t < tmpSize; ++t) {
3934       DMPolytopeType ft;
3935 
3936       PetscCall(DMPlexGetCellType(dm, tmp[t], &ft));
3937       closure[off++] = tmp[arr[t]];
3938       closure[off++] = tmpO ? DMPolytopeTypeComposeOrientation(ft, ornt, tmpO[t]) : 0;
3939     }
3940   }
3941   PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, p, useCone, &tmpSize, &tmp, &tmpO));
3942   if (numPoints) *numPoints = tmpSize + 1;
3943   if (points) *points = closure;
3944   PetscFunctionReturn(PETSC_SUCCESS);
3945 }
3946 
3947 /* We need a special tensor version because we want to allow duplicate points in the endcaps for hybrid cells */
3948 static PetscErrorCode DMPlexTransitiveClosure_Tensor_Internal(DM dm, PetscInt point, DMPolytopeType ct, PetscInt o, PetscBool useCone, PetscInt *numPoints, PetscInt **points)
3949 {
3950   const PetscInt *arr = DMPolytopeTypeGetArrangement(ct, o);
3951   const PetscInt *cone, *ornt;
3952   PetscInt       *pts, *closure = NULL;
3953   DMPolytopeType  ft;
3954   PetscInt        maxConeSize, maxSupportSize, coneSeries, supportSeries, maxSize;
3955   PetscInt        dim, coneSize, c, d, clSize, cl;
3956 
3957   PetscFunctionBeginHot;
3958   PetscCall(DMGetDimension(dm, &dim));
3959   PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, point, PETSC_TRUE, &coneSize, &cone, &ornt));
3960   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3961   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, dim + 1) - 1) / (maxConeSize - 1)) : dim + 1;
3962   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, dim + 1) - 1) / (maxSupportSize - 1)) : dim + 1;
3963   maxSize       = PetscMax(coneSeries, supportSeries);
3964   if (*points) {
3965     pts = *points;
3966   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &pts));
3967   c        = 0;
3968   pts[c++] = point;
3969   pts[c++] = o;
3970   PetscCall(DMPlexGetCellType(dm, cone[arr[0 * 2 + 0]], &ft));
3971   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[0 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[0 * 2 + 1], ornt[0]), useCone, &clSize, &closure));
3972   for (cl = 0; cl < clSize * 2; cl += 2) {
3973     pts[c++] = closure[cl];
3974     pts[c++] = closure[cl + 1];
3975   }
3976   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[1 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[1 * 2 + 1], ornt[1]), useCone, &clSize, &closure));
3977   for (cl = 0; cl < clSize * 2; cl += 2) {
3978     pts[c++] = closure[cl];
3979     pts[c++] = closure[cl + 1];
3980   }
3981   PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[0], useCone, &clSize, &closure));
3982   for (d = 2; d < coneSize; ++d) {
3983     PetscCall(DMPlexGetCellType(dm, cone[arr[d * 2 + 0]], &ft));
3984     pts[c++] = cone[arr[d * 2 + 0]];
3985     pts[c++] = DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]);
3986   }
3987   PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, point, PETSC_TRUE, &coneSize, &cone, &ornt));
3988   if (dim >= 3) {
3989     for (d = 2; d < coneSize; ++d) {
3990       const PetscInt  fpoint = cone[arr[d * 2 + 0]];
3991       const PetscInt *fcone, *fornt;
3992       PetscInt        fconeSize, fc, i;
3993 
3994       PetscCall(DMPlexGetCellType(dm, fpoint, &ft));
3995       const PetscInt *farr = DMPolytopeTypeGetArrangement(ft, DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]));
3996       PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, fpoint, PETSC_TRUE, &fconeSize, &fcone, &fornt));
3997       for (fc = 0; fc < fconeSize; ++fc) {
3998         const PetscInt cp = fcone[farr[fc * 2 + 0]];
3999         const PetscInt co = farr[fc * 2 + 1];
4000 
4001         for (i = 0; i < c; i += 2)
4002           if (pts[i] == cp) break;
4003         if (i == c) {
4004           PetscCall(DMPlexGetCellType(dm, cp, &ft));
4005           pts[c++] = cp;
4006           pts[c++] = DMPolytopeTypeComposeOrientation(ft, co, fornt[farr[fc * 2 + 0]]);
4007         }
4008       }
4009       PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, fpoint, PETSC_TRUE, &fconeSize, &fcone, &fornt));
4010     }
4011   }
4012   *numPoints = c / 2;
4013   *points    = pts;
4014   PetscFunctionReturn(PETSC_SUCCESS);
4015 }
4016 
4017 PetscErrorCode DMPlexGetTransitiveClosure_Internal(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
4018 {
4019   DMPolytopeType ct;
4020   PetscInt      *closure, *fifo;
4021   PetscInt       closureSize = 0, fifoStart = 0, fifoSize = 0;
4022   PetscInt       maxConeSize, maxSupportSize, coneSeries, supportSeries;
4023   PetscInt       depth, maxSize;
4024 
4025   PetscFunctionBeginHot;
4026   PetscCall(DMPlexGetDepth(dm, &depth));
4027   if (depth == 1) {
4028     PetscCall(DMPlexGetTransitiveClosure_Depth1_Private(dm, p, ornt, useCone, numPoints, points));
4029     PetscFunctionReturn(PETSC_SUCCESS);
4030   }
4031   PetscCall(DMPlexGetCellType(dm, p, &ct));
4032   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;
4033   if (DMPolytopeTypeIsHybrid(ct) && ct != DM_POLYTOPE_POINT_PRISM_TENSOR) {
4034     PetscCall(DMPlexTransitiveClosure_Tensor_Internal(dm, p, ct, ornt, useCone, numPoints, points));
4035     PetscFunctionReturn(PETSC_SUCCESS);
4036   }
4037   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
4038   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, depth + 1) - 1) / (maxConeSize - 1)) : depth + 1;
4039   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, depth + 1) - 1) / (maxSupportSize - 1)) : depth + 1;
4040   maxSize       = PetscMax(coneSeries, supportSeries);
4041   PetscCall(DMGetWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
4042   if (*points) {
4043     closure = *points;
4044   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &closure));
4045   closure[closureSize++] = p;
4046   closure[closureSize++] = ornt;
4047   fifo[fifoSize++]       = p;
4048   fifo[fifoSize++]       = ornt;
4049   fifo[fifoSize++]       = ct;
4050   /* Should kick out early when depth is reached, rather than checking all vertices for empty cones */
4051   while (fifoSize - fifoStart) {
4052     const PetscInt       q    = fifo[fifoStart++];
4053     const PetscInt       o    = fifo[fifoStart++];
4054     const DMPolytopeType qt   = (DMPolytopeType)fifo[fifoStart++];
4055     const PetscInt      *qarr = DMPolytopeTypeGetArrangement(qt, o);
4056     const PetscInt      *tmp, *tmpO = NULL;
4057     PetscInt             tmpSize, t;
4058 
4059     if (PetscDefined(USE_DEBUG)) {
4060       PetscInt nO = DMPolytopeTypeGetNumArrangements(qt) / 2;
4061       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);
4062     }
4063     PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, q, useCone, &tmpSize, &tmp, &tmpO));
4064     for (t = 0; t < tmpSize; ++t) {
4065       const PetscInt ip = useCone && qarr ? qarr[t * 2] : t;
4066       const PetscInt io = useCone && qarr ? qarr[t * 2 + 1] : 0;
4067       const PetscInt cp = tmp[ip];
4068       PetscCall(DMPlexGetCellType(dm, cp, &ct));
4069       const PetscInt co = tmpO ? DMPolytopeTypeComposeOrientation(ct, io, tmpO[ip]) : 0;
4070       PetscInt       c;
4071 
4072       /* Check for duplicate */
4073       for (c = 0; c < closureSize; c += 2) {
4074         if (closure[c] == cp) break;
4075       }
4076       if (c == closureSize) {
4077         closure[closureSize++] = cp;
4078         closure[closureSize++] = co;
4079         fifo[fifoSize++]       = cp;
4080         fifo[fifoSize++]       = co;
4081         fifo[fifoSize++]       = ct;
4082       }
4083     }
4084     PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, q, useCone, &tmpSize, &tmp, &tmpO));
4085   }
4086   PetscCall(DMRestoreWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
4087   if (numPoints) *numPoints = closureSize / 2;
4088   if (points) *points = closure;
4089   PetscFunctionReturn(PETSC_SUCCESS);
4090 }
4091 
4092 /*@C
4093   DMPlexGetTransitiveClosure - Return the points on the transitive closure of the in-edges or out-edges for this point in the DAG
4094 
4095   Not Collective
4096 
4097   Input Parameters:
4098 + dm      - The `DMPLEX`
4099 . p       - The mesh point
4100 - useCone - `PETSC_TRUE` for the closure, otherwise return the star
4101 
4102   Input/Output Parameter:
4103 . points - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...];
4104            if *points is `NULL` on input, internal storage will be returned, use `DMPlexRestoreTransitiveClosure()`,
4105            otherwise the provided array is used to hold the values
4106 
4107   Output Parameter:
4108 . numPoints - The number of points in the closure, so `points` is of size 2*`numPoints`
4109 
4110   Level: beginner
4111 
4112   Note:
4113   If using internal storage (points is `NULL` on input), each call overwrites the last output.
4114 
4115   Fortran Notes:
4116   `points` must be declared with
4117 .vb
4118   PetscInt, pointer :: points(:)
4119 .ve
4120   and is always allocated by the function.
4121 
4122   The `numPoints` argument is not present in the Fortran binding.
4123 
4124 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
4125 @*/
4126 PetscErrorCode DMPlexGetTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
4127 {
4128   PetscFunctionBeginHot;
4129   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4130   if (numPoints) PetscAssertPointer(numPoints, 4);
4131   if (points) PetscAssertPointer(points, 5);
4132   if (PetscDefined(USE_DEBUG)) {
4133     PetscInt pStart, pEnd;
4134     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4135     PetscCheck(p >= pStart && p < pEnd, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Point %" PetscInt_FMT " is not in [%" PetscInt_FMT ", %" PetscInt_FMT ")", p, pStart, pEnd);
4136   }
4137   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, p, 0, useCone, numPoints, points));
4138   PetscFunctionReturn(PETSC_SUCCESS);
4139 }
4140 
4141 /*@C
4142   DMPlexRestoreTransitiveClosure - Restore the array of points on the transitive closure of the in-edges or out-edges for this point in the DAG
4143 
4144   Not Collective
4145 
4146   Input Parameters:
4147 + dm        - The `DMPLEX`
4148 . p         - The mesh point
4149 . useCone   - `PETSC_TRUE` for the closure, otherwise return the star
4150 . numPoints - The number of points in the closure, so points[] is of size 2*`numPoints`
4151 - points    - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...]
4152 
4153   Level: beginner
4154 
4155   Note:
4156   If not using internal storage (points is not `NULL` on input), this call is unnecessary
4157 
4158 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
4159 @*/
4160 PetscErrorCode DMPlexRestoreTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
4161 {
4162   PetscFunctionBeginHot;
4163   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4164   if (numPoints) *numPoints = 0;
4165   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, points));
4166   PetscFunctionReturn(PETSC_SUCCESS);
4167 }
4168 
4169 /*@
4170   DMPlexGetMaxSizes - Return the maximum number of in-edges (cone) and out-edges (support) for any point in the DAG
4171 
4172   Not Collective
4173 
4174   Input Parameter:
4175 . dm - The `DMPLEX`
4176 
4177   Output Parameters:
4178 + maxConeSize    - The maximum number of in-edges
4179 - maxSupportSize - The maximum number of out-edges
4180 
4181   Level: beginner
4182 
4183 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
4184 @*/
4185 PetscErrorCode DMPlexGetMaxSizes(DM dm, PetscInt *maxConeSize, PetscInt *maxSupportSize)
4186 {
4187   DM_Plex *mesh = (DM_Plex *)dm->data;
4188 
4189   PetscFunctionBegin;
4190   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4191   if (maxConeSize) PetscCall(PetscSectionGetMaxDof(mesh->coneSection, maxConeSize));
4192   if (maxSupportSize) PetscCall(PetscSectionGetMaxDof(mesh->supportSection, maxSupportSize));
4193   PetscFunctionReturn(PETSC_SUCCESS);
4194 }
4195 
4196 PetscErrorCode DMSetUp_Plex(DM dm)
4197 {
4198   DM_Plex *mesh = (DM_Plex *)dm->data;
4199   PetscInt size, maxSupportSize;
4200 
4201   PetscFunctionBegin;
4202   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4203   PetscCall(PetscSectionSetUp(mesh->coneSection));
4204   PetscCall(PetscSectionGetStorageSize(mesh->coneSection, &size));
4205   PetscCall(PetscMalloc1(size, &mesh->cones));
4206   PetscCall(PetscCalloc1(size, &mesh->coneOrientations));
4207   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
4208   if (maxSupportSize) {
4209     PetscCall(PetscSectionSetUp(mesh->supportSection));
4210     PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &size));
4211     PetscCall(PetscMalloc1(size, &mesh->supports));
4212   }
4213   PetscFunctionReturn(PETSC_SUCCESS);
4214 }
4215 
4216 PetscErrorCode DMCreateSubDM_Plex(DM dm, PetscInt numFields, const PetscInt fields[], IS *is, DM *subdm)
4217 {
4218   PetscFunctionBegin;
4219   if (subdm) PetscCall(DMClone(dm, subdm));
4220   PetscCall(DMCreateSectionSubDM(dm, numFields, fields, NULL, NULL, is, subdm));
4221   if (subdm) (*subdm)->useNatural = dm->useNatural;
4222   if (dm->useNatural && dm->sfMigration) {
4223     PetscSF sfNatural;
4224 
4225     (*subdm)->sfMigration = dm->sfMigration;
4226     PetscCall(PetscObjectReference((PetscObject)dm->sfMigration));
4227     PetscCall(DMPlexCreateGlobalToNaturalSF(*subdm, NULL, (*subdm)->sfMigration, &sfNatural));
4228     (*subdm)->sfNatural = sfNatural;
4229   }
4230   PetscFunctionReturn(PETSC_SUCCESS);
4231 }
4232 
4233 PetscErrorCode DMCreateSuperDM_Plex(DM dms[], PetscInt len, IS **is, DM *superdm)
4234 {
4235   PetscInt i = 0;
4236 
4237   PetscFunctionBegin;
4238   PetscCall(DMClone(dms[0], superdm));
4239   PetscCall(DMCreateSectionSuperDM(dms, len, is, superdm));
4240   (*superdm)->useNatural = PETSC_FALSE;
4241   for (i = 0; i < len; i++) {
4242     if (dms[i]->useNatural && dms[i]->sfMigration) {
4243       PetscSF sfNatural;
4244 
4245       (*superdm)->sfMigration = dms[i]->sfMigration;
4246       PetscCall(PetscObjectReference((PetscObject)dms[i]->sfMigration));
4247       (*superdm)->useNatural = PETSC_TRUE;
4248       PetscCall(DMPlexCreateGlobalToNaturalSF(*superdm, NULL, (*superdm)->sfMigration, &sfNatural));
4249       (*superdm)->sfNatural = sfNatural;
4250       break;
4251     }
4252   }
4253   PetscFunctionReturn(PETSC_SUCCESS);
4254 }
4255 
4256 /*@
4257   DMPlexSymmetrize - Create support (out-edge) information from cone (in-edge) information
4258 
4259   Not Collective
4260 
4261   Input Parameter:
4262 . dm - The `DMPLEX`
4263 
4264   Level: beginner
4265 
4266   Note:
4267   This should be called after all calls to `DMPlexSetCone()`
4268 
4269 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMPlexSetCone()`
4270 @*/
4271 PetscErrorCode DMPlexSymmetrize(DM dm)
4272 {
4273   DM_Plex  *mesh = (DM_Plex *)dm->data;
4274   PetscInt *offsets;
4275   PetscInt  supportSize;
4276   PetscInt  pStart, pEnd, p;
4277 
4278   PetscFunctionBegin;
4279   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4280   PetscCheck(!mesh->supports, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONGSTATE, "Supports were already setup in this DMPlex");
4281   PetscCall(PetscLogEventBegin(DMPLEX_Symmetrize, dm, 0, 0, 0));
4282   /* Calculate support sizes */
4283   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4284   for (p = pStart; p < pEnd; ++p) {
4285     PetscInt dof, off, c;
4286 
4287     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
4288     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
4289     for (c = off; c < off + dof; ++c) PetscCall(PetscSectionAddDof(mesh->supportSection, mesh->cones[c], 1));
4290   }
4291   PetscCall(PetscSectionSetUp(mesh->supportSection));
4292   /* Calculate supports */
4293   PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &supportSize));
4294   PetscCall(PetscMalloc1(supportSize, &mesh->supports));
4295   PetscCall(PetscCalloc1(pEnd - pStart, &offsets));
4296   for (p = pStart; p < pEnd; ++p) {
4297     PetscInt dof, off, c;
4298 
4299     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
4300     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
4301     for (c = off; c < off + dof; ++c) {
4302       const PetscInt q = mesh->cones[c];
4303       PetscInt       offS;
4304 
4305       PetscCall(PetscSectionGetOffset(mesh->supportSection, q, &offS));
4306 
4307       mesh->supports[offS + offsets[q]] = p;
4308       ++offsets[q];
4309     }
4310   }
4311   PetscCall(PetscFree(offsets));
4312   PetscCall(PetscLogEventEnd(DMPLEX_Symmetrize, dm, 0, 0, 0));
4313   PetscFunctionReturn(PETSC_SUCCESS);
4314 }
4315 
4316 static PetscErrorCode DMPlexCreateDepthStratum(DM dm, DMLabel label, PetscInt depth, PetscInt pStart, PetscInt pEnd)
4317 {
4318   IS stratumIS;
4319 
4320   PetscFunctionBegin;
4321   if (pStart >= pEnd) PetscFunctionReturn(PETSC_SUCCESS);
4322   if (PetscDefined(USE_DEBUG)) {
4323     PetscInt  qStart, qEnd, numLevels, level;
4324     PetscBool overlap = PETSC_FALSE;
4325     PetscCall(DMLabelGetNumValues(label, &numLevels));
4326     for (level = 0; level < numLevels; level++) {
4327       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4328       if ((pStart >= qStart && pStart < qEnd) || (pEnd > qStart && pEnd <= qEnd)) {
4329         overlap = PETSC_TRUE;
4330         break;
4331       }
4332     }
4333     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);
4334   }
4335   PetscCall(ISCreateStride(PETSC_COMM_SELF, pEnd - pStart, pStart, 1, &stratumIS));
4336   PetscCall(DMLabelSetStratumIS(label, depth, stratumIS));
4337   PetscCall(ISDestroy(&stratumIS));
4338   PetscFunctionReturn(PETSC_SUCCESS);
4339 }
4340 
4341 static PetscErrorCode DMPlexStratify_CellType_Private(DM dm, DMLabel label)
4342 {
4343   PetscInt *pMin, *pMax;
4344   PetscInt  pStart, pEnd;
4345   PetscInt  dmin = PETSC_MAX_INT, dmax = PETSC_MIN_INT;
4346 
4347   PetscFunctionBegin;
4348   {
4349     DMLabel label2;
4350 
4351     PetscCall(DMPlexGetCellTypeLabel(dm, &label2));
4352     PetscCall(PetscObjectViewFromOptions((PetscObject)label2, NULL, "-ct_view"));
4353   }
4354   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4355   for (PetscInt p = pStart; p < pEnd; ++p) {
4356     DMPolytopeType ct;
4357 
4358     PetscCall(DMPlexGetCellType(dm, p, &ct));
4359     dmin = PetscMin(DMPolytopeTypeGetDim(ct), dmin);
4360     dmax = PetscMax(DMPolytopeTypeGetDim(ct), dmax);
4361   }
4362   PetscCall(PetscMalloc2(dmax + 1, &pMin, dmax + 1, &pMax));
4363   for (PetscInt d = dmin; d <= dmax; ++d) {
4364     pMin[d] = PETSC_MAX_INT;
4365     pMax[d] = PETSC_MIN_INT;
4366   }
4367   for (PetscInt p = pStart; p < pEnd; ++p) {
4368     DMPolytopeType ct;
4369     PetscInt       d;
4370 
4371     PetscCall(DMPlexGetCellType(dm, p, &ct));
4372     d       = DMPolytopeTypeGetDim(ct);
4373     pMin[d] = PetscMin(p, pMin[d]);
4374     pMax[d] = PetscMax(p, pMax[d]);
4375   }
4376   for (PetscInt d = dmin; d <= dmax; ++d) {
4377     if (pMin[d] > pMax[d]) continue;
4378     PetscCall(DMPlexCreateDepthStratum(dm, label, d, pMin[d], pMax[d] + 1));
4379   }
4380   PetscCall(PetscFree2(pMin, pMax));
4381   PetscFunctionReturn(PETSC_SUCCESS);
4382 }
4383 
4384 static PetscErrorCode DMPlexStratify_Topological_Private(DM dm, DMLabel label)
4385 {
4386   PetscInt pStart, pEnd;
4387   PetscInt numRoots = 0, numLeaves = 0;
4388 
4389   PetscFunctionBegin;
4390   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4391   {
4392     /* Initialize roots and count leaves */
4393     PetscInt sMin = PETSC_MAX_INT;
4394     PetscInt sMax = PETSC_MIN_INT;
4395     PetscInt coneSize, supportSize;
4396 
4397     for (PetscInt p = pStart; p < pEnd; ++p) {
4398       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4399       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
4400       if (!coneSize && supportSize) {
4401         sMin = PetscMin(p, sMin);
4402         sMax = PetscMax(p, sMax);
4403         ++numRoots;
4404       } else if (!supportSize && coneSize) {
4405         ++numLeaves;
4406       } else if (!supportSize && !coneSize) {
4407         /* Isolated points */
4408         sMin = PetscMin(p, sMin);
4409         sMax = PetscMax(p, sMax);
4410       }
4411     }
4412     PetscCall(DMPlexCreateDepthStratum(dm, label, 0, sMin, sMax + 1));
4413   }
4414 
4415   if (numRoots + numLeaves == (pEnd - pStart)) {
4416     PetscInt sMin = PETSC_MAX_INT;
4417     PetscInt sMax = PETSC_MIN_INT;
4418     PetscInt coneSize, supportSize;
4419 
4420     for (PetscInt p = pStart; p < pEnd; ++p) {
4421       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4422       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
4423       if (!supportSize && coneSize) {
4424         sMin = PetscMin(p, sMin);
4425         sMax = PetscMax(p, sMax);
4426       }
4427     }
4428     PetscCall(DMPlexCreateDepthStratum(dm, label, 1, sMin, sMax + 1));
4429   } else {
4430     PetscInt level = 0;
4431     PetscInt qStart, qEnd;
4432 
4433     PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4434     while (qEnd > qStart) {
4435       PetscInt sMin = PETSC_MAX_INT;
4436       PetscInt sMax = PETSC_MIN_INT;
4437 
4438       for (PetscInt q = qStart; q < qEnd; ++q) {
4439         const PetscInt *support;
4440         PetscInt        supportSize;
4441 
4442         PetscCall(DMPlexGetSupportSize(dm, q, &supportSize));
4443         PetscCall(DMPlexGetSupport(dm, q, &support));
4444         for (PetscInt s = 0; s < supportSize; ++s) {
4445           sMin = PetscMin(support[s], sMin);
4446           sMax = PetscMax(support[s], sMax);
4447         }
4448       }
4449       PetscCall(DMLabelGetNumValues(label, &level));
4450       PetscCall(DMPlexCreateDepthStratum(dm, label, level, sMin, sMax + 1));
4451       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4452     }
4453   }
4454   PetscFunctionReturn(PETSC_SUCCESS);
4455 }
4456 
4457 /*@
4458   DMPlexStratify - Computes the strata for all points in the `DMPLEX`
4459 
4460   Collective
4461 
4462   Input Parameter:
4463 . dm - The `DMPLEX`
4464 
4465   Level: beginner
4466 
4467   Notes:
4468   The strata group all points of the same grade, and this function calculates the strata. This
4469   grade can be seen as the height (or depth) of the point in the DAG.
4470 
4471   The DAG for most topologies is a graded poset (https://en.wikipedia.org/wiki/Graded_poset), and
4472   can be illustrated by a Hasse Diagram (https://en.wikipedia.org/wiki/Hasse_diagram).
4473   Concretely, `DMPlexStratify()` creates a new label named "depth" containing the depth in the DAG of each point. For cell-vertex
4474   meshes, vertices are depth 0 and cells are depth 1. For fully interpolated meshes, depth 0 for vertices, 1 for edges, and so on
4475   until cells have depth equal to the dimension of the mesh. The depth label can be accessed through `DMPlexGetDepthLabel()` or `DMPlexGetDepthStratum()`, or
4476   manually via `DMGetLabel()`.  The height is defined implicitly by height = maxDimension - depth, and can be accessed
4477   via `DMPlexGetHeightStratum()`.  For example, cells have height 0 and faces have height 1.
4478 
4479   The depth of a point is calculated by executing a breadth-first search (BFS) on the DAG. This could produce surprising results
4480   if run on a partially interpolated mesh, meaning one that had some edges and faces, but not others. For example, suppose that
4481   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
4482   to interpolate only that one (e0), so that
4483 .vb
4484   cone(c0) = {e0, v2}
4485   cone(e0) = {v0, v1}
4486 .ve
4487   If `DMPlexStratify()` is run on this mesh, it will give depths
4488 .vb
4489    depth 0 = {v0, v1, v2}
4490    depth 1 = {e0, c0}
4491 .ve
4492   where the triangle has been given depth 1, instead of 2, because it is reachable from vertex v2.
4493 
4494   `DMPlexStratify()` should be called after all calls to `DMPlexSymmetrize()`
4495 
4496 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexComputeCellTypes()`
4497 @*/
4498 PetscErrorCode DMPlexStratify(DM dm)
4499 {
4500   DM_Plex  *mesh = (DM_Plex *)dm->data;
4501   DMLabel   label;
4502   PetscBool flg = PETSC_FALSE;
4503 
4504   PetscFunctionBegin;
4505   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4506   PetscCall(PetscLogEventBegin(DMPLEX_Stratify, dm, 0, 0, 0));
4507 
4508   // Create depth label
4509   PetscCall(DMRemoveLabel(dm, "depth", NULL));
4510   PetscCall(DMCreateLabel(dm, "depth"));
4511   PetscCall(DMPlexGetDepthLabel(dm, &label));
4512 
4513   PetscCall(PetscOptionsGetBool(NULL, dm->hdr.prefix, "-dm_plex_stratify_celltype", &flg, NULL));
4514   if (flg) PetscCall(DMPlexStratify_CellType_Private(dm, label));
4515   else PetscCall(DMPlexStratify_Topological_Private(dm, label));
4516 
4517   { /* just in case there is an empty process */
4518     PetscInt numValues, maxValues = 0, v;
4519 
4520     PetscCall(DMLabelGetNumValues(label, &numValues));
4521     PetscCall(MPIU_Allreduce(&numValues, &maxValues, 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
4522     for (v = numValues; v < maxValues; v++) PetscCall(DMLabelAddStratum(label, v));
4523   }
4524   PetscCall(PetscObjectStateGet((PetscObject)label, &mesh->depthState));
4525   PetscCall(PetscLogEventEnd(DMPLEX_Stratify, dm, 0, 0, 0));
4526   PetscFunctionReturn(PETSC_SUCCESS);
4527 }
4528 
4529 PetscErrorCode DMPlexComputeCellType_Internal(DM dm, PetscInt p, PetscInt pdepth, DMPolytopeType *pt)
4530 {
4531   DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4532   PetscInt       dim, depth, pheight, coneSize;
4533 
4534   PetscFunctionBeginHot;
4535   PetscCall(DMGetDimension(dm, &dim));
4536   PetscCall(DMPlexGetDepth(dm, &depth));
4537   PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4538   pheight = depth - pdepth;
4539   if (depth <= 1) {
4540     switch (pdepth) {
4541     case 0:
4542       ct = DM_POLYTOPE_POINT;
4543       break;
4544     case 1:
4545       switch (coneSize) {
4546       case 2:
4547         ct = DM_POLYTOPE_SEGMENT;
4548         break;
4549       case 3:
4550         ct = DM_POLYTOPE_TRIANGLE;
4551         break;
4552       case 4:
4553         switch (dim) {
4554         case 2:
4555           ct = DM_POLYTOPE_QUADRILATERAL;
4556           break;
4557         case 3:
4558           ct = DM_POLYTOPE_TETRAHEDRON;
4559           break;
4560         default:
4561           break;
4562         }
4563         break;
4564       case 5:
4565         ct = DM_POLYTOPE_PYRAMID;
4566         break;
4567       case 6:
4568         ct = DM_POLYTOPE_TRI_PRISM_TENSOR;
4569         break;
4570       case 8:
4571         ct = DM_POLYTOPE_HEXAHEDRON;
4572         break;
4573       default:
4574         break;
4575       }
4576     }
4577   } else {
4578     if (pdepth == 0) {
4579       ct = DM_POLYTOPE_POINT;
4580     } else if (pheight == 0) {
4581       switch (dim) {
4582       case 1:
4583         switch (coneSize) {
4584         case 2:
4585           ct = DM_POLYTOPE_SEGMENT;
4586           break;
4587         default:
4588           break;
4589         }
4590         break;
4591       case 2:
4592         switch (coneSize) {
4593         case 3:
4594           ct = DM_POLYTOPE_TRIANGLE;
4595           break;
4596         case 4:
4597           ct = DM_POLYTOPE_QUADRILATERAL;
4598           break;
4599         default:
4600           break;
4601         }
4602         break;
4603       case 3:
4604         switch (coneSize) {
4605         case 4:
4606           ct = DM_POLYTOPE_TETRAHEDRON;
4607           break;
4608         case 5: {
4609           const PetscInt *cone;
4610           PetscInt        faceConeSize;
4611 
4612           PetscCall(DMPlexGetCone(dm, p, &cone));
4613           PetscCall(DMPlexGetConeSize(dm, cone[0], &faceConeSize));
4614           switch (faceConeSize) {
4615           case 3:
4616             ct = DM_POLYTOPE_TRI_PRISM_TENSOR;
4617             break;
4618           case 4:
4619             ct = DM_POLYTOPE_PYRAMID;
4620             break;
4621           }
4622         } break;
4623         case 6:
4624           ct = DM_POLYTOPE_HEXAHEDRON;
4625           break;
4626         default:
4627           break;
4628         }
4629         break;
4630       default:
4631         break;
4632       }
4633     } else if (pheight > 0) {
4634       switch (coneSize) {
4635       case 2:
4636         ct = DM_POLYTOPE_SEGMENT;
4637         break;
4638       case 3:
4639         ct = DM_POLYTOPE_TRIANGLE;
4640         break;
4641       case 4:
4642         ct = DM_POLYTOPE_QUADRILATERAL;
4643         break;
4644       default:
4645         break;
4646       }
4647     }
4648   }
4649   *pt = ct;
4650   PetscFunctionReturn(PETSC_SUCCESS);
4651 }
4652 
4653 /*@
4654   DMPlexComputeCellTypes - Infer the polytope type of every cell using its dimension and cone size.
4655 
4656   Collective
4657 
4658   Input Parameter:
4659 . dm - The `DMPLEX`
4660 
4661   Level: developer
4662 
4663   Note:
4664   This function is normally called automatically when a cell type is requested. It creates an
4665   internal `DMLabel` named "celltype" which can be directly accessed using `DMGetLabel()`. A user may disable
4666   automatic creation by creating the label manually, using `DMCreateLabel`(dm, "celltype").
4667 
4668   `DMPlexComputeCellTypes()` should be called after all calls to `DMPlexSymmetrize()` and `DMPlexStratify()`
4669 
4670 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexStratify()`, `DMGetLabel()`, `DMCreateLabel()`
4671 @*/
4672 PetscErrorCode DMPlexComputeCellTypes(DM dm)
4673 {
4674   DM_Plex *mesh;
4675   DMLabel  ctLabel;
4676   PetscInt pStart, pEnd, p;
4677 
4678   PetscFunctionBegin;
4679   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4680   mesh = (DM_Plex *)dm->data;
4681   PetscCall(DMCreateLabel(dm, "celltype"));
4682   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
4683   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4684   PetscCall(PetscFree(mesh->cellTypes));
4685   PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
4686   for (p = pStart; p < pEnd; ++p) {
4687     DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4688     PetscInt       pdepth;
4689 
4690     PetscCall(DMPlexGetPointDepth(dm, p, &pdepth));
4691     PetscCall(DMPlexComputeCellType_Internal(dm, p, pdepth, &ct));
4692     PetscCheck(ct != DM_POLYTOPE_UNKNOWN && ct != DM_POLYTOPE_UNKNOWN_CELL && ct != DM_POLYTOPE_UNKNOWN_FACE, PETSC_COMM_SELF, PETSC_ERR_SUP, "Point %" PetscInt_FMT " has invalid celltype (%s)", p, DMPolytopeTypes[ct]);
4693     PetscCall(DMLabelSetValue(ctLabel, p, ct));
4694     mesh->cellTypes[p - pStart].value_as_uint8 = ct;
4695   }
4696   PetscCall(PetscObjectStateGet((PetscObject)ctLabel, &mesh->celltypeState));
4697   PetscCall(PetscObjectViewFromOptions((PetscObject)ctLabel, NULL, "-dm_plex_celltypes_view"));
4698   PetscFunctionReturn(PETSC_SUCCESS);
4699 }
4700 
4701 /*@C
4702   DMPlexGetJoin - Get an array for the join of the set of points
4703 
4704   Not Collective
4705 
4706   Input Parameters:
4707 + dm        - The `DMPLEX` object
4708 . numPoints - The number of input points for the join
4709 - points    - The input points
4710 
4711   Output Parameters:
4712 + numCoveredPoints - The number of points in the join
4713 - coveredPoints    - The points in the join
4714 
4715   Level: intermediate
4716 
4717   Note:
4718   Currently, this is restricted to a single level join
4719 
4720   Fortran Notes:
4721   `converedPoints` must be declared with
4722 .vb
4723   PetscInt, pointer :: coveredPints(:)
4724 .ve
4725 
4726   The `numCoveredPoints` argument is not present in the Fortran binding.
4727 
4728 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4729 @*/
4730 PetscErrorCode DMPlexGetJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt *coveredPoints[])
4731 {
4732   DM_Plex  *mesh = (DM_Plex *)dm->data;
4733   PetscInt *join[2];
4734   PetscInt  joinSize, i = 0;
4735   PetscInt  dof, off, p, c, m;
4736   PetscInt  maxSupportSize;
4737 
4738   PetscFunctionBegin;
4739   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4740   PetscAssertPointer(points, 3);
4741   PetscAssertPointer(numCoveredPoints, 4);
4742   PetscAssertPointer(coveredPoints, 5);
4743   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
4744   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[0]));
4745   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[1]));
4746   /* Copy in support of first point */
4747   PetscCall(PetscSectionGetDof(mesh->supportSection, points[0], &dof));
4748   PetscCall(PetscSectionGetOffset(mesh->supportSection, points[0], &off));
4749   for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = mesh->supports[off + joinSize];
4750   /* Check each successive support */
4751   for (p = 1; p < numPoints; ++p) {
4752     PetscInt newJoinSize = 0;
4753 
4754     PetscCall(PetscSectionGetDof(mesh->supportSection, points[p], &dof));
4755     PetscCall(PetscSectionGetOffset(mesh->supportSection, points[p], &off));
4756     for (c = 0; c < dof; ++c) {
4757       const PetscInt point = mesh->supports[off + c];
4758 
4759       for (m = 0; m < joinSize; ++m) {
4760         if (point == join[i][m]) {
4761           join[1 - i][newJoinSize++] = point;
4762           break;
4763         }
4764       }
4765     }
4766     joinSize = newJoinSize;
4767     i        = 1 - i;
4768   }
4769   *numCoveredPoints = joinSize;
4770   *coveredPoints    = join[i];
4771   PetscCall(DMRestoreWorkArray(dm, maxSupportSize, MPIU_INT, &join[1 - i]));
4772   PetscFunctionReturn(PETSC_SUCCESS);
4773 }
4774 
4775 /*@C
4776   DMPlexRestoreJoin - Restore an array for the join of the set of points obtained with `DMPlexGetJoin()`
4777 
4778   Not Collective
4779 
4780   Input Parameters:
4781 + dm        - The `DMPLEX` object
4782 . numPoints - The number of input points for the join
4783 - points    - The input points
4784 
4785   Output Parameters:
4786 + numCoveredPoints - The number of points in the join
4787 - coveredPoints    - The points in the join
4788 
4789   Level: intermediate
4790 
4791   Fortran Notes:
4792   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4793 
4794 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetJoin()`, `DMPlexGetFullJoin()`, `DMPlexGetMeet()`
4795 @*/
4796 PetscErrorCode DMPlexRestoreJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt *coveredPoints[])
4797 {
4798   PetscFunctionBegin;
4799   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4800   if (points) PetscAssertPointer(points, 3);
4801   if (numCoveredPoints) PetscAssertPointer(numCoveredPoints, 4);
4802   PetscAssertPointer(coveredPoints, 5);
4803   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
4804   if (numCoveredPoints) *numCoveredPoints = 0;
4805   PetscFunctionReturn(PETSC_SUCCESS);
4806 }
4807 
4808 /*@C
4809   DMPlexGetFullJoin - Get an array for the join of the set of points
4810 
4811   Not Collective
4812 
4813   Input Parameters:
4814 + dm        - The `DMPLEX` object
4815 . numPoints - The number of input points for the join
4816 - points    - The input points, its length is `numPoints`
4817 
4818   Output Parameters:
4819 + numCoveredPoints - The number of points in the join
4820 - coveredPoints    - The points in the join, its length is `numCoveredPoints`
4821 
4822   Level: intermediate
4823 
4824   Fortran Notes:
4825   `points` and `converedPoints` must be declared with
4826 .vb
4827   PetscInt, pointer :: points(:)
4828   PetscInt, pointer :: coveredPints(:)
4829 .ve
4830 
4831   The `numCoveredPoints` argument is not present in the Fortran binding.
4832 
4833 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetJoin()`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4834 @*/
4835 PetscErrorCode DMPlexGetFullJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt *coveredPoints[])
4836 {
4837   PetscInt *offsets, **closures;
4838   PetscInt *join[2];
4839   PetscInt  depth = 0, maxSize, joinSize = 0, i = 0;
4840   PetscInt  p, d, c, m, ms;
4841 
4842   PetscFunctionBegin;
4843   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4844   PetscAssertPointer(points, 3);
4845   PetscAssertPointer(numCoveredPoints, 4);
4846   PetscAssertPointer(coveredPoints, 5);
4847 
4848   PetscCall(DMPlexGetDepth(dm, &depth));
4849   PetscCall(PetscCalloc1(numPoints, &closures));
4850   PetscCall(DMGetWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
4851   PetscCall(DMPlexGetMaxSizes(dm, NULL, &ms));
4852   maxSize = (ms > 1) ? ((PetscPowInt(ms, depth + 1) - 1) / (ms - 1)) : depth + 1;
4853   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[0]));
4854   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[1]));
4855 
4856   for (p = 0; p < numPoints; ++p) {
4857     PetscInt closureSize;
4858 
4859     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_FALSE, &closureSize, &closures[p]));
4860 
4861     offsets[p * (depth + 2) + 0] = 0;
4862     for (d = 0; d < depth + 1; ++d) {
4863       PetscInt pStart, pEnd, i;
4864 
4865       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
4866       for (i = offsets[p * (depth + 2) + d]; i < closureSize; ++i) {
4867         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
4868           offsets[p * (depth + 2) + d + 1] = i;
4869           break;
4870         }
4871       }
4872       if (i == closureSize) offsets[p * (depth + 2) + d + 1] = i;
4873     }
4874     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);
4875   }
4876   for (d = 0; d < depth + 1; ++d) {
4877     PetscInt dof;
4878 
4879     /* Copy in support of first point */
4880     dof = offsets[d + 1] - offsets[d];
4881     for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = closures[0][(offsets[d] + joinSize) * 2];
4882     /* Check each successive cone */
4883     for (p = 1; p < numPoints && joinSize; ++p) {
4884       PetscInt newJoinSize = 0;
4885 
4886       dof = offsets[p * (depth + 2) + d + 1] - offsets[p * (depth + 2) + d];
4887       for (c = 0; c < dof; ++c) {
4888         const PetscInt point = closures[p][(offsets[p * (depth + 2) + d] + c) * 2];
4889 
4890         for (m = 0; m < joinSize; ++m) {
4891           if (point == join[i][m]) {
4892             join[1 - i][newJoinSize++] = point;
4893             break;
4894           }
4895         }
4896       }
4897       joinSize = newJoinSize;
4898       i        = 1 - i;
4899     }
4900     if (joinSize) break;
4901   }
4902   *numCoveredPoints = joinSize;
4903   *coveredPoints    = join[i];
4904   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_FALSE, NULL, &closures[p]));
4905   PetscCall(PetscFree(closures));
4906   PetscCall(DMRestoreWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
4907   PetscCall(DMRestoreWorkArray(dm, ms, MPIU_INT, &join[1 - i]));
4908   PetscFunctionReturn(PETSC_SUCCESS);
4909 }
4910 
4911 /*@C
4912   DMPlexGetMeet - Get an array for the meet of the set of points
4913 
4914   Not Collective
4915 
4916   Input Parameters:
4917 + dm        - The `DMPLEX` object
4918 . numPoints - The number of input points for the meet
4919 - points    - The input points, of length `numPoints`
4920 
4921   Output Parameters:
4922 + numCoveringPoints - The number of points in the meet
4923 - coveringPoints    - The points in the meet, of length `numCoveringPoints`
4924 
4925   Level: intermediate
4926 
4927   Note:
4928   Currently, this is restricted to a single level meet
4929 
4930   Fortran Notes:
4931   `coveringPoints` must be declared with
4932 .vb
4933   PetscInt, pointer :: coveringPoints(:)
4934 .ve
4935 
4936   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4937 
4938 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
4939 @*/
4940 PetscErrorCode DMPlexGetMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveringPoints, const PetscInt *coveringPoints[])
4941 {
4942   DM_Plex  *mesh = (DM_Plex *)dm->data;
4943   PetscInt *meet[2];
4944   PetscInt  meetSize, i = 0;
4945   PetscInt  dof, off, p, c, m;
4946   PetscInt  maxConeSize;
4947 
4948   PetscFunctionBegin;
4949   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4950   PetscAssertPointer(points, 3);
4951   PetscAssertPointer(numCoveringPoints, 4);
4952   PetscAssertPointer(coveringPoints, 5);
4953   PetscCall(PetscSectionGetMaxDof(mesh->coneSection, &maxConeSize));
4954   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[0]));
4955   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[1]));
4956   /* Copy in cone of first point */
4957   PetscCall(PetscSectionGetDof(mesh->coneSection, points[0], &dof));
4958   PetscCall(PetscSectionGetOffset(mesh->coneSection, points[0], &off));
4959   for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = mesh->cones[off + meetSize];
4960   /* Check each successive cone */
4961   for (p = 1; p < numPoints; ++p) {
4962     PetscInt newMeetSize = 0;
4963 
4964     PetscCall(PetscSectionGetDof(mesh->coneSection, points[p], &dof));
4965     PetscCall(PetscSectionGetOffset(mesh->coneSection, points[p], &off));
4966     for (c = 0; c < dof; ++c) {
4967       const PetscInt point = mesh->cones[off + c];
4968 
4969       for (m = 0; m < meetSize; ++m) {
4970         if (point == meet[i][m]) {
4971           meet[1 - i][newMeetSize++] = point;
4972           break;
4973         }
4974       }
4975     }
4976     meetSize = newMeetSize;
4977     i        = 1 - i;
4978   }
4979   *numCoveringPoints = meetSize;
4980   *coveringPoints    = meet[i];
4981   PetscCall(DMRestoreWorkArray(dm, maxConeSize, MPIU_INT, &meet[1 - i]));
4982   PetscFunctionReturn(PETSC_SUCCESS);
4983 }
4984 
4985 /*@C
4986   DMPlexRestoreMeet - Restore an array for the meet of the set of points obtained with `DMPlexGetMeet()`
4987 
4988   Not Collective
4989 
4990   Input Parameters:
4991 + dm        - The `DMPLEX` object
4992 . numPoints - The number of input points for the meet
4993 - points    - The input points
4994 
4995   Output Parameters:
4996 + numCoveredPoints - The number of points in the meet
4997 - coveredPoints    - The points in the meet
4998 
4999   Level: intermediate
5000 
5001   Fortran Notes:
5002   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
5003 
5004 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetMeet()`, `DMPlexGetFullMeet()`, `DMPlexGetJoin()`
5005 @*/
5006 PetscErrorCode DMPlexRestoreMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt *coveredPoints[])
5007 {
5008   PetscFunctionBegin;
5009   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5010   if (points) PetscAssertPointer(points, 3);
5011   if (numCoveredPoints) PetscAssertPointer(numCoveredPoints, 4);
5012   PetscAssertPointer(coveredPoints, 5);
5013   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
5014   if (numCoveredPoints) *numCoveredPoints = 0;
5015   PetscFunctionReturn(PETSC_SUCCESS);
5016 }
5017 
5018 /*@C
5019   DMPlexGetFullMeet - Get an array for the meet of the set of points
5020 
5021   Not Collective
5022 
5023   Input Parameters:
5024 + dm        - The `DMPLEX` object
5025 . numPoints - The number of input points for the meet
5026 - points    - The input points, of length  `numPoints`
5027 
5028   Output Parameters:
5029 + numCoveredPoints - The number of points in the meet
5030 - coveredPoints    - The points in the meet, of length  `numCoveredPoints`
5031 
5032   Level: intermediate
5033 
5034   Fortran Notes:
5035   `points` and `coveredPoints` must be declared with
5036 .vb
5037   PetscInt, pointer :: points(:)
5038   PetscInt, pointer :: coveredPoints(:)
5039 .ve
5040 
5041   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
5042 
5043 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetMeet()`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
5044 @*/
5045 PetscErrorCode DMPlexGetFullMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt *coveredPoints[])
5046 {
5047   PetscInt *offsets, **closures;
5048   PetscInt *meet[2];
5049   PetscInt  height = 0, maxSize, meetSize = 0, i = 0;
5050   PetscInt  p, h, c, m, mc;
5051 
5052   PetscFunctionBegin;
5053   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5054   PetscAssertPointer(points, 3);
5055   PetscAssertPointer(numCoveredPoints, 4);
5056   PetscAssertPointer(coveredPoints, 5);
5057 
5058   PetscCall(DMPlexGetDepth(dm, &height));
5059   PetscCall(PetscMalloc1(numPoints, &closures));
5060   PetscCall(DMGetWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
5061   PetscCall(DMPlexGetMaxSizes(dm, &mc, NULL));
5062   maxSize = (mc > 1) ? ((PetscPowInt(mc, height + 1) - 1) / (mc - 1)) : height + 1;
5063   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[0]));
5064   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[1]));
5065 
5066   for (p = 0; p < numPoints; ++p) {
5067     PetscInt closureSize;
5068 
5069     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_TRUE, &closureSize, &closures[p]));
5070 
5071     offsets[p * (height + 2) + 0] = 0;
5072     for (h = 0; h < height + 1; ++h) {
5073       PetscInt pStart, pEnd, i;
5074 
5075       PetscCall(DMPlexGetHeightStratum(dm, h, &pStart, &pEnd));
5076       for (i = offsets[p * (height + 2) + h]; i < closureSize; ++i) {
5077         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
5078           offsets[p * (height + 2) + h + 1] = i;
5079           break;
5080         }
5081       }
5082       if (i == closureSize) offsets[p * (height + 2) + h + 1] = i;
5083     }
5084     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);
5085   }
5086   for (h = 0; h < height + 1; ++h) {
5087     PetscInt dof;
5088 
5089     /* Copy in cone of first point */
5090     dof = offsets[h + 1] - offsets[h];
5091     for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = closures[0][(offsets[h] + meetSize) * 2];
5092     /* Check each successive cone */
5093     for (p = 1; p < numPoints && meetSize; ++p) {
5094       PetscInt newMeetSize = 0;
5095 
5096       dof = offsets[p * (height + 2) + h + 1] - offsets[p * (height + 2) + h];
5097       for (c = 0; c < dof; ++c) {
5098         const PetscInt point = closures[p][(offsets[p * (height + 2) + h] + c) * 2];
5099 
5100         for (m = 0; m < meetSize; ++m) {
5101           if (point == meet[i][m]) {
5102             meet[1 - i][newMeetSize++] = point;
5103             break;
5104           }
5105         }
5106       }
5107       meetSize = newMeetSize;
5108       i        = 1 - i;
5109     }
5110     if (meetSize) break;
5111   }
5112   *numCoveredPoints = meetSize;
5113   *coveredPoints    = meet[i];
5114   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_TRUE, NULL, &closures[p]));
5115   PetscCall(PetscFree(closures));
5116   PetscCall(DMRestoreWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
5117   PetscCall(DMRestoreWorkArray(dm, mc, MPIU_INT, &meet[1 - i]));
5118   PetscFunctionReturn(PETSC_SUCCESS);
5119 }
5120 
5121 /*@
5122   DMPlexEqual - Determine if two `DM` have the same topology
5123 
5124   Not Collective
5125 
5126   Input Parameters:
5127 + dmA - A `DMPLEX` object
5128 - dmB - A `DMPLEX` object
5129 
5130   Output Parameter:
5131 . equal - `PETSC_TRUE` if the topologies are identical
5132 
5133   Level: intermediate
5134 
5135   Note:
5136   We are not solving graph isomorphism, so we do not permute.
5137 
5138 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCone()`
5139 @*/
5140 PetscErrorCode DMPlexEqual(DM dmA, DM dmB, PetscBool *equal)
5141 {
5142   PetscInt depth, depthB, pStart, pEnd, pStartB, pEndB, p;
5143 
5144   PetscFunctionBegin;
5145   PetscValidHeaderSpecific(dmA, DM_CLASSID, 1);
5146   PetscValidHeaderSpecific(dmB, DM_CLASSID, 2);
5147   PetscAssertPointer(equal, 3);
5148 
5149   *equal = PETSC_FALSE;
5150   PetscCall(DMPlexGetDepth(dmA, &depth));
5151   PetscCall(DMPlexGetDepth(dmB, &depthB));
5152   if (depth != depthB) PetscFunctionReturn(PETSC_SUCCESS);
5153   PetscCall(DMPlexGetChart(dmA, &pStart, &pEnd));
5154   PetscCall(DMPlexGetChart(dmB, &pStartB, &pEndB));
5155   if ((pStart != pStartB) || (pEnd != pEndB)) PetscFunctionReturn(PETSC_SUCCESS);
5156   for (p = pStart; p < pEnd; ++p) {
5157     const PetscInt *cone, *coneB, *ornt, *orntB, *support, *supportB;
5158     PetscInt        coneSize, coneSizeB, c, supportSize, supportSizeB, s;
5159 
5160     PetscCall(DMPlexGetConeSize(dmA, p, &coneSize));
5161     PetscCall(DMPlexGetCone(dmA, p, &cone));
5162     PetscCall(DMPlexGetConeOrientation(dmA, p, &ornt));
5163     PetscCall(DMPlexGetConeSize(dmB, p, &coneSizeB));
5164     PetscCall(DMPlexGetCone(dmB, p, &coneB));
5165     PetscCall(DMPlexGetConeOrientation(dmB, p, &orntB));
5166     if (coneSize != coneSizeB) PetscFunctionReturn(PETSC_SUCCESS);
5167     for (c = 0; c < coneSize; ++c) {
5168       if (cone[c] != coneB[c]) PetscFunctionReturn(PETSC_SUCCESS);
5169       if (ornt[c] != orntB[c]) PetscFunctionReturn(PETSC_SUCCESS);
5170     }
5171     PetscCall(DMPlexGetSupportSize(dmA, p, &supportSize));
5172     PetscCall(DMPlexGetSupport(dmA, p, &support));
5173     PetscCall(DMPlexGetSupportSize(dmB, p, &supportSizeB));
5174     PetscCall(DMPlexGetSupport(dmB, p, &supportB));
5175     if (supportSize != supportSizeB) PetscFunctionReturn(PETSC_SUCCESS);
5176     for (s = 0; s < supportSize; ++s) {
5177       if (support[s] != supportB[s]) PetscFunctionReturn(PETSC_SUCCESS);
5178     }
5179   }
5180   *equal = PETSC_TRUE;
5181   PetscFunctionReturn(PETSC_SUCCESS);
5182 }
5183 
5184 /*@
5185   DMPlexGetNumFaceVertices - Returns the number of vertices on a face
5186 
5187   Not Collective
5188 
5189   Input Parameters:
5190 + dm         - The `DMPLEX`
5191 . cellDim    - The cell dimension
5192 - numCorners - The number of vertices on a cell
5193 
5194   Output Parameter:
5195 . numFaceVertices - The number of vertices on a face
5196 
5197   Level: developer
5198 
5199   Note:
5200   Of course this can only work for a restricted set of symmetric shapes
5201 
5202 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCone()`
5203 @*/
5204 PetscErrorCode DMPlexGetNumFaceVertices(DM dm, PetscInt cellDim, PetscInt numCorners, PetscInt *numFaceVertices)
5205 {
5206   MPI_Comm comm;
5207 
5208   PetscFunctionBegin;
5209   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
5210   PetscAssertPointer(numFaceVertices, 4);
5211   switch (cellDim) {
5212   case 0:
5213     *numFaceVertices = 0;
5214     break;
5215   case 1:
5216     *numFaceVertices = 1;
5217     break;
5218   case 2:
5219     switch (numCorners) {
5220     case 3:                 /* triangle */
5221       *numFaceVertices = 2; /* Edge has 2 vertices */
5222       break;
5223     case 4:                 /* quadrilateral */
5224       *numFaceVertices = 2; /* Edge has 2 vertices */
5225       break;
5226     case 6:                 /* quadratic triangle, tri and quad cohesive Lagrange cells */
5227       *numFaceVertices = 3; /* Edge has 3 vertices */
5228       break;
5229     case 9:                 /* quadratic quadrilateral, quadratic quad cohesive Lagrange cells */
5230       *numFaceVertices = 3; /* Edge has 3 vertices */
5231       break;
5232     default:
5233       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
5234     }
5235     break;
5236   case 3:
5237     switch (numCorners) {
5238     case 4:                 /* tetradehdron */
5239       *numFaceVertices = 3; /* Face has 3 vertices */
5240       break;
5241     case 6:                 /* tet cohesive cells */
5242       *numFaceVertices = 4; /* Face has 4 vertices */
5243       break;
5244     case 8:                 /* hexahedron */
5245       *numFaceVertices = 4; /* Face has 4 vertices */
5246       break;
5247     case 9:                 /* tet cohesive Lagrange cells */
5248       *numFaceVertices = 6; /* Face has 6 vertices */
5249       break;
5250     case 10:                /* quadratic tetrahedron */
5251       *numFaceVertices = 6; /* Face has 6 vertices */
5252       break;
5253     case 12:                /* hex cohesive Lagrange cells */
5254       *numFaceVertices = 6; /* Face has 6 vertices */
5255       break;
5256     case 18:                /* quadratic tet cohesive Lagrange cells */
5257       *numFaceVertices = 6; /* Face has 6 vertices */
5258       break;
5259     case 27:                /* quadratic hexahedron, quadratic hex cohesive Lagrange cells */
5260       *numFaceVertices = 9; /* Face has 9 vertices */
5261       break;
5262     default:
5263       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
5264     }
5265     break;
5266   default:
5267     SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid cell dimension %" PetscInt_FMT, cellDim);
5268   }
5269   PetscFunctionReturn(PETSC_SUCCESS);
5270 }
5271 
5272 /*@
5273   DMPlexGetDepthLabel - Get the `DMLabel` recording the depth of each point
5274 
5275   Not Collective
5276 
5277   Input Parameter:
5278 . dm - The `DMPLEX` object
5279 
5280   Output Parameter:
5281 . depthLabel - The `DMLabel` recording point depth
5282 
5283   Level: developer
5284 
5285 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepth()`, `DMPlexGetHeightStratum()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`,
5286 @*/
5287 PetscErrorCode DMPlexGetDepthLabel(DM dm, DMLabel *depthLabel)
5288 {
5289   PetscFunctionBegin;
5290   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5291   PetscAssertPointer(depthLabel, 2);
5292   *depthLabel = dm->depthLabel;
5293   PetscFunctionReturn(PETSC_SUCCESS);
5294 }
5295 
5296 /*@
5297   DMPlexGetDepth - Get the depth of the DAG representing this mesh
5298 
5299   Not Collective
5300 
5301   Input Parameter:
5302 . dm - The `DMPLEX` object
5303 
5304   Output Parameter:
5305 . depth - The number of strata (breadth first levels) in the DAG
5306 
5307   Level: developer
5308 
5309   Notes:
5310   This returns maximum of point depths over all points, i.e. maximum value of the label returned by `DMPlexGetDepthLabel()`.
5311 
5312   The point depth is described more in detail in `DMPlexGetDepthStratum()`.
5313 
5314   An empty mesh gives -1.
5315 
5316 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepthLabel()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`
5317 @*/
5318 PetscErrorCode DMPlexGetDepth(DM dm, PetscInt *depth)
5319 {
5320   DM_Plex *mesh = (DM_Plex *)dm->data;
5321   DMLabel  label;
5322   PetscInt d = -1;
5323 
5324   PetscFunctionBegin;
5325   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5326   PetscAssertPointer(depth, 2);
5327   if (mesh->tr) {
5328     PetscCall(DMPlexTransformGetDepth(mesh->tr, depth));
5329   } else {
5330     PetscCall(DMPlexGetDepthLabel(dm, &label));
5331     // Allow missing depths
5332     if (label) PetscCall(DMLabelGetValueBounds(label, NULL, &d));
5333     *depth = d;
5334   }
5335   PetscFunctionReturn(PETSC_SUCCESS);
5336 }
5337 
5338 /*@
5339   DMPlexGetDepthStratum - Get the bounds [`start`, `end`) for all points at a certain depth.
5340 
5341   Not Collective
5342 
5343   Input Parameters:
5344 + dm    - The `DMPLEX` object
5345 - depth - The requested depth
5346 
5347   Output Parameters:
5348 + start - The first point at this `depth`
5349 - end   - One beyond the last point at this `depth`
5350 
5351   Level: developer
5352 
5353   Notes:
5354   Depth indexing is related to topological dimension.  Depth stratum 0 contains the lowest topological dimension points,
5355   often "vertices".  If the mesh is "interpolated" (see `DMPlexInterpolate()`), then depth stratum 1 contains the next
5356   higher dimension, e.g., "edges".
5357 
5358 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetHeightStratum()`, `DMPlexGetCellTypeStratum()`, `DMPlexGetDepth()`, `DMPlexGetDepthLabel()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`, `DMPlexInterpolate()`
5359 @*/
5360 PetscErrorCode DMPlexGetDepthStratum(DM dm, PetscInt depth, PetscInt *start, PetscInt *end)
5361 {
5362   DM_Plex *mesh = (DM_Plex *)dm->data;
5363   DMLabel  label;
5364   PetscInt pStart, pEnd;
5365 
5366   PetscFunctionBegin;
5367   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5368   if (start) {
5369     PetscAssertPointer(start, 3);
5370     *start = 0;
5371   }
5372   if (end) {
5373     PetscAssertPointer(end, 4);
5374     *end = 0;
5375   }
5376   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
5377   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
5378   if (depth < 0) {
5379     if (start) *start = pStart;
5380     if (end) *end = pEnd;
5381     PetscFunctionReturn(PETSC_SUCCESS);
5382   }
5383   if (mesh->tr) {
5384     PetscCall(DMPlexTransformGetDepthStratum(mesh->tr, depth, start, end));
5385   } else {
5386     PetscCall(DMPlexGetDepthLabel(dm, &label));
5387     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named depth was found");
5388     PetscCall(DMLabelGetStratumBounds(label, depth, start, end));
5389   }
5390   PetscFunctionReturn(PETSC_SUCCESS);
5391 }
5392 
5393 /*@
5394   DMPlexGetHeightStratum - Get the bounds [`start`, `end`) for all points at a certain height.
5395 
5396   Not Collective
5397 
5398   Input Parameters:
5399 + dm     - The `DMPLEX` object
5400 - height - The requested height
5401 
5402   Output Parameters:
5403 + start - The first point at this `height`
5404 - end   - One beyond the last point at this `height`
5405 
5406   Level: developer
5407 
5408   Notes:
5409   Height indexing is related to topological codimension.  Height stratum 0 contains the highest topological dimension
5410   points, often called "cells" or "elements".  If the mesh is "interpolated" (see `DMPlexInterpolate()`), then height
5411   stratum 1 contains the boundary of these "cells", often called "faces" or "facets".
5412 
5413 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepthStratum()`, `DMPlexGetCellTypeStratum()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
5414 @*/
5415 PetscErrorCode DMPlexGetHeightStratum(DM dm, PetscInt height, PetscInt *start, PetscInt *end)
5416 {
5417   DMLabel  label;
5418   PetscInt depth, pStart, pEnd;
5419 
5420   PetscFunctionBegin;
5421   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5422   if (start) {
5423     PetscAssertPointer(start, 3);
5424     *start = 0;
5425   }
5426   if (end) {
5427     PetscAssertPointer(end, 4);
5428     *end = 0;
5429   }
5430   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
5431   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
5432   if (height < 0) {
5433     if (start) *start = pStart;
5434     if (end) *end = pEnd;
5435     PetscFunctionReturn(PETSC_SUCCESS);
5436   }
5437   PetscCall(DMPlexGetDepthLabel(dm, &label));
5438   if (label) PetscCall(DMLabelGetNumValues(label, &depth));
5439   else PetscCall(DMGetDimension(dm, &depth));
5440   PetscCheck(depth >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Depth not yet computed");
5441   PetscCall(DMPlexGetDepthStratum(dm, depth - 1 - height, start, end));
5442   PetscFunctionReturn(PETSC_SUCCESS);
5443 }
5444 
5445 /*@
5446   DMPlexGetPointDepth - Get the `depth` of a given point
5447 
5448   Not Collective
5449 
5450   Input Parameters:
5451 + dm    - The `DMPLEX` object
5452 - point - The point
5453 
5454   Output Parameter:
5455 . depth - The depth of the `point`
5456 
5457   Level: intermediate
5458 
5459 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
5460 @*/
5461 PetscErrorCode DMPlexGetPointDepth(DM dm, PetscInt point, PetscInt *depth)
5462 {
5463   PetscFunctionBegin;
5464   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5465   PetscAssertPointer(depth, 3);
5466   PetscCall(DMLabelGetValue(dm->depthLabel, point, depth));
5467   PetscFunctionReturn(PETSC_SUCCESS);
5468 }
5469 
5470 /*@
5471   DMPlexGetPointHeight - Get the `height` of a given point
5472 
5473   Not Collective
5474 
5475   Input Parameters:
5476 + dm    - The `DMPLEX` object
5477 - point - The point
5478 
5479   Output Parameter:
5480 . height - The height of the `point`
5481 
5482   Level: intermediate
5483 
5484 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointDepth()`
5485 @*/
5486 PetscErrorCode DMPlexGetPointHeight(DM dm, PetscInt point, PetscInt *height)
5487 {
5488   PetscInt n, pDepth;
5489 
5490   PetscFunctionBegin;
5491   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5492   PetscAssertPointer(height, 3);
5493   PetscCall(DMLabelGetNumValues(dm->depthLabel, &n));
5494   PetscCall(DMLabelGetValue(dm->depthLabel, point, &pDepth));
5495   *height = n - 1 - pDepth; /* DAG depth is n-1 */
5496   PetscFunctionReturn(PETSC_SUCCESS);
5497 }
5498 
5499 /*@
5500   DMPlexGetCellTypeLabel - Get the `DMLabel` recording the polytope type of each cell
5501 
5502   Not Collective
5503 
5504   Input Parameter:
5505 . dm - The `DMPLEX` object
5506 
5507   Output Parameter:
5508 . celltypeLabel - The `DMLabel` recording cell polytope type
5509 
5510   Level: developer
5511 
5512   Note:
5513   This function will trigger automatica computation of cell types. This can be disabled by calling
5514   `DMCreateLabel`(dm, "celltype") beforehand.
5515 
5516 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMCreateLabel()`
5517 @*/
5518 PetscErrorCode DMPlexGetCellTypeLabel(DM dm, DMLabel *celltypeLabel)
5519 {
5520   PetscFunctionBegin;
5521   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5522   PetscAssertPointer(celltypeLabel, 2);
5523   if (!dm->celltypeLabel) PetscCall(DMPlexComputeCellTypes(dm));
5524   *celltypeLabel = dm->celltypeLabel;
5525   PetscFunctionReturn(PETSC_SUCCESS);
5526 }
5527 
5528 /*@
5529   DMPlexGetCellType - Get the polytope type of a given cell
5530 
5531   Not Collective
5532 
5533   Input Parameters:
5534 + dm   - The `DMPLEX` object
5535 - cell - The cell
5536 
5537   Output Parameter:
5538 . celltype - The polytope type of the cell
5539 
5540   Level: intermediate
5541 
5542 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPolytopeType`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`
5543 @*/
5544 PetscErrorCode DMPlexGetCellType(DM dm, PetscInt cell, DMPolytopeType *celltype)
5545 {
5546   DM_Plex *mesh = (DM_Plex *)dm->data;
5547   DMLabel  label;
5548   PetscInt ct;
5549 
5550   PetscFunctionBegin;
5551   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5552   PetscAssertPointer(celltype, 3);
5553   if (mesh->tr) {
5554     PetscCall(DMPlexTransformGetCellType(mesh->tr, cell, celltype));
5555   } else {
5556     PetscInt pStart, pEnd;
5557 
5558     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, NULL));
5559     if (!mesh->cellTypes) { /* XXX remove? optimize? */
5560       PetscCall(PetscSectionGetChart(mesh->coneSection, NULL, &pEnd));
5561       PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
5562       PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5563       for (PetscInt p = pStart; p < pEnd; p++) {
5564         PetscCall(DMLabelGetValue(label, p, &ct));
5565         mesh->cellTypes[p - pStart].value_as_uint8 = (DMPolytopeType)ct;
5566       }
5567     }
5568     *celltype = (DMPolytopeType)mesh->cellTypes[cell - pStart].value_as_uint8;
5569     if (PetscDefined(USE_DEBUG)) {
5570       PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5571       PetscCall(DMLabelGetValue(label, cell, &ct));
5572       PetscCheck(ct >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Cell %" PetscInt_FMT " has not been assigned a cell type", cell);
5573       PetscCheck(ct == (PetscInt)*celltype, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid cellType for %" PetscInt_FMT ": %d != %" PetscInt_FMT, cell, (int)*celltype, ct);
5574     }
5575   }
5576   PetscFunctionReturn(PETSC_SUCCESS);
5577 }
5578 
5579 /*@
5580   DMPlexSetCellType - Set the polytope type of a given cell
5581 
5582   Not Collective
5583 
5584   Input Parameters:
5585 + dm       - The `DMPLEX` object
5586 . cell     - The cell
5587 - celltype - The polytope type of the cell
5588 
5589   Level: advanced
5590 
5591   Note:
5592   By default, cell types will be automatically computed using `DMPlexComputeCellTypes()` before this function
5593   is executed. This function will override the computed type. However, if automatic classification will not succeed
5594   and a user wants to manually specify all types, the classification must be disabled by calling
5595   DMCreateLabel(dm, "celltype") before getting or setting any cell types.
5596 
5597 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexComputeCellTypes()`, `DMCreateLabel()`
5598 @*/
5599 PetscErrorCode DMPlexSetCellType(DM dm, PetscInt cell, DMPolytopeType celltype)
5600 {
5601   DM_Plex *mesh = (DM_Plex *)dm->data;
5602   DMLabel  label;
5603   PetscInt pStart, pEnd;
5604 
5605   PetscFunctionBegin;
5606   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5607   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
5608   PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5609   PetscCall(DMLabelSetValue(label, cell, celltype));
5610   if (!mesh->cellTypes) PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
5611   mesh->cellTypes[cell - pStart].value_as_uint8 = celltype;
5612   PetscFunctionReturn(PETSC_SUCCESS);
5613 }
5614 
5615 PetscErrorCode DMCreateCoordinateDM_Plex(DM dm, DM *cdm)
5616 {
5617   PetscSection section;
5618   PetscInt     maxHeight;
5619   const char  *prefix;
5620 
5621   PetscFunctionBegin;
5622   PetscCall(DMClone(dm, cdm));
5623   PetscCall(PetscObjectGetOptionsPrefix((PetscObject)dm, &prefix));
5624   PetscCall(PetscObjectSetOptionsPrefix((PetscObject)*cdm, prefix));
5625   PetscCall(PetscObjectAppendOptionsPrefix((PetscObject)*cdm, "cdm_"));
5626   PetscCall(DMPlexGetMaxProjectionHeight(dm, &maxHeight));
5627   PetscCall(DMPlexSetMaxProjectionHeight(*cdm, maxHeight));
5628   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
5629   PetscCall(DMSetLocalSection(*cdm, section));
5630   PetscCall(PetscSectionDestroy(&section));
5631 
5632   PetscCall(DMSetNumFields(*cdm, 1));
5633   PetscCall(DMCreateDS(*cdm));
5634   (*cdm)->cloneOpts = PETSC_TRUE;
5635   if (dm->setfromoptionscalled) PetscCall(DMSetFromOptions(*cdm));
5636   PetscFunctionReturn(PETSC_SUCCESS);
5637 }
5638 
5639 PetscErrorCode DMCreateCoordinateField_Plex(DM dm, DMField *field)
5640 {
5641   Vec coordsLocal, cellCoordsLocal;
5642   DM  coordsDM, cellCoordsDM;
5643 
5644   PetscFunctionBegin;
5645   *field = NULL;
5646   PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
5647   PetscCall(DMGetCoordinateDM(dm, &coordsDM));
5648   PetscCall(DMGetCellCoordinatesLocal(dm, &cellCoordsLocal));
5649   PetscCall(DMGetCellCoordinateDM(dm, &cellCoordsDM));
5650   if (coordsLocal && coordsDM) {
5651     if (cellCoordsLocal && cellCoordsDM) PetscCall(DMFieldCreateDSWithDG(coordsDM, cellCoordsDM, 0, coordsLocal, cellCoordsLocal, field));
5652     else PetscCall(DMFieldCreateDS(coordsDM, 0, coordsLocal, field));
5653   }
5654   PetscFunctionReturn(PETSC_SUCCESS);
5655 }
5656 
5657 /*@
5658   DMPlexGetConeSection - Return a section which describes the layout of cone data
5659 
5660   Not Collective
5661 
5662   Input Parameter:
5663 . dm - The `DMPLEX` object
5664 
5665   Output Parameter:
5666 . section - The `PetscSection` object
5667 
5668   Level: developer
5669 
5670 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSupportSection()`, `DMPlexGetCones()`, `DMPlexGetConeOrientations()`, `PetscSection`
5671 @*/
5672 PetscErrorCode DMPlexGetConeSection(DM dm, PetscSection *section)
5673 {
5674   DM_Plex *mesh = (DM_Plex *)dm->data;
5675 
5676   PetscFunctionBegin;
5677   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5678   if (section) *section = mesh->coneSection;
5679   PetscFunctionReturn(PETSC_SUCCESS);
5680 }
5681 
5682 /*@
5683   DMPlexGetSupportSection - Return a section which describes the layout of support data
5684 
5685   Not Collective
5686 
5687   Input Parameter:
5688 . dm - The `DMPLEX` object
5689 
5690   Output Parameter:
5691 . section - The `PetscSection` object
5692 
5693   Level: developer
5694 
5695 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `PetscSection`
5696 @*/
5697 PetscErrorCode DMPlexGetSupportSection(DM dm, PetscSection *section)
5698 {
5699   DM_Plex *mesh = (DM_Plex *)dm->data;
5700 
5701   PetscFunctionBegin;
5702   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5703   if (section) *section = mesh->supportSection;
5704   PetscFunctionReturn(PETSC_SUCCESS);
5705 }
5706 
5707 /*@C
5708   DMPlexGetCones - Return cone data
5709 
5710   Not Collective
5711 
5712   Input Parameter:
5713 . dm - The `DMPLEX` object
5714 
5715   Output Parameter:
5716 . cones - The cone for each point
5717 
5718   Level: developer
5719 
5720 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`
5721 @*/
5722 PetscErrorCode DMPlexGetCones(DM dm, PetscInt *cones[])
5723 {
5724   DM_Plex *mesh = (DM_Plex *)dm->data;
5725 
5726   PetscFunctionBegin;
5727   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5728   if (cones) *cones = mesh->cones;
5729   PetscFunctionReturn(PETSC_SUCCESS);
5730 }
5731 
5732 /*@C
5733   DMPlexGetConeOrientations - Return cone orientation data
5734 
5735   Not Collective
5736 
5737   Input Parameter:
5738 . dm - The `DMPLEX` object
5739 
5740   Output Parameter:
5741 . coneOrientations - The array of cone orientations for all points
5742 
5743   Level: developer
5744 
5745   Notes:
5746   The `PetscSection` returned by `DMPlexGetConeSection()` partitions coneOrientations into cone orientations of particular points
5747   as returned by `DMPlexGetConeOrientation()`.
5748 
5749   The meaning of coneOrientations values is detailed in `DMPlexGetConeOrientation()`.
5750 
5751 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `DMPlexGetConeOrientation()`, `PetscSection`
5752 @*/
5753 PetscErrorCode DMPlexGetConeOrientations(DM dm, PetscInt *coneOrientations[])
5754 {
5755   DM_Plex *mesh = (DM_Plex *)dm->data;
5756 
5757   PetscFunctionBegin;
5758   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5759   if (coneOrientations) *coneOrientations = mesh->coneOrientations;
5760   PetscFunctionReturn(PETSC_SUCCESS);
5761 }
5762 
5763 /******************************** FEM Support **********************************/
5764 
5765 PetscErrorCode DMPlexGetAllCells_Internal(DM plex, IS *cellIS)
5766 {
5767   PetscInt depth;
5768 
5769   PetscFunctionBegin;
5770   PetscCall(DMPlexGetDepth(plex, &depth));
5771   PetscCall(DMGetStratumIS(plex, "dim", depth, cellIS));
5772   if (!*cellIS) PetscCall(DMGetStratumIS(plex, "depth", depth, cellIS));
5773   PetscFunctionReturn(PETSC_SUCCESS);
5774 }
5775 
5776 PetscErrorCode DMPlexGetAllFaces_Internal(DM plex, IS *faceIS)
5777 {
5778   PetscInt depth;
5779 
5780   PetscFunctionBegin;
5781   PetscCall(DMPlexGetDepth(plex, &depth));
5782   PetscCall(DMGetStratumIS(plex, "dim", depth - 1, faceIS));
5783   if (!*faceIS) PetscCall(DMGetStratumIS(plex, "depth", depth - 1, faceIS));
5784   PetscFunctionReturn(PETSC_SUCCESS);
5785 }
5786 
5787 /*
5788  Returns number of components and tensor degree for the field.  For interpolated meshes, line should be a point
5789  representing a line in the section.
5790 */
5791 static PetscErrorCode PetscSectionFieldGetTensorDegree_Private(DM dm, PetscSection section, PetscInt field, PetscInt line, PetscInt *Nc, PetscInt *k, PetscBool *continuous, PetscBool *tensor)
5792 {
5793   PetscObject  obj;
5794   PetscClassId id;
5795   PetscFE      fe = NULL;
5796 
5797   PetscFunctionBeginHot;
5798   PetscCall(PetscSectionGetFieldComponents(section, field, Nc));
5799   PetscCall(DMGetField(dm, field, NULL, &obj));
5800   PetscCall(PetscObjectGetClassId(obj, &id));
5801   if (id == PETSCFE_CLASSID) fe = (PetscFE)obj;
5802 
5803   if (!fe) {
5804     /* Assume the full interpolated mesh is in the chart; lines in particular */
5805     /* An order k SEM disc has k-1 dofs on an edge */
5806     PetscCall(PetscSectionGetFieldDof(section, line, field, k));
5807     *k = *k / *Nc + 1;
5808   } else {
5809     PetscInt       dual_space_size, dim;
5810     PetscDualSpace dsp;
5811 
5812     PetscCall(DMGetDimension(dm, &dim));
5813     PetscCall(PetscFEGetDualSpace(fe, &dsp));
5814     PetscCall(PetscDualSpaceGetDimension(dsp, &dual_space_size));
5815     *k = (PetscInt)PetscCeilReal(PetscPowReal(dual_space_size / *Nc, 1.0 / dim)) - 1;
5816     PetscCall(PetscDualSpaceLagrangeGetContinuity(dsp, continuous));
5817     PetscCall(PetscDualSpaceLagrangeGetTensor(dsp, tensor));
5818   }
5819   PetscFunctionReturn(PETSC_SUCCESS);
5820 }
5821 
5822 static PetscErrorCode GetFieldSize_Private(PetscInt dim, PetscInt k, PetscBool tensor, PetscInt *dof)
5823 {
5824   PetscFunctionBeginHot;
5825   if (tensor) {
5826     *dof = PetscPowInt(k + 1, dim);
5827   } else {
5828     switch (dim) {
5829     case 1:
5830       *dof = k + 1;
5831       break;
5832     case 2:
5833       *dof = ((k + 1) * (k + 2)) / 2;
5834       break;
5835     case 3:
5836       *dof = ((k + 1) * (k + 2) * (k + 3)) / 6;
5837       break;
5838     default:
5839       *dof = 0;
5840     }
5841   }
5842   PetscFunctionReturn(PETSC_SUCCESS);
5843 }
5844 
5845 /*@
5846   DMPlexSetClosurePermutationTensor - Create a permutation from the default (BFS) point ordering in the closure, to a
5847   lexicographic ordering over the tensor product cell (i.e., line, quad, hex, etc.), and set this permutation in the
5848   section provided (or the section of the `DM`).
5849 
5850   Input Parameters:
5851 + dm      - The `DM`
5852 . point   - Either a cell (highest dim point) or an edge (dim 1 point), or `PETSC_DETERMINE`
5853 - section - The `PetscSection` to reorder, or `NULL` for the default section
5854 
5855   Example:
5856   A typical interpolated single-quad mesh might order points as
5857 .vb
5858   [c0, v1, v2, v3, v4, e5, e6, e7, e8]
5859 
5860   v4 -- e6 -- v3
5861   |           |
5862   e7    c0    e8
5863   |           |
5864   v1 -- e5 -- v2
5865 .ve
5866 
5867   (There is no significance to the ordering described here.)  The default section for a Q3 quad might typically assign
5868   dofs in the order of points, e.g.,
5869 .vb
5870     c0 -> [0,1,2,3]
5871     v1 -> [4]
5872     ...
5873     e5 -> [8, 9]
5874 .ve
5875 
5876   which corresponds to the dofs
5877 .vb
5878     6   10  11  7
5879     13  2   3   15
5880     12  0   1   14
5881     4   8   9   5
5882 .ve
5883 
5884   The closure in BFS ordering works through height strata (cells, edges, vertices) to produce the ordering
5885 .vb
5886   0 1 2 3 8 9 14 15 11 10 13 12 4 5 7 6
5887 .ve
5888 
5889   After calling DMPlexSetClosurePermutationTensor(), the closure will be ordered lexicographically,
5890 .vb
5891    4 8 9 5 12 0 1 14 13 2 3 15 6 10 11 7
5892 .ve
5893 
5894   Level: developer
5895 
5896   Notes:
5897   The point is used to determine the number of dofs/field on an edge. For SEM, this is related to the polynomial
5898   degree of the basis.
5899 
5900   This is required to run with libCEED.
5901 
5902 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMGetLocalSection()`, `PetscSectionSetClosurePermutation()`, `DMSetGlobalSection()`
5903 @*/
5904 PetscErrorCode DMPlexSetClosurePermutationTensor(DM dm, PetscInt point, PetscSection section)
5905 {
5906   DMLabel   label;
5907   PetscInt  dim, depth = -1, eStart = -1, Nf;
5908   PetscBool continuous = PETSC_TRUE, tensor = PETSC_TRUE;
5909 
5910   PetscFunctionBegin;
5911   PetscCall(DMGetDimension(dm, &dim));
5912   if (dim < 1) PetscFunctionReturn(PETSC_SUCCESS);
5913   if (point < 0) {
5914     PetscInt sStart, sEnd;
5915 
5916     PetscCall(DMPlexGetDepthStratum(dm, 1, &sStart, &sEnd));
5917     point = sEnd - sStart ? sStart : point;
5918   }
5919   PetscCall(DMPlexGetDepthLabel(dm, &label));
5920   if (point >= 0) PetscCall(DMLabelGetValue(label, point, &depth));
5921   if (!section) PetscCall(DMGetLocalSection(dm, &section));
5922   if (depth == 1) {
5923     eStart = point;
5924   } else if (depth == dim) {
5925     const PetscInt *cone;
5926 
5927     PetscCall(DMPlexGetCone(dm, point, &cone));
5928     if (dim == 2) eStart = cone[0];
5929     else if (dim == 3) {
5930       const PetscInt *cone2;
5931       PetscCall(DMPlexGetCone(dm, cone[0], &cone2));
5932       eStart = cone2[0];
5933     } 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);
5934   } 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);
5935 
5936   PetscCall(PetscSectionGetNumFields(section, &Nf));
5937   for (PetscInt d = 1; d <= dim; d++) {
5938     PetscInt  k, f, Nc, c, i, j, size = 0, offset = 0, foffset = 0;
5939     PetscInt *perm;
5940 
5941     for (f = 0; f < Nf; ++f) {
5942       PetscInt dof;
5943 
5944       PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
5945       PetscCheck(dim == 1 || tensor || !continuous, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Continuous field %" PetscInt_FMT " must have a tensor product discretization", f);
5946       if (!continuous && d < dim) continue;
5947       PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
5948       size += dof * Nc;
5949     }
5950     PetscCall(PetscMalloc1(size, &perm));
5951     for (f = 0; f < Nf; ++f) {
5952       switch (d) {
5953       case 1:
5954         PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
5955         if (!continuous && d < dim) continue;
5956         /*
5957          Original ordering is [ edge of length k-1; vtx0; vtx1 ]
5958          We want              [ vtx0; edge of length k-1; vtx1 ]
5959          */
5960         if (continuous) {
5961           for (c = 0; c < Nc; c++, offset++) perm[offset] = (k - 1) * Nc + c + foffset;
5962           for (i = 0; i < k - 1; i++)
5963             for (c = 0; c < Nc; c++, offset++) perm[offset] = i * Nc + c + foffset;
5964           for (c = 0; c < Nc; c++, offset++) perm[offset] = k * Nc + c + foffset;
5965           foffset = offset;
5966         } else {
5967           PetscInt dof;
5968 
5969           PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
5970           for (i = 0; i < dof * Nc; ++i, ++offset) perm[offset] = i + foffset;
5971           foffset = offset;
5972         }
5973         break;
5974       case 2:
5975         /* The original quad closure is oriented clockwise, {f, e_b, e_r, e_t, e_l, v_lb, v_rb, v_tr, v_tl} */
5976         PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
5977         if (!continuous && d < dim) continue;
5978         /* The SEM order is
5979 
5980          v_lb, {e_b}, v_rb,
5981          e^{(k-1)-i}_l, {f^{i*(k-1)}}, e^i_r,
5982          v_lt, reverse {e_t}, v_rt
5983          */
5984         if (continuous) {
5985           const PetscInt of   = 0;
5986           const PetscInt oeb  = of + PetscSqr(k - 1);
5987           const PetscInt oer  = oeb + (k - 1);
5988           const PetscInt oet  = oer + (k - 1);
5989           const PetscInt oel  = oet + (k - 1);
5990           const PetscInt ovlb = oel + (k - 1);
5991           const PetscInt ovrb = ovlb + 1;
5992           const PetscInt ovrt = ovrb + 1;
5993           const PetscInt ovlt = ovrt + 1;
5994           PetscInt       o;
5995 
5996           /* bottom */
5997           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlb * Nc + c + foffset;
5998           for (o = oeb; o < oer; ++o)
5999             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6000           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrb * Nc + c + foffset;
6001           /* middle */
6002           for (i = 0; i < k - 1; ++i) {
6003             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oel + (k - 2) - i) * Nc + c + foffset;
6004             for (o = of + (k - 1) * i; o < of + (k - 1) * (i + 1); ++o)
6005               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6006             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oer + i) * Nc + c + foffset;
6007           }
6008           /* top */
6009           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlt * Nc + c + foffset;
6010           for (o = oel - 1; o >= oet; --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] = ovrt * Nc + c + foffset;
6013           foffset = offset;
6014         } else {
6015           PetscInt dof;
6016 
6017           PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
6018           for (i = 0; i < dof * Nc; ++i, ++offset) perm[offset] = i + foffset;
6019           foffset = offset;
6020         }
6021         break;
6022       case 3:
6023         /* The original hex closure is
6024 
6025          {c,
6026          f_b, f_t, f_f, f_b, f_r, f_l,
6027          e_bl, e_bb, e_br, e_bf,  e_tf, e_tr, e_tb, e_tl,  e_rf, e_lf, e_lb, e_rb,
6028          v_blf, v_blb, v_brb, v_brf, v_tlf, v_trf, v_trb, v_tlb}
6029          */
6030         PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
6031         if (!continuous && d < dim) continue;
6032         /* The SEM order is
6033          Bottom Slice
6034          v_blf, {e^{(k-1)-n}_bf}, v_brf,
6035          e^{i}_bl, f^{n*(k-1)+(k-1)-i}_b, e^{(k-1)-i}_br,
6036          v_blb, {e_bb}, v_brb,
6037 
6038          Middle Slice (j)
6039          {e^{(k-1)-j}_lf}, {f^{j*(k-1)+n}_f}, e^j_rf,
6040          f^{i*(k-1)+j}_l, {c^{(j*(k-1) + i)*(k-1)+n}_t}, f^{j*(k-1)+i}_r,
6041          e^j_lb, {f^{j*(k-1)+(k-1)-n}_b}, e^{(k-1)-j}_rb,
6042 
6043          Top Slice
6044          v_tlf, {e_tf}, v_trf,
6045          e^{(k-1)-i}_tl, {f^{i*(k-1)}_t}, e^{i}_tr,
6046          v_tlb, {e^{(k-1)-n}_tb}, v_trb,
6047          */
6048         if (continuous) {
6049           const PetscInt oc    = 0;
6050           const PetscInt ofb   = oc + PetscSqr(k - 1) * (k - 1);
6051           const PetscInt oft   = ofb + PetscSqr(k - 1);
6052           const PetscInt off   = oft + PetscSqr(k - 1);
6053           const PetscInt ofk   = off + PetscSqr(k - 1);
6054           const PetscInt ofr   = ofk + PetscSqr(k - 1);
6055           const PetscInt ofl   = ofr + PetscSqr(k - 1);
6056           const PetscInt oebl  = ofl + PetscSqr(k - 1);
6057           const PetscInt oebb  = oebl + (k - 1);
6058           const PetscInt oebr  = oebb + (k - 1);
6059           const PetscInt oebf  = oebr + (k - 1);
6060           const PetscInt oetf  = oebf + (k - 1);
6061           const PetscInt oetr  = oetf + (k - 1);
6062           const PetscInt oetb  = oetr + (k - 1);
6063           const PetscInt oetl  = oetb + (k - 1);
6064           const PetscInt oerf  = oetl + (k - 1);
6065           const PetscInt oelf  = oerf + (k - 1);
6066           const PetscInt oelb  = oelf + (k - 1);
6067           const PetscInt oerb  = oelb + (k - 1);
6068           const PetscInt ovblf = oerb + (k - 1);
6069           const PetscInt ovblb = ovblf + 1;
6070           const PetscInt ovbrb = ovblb + 1;
6071           const PetscInt ovbrf = ovbrb + 1;
6072           const PetscInt ovtlf = ovbrf + 1;
6073           const PetscInt ovtrf = ovtlf + 1;
6074           const PetscInt ovtrb = ovtrf + 1;
6075           const PetscInt ovtlb = ovtrb + 1;
6076           PetscInt       o, n;
6077 
6078           /* Bottom Slice */
6079           /*   bottom */
6080           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblf * Nc + c + foffset;
6081           for (o = oetf - 1; o >= oebf; --o)
6082             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6083           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrf * Nc + c + foffset;
6084           /*   middle */
6085           for (i = 0; i < k - 1; ++i) {
6086             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebl + i) * Nc + c + foffset;
6087             for (n = 0; n < k - 1; ++n) {
6088               o = ofb + n * (k - 1) + i;
6089               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6090             }
6091             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebr + (k - 2) - i) * Nc + c + foffset;
6092           }
6093           /*   top */
6094           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblb * Nc + c + foffset;
6095           for (o = oebb; o < oebr; ++o)
6096             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6097           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrb * Nc + c + foffset;
6098 
6099           /* Middle Slice */
6100           for (j = 0; j < k - 1; ++j) {
6101             /*   bottom */
6102             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelf + (k - 2) - j) * Nc + c + foffset;
6103             for (o = off + j * (k - 1); o < off + (j + 1) * (k - 1); ++o)
6104               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6105             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerf + j) * Nc + c + foffset;
6106             /*   middle */
6107             for (i = 0; i < k - 1; ++i) {
6108               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofl + i * (k - 1) + j) * Nc + c + foffset;
6109               for (n = 0; n < k - 1; ++n)
6110                 for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oc + (j * (k - 1) + i) * (k - 1) + n) * Nc + c + foffset;
6111               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofr + j * (k - 1) + i) * Nc + c + foffset;
6112             }
6113             /*   top */
6114             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelb + j) * Nc + c + foffset;
6115             for (o = ofk + j * (k - 1) + (k - 2); o >= ofk + j * (k - 1); --o)
6116               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6117             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerb + (k - 2) - j) * Nc + c + foffset;
6118           }
6119 
6120           /* Top Slice */
6121           /*   bottom */
6122           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlf * Nc + c + foffset;
6123           for (o = oetf; o < oetr; ++o)
6124             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6125           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrf * Nc + c + foffset;
6126           /*   middle */
6127           for (i = 0; i < k - 1; ++i) {
6128             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetl + (k - 2) - i) * Nc + c + foffset;
6129             for (n = 0; n < k - 1; ++n)
6130               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oft + i * (k - 1) + n) * Nc + c + foffset;
6131             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetr + i) * Nc + c + foffset;
6132           }
6133           /*   top */
6134           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlb * Nc + c + foffset;
6135           for (o = oetl - 1; o >= oetb; --o)
6136             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6137           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrb * Nc + c + foffset;
6138 
6139           foffset = offset;
6140         } else {
6141           PetscInt dof;
6142 
6143           PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
6144           for (i = 0; i < dof * Nc; ++i, ++offset) perm[offset] = i + foffset;
6145           foffset = offset;
6146         }
6147         break;
6148       default:
6149         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "No spectral ordering for dimension %" PetscInt_FMT, d);
6150       }
6151     }
6152     PetscCheck(offset == size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Number of permutation entries %" PetscInt_FMT " != %" PetscInt_FMT, offset, size);
6153     /* Check permutation */
6154     {
6155       PetscInt *check;
6156 
6157       PetscCall(PetscMalloc1(size, &check));
6158       for (i = 0; i < size; ++i) {
6159         check[i] = -1;
6160         PetscCheck(perm[i] >= 0 && perm[i] < size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid permutation index p[%" PetscInt_FMT "] = %" PetscInt_FMT, i, perm[i]);
6161       }
6162       for (i = 0; i < size; ++i) check[perm[i]] = i;
6163       for (i = 0; i < size; ++i) PetscCheck(check[i] >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Missing permutation index %" PetscInt_FMT, i);
6164       PetscCall(PetscFree(check));
6165     }
6166     PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size, PETSC_OWN_POINTER, perm));
6167     if (d == dim) { // Add permutation for localized (in case this is a coordinate DM)
6168       PetscInt *loc_perm;
6169       PetscCall(PetscMalloc1(size * 2, &loc_perm));
6170       for (PetscInt i = 0; i < size; i++) {
6171         loc_perm[i]        = perm[i];
6172         loc_perm[size + i] = size + perm[i];
6173       }
6174       PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size * 2, PETSC_OWN_POINTER, loc_perm));
6175     }
6176   }
6177   PetscFunctionReturn(PETSC_SUCCESS);
6178 }
6179 
6180 PetscErrorCode DMPlexGetPointDualSpaceFEM(DM dm, PetscInt point, PetscInt field, PetscDualSpace *dspace)
6181 {
6182   PetscDS  prob;
6183   PetscInt depth, Nf, h;
6184   DMLabel  label;
6185 
6186   PetscFunctionBeginHot;
6187   PetscCall(DMGetDS(dm, &prob));
6188   Nf      = prob->Nf;
6189   label   = dm->depthLabel;
6190   *dspace = NULL;
6191   if (field < Nf) {
6192     PetscObject disc = prob->disc[field];
6193 
6194     if (disc->classid == PETSCFE_CLASSID) {
6195       PetscDualSpace dsp;
6196 
6197       PetscCall(PetscFEGetDualSpace((PetscFE)disc, &dsp));
6198       PetscCall(DMLabelGetNumValues(label, &depth));
6199       PetscCall(DMLabelGetValue(label, point, &h));
6200       h = depth - 1 - h;
6201       if (h) {
6202         PetscCall(PetscDualSpaceGetHeightSubspace(dsp, h, dspace));
6203       } else {
6204         *dspace = dsp;
6205       }
6206     }
6207   }
6208   PetscFunctionReturn(PETSC_SUCCESS);
6209 }
6210 
6211 static inline PetscErrorCode DMPlexVecGetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6212 {
6213   PetscScalar       *array;
6214   const PetscScalar *vArray;
6215   const PetscInt    *cone, *coneO;
6216   PetscInt           pStart, pEnd, p, numPoints, size = 0, offset = 0;
6217 
6218   PetscFunctionBeginHot;
6219   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6220   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
6221   PetscCall(DMPlexGetCone(dm, point, &cone));
6222   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
6223   if (!values || !*values) {
6224     if ((point >= pStart) && (point < pEnd)) {
6225       PetscInt dof;
6226 
6227       PetscCall(PetscSectionGetDof(section, point, &dof));
6228       size += dof;
6229     }
6230     for (p = 0; p < numPoints; ++p) {
6231       const PetscInt cp = cone[p];
6232       PetscInt       dof;
6233 
6234       if ((cp < pStart) || (cp >= pEnd)) continue;
6235       PetscCall(PetscSectionGetDof(section, cp, &dof));
6236       size += dof;
6237     }
6238     if (!values) {
6239       if (csize) *csize = size;
6240       PetscFunctionReturn(PETSC_SUCCESS);
6241     }
6242     PetscCall(DMGetWorkArray(dm, size, MPIU_SCALAR, &array));
6243   } else {
6244     array = *values;
6245   }
6246   size = 0;
6247   PetscCall(VecGetArrayRead(v, &vArray));
6248   if ((point >= pStart) && (point < pEnd)) {
6249     PetscInt           dof, off, d;
6250     const PetscScalar *varr;
6251 
6252     PetscCall(PetscSectionGetDof(section, point, &dof));
6253     PetscCall(PetscSectionGetOffset(section, point, &off));
6254     varr = PetscSafePointerPlusOffset(vArray, off);
6255     for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
6256     size += dof;
6257   }
6258   for (p = 0; p < numPoints; ++p) {
6259     const PetscInt     cp = cone[p];
6260     PetscInt           o  = coneO[p];
6261     PetscInt           dof, off, d;
6262     const PetscScalar *varr;
6263 
6264     if ((cp < pStart) || (cp >= pEnd)) continue;
6265     PetscCall(PetscSectionGetDof(section, cp, &dof));
6266     PetscCall(PetscSectionGetOffset(section, cp, &off));
6267     varr = PetscSafePointerPlusOffset(vArray, off);
6268     if (o >= 0) {
6269       for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
6270     } else {
6271       for (d = dof - 1; d >= 0; --d, ++offset) array[offset] = varr[d];
6272     }
6273     size += dof;
6274   }
6275   PetscCall(VecRestoreArrayRead(v, &vArray));
6276   if (!*values) {
6277     if (csize) *csize = size;
6278     *values = array;
6279   } else {
6280     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
6281     *csize = size;
6282   }
6283   PetscFunctionReturn(PETSC_SUCCESS);
6284 }
6285 
6286 /* Compress out points not in the section */
6287 static inline PetscErrorCode CompressPoints_Private(PetscSection section, PetscInt *numPoints, PetscInt points[])
6288 {
6289   const PetscInt np = *numPoints;
6290   PetscInt       pStart, pEnd, p, q;
6291 
6292   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6293   for (p = 0, q = 0; p < np; ++p) {
6294     const PetscInt r = points[p * 2];
6295     if ((r >= pStart) && (r < pEnd)) {
6296       points[q * 2]     = r;
6297       points[q * 2 + 1] = points[p * 2 + 1];
6298       ++q;
6299     }
6300   }
6301   *numPoints = q;
6302   return PETSC_SUCCESS;
6303 }
6304 
6305 /* Compressed closure does not apply closure permutation */
6306 PetscErrorCode DMPlexGetCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt ornt, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
6307 {
6308   const PetscInt *cla = NULL;
6309   PetscInt        np, *pts = NULL;
6310 
6311   PetscFunctionBeginHot;
6312   PetscCall(PetscSectionGetClosureIndex(section, (PetscObject)dm, clSec, clPoints));
6313   if (!ornt && *clPoints) {
6314     PetscInt dof, off;
6315 
6316     PetscCall(PetscSectionGetDof(*clSec, point, &dof));
6317     PetscCall(PetscSectionGetOffset(*clSec, point, &off));
6318     PetscCall(ISGetIndices(*clPoints, &cla));
6319     np  = dof / 2;
6320     pts = PetscSafePointerPlusOffset((PetscInt *)cla, off);
6321   } else {
6322     PetscCall(DMPlexGetTransitiveClosure_Internal(dm, point, ornt, PETSC_TRUE, &np, &pts));
6323     PetscCall(CompressPoints_Private(section, &np, pts));
6324   }
6325   *numPoints = np;
6326   *points    = pts;
6327   *clp       = cla;
6328   PetscFunctionReturn(PETSC_SUCCESS);
6329 }
6330 
6331 PetscErrorCode DMPlexRestoreCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
6332 {
6333   PetscFunctionBeginHot;
6334   if (!*clPoints) {
6335     PetscCall(DMPlexRestoreTransitiveClosure(dm, point, PETSC_TRUE, numPoints, points));
6336   } else {
6337     PetscCall(ISRestoreIndices(*clPoints, clp));
6338   }
6339   *numPoints = 0;
6340   *points    = NULL;
6341   *clSec     = NULL;
6342   *clPoints  = NULL;
6343   *clp       = NULL;
6344   PetscFunctionReturn(PETSC_SUCCESS);
6345 }
6346 
6347 static inline PetscErrorCode DMPlexVecGetClosure_Static(DM dm, PetscSection section, PetscInt numPoints, const PetscInt points[], const PetscInt clperm[], const PetscScalar vArray[], PetscInt *size, PetscScalar array[])
6348 {
6349   PetscInt            offset = 0, p;
6350   const PetscInt    **perms  = NULL;
6351   const PetscScalar **flips  = NULL;
6352 
6353   PetscFunctionBeginHot;
6354   *size = 0;
6355   PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
6356   for (p = 0; p < numPoints; p++) {
6357     const PetscInt     point = points[2 * p];
6358     const PetscInt    *perm  = perms ? perms[p] : NULL;
6359     const PetscScalar *flip  = flips ? flips[p] : NULL;
6360     PetscInt           dof, off, d;
6361     const PetscScalar *varr;
6362 
6363     PetscCall(PetscSectionGetDof(section, point, &dof));
6364     PetscCall(PetscSectionGetOffset(section, point, &off));
6365     varr = PetscSafePointerPlusOffset(vArray, off);
6366     if (clperm) {
6367       if (perm) {
6368         for (d = 0; d < dof; d++) array[clperm[offset + perm[d]]] = varr[d];
6369       } else {
6370         for (d = 0; d < dof; d++) array[clperm[offset + d]] = varr[d];
6371       }
6372       if (flip) {
6373         for (d = 0; d < dof; d++) array[clperm[offset + d]] *= flip[d];
6374       }
6375     } else {
6376       if (perm) {
6377         for (d = 0; d < dof; d++) array[offset + perm[d]] = varr[d];
6378       } else {
6379         for (d = 0; d < dof; d++) array[offset + d] = varr[d];
6380       }
6381       if (flip) {
6382         for (d = 0; d < dof; d++) array[offset + d] *= flip[d];
6383       }
6384     }
6385     offset += dof;
6386   }
6387   PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
6388   *size = offset;
6389   PetscFunctionReturn(PETSC_SUCCESS);
6390 }
6391 
6392 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[])
6393 {
6394   PetscInt offset = 0, f;
6395 
6396   PetscFunctionBeginHot;
6397   *size = 0;
6398   for (f = 0; f < numFields; ++f) {
6399     PetscInt            p;
6400     const PetscInt    **perms = NULL;
6401     const PetscScalar **flips = NULL;
6402 
6403     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6404     for (p = 0; p < numPoints; p++) {
6405       const PetscInt     point = points[2 * p];
6406       PetscInt           fdof, foff, b;
6407       const PetscScalar *varr;
6408       const PetscInt    *perm = perms ? perms[p] : NULL;
6409       const PetscScalar *flip = flips ? flips[p] : NULL;
6410 
6411       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6412       PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6413       varr = &vArray[foff];
6414       if (clperm) {
6415         if (perm) {
6416           for (b = 0; b < fdof; b++) array[clperm[offset + perm[b]]] = varr[b];
6417         } else {
6418           for (b = 0; b < fdof; b++) array[clperm[offset + b]] = varr[b];
6419         }
6420         if (flip) {
6421           for (b = 0; b < fdof; b++) array[clperm[offset + b]] *= flip[b];
6422         }
6423       } else {
6424         if (perm) {
6425           for (b = 0; b < fdof; b++) array[offset + perm[b]] = varr[b];
6426         } else {
6427           for (b = 0; b < fdof; b++) array[offset + b] = varr[b];
6428         }
6429         if (flip) {
6430           for (b = 0; b < fdof; b++) array[offset + b] *= flip[b];
6431         }
6432       }
6433       offset += fdof;
6434     }
6435     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6436   }
6437   *size = offset;
6438   PetscFunctionReturn(PETSC_SUCCESS);
6439 }
6440 
6441 PetscErrorCode DMPlexVecGetOrientedClosure_Internal(DM dm, PetscSection section, PetscBool useClPerm, Vec v, PetscInt point, PetscInt ornt, PetscInt *csize, PetscScalar *values[])
6442 {
6443   PetscSection    clSection;
6444   IS              clPoints;
6445   PetscInt       *points = NULL;
6446   const PetscInt *clp, *perm = NULL;
6447   PetscInt        depth, numFields, numPoints, asize;
6448 
6449   PetscFunctionBeginHot;
6450   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6451   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6452   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6453   PetscValidHeaderSpecific(v, VEC_CLASSID, 4);
6454   PetscCall(DMPlexGetDepth(dm, &depth));
6455   PetscCall(PetscSectionGetNumFields(section, &numFields));
6456   if (depth == 1 && numFields < 2) {
6457     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6458     PetscFunctionReturn(PETSC_SUCCESS);
6459   }
6460   /* Get points */
6461   PetscCall(DMPlexGetCompressedClosure(dm, section, point, ornt, &numPoints, &points, &clSection, &clPoints, &clp));
6462   /* Get sizes */
6463   asize = 0;
6464   for (PetscInt p = 0; p < numPoints * 2; p += 2) {
6465     PetscInt dof;
6466     PetscCall(PetscSectionGetDof(section, points[p], &dof));
6467     asize += dof;
6468   }
6469   if (values) {
6470     const PetscScalar *vArray;
6471     PetscInt           size;
6472 
6473     if (*values) {
6474       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);
6475     } else PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, values));
6476     if (useClPerm) PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, asize, &perm));
6477     PetscCall(VecGetArrayRead(v, &vArray));
6478     /* Get values */
6479     if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, numPoints, points, numFields, perm, vArray, &size, *values));
6480     else PetscCall(DMPlexVecGetClosure_Static(dm, section, numPoints, points, perm, vArray, &size, *values));
6481     PetscCheck(asize == size, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Section size %" PetscInt_FMT " does not match Vec closure size %" PetscInt_FMT, asize, size);
6482     /* Cleanup array */
6483     PetscCall(VecRestoreArrayRead(v, &vArray));
6484   }
6485   if (csize) *csize = asize;
6486   /* Cleanup points */
6487   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6488   PetscFunctionReturn(PETSC_SUCCESS);
6489 }
6490 
6491 /*@C
6492   DMPlexVecGetClosure - Get an array of the values on the closure of 'point'
6493 
6494   Not collective
6495 
6496   Input Parameters:
6497 + dm      - The `DM`
6498 . section - The section describing the layout in `v`, or `NULL` to use the default section
6499 . v       - The local vector
6500 - point   - The point in the `DM`
6501 
6502   Input/Output Parameters:
6503 + csize  - The size of the input values array, or `NULL`; on output the number of values in the closure
6504 - values - An array to use for the values, or *values = `NULL` to have it allocated automatically;
6505            if the user provided `NULL`, it is a borrowed array and should not be freed, use  `DMPlexVecRestoreClosure()` to return it
6506 
6507   Level: intermediate
6508 
6509   Notes:
6510   `DMPlexVecGetClosure()`/`DMPlexVecRestoreClosure()` only allocates the values array if it set to `NULL` in the
6511   calling function. This is because `DMPlexVecGetClosure()` is typically called in the inner loop of a `Vec` or `Mat`
6512   assembly function, and a user may already have allocated storage for this operation.
6513 
6514   A typical use could be
6515 .vb
6516    values = NULL;
6517    PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
6518    for (cl = 0; cl < clSize; ++cl) {
6519      <Compute on closure>
6520    }
6521    PetscCall(DMPlexVecRestoreClosure(dm, NULL, v, p, &clSize, &values));
6522 .ve
6523   or
6524 .vb
6525    PetscMalloc1(clMaxSize, &values);
6526    for (p = pStart; p < pEnd; ++p) {
6527      clSize = clMaxSize;
6528      PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
6529      for (cl = 0; cl < clSize; ++cl) {
6530        <Compute on closure>
6531      }
6532    }
6533    PetscFree(values);
6534 .ve
6535 
6536   Fortran Notes:
6537   The `csize` argument is not present in the Fortran binding.
6538 
6539   `values` must be declared with
6540 .vb
6541   PetscScalar,dimension(:),pointer   :: values
6542 .ve
6543   and it will be allocated internally by PETSc to hold the values returned
6544 
6545 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecRestoreClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6546 @*/
6547 PetscErrorCode DMPlexVecGetClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6548 {
6549   PetscFunctionBeginHot;
6550   PetscCall(DMPlexVecGetOrientedClosure_Internal(dm, section, PETSC_TRUE, v, point, 0, csize, values));
6551   PetscFunctionReturn(PETSC_SUCCESS);
6552 }
6553 
6554 PetscErrorCode DMPlexVecGetClosureAtDepth_Internal(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt depth, PetscInt *csize, PetscScalar *values[])
6555 {
6556   DMLabel            depthLabel;
6557   PetscSection       clSection;
6558   IS                 clPoints;
6559   PetscScalar       *array;
6560   const PetscScalar *vArray;
6561   PetscInt          *points = NULL;
6562   const PetscInt    *clp, *perm = NULL;
6563   PetscInt           mdepth, numFields, numPoints, Np = 0, p, clsize, size;
6564 
6565   PetscFunctionBeginHot;
6566   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6567   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6568   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6569   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6570   PetscCall(DMPlexGetDepth(dm, &mdepth));
6571   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
6572   PetscCall(PetscSectionGetNumFields(section, &numFields));
6573   if (mdepth == 1 && numFields < 2) {
6574     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6575     PetscFunctionReturn(PETSC_SUCCESS);
6576   }
6577   /* Get points */
6578   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
6579   for (clsize = 0, p = 0; p < Np; p++) {
6580     PetscInt dof;
6581     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
6582     clsize += dof;
6583   }
6584   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &perm));
6585   /* Filter points */
6586   for (p = 0; p < numPoints * 2; p += 2) {
6587     PetscInt dep;
6588 
6589     PetscCall(DMLabelGetValue(depthLabel, points[p], &dep));
6590     if (dep != depth) continue;
6591     points[Np * 2 + 0] = points[p];
6592     points[Np * 2 + 1] = points[p + 1];
6593     ++Np;
6594   }
6595   /* Get array */
6596   if (!values || !*values) {
6597     PetscInt asize = 0, dof;
6598 
6599     for (p = 0; p < Np * 2; p += 2) {
6600       PetscCall(PetscSectionGetDof(section, points[p], &dof));
6601       asize += dof;
6602     }
6603     if (!values) {
6604       PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6605       if (csize) *csize = asize;
6606       PetscFunctionReturn(PETSC_SUCCESS);
6607     }
6608     PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, &array));
6609   } else {
6610     array = *values;
6611   }
6612   PetscCall(VecGetArrayRead(v, &vArray));
6613   /* Get values */
6614   if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, Np, points, numFields, perm, vArray, &size, array));
6615   else PetscCall(DMPlexVecGetClosure_Static(dm, section, Np, points, perm, vArray, &size, array));
6616   /* Cleanup points */
6617   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6618   /* Cleanup array */
6619   PetscCall(VecRestoreArrayRead(v, &vArray));
6620   if (!*values) {
6621     if (csize) *csize = size;
6622     *values = array;
6623   } else {
6624     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
6625     *csize = size;
6626   }
6627   PetscFunctionReturn(PETSC_SUCCESS);
6628 }
6629 
6630 /*@C
6631   DMPlexVecRestoreClosure - Restore the array of the values on the closure of 'point' obtained with `DMPlexVecGetClosure()`
6632 
6633   Not collective
6634 
6635   Input Parameters:
6636 + dm      - The `DM`
6637 . section - The section describing the layout in `v`, or `NULL` to use the default section
6638 . v       - The local vector
6639 . point   - The point in the `DM`
6640 . csize   - The number of values in the closure, or `NULL`
6641 - values  - The array of values
6642 
6643   Level: intermediate
6644 
6645   Note:
6646   The array values are discarded and not copied back into `v`. In order to copy values back to `v`, use `DMPlexVecSetClosure()`
6647 
6648   Fortran Note:
6649   The `csize` argument is not present in the Fortran binding since it is internal to the array.
6650 
6651 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6652 @*/
6653 PetscErrorCode DMPlexVecRestoreClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6654 {
6655   PetscInt size = 0;
6656 
6657   PetscFunctionBegin;
6658   /* Should work without recalculating size */
6659   PetscCall(DMRestoreWorkArray(dm, size, MPIU_SCALAR, (void *)values));
6660   *values = NULL;
6661   PetscFunctionReturn(PETSC_SUCCESS);
6662 }
6663 
6664 static inline void add(PetscScalar *x, PetscScalar y)
6665 {
6666   *x += y;
6667 }
6668 static inline void insert(PetscScalar *x, PetscScalar y)
6669 {
6670   *x = y;
6671 }
6672 
6673 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[])
6674 {
6675   PetscInt        cdof;  /* The number of constraints on this point */
6676   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6677   PetscScalar    *a;
6678   PetscInt        off, cind = 0, k;
6679 
6680   PetscFunctionBegin;
6681   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6682   PetscCall(PetscSectionGetOffset(section, point, &off));
6683   a = &array[off];
6684   if (!cdof || setBC) {
6685     if (clperm) {
6686       if (perm) {
6687         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6688       } else {
6689         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6690       }
6691     } else {
6692       if (perm) {
6693         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6694       } else {
6695         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6696       }
6697     }
6698   } else {
6699     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6700     if (clperm) {
6701       if (perm) {
6702         for (k = 0; k < dof; ++k) {
6703           if ((cind < cdof) && (k == cdofs[cind])) {
6704             ++cind;
6705             continue;
6706           }
6707           fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6708         }
6709       } else {
6710         for (k = 0; k < dof; ++k) {
6711           if ((cind < cdof) && (k == cdofs[cind])) {
6712             ++cind;
6713             continue;
6714           }
6715           fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6716         }
6717       }
6718     } else {
6719       if (perm) {
6720         for (k = 0; k < dof; ++k) {
6721           if ((cind < cdof) && (k == cdofs[cind])) {
6722             ++cind;
6723             continue;
6724           }
6725           fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6726         }
6727       } else {
6728         for (k = 0; k < dof; ++k) {
6729           if ((cind < cdof) && (k == cdofs[cind])) {
6730             ++cind;
6731             continue;
6732           }
6733           fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6734         }
6735       }
6736     }
6737   }
6738   PetscFunctionReturn(PETSC_SUCCESS);
6739 }
6740 
6741 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[])
6742 {
6743   PetscInt        cdof;  /* The number of constraints on this point */
6744   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6745   PetscScalar    *a;
6746   PetscInt        off, cind = 0, k;
6747 
6748   PetscFunctionBegin;
6749   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6750   PetscCall(PetscSectionGetOffset(section, point, &off));
6751   a = &array[off];
6752   if (cdof) {
6753     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6754     if (clperm) {
6755       if (perm) {
6756         for (k = 0; k < dof; ++k) {
6757           if ((cind < cdof) && (k == cdofs[cind])) {
6758             fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6759             cind++;
6760           }
6761         }
6762       } else {
6763         for (k = 0; k < dof; ++k) {
6764           if ((cind < cdof) && (k == cdofs[cind])) {
6765             fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6766             cind++;
6767           }
6768         }
6769       }
6770     } else {
6771       if (perm) {
6772         for (k = 0; k < dof; ++k) {
6773           if ((cind < cdof) && (k == cdofs[cind])) {
6774             fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6775             cind++;
6776           }
6777         }
6778       } else {
6779         for (k = 0; k < dof; ++k) {
6780           if ((cind < cdof) && (k == cdofs[cind])) {
6781             fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6782             cind++;
6783           }
6784         }
6785       }
6786     }
6787   }
6788   PetscFunctionReturn(PETSC_SUCCESS);
6789 }
6790 
6791 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[])
6792 {
6793   PetscScalar    *a;
6794   PetscInt        fdof, foff, fcdof, foffset = *offset;
6795   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6796   PetscInt        cind = 0, b;
6797 
6798   PetscFunctionBegin;
6799   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6800   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6801   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6802   a = &array[foff];
6803   if (!fcdof || setBC) {
6804     if (clperm) {
6805       if (perm) {
6806         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6807       } else {
6808         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6809       }
6810     } else {
6811       if (perm) {
6812         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6813       } else {
6814         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6815       }
6816     }
6817   } else {
6818     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6819     if (clperm) {
6820       if (perm) {
6821         for (b = 0; b < fdof; b++) {
6822           if ((cind < fcdof) && (b == fcdofs[cind])) {
6823             ++cind;
6824             continue;
6825           }
6826           fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6827         }
6828       } else {
6829         for (b = 0; b < fdof; b++) {
6830           if ((cind < fcdof) && (b == fcdofs[cind])) {
6831             ++cind;
6832             continue;
6833           }
6834           fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6835         }
6836       }
6837     } else {
6838       if (perm) {
6839         for (b = 0; b < fdof; b++) {
6840           if ((cind < fcdof) && (b == fcdofs[cind])) {
6841             ++cind;
6842             continue;
6843           }
6844           fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6845         }
6846       } else {
6847         for (b = 0; b < fdof; b++) {
6848           if ((cind < fcdof) && (b == fcdofs[cind])) {
6849             ++cind;
6850             continue;
6851           }
6852           fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6853         }
6854       }
6855     }
6856   }
6857   *offset += fdof;
6858   PetscFunctionReturn(PETSC_SUCCESS);
6859 }
6860 
6861 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[])
6862 {
6863   PetscScalar    *a;
6864   PetscInt        fdof, foff, fcdof, foffset = *offset;
6865   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6866   PetscInt        Nc, cind = 0, ncind = 0, b;
6867   PetscBool       ncSet, fcSet;
6868 
6869   PetscFunctionBegin;
6870   PetscCall(PetscSectionGetFieldComponents(section, f, &Nc));
6871   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6872   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6873   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6874   a = &array[foff];
6875   if (fcdof) {
6876     /* We just override fcdof and fcdofs with Ncc and comps */
6877     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6878     if (clperm) {
6879       if (perm) {
6880         if (comps) {
6881           for (b = 0; b < fdof; b++) {
6882             ncSet = fcSet = PETSC_FALSE;
6883             if (b % Nc == comps[ncind]) {
6884               ncind = (ncind + 1) % Ncc;
6885               ncSet = PETSC_TRUE;
6886             }
6887             if ((cind < fcdof) && (b == fcdofs[cind])) {
6888               ++cind;
6889               fcSet = PETSC_TRUE;
6890             }
6891             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6892           }
6893         } else {
6894           for (b = 0; b < fdof; b++) {
6895             if ((cind < fcdof) && (b == fcdofs[cind])) {
6896               fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6897               ++cind;
6898             }
6899           }
6900         }
6901       } else {
6902         if (comps) {
6903           for (b = 0; b < fdof; b++) {
6904             ncSet = fcSet = PETSC_FALSE;
6905             if (b % Nc == comps[ncind]) {
6906               ncind = (ncind + 1) % Ncc;
6907               ncSet = PETSC_TRUE;
6908             }
6909             if ((cind < fcdof) && (b == fcdofs[cind])) {
6910               ++cind;
6911               fcSet = PETSC_TRUE;
6912             }
6913             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6914           }
6915         } else {
6916           for (b = 0; b < fdof; b++) {
6917             if ((cind < fcdof) && (b == fcdofs[cind])) {
6918               fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6919               ++cind;
6920             }
6921           }
6922         }
6923       }
6924     } else {
6925       if (perm) {
6926         if (comps) {
6927           for (b = 0; b < fdof; b++) {
6928             ncSet = fcSet = PETSC_FALSE;
6929             if (b % Nc == comps[ncind]) {
6930               ncind = (ncind + 1) % Ncc;
6931               ncSet = PETSC_TRUE;
6932             }
6933             if ((cind < fcdof) && (b == fcdofs[cind])) {
6934               ++cind;
6935               fcSet = PETSC_TRUE;
6936             }
6937             if (ncSet && fcSet) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6938           }
6939         } else {
6940           for (b = 0; b < fdof; b++) {
6941             if ((cind < fcdof) && (b == fcdofs[cind])) {
6942               fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6943               ++cind;
6944             }
6945           }
6946         }
6947       } else {
6948         if (comps) {
6949           for (b = 0; b < fdof; b++) {
6950             ncSet = fcSet = PETSC_FALSE;
6951             if (b % Nc == comps[ncind]) {
6952               ncind = (ncind + 1) % Ncc;
6953               ncSet = PETSC_TRUE;
6954             }
6955             if ((cind < fcdof) && (b == fcdofs[cind])) {
6956               ++cind;
6957               fcSet = PETSC_TRUE;
6958             }
6959             if (ncSet && fcSet) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6960           }
6961         } else {
6962           for (b = 0; b < fdof; b++) {
6963             if ((cind < fcdof) && (b == fcdofs[cind])) {
6964               fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6965               ++cind;
6966             }
6967           }
6968         }
6969       }
6970     }
6971   }
6972   *offset += fdof;
6973   PetscFunctionReturn(PETSC_SUCCESS);
6974 }
6975 
6976 static inline PetscErrorCode DMPlexVecSetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6977 {
6978   PetscScalar    *array;
6979   const PetscInt *cone, *coneO;
6980   PetscInt        pStart, pEnd, p, numPoints, off, dof;
6981 
6982   PetscFunctionBeginHot;
6983   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6984   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
6985   PetscCall(DMPlexGetCone(dm, point, &cone));
6986   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
6987   PetscCall(VecGetArray(v, &array));
6988   for (p = 0, off = 0; p <= numPoints; ++p, off += dof) {
6989     const PetscInt cp = !p ? point : cone[p - 1];
6990     const PetscInt o  = !p ? 0 : coneO[p - 1];
6991 
6992     if ((cp < pStart) || (cp >= pEnd)) {
6993       dof = 0;
6994       continue;
6995     }
6996     PetscCall(PetscSectionGetDof(section, cp, &dof));
6997     /* ADD_VALUES */
6998     {
6999       const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
7000       PetscScalar    *a;
7001       PetscInt        cdof, coff, cind = 0, k;
7002 
7003       PetscCall(PetscSectionGetConstraintDof(section, cp, &cdof));
7004       PetscCall(PetscSectionGetOffset(section, cp, &coff));
7005       a = &array[coff];
7006       if (!cdof) {
7007         if (o >= 0) {
7008           for (k = 0; k < dof; ++k) a[k] += values[off + k];
7009         } else {
7010           for (k = 0; k < dof; ++k) a[k] += values[off + dof - k - 1];
7011         }
7012       } else {
7013         PetscCall(PetscSectionGetConstraintIndices(section, cp, &cdofs));
7014         if (o >= 0) {
7015           for (k = 0; k < dof; ++k) {
7016             if ((cind < cdof) && (k == cdofs[cind])) {
7017               ++cind;
7018               continue;
7019             }
7020             a[k] += values[off + k];
7021           }
7022         } else {
7023           for (k = 0; k < dof; ++k) {
7024             if ((cind < cdof) && (k == cdofs[cind])) {
7025               ++cind;
7026               continue;
7027             }
7028             a[k] += values[off + dof - k - 1];
7029           }
7030         }
7031       }
7032     }
7033   }
7034   PetscCall(VecRestoreArray(v, &array));
7035   PetscFunctionReturn(PETSC_SUCCESS);
7036 }
7037 
7038 /*@C
7039   DMPlexVecSetClosure - Set an array of the values on the closure of `point`
7040 
7041   Not collective
7042 
7043   Input Parameters:
7044 + dm      - The `DM`
7045 . section - The section describing the layout in `v`, or `NULL` to use the default section
7046 . v       - The local vector
7047 . point   - The point in the `DM`
7048 . values  - The array of values
7049 - mode    - The insert mode. One of `INSERT_ALL_VALUES`, `ADD_ALL_VALUES`, `INSERT_VALUES`, `ADD_VALUES`, `INSERT_BC_VALUES`, and `ADD_BC_VALUES`,
7050             where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions.
7051 
7052   Level: intermediate
7053 
7054   Note:
7055   Usually the input arrays were obtained with `DMPlexVecGetClosure()`
7056 
7057   Fortran Note:
7058   `values` must be declared with
7059 .vb
7060   PetscScalar,dimension(:),pointer   :: values
7061 .ve
7062 
7063 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`
7064 @*/
7065 PetscErrorCode DMPlexVecSetClosure(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
7066 {
7067   PetscSection    clSection;
7068   IS              clPoints;
7069   PetscScalar    *array;
7070   PetscInt       *points = NULL;
7071   const PetscInt *clp, *clperm = NULL;
7072   PetscInt        depth, numFields, numPoints, p, clsize;
7073 
7074   PetscFunctionBeginHot;
7075   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7076   if (!section) PetscCall(DMGetLocalSection(dm, &section));
7077   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7078   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
7079   PetscCall(DMPlexGetDepth(dm, &depth));
7080   PetscCall(PetscSectionGetNumFields(section, &numFields));
7081   if (depth == 1 && numFields < 2 && mode == ADD_VALUES) {
7082     PetscCall(DMPlexVecSetClosure_Depth1_Static(dm, section, v, point, values, mode));
7083     PetscFunctionReturn(PETSC_SUCCESS);
7084   }
7085   /* Get points */
7086   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
7087   for (clsize = 0, p = 0; p < numPoints; p++) {
7088     PetscInt dof;
7089     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
7090     clsize += dof;
7091   }
7092   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
7093   /* Get array */
7094   PetscCall(VecGetArray(v, &array));
7095   /* Get values */
7096   if (numFields > 0) {
7097     PetscInt offset = 0, f;
7098     for (f = 0; f < numFields; ++f) {
7099       const PetscInt    **perms = NULL;
7100       const PetscScalar **flips = NULL;
7101 
7102       PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7103       switch (mode) {
7104       case INSERT_VALUES:
7105         for (p = 0; p < numPoints; p++) {
7106           const PetscInt     point = points[2 * p];
7107           const PetscInt    *perm  = perms ? perms[p] : NULL;
7108           const PetscScalar *flip  = flips ? flips[p] : NULL;
7109           PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, clperm, values, &offset, array));
7110         }
7111         break;
7112       case INSERT_ALL_VALUES:
7113         for (p = 0; p < numPoints; p++) {
7114           const PetscInt     point = points[2 * p];
7115           const PetscInt    *perm  = perms ? perms[p] : NULL;
7116           const PetscScalar *flip  = flips ? flips[p] : NULL;
7117           PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, clperm, values, &offset, array));
7118         }
7119         break;
7120       case INSERT_BC_VALUES:
7121         for (p = 0; p < numPoints; p++) {
7122           const PetscInt     point = points[2 * p];
7123           const PetscInt    *perm  = perms ? perms[p] : NULL;
7124           const PetscScalar *flip  = flips ? flips[p] : NULL;
7125           PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, insert, clperm, values, &offset, array));
7126         }
7127         break;
7128       case ADD_VALUES:
7129         for (p = 0; p < numPoints; p++) {
7130           const PetscInt     point = points[2 * p];
7131           const PetscInt    *perm  = perms ? perms[p] : NULL;
7132           const PetscScalar *flip  = flips ? flips[p] : NULL;
7133           PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, clperm, values, &offset, array));
7134         }
7135         break;
7136       case ADD_ALL_VALUES:
7137         for (p = 0; p < numPoints; p++) {
7138           const PetscInt     point = points[2 * p];
7139           const PetscInt    *perm  = perms ? perms[p] : NULL;
7140           const PetscScalar *flip  = flips ? flips[p] : NULL;
7141           PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, clperm, values, &offset, array));
7142         }
7143         break;
7144       case ADD_BC_VALUES:
7145         for (p = 0; p < numPoints; p++) {
7146           const PetscInt     point = points[2 * p];
7147           const PetscInt    *perm  = perms ? perms[p] : NULL;
7148           const PetscScalar *flip  = flips ? flips[p] : NULL;
7149           PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, add, clperm, values, &offset, array));
7150         }
7151         break;
7152       default:
7153         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
7154       }
7155       PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7156     }
7157   } else {
7158     PetscInt            dof, off;
7159     const PetscInt    **perms = NULL;
7160     const PetscScalar **flips = NULL;
7161 
7162     PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
7163     switch (mode) {
7164     case INSERT_VALUES:
7165       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7166         const PetscInt     point = points[2 * p];
7167         const PetscInt    *perm  = perms ? perms[p] : NULL;
7168         const PetscScalar *flip  = flips ? flips[p] : NULL;
7169         PetscCall(PetscSectionGetDof(section, point, &dof));
7170         PetscCall(updatePoint_private(section, point, dof, insert, PETSC_FALSE, perm, flip, clperm, values, off, array));
7171       }
7172       break;
7173     case INSERT_ALL_VALUES:
7174       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7175         const PetscInt     point = points[2 * p];
7176         const PetscInt    *perm  = perms ? perms[p] : NULL;
7177         const PetscScalar *flip  = flips ? flips[p] : NULL;
7178         PetscCall(PetscSectionGetDof(section, point, &dof));
7179         PetscCall(updatePoint_private(section, point, dof, insert, PETSC_TRUE, perm, flip, clperm, values, off, array));
7180       }
7181       break;
7182     case INSERT_BC_VALUES:
7183       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7184         const PetscInt     point = points[2 * p];
7185         const PetscInt    *perm  = perms ? perms[p] : NULL;
7186         const PetscScalar *flip  = flips ? flips[p] : NULL;
7187         PetscCall(PetscSectionGetDof(section, point, &dof));
7188         PetscCall(updatePointBC_private(section, point, dof, insert, perm, flip, clperm, values, off, array));
7189       }
7190       break;
7191     case ADD_VALUES:
7192       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7193         const PetscInt     point = points[2 * p];
7194         const PetscInt    *perm  = perms ? perms[p] : NULL;
7195         const PetscScalar *flip  = flips ? flips[p] : NULL;
7196         PetscCall(PetscSectionGetDof(section, point, &dof));
7197         PetscCall(updatePoint_private(section, point, dof, add, PETSC_FALSE, perm, flip, clperm, values, off, array));
7198       }
7199       break;
7200     case ADD_ALL_VALUES:
7201       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7202         const PetscInt     point = points[2 * p];
7203         const PetscInt    *perm  = perms ? perms[p] : NULL;
7204         const PetscScalar *flip  = flips ? flips[p] : NULL;
7205         PetscCall(PetscSectionGetDof(section, point, &dof));
7206         PetscCall(updatePoint_private(section, point, dof, add, PETSC_TRUE, perm, flip, clperm, values, off, array));
7207       }
7208       break;
7209     case ADD_BC_VALUES:
7210       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7211         const PetscInt     point = points[2 * p];
7212         const PetscInt    *perm  = perms ? perms[p] : NULL;
7213         const PetscScalar *flip  = flips ? flips[p] : NULL;
7214         PetscCall(PetscSectionGetDof(section, point, &dof));
7215         PetscCall(updatePointBC_private(section, point, dof, add, perm, flip, clperm, values, off, array));
7216       }
7217       break;
7218     default:
7219       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
7220     }
7221     PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
7222   }
7223   /* Cleanup points */
7224   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
7225   /* Cleanup array */
7226   PetscCall(VecRestoreArray(v, &array));
7227   PetscFunctionReturn(PETSC_SUCCESS);
7228 }
7229 
7230 /* Check whether the given point is in the label. If not, update the offset to skip this point */
7231 static inline PetscErrorCode CheckPoint_Private(DMLabel label, PetscInt labelId, PetscSection section, PetscInt point, PetscInt f, PetscInt *offset, PetscBool *contains)
7232 {
7233   PetscFunctionBegin;
7234   *contains = PETSC_TRUE;
7235   if (label) {
7236     PetscInt fdof;
7237 
7238     PetscCall(DMLabelStratumHasPoint(label, labelId, point, contains));
7239     if (!*contains) {
7240       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7241       *offset += fdof;
7242       PetscFunctionReturn(PETSC_SUCCESS);
7243     }
7244   }
7245   PetscFunctionReturn(PETSC_SUCCESS);
7246 }
7247 
7248 /* Unlike DMPlexVecSetClosure(), this uses plex-native closure permutation, not a user-specified permutation such as DMPlexSetClosurePermutationTensor(). */
7249 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)
7250 {
7251   PetscSection    clSection;
7252   IS              clPoints;
7253   PetscScalar    *array;
7254   PetscInt       *points = NULL;
7255   const PetscInt *clp;
7256   PetscInt        numFields, numPoints, p;
7257   PetscInt        offset = 0, f;
7258 
7259   PetscFunctionBeginHot;
7260   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7261   if (!section) PetscCall(DMGetLocalSection(dm, &section));
7262   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7263   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
7264   PetscCall(PetscSectionGetNumFields(section, &numFields));
7265   /* Get points */
7266   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
7267   /* Get array */
7268   PetscCall(VecGetArray(v, &array));
7269   /* Get values */
7270   for (f = 0; f < numFields; ++f) {
7271     const PetscInt    **perms = NULL;
7272     const PetscScalar **flips = NULL;
7273     PetscBool           contains;
7274 
7275     if (!fieldActive[f]) {
7276       for (p = 0; p < numPoints * 2; p += 2) {
7277         PetscInt fdof;
7278         PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
7279         offset += fdof;
7280       }
7281       continue;
7282     }
7283     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7284     switch (mode) {
7285     case INSERT_VALUES:
7286       for (p = 0; p < numPoints; p++) {
7287         const PetscInt     point = points[2 * p];
7288         const PetscInt    *perm  = perms ? perms[p] : NULL;
7289         const PetscScalar *flip  = flips ? flips[p] : NULL;
7290         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7291         if (!contains) continue;
7292         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, NULL, values, &offset, array));
7293       }
7294       break;
7295     case INSERT_ALL_VALUES:
7296       for (p = 0; p < numPoints; p++) {
7297         const PetscInt     point = points[2 * p];
7298         const PetscInt    *perm  = perms ? perms[p] : NULL;
7299         const PetscScalar *flip  = flips ? flips[p] : NULL;
7300         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7301         if (!contains) continue;
7302         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, NULL, values, &offset, array));
7303       }
7304       break;
7305     case INSERT_BC_VALUES:
7306       for (p = 0; p < numPoints; p++) {
7307         const PetscInt     point = points[2 * p];
7308         const PetscInt    *perm  = perms ? perms[p] : NULL;
7309         const PetscScalar *flip  = flips ? flips[p] : NULL;
7310         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7311         if (!contains) continue;
7312         PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, Ncc, comps, insert, NULL, values, &offset, array));
7313       }
7314       break;
7315     case ADD_VALUES:
7316       for (p = 0; p < numPoints; p++) {
7317         const PetscInt     point = points[2 * p];
7318         const PetscInt    *perm  = perms ? perms[p] : NULL;
7319         const PetscScalar *flip  = flips ? flips[p] : NULL;
7320         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7321         if (!contains) continue;
7322         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, NULL, values, &offset, array));
7323       }
7324       break;
7325     case ADD_ALL_VALUES:
7326       for (p = 0; p < numPoints; p++) {
7327         const PetscInt     point = points[2 * p];
7328         const PetscInt    *perm  = perms ? perms[p] : NULL;
7329         const PetscScalar *flip  = flips ? flips[p] : NULL;
7330         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7331         if (!contains) continue;
7332         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, NULL, values, &offset, array));
7333       }
7334       break;
7335     default:
7336       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
7337     }
7338     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7339   }
7340   /* Cleanup points */
7341   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
7342   /* Cleanup array */
7343   PetscCall(VecRestoreArray(v, &array));
7344   PetscFunctionReturn(PETSC_SUCCESS);
7345 }
7346 
7347 static PetscErrorCode DMPlexPrintMatSetValues(PetscViewer viewer, Mat A, PetscInt point, PetscInt numRIndices, const PetscInt rindices[], PetscInt numCIndices, const PetscInt cindices[], const PetscScalar values[])
7348 {
7349   PetscMPIInt rank;
7350   PetscInt    i, j;
7351 
7352   PetscFunctionBegin;
7353   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7354   PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat for point %" PetscInt_FMT "\n", rank, point));
7355   for (i = 0; i < numRIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat row indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, rindices[i]));
7356   for (i = 0; i < numCIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat col indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, cindices[i]));
7357   numCIndices = numCIndices ? numCIndices : numRIndices;
7358   if (!values) PetscFunctionReturn(PETSC_SUCCESS);
7359   for (i = 0; i < numRIndices; i++) {
7360     PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]", rank));
7361     for (j = 0; j < numCIndices; j++) {
7362 #if defined(PETSC_USE_COMPLEX)
7363       PetscCall(PetscViewerASCIIPrintf(viewer, " (%g,%g)", (double)PetscRealPart(values[i * numCIndices + j]), (double)PetscImaginaryPart(values[i * numCIndices + j])));
7364 #else
7365       PetscCall(PetscViewerASCIIPrintf(viewer, " %g", (double)values[i * numCIndices + j]));
7366 #endif
7367     }
7368     PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
7369   }
7370   PetscFunctionReturn(PETSC_SUCCESS);
7371 }
7372 
7373 /*
7374   DMPlexGetIndicesPoint_Internal - Add the indices for dofs on a point to an index array
7375 
7376   Input Parameters:
7377 + section - The section for this data layout
7378 . islocal - Is the section (and thus indices being requested) local or global?
7379 . point   - The point contributing dofs with these indices
7380 . off     - The global offset of this point
7381 . loff    - The local offset of each field
7382 . setBC   - The flag determining whether to include indices of boundary values
7383 . perm    - A permutation of the dofs on this point, or NULL
7384 - indperm - A permutation of the entire indices array, or NULL
7385 
7386   Output Parameter:
7387 . indices - Indices for dofs on this point
7388 
7389   Level: developer
7390 
7391   Note: The indices could be local or global, depending on the value of 'off'.
7392 */
7393 PetscErrorCode DMPlexGetIndicesPoint_Internal(PetscSection section, PetscBool islocal, PetscInt point, PetscInt off, PetscInt *loff, PetscBool setBC, const PetscInt perm[], const PetscInt indperm[], PetscInt indices[])
7394 {
7395   PetscInt        dof;   /* The number of unknowns on this point */
7396   PetscInt        cdof;  /* The number of constraints on this point */
7397   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
7398   PetscInt        cind = 0, k;
7399 
7400   PetscFunctionBegin;
7401   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
7402   PetscCall(PetscSectionGetDof(section, point, &dof));
7403   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
7404   if (!cdof || setBC) {
7405     for (k = 0; k < dof; ++k) {
7406       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
7407       const PetscInt ind    = indperm ? indperm[preind] : preind;
7408 
7409       indices[ind] = off + k;
7410     }
7411   } else {
7412     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
7413     for (k = 0; k < dof; ++k) {
7414       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
7415       const PetscInt ind    = indperm ? indperm[preind] : preind;
7416 
7417       if ((cind < cdof) && (k == cdofs[cind])) {
7418         /* Insert check for returning constrained indices */
7419         indices[ind] = -(off + k + 1);
7420         ++cind;
7421       } else {
7422         indices[ind] = off + k - (islocal ? 0 : cind);
7423       }
7424     }
7425   }
7426   *loff += dof;
7427   PetscFunctionReturn(PETSC_SUCCESS);
7428 }
7429 
7430 /*
7431  DMPlexGetIndicesPointFields_Internal - gets section indices for a point in its canonical ordering.
7432 
7433  Input Parameters:
7434 + section - a section (global or local)
7435 - islocal - `PETSC_TRUE` if requesting local indices (i.e., section is local); `PETSC_FALSE` for global
7436 . point - point within section
7437 . off - The offset of this point in the (local or global) indexed space - should match islocal and (usually) the section
7438 . foffs - array of length numFields containing the offset in canonical point ordering (the location in indices) of each field
7439 . setBC - identify constrained (boundary condition) points via involution.
7440 . perms - perms[f][permsoff][:] is a permutation of dofs within each field
7441 . permsoff - offset
7442 - indperm - index permutation
7443 
7444  Output Parameter:
7445 . foffs - each entry is incremented by the number of (unconstrained if setBC=FALSE) dofs in that field
7446 . indices - array to hold indices (as defined by section) of each dof associated with point
7447 
7448  Notes:
7449  If section is local and setBC=true, there is no distinction between constrained and unconstrained dofs.
7450  If section is local and setBC=false, the indices for constrained points are the involution -(i+1) of their position
7451  in the local vector.
7452 
7453  If section is global and setBC=false, the indices for constrained points are negative (and their value is not
7454  significant).  It is invalid to call with a global section and setBC=true.
7455 
7456  Developer Note:
7457  The section is only used for field layout, so islocal is technically a statement about the offset (off).  At some point
7458  in the future, global sections may have fields set, in which case we could pass the global section and obtain the
7459  offset could be obtained from the section instead of passing it explicitly as we do now.
7460 
7461  Example:
7462  Suppose a point contains one field with three components, and for which the unconstrained indices are {10, 11, 12}.
7463  When the middle component is constrained, we get the array {10, -12, 12} for (islocal=TRUE, setBC=FALSE).
7464  Note that -12 is the involution of 11, so the user can involute negative indices to recover local indices.
7465  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.
7466 
7467  Level: developer
7468 */
7469 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[])
7470 {
7471   PetscInt numFields, foff, f;
7472 
7473   PetscFunctionBegin;
7474   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
7475   PetscCall(PetscSectionGetNumFields(section, &numFields));
7476   for (f = 0, foff = 0; f < numFields; ++f) {
7477     PetscInt        fdof, cfdof;
7478     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7479     PetscInt        cind = 0, b;
7480     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7481 
7482     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7483     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7484     if (!cfdof || setBC) {
7485       for (b = 0; b < fdof; ++b) {
7486         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7487         const PetscInt ind    = indperm ? indperm[preind] : preind;
7488 
7489         indices[ind] = off + foff + b;
7490       }
7491     } else {
7492       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7493       for (b = 0; b < fdof; ++b) {
7494         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7495         const PetscInt ind    = indperm ? indperm[preind] : preind;
7496 
7497         if ((cind < cfdof) && (b == fcdofs[cind])) {
7498           indices[ind] = -(off + foff + b + 1);
7499           ++cind;
7500         } else {
7501           indices[ind] = off + foff + b - (islocal ? 0 : cind);
7502         }
7503       }
7504     }
7505     foff += (setBC || islocal ? fdof : (fdof - cfdof));
7506     foffs[f] += fdof;
7507   }
7508   PetscFunctionReturn(PETSC_SUCCESS);
7509 }
7510 
7511 /*
7512   This version believes the globalSection offsets for each field, rather than just the point offset
7513 
7514  . foffs - The offset into 'indices' for each field, since it is segregated by field
7515 
7516  Notes:
7517  The semantics of this function relate to that of setBC=FALSE in DMPlexGetIndicesPointFields_Internal.
7518  Since this function uses global indices, setBC=TRUE would be invalid, so no such argument exists.
7519 */
7520 static PetscErrorCode DMPlexGetIndicesPointFieldsSplit_Internal(PetscSection section, PetscSection globalSection, PetscInt point, PetscInt foffs[], const PetscInt ***perms, PetscInt permsoff, const PetscInt indperm[], PetscInt indices[])
7521 {
7522   PetscInt numFields, foff, f;
7523 
7524   PetscFunctionBegin;
7525   PetscCall(PetscSectionGetNumFields(section, &numFields));
7526   for (f = 0; f < numFields; ++f) {
7527     PetscInt        fdof, cfdof;
7528     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7529     PetscInt        cind = 0, b;
7530     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7531 
7532     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7533     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7534     PetscCall(PetscSectionGetFieldOffset(globalSection, point, f, &foff));
7535     if (!cfdof) {
7536       for (b = 0; b < fdof; ++b) {
7537         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7538         const PetscInt ind    = indperm ? indperm[preind] : preind;
7539 
7540         indices[ind] = foff + b;
7541       }
7542     } else {
7543       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7544       for (b = 0; b < fdof; ++b) {
7545         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7546         const PetscInt ind    = indperm ? indperm[preind] : preind;
7547 
7548         if ((cind < cfdof) && (b == fcdofs[cind])) {
7549           indices[ind] = -(foff + b + 1);
7550           ++cind;
7551         } else {
7552           indices[ind] = foff + b - cind;
7553         }
7554       }
7555     }
7556     foffs[f] += fdof;
7557   }
7558   PetscFunctionReturn(PETSC_SUCCESS);
7559 }
7560 
7561 static PetscErrorCode DMPlexAnchorsGetSubMatIndices(PetscInt nPoints, const PetscInt pnts[], PetscSection section, PetscSection cSec, PetscInt tmpIndices[], PetscInt fieldOffsets[], PetscInt indices[], const PetscInt ***perms)
7562 {
7563   PetscInt numFields, sStart, sEnd, cStart, cEnd;
7564 
7565   PetscFunctionBegin;
7566   PetscCall(PetscSectionGetNumFields(section, &numFields));
7567   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
7568   PetscCall(PetscSectionGetChart(cSec, &cStart, &cEnd));
7569   for (PetscInt p = 0; p < nPoints; p++) {
7570     PetscInt     b       = pnts[2 * p];
7571     PetscInt     bSecDof = 0, bOff;
7572     PetscInt     cSecDof = 0;
7573     PetscSection indices_section;
7574 
7575     if (b >= sStart && b < sEnd) PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7576     if (!bSecDof) continue;
7577     if (b >= cStart && b < cEnd) PetscCall(PetscSectionGetDof(cSec, b, &cSecDof));
7578     indices_section = cSecDof > 0 ? cSec : section;
7579     if (numFields) {
7580       PetscInt fStart[32], fEnd[32];
7581 
7582       fStart[0] = 0;
7583       fEnd[0]   = 0;
7584       for (PetscInt f = 0; f < numFields; f++) {
7585         PetscInt fDof = 0;
7586 
7587         PetscCall(PetscSectionGetFieldDof(indices_section, b, f, &fDof));
7588         fStart[f + 1] = fStart[f] + fDof;
7589         fEnd[f + 1]   = fStart[f + 1];
7590       }
7591       PetscCall(PetscSectionGetOffset(indices_section, b, &bOff));
7592       // only apply permutations on one side
7593       PetscCall(DMPlexGetIndicesPointFields_Internal(indices_section, PETSC_TRUE, b, bOff, fEnd, PETSC_TRUE, perms, perms ? p : -1, NULL, tmpIndices));
7594       for (PetscInt f = 0; f < numFields; f++) {
7595         for (PetscInt i = fStart[f]; i < fEnd[f]; i++) { indices[fieldOffsets[f]++] = (cSecDof > 0) ? tmpIndices[i] : -(tmpIndices[i] + 1); }
7596       }
7597     } else {
7598       PetscInt bEnd = 0;
7599 
7600       PetscCall(PetscSectionGetOffset(indices_section, b, &bOff));
7601       PetscCall(DMPlexGetIndicesPoint_Internal(indices_section, PETSC_TRUE, b, bOff, &bEnd, PETSC_TRUE, (perms && perms[0]) ? perms[0][p] : NULL, NULL, tmpIndices));
7602 
7603       for (PetscInt i = 0; i < bEnd; i++) indices[fieldOffsets[0]++] = (cSecDof > 0) ? tmpIndices[i] : -(tmpIndices[i] + 1);
7604     }
7605   }
7606   PetscFunctionReturn(PETSC_SUCCESS);
7607 }
7608 
7609 PETSC_INTERN PetscErrorCode DMPlexAnchorsGetSubMatModification(DM dm, PetscSection section, PetscInt numPoints, PetscInt numIndices, const PetscInt points[], const PetscInt ***perms, PetscInt *outNumPoints, PetscInt *outNumIndices, PetscInt *outPoints[], PetscInt offsets[], PetscScalar *outMat[])
7610 {
7611   Mat             cMat;
7612   PetscSection    aSec, cSec;
7613   IS              aIS;
7614   PetscInt        aStart = -1, aEnd = -1;
7615   PetscInt        sStart = -1, sEnd = -1;
7616   PetscInt        cStart = -1, cEnd = -1;
7617   const PetscInt *anchors;
7618   PetscInt        numFields, p;
7619   PetscInt        newNumPoints = 0, newNumIndices = 0;
7620   PetscInt       *newPoints, *indices, *newIndices, *tmpIndices, *tmpNewIndices;
7621   PetscInt        oldOffsets[32];
7622   PetscInt        newOffsets[32];
7623   PetscInt        oldOffsetsCopy[32];
7624   PetscInt        newOffsetsCopy[32];
7625   PetscScalar    *modMat         = NULL;
7626   PetscBool       anyConstrained = PETSC_FALSE;
7627 
7628   PetscFunctionBegin;
7629   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7630   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7631   PetscCall(PetscSectionGetNumFields(section, &numFields));
7632 
7633   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
7634   /* if there are point-to-point constraints */
7635   if (aSec) {
7636     PetscCall(PetscArrayzero(newOffsets, 32));
7637     PetscCall(PetscArrayzero(oldOffsets, 32));
7638     PetscCall(ISGetIndices(aIS, &anchors));
7639     PetscCall(PetscSectionGetChart(aSec, &aStart, &aEnd));
7640     PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
7641     /* figure out how many points are going to be in the new element matrix
7642      * (we allow double counting, because it's all just going to be summed
7643      * into the global matrix anyway) */
7644     for (p = 0; p < 2 * numPoints; p += 2) {
7645       PetscInt b    = points[p];
7646       PetscInt bDof = 0, bSecDof = 0;
7647 
7648       if (b >= sStart && b < sEnd) PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7649       if (!bSecDof) continue;
7650 
7651       for (PetscInt f = 0; f < numFields; f++) {
7652         PetscInt fDof = 0;
7653 
7654         PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7655         oldOffsets[f + 1] += fDof;
7656       }
7657       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7658       if (bDof) {
7659         /* this point is constrained */
7660         /* it is going to be replaced by its anchors */
7661         PetscInt bOff, q;
7662 
7663         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7664         for (q = 0; q < bDof; q++) {
7665           PetscInt a    = anchors[bOff + q];
7666           PetscInt aDof = 0;
7667 
7668           if (a >= sStart && a < sEnd) PetscCall(PetscSectionGetDof(section, a, &aDof));
7669           if (aDof) {
7670             anyConstrained = PETSC_TRUE;
7671             newNumPoints += 1;
7672           }
7673           newNumIndices += aDof;
7674           for (PetscInt f = 0; f < numFields; ++f) {
7675             PetscInt fDof = 0;
7676 
7677             if (a >= sStart && a < sEnd) PetscCall(PetscSectionGetFieldDof(section, a, f, &fDof));
7678             newOffsets[f + 1] += fDof;
7679           }
7680         }
7681       } else {
7682         /* this point is not constrained */
7683         newNumPoints++;
7684         newNumIndices += bSecDof;
7685         for (PetscInt f = 0; f < numFields; ++f) {
7686           PetscInt fDof;
7687 
7688           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7689           newOffsets[f + 1] += fDof;
7690         }
7691       }
7692     }
7693   }
7694   if (!anyConstrained) {
7695     if (outNumPoints) *outNumPoints = 0;
7696     if (outNumIndices) *outNumIndices = 0;
7697     if (outPoints) *outPoints = NULL;
7698     if (outMat) *outMat = NULL;
7699     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7700     PetscFunctionReturn(PETSC_SUCCESS);
7701   }
7702 
7703   if (outNumPoints) *outNumPoints = newNumPoints;
7704   if (outNumIndices) *outNumIndices = newNumIndices;
7705 
7706   for (PetscInt f = 0; f < numFields; ++f) newOffsets[f + 1] += newOffsets[f];
7707   for (PetscInt f = 0; f < numFields; ++f) oldOffsets[f + 1] += oldOffsets[f];
7708 
7709   if (!outPoints && !outMat) {
7710     if (offsets) {
7711       for (PetscInt f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7712     }
7713     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7714     PetscFunctionReturn(PETSC_SUCCESS);
7715   }
7716 
7717   PetscCheck(!numFields || newOffsets[numFields] == newNumIndices, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, newOffsets[numFields], newNumIndices);
7718   PetscCheck(!numFields || oldOffsets[numFields] == numIndices, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, oldOffsets[numFields], numIndices);
7719 
7720   PetscCall(DMGetDefaultConstraints(dm, &cSec, &cMat, NULL));
7721   PetscCall(PetscSectionGetChart(cSec, &cStart, &cEnd));
7722 
7723   /* output arrays */
7724   PetscCall(DMGetWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7725   PetscCall(PetscArrayzero(newPoints, 2 * newNumPoints));
7726 
7727   // get the new Points
7728   for (PetscInt p = 0, newP = 0; p < numPoints; p++) {
7729     PetscInt b    = points[2 * p];
7730     PetscInt bDof = 0, bSecDof = 0, bOff;
7731 
7732     if (b >= sStart && b < sEnd) PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7733     if (!bSecDof) continue;
7734     if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7735     if (bDof) {
7736       PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7737       for (PetscInt q = 0; q < bDof; q++) {
7738         PetscInt a = anchors[bOff + q], aDof = 0;
7739 
7740         if (a >= sStart && a < sEnd) PetscCall(PetscSectionGetDof(section, a, &aDof));
7741         if (aDof) {
7742           newPoints[2 * newP]     = a;
7743           newPoints[2 * newP + 1] = 0; // orientations are accounted for in constructing the matrix, newly added points are in default orientation
7744           newP++;
7745         }
7746       }
7747     } else {
7748       newPoints[2 * newP]     = b;
7749       newPoints[2 * newP + 1] = points[2 * p + 1];
7750       newP++;
7751     }
7752   }
7753 
7754   if (outMat) {
7755     PetscScalar *tmpMat;
7756     PetscCall(PetscArraycpy(oldOffsetsCopy, oldOffsets, 32));
7757     PetscCall(PetscArraycpy(newOffsetsCopy, newOffsets, 32));
7758 
7759     PetscCall(DMGetWorkArray(dm, numIndices, MPIU_INT, &indices));
7760     PetscCall(DMGetWorkArray(dm, numIndices, MPIU_INT, &tmpIndices));
7761     PetscCall(DMGetWorkArray(dm, newNumIndices, MPIU_INT, &newIndices));
7762     PetscCall(DMGetWorkArray(dm, newNumIndices, MPIU_INT, &tmpNewIndices));
7763 
7764     for (PetscInt i = 0; i < numIndices; i++) indices[i] = -1;
7765     for (PetscInt i = 0; i < newNumIndices; i++) newIndices[i] = -1;
7766 
7767     PetscCall(DMPlexAnchorsGetSubMatIndices(numPoints, points, section, cSec, tmpIndices, oldOffsetsCopy, indices, perms));
7768     PetscCall(DMPlexAnchorsGetSubMatIndices(newNumPoints, newPoints, section, section, tmpNewIndices, newOffsetsCopy, newIndices, NULL));
7769 
7770     PetscCall(DMGetWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &modMat));
7771     PetscCall(DMGetWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &tmpMat));
7772     PetscCall(PetscArrayzero(modMat, newNumIndices * numIndices));
7773     // for each field, insert the anchor modification into modMat
7774     for (PetscInt f = 0; f < PetscMax(1, numFields); f++) {
7775       PetscInt fStart    = oldOffsets[f];
7776       PetscInt fNewStart = newOffsets[f];
7777       for (PetscInt p = 0, newP = 0, o = fStart, oNew = fNewStart; p < numPoints; p++) {
7778         PetscInt b    = points[2 * p];
7779         PetscInt bDof = 0, bSecDof = 0, bOff;
7780 
7781         if (b >= sStart && b < sEnd) {
7782           if (numFields) {
7783             PetscCall(PetscSectionGetFieldDof(section, b, f, &bSecDof));
7784           } else {
7785             PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7786           }
7787         }
7788         if (!bSecDof) continue;
7789         if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7790         if (bDof) {
7791           PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7792           for (PetscInt q = 0; q < bDof; q++, newP++) {
7793             PetscInt a = anchors[bOff + q], aDof = 0;
7794 
7795             if (a >= sStart && a < sEnd) {
7796               if (numFields) {
7797                 PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
7798               } else {
7799                 PetscCall(PetscSectionGetDof(section, a, &aDof));
7800               }
7801             }
7802             if (aDof) {
7803               PetscCall(MatGetValues(cMat, bSecDof, &indices[o], aDof, &newIndices[oNew], tmpMat));
7804               for (PetscInt d = 0; d < bSecDof; d++) {
7805                 for (PetscInt e = 0; e < aDof; e++) modMat[(o + d) * newNumIndices + oNew + e] = tmpMat[d * aDof + e];
7806               }
7807             }
7808             oNew += aDof;
7809           }
7810         } else {
7811           // Insert the identity matrix in this block
7812           for (PetscInt d = 0; d < bSecDof; d++) modMat[(o + d) * newNumIndices + oNew + d] = 1;
7813           oNew += bSecDof;
7814           newP++;
7815         }
7816         o += bSecDof;
7817       }
7818     }
7819 
7820     *outMat = modMat;
7821 
7822     PetscCall(DMRestoreWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &tmpMat));
7823     PetscCall(DMRestoreWorkArray(dm, newNumIndices, MPIU_INT, &tmpNewIndices));
7824     PetscCall(DMRestoreWorkArray(dm, newNumIndices, MPIU_INT, &newIndices));
7825     PetscCall(DMRestoreWorkArray(dm, numIndices, MPIU_INT, &tmpIndices));
7826     PetscCall(DMRestoreWorkArray(dm, numIndices, MPIU_INT, &indices));
7827   }
7828   PetscCall(ISRestoreIndices(aIS, &anchors));
7829 
7830   /* output */
7831   if (outPoints) {
7832     *outPoints = newPoints;
7833   } else {
7834     PetscCall(DMRestoreWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7835   }
7836   for (PetscInt f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7837   PetscFunctionReturn(PETSC_SUCCESS);
7838 }
7839 
7840 PETSC_INTERN PetscErrorCode DMPlexAnchorsModifyMat_Internal(DM dm, PetscSection section, PetscInt numPoints, PetscInt numIndices, const PetscInt points[], const PetscInt ***perms, PetscInt numRows, PetscInt numCols, const PetscScalar values[], PetscInt *outNumPoints, PetscInt *outNumIndices, PetscInt *outPoints[], PetscScalar *outValues[], PetscInt offsets[], PetscBool multiplyRight, PetscBool multiplyLeft)
7841 {
7842   PetscScalar *modMat        = NULL;
7843   PetscInt     newNumIndices = -1;
7844 
7845   PetscFunctionBegin;
7846   /* If M is the matrix represented by values, get the matrix C such that we will add M * C (or, if multiplyLeft, C^T * M * C) into the global matrix.
7847      modMat is that matrix C */
7848   PetscCall(DMPlexAnchorsGetSubMatModification(dm, section, numPoints, numIndices, points, perms, outNumPoints, &newNumIndices, outPoints, offsets, outValues ? &modMat : NULL));
7849   if (outNumIndices) *outNumIndices = newNumIndices;
7850   if (modMat) {
7851     const PetscScalar *newValues = values;
7852 
7853     if (multiplyRight) {
7854       PetscScalar *newNewValues = NULL;
7855       PetscBLASInt M            = newNumIndices;
7856       PetscBLASInt N            = numRows;
7857       PetscBLASInt K            = numIndices;
7858       PetscScalar  a = 1.0, b = 0.0;
7859 
7860       PetscCheck(numCols == numIndices, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "values matrix has the wrong number of columns: %" PetscInt_FMT ", expected %" PetscInt_FMT, numCols, numIndices);
7861 
7862       PetscCall(DMGetWorkArray(dm, numRows * newNumIndices, MPIU_SCALAR, &newNewValues));
7863       // row-major to column-major conversion, right multiplication becomes left multiplication
7864       PetscCallBLAS("BLASgemm", BLASgemm_("N", "N", &M, &N, &K, &a, modMat, &M, newValues, &K, &b, newNewValues, &M));
7865 
7866       numCols   = newNumIndices;
7867       newValues = newNewValues;
7868     }
7869 
7870     if (multiplyLeft) {
7871       PetscScalar *newNewValues = NULL;
7872       PetscBLASInt M            = numCols;
7873       PetscBLASInt N            = newNumIndices;
7874       PetscBLASInt K            = numIndices;
7875       PetscScalar  a = 1.0, b = 0.0;
7876 
7877       PetscCheck(numRows == numIndices, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "values matrix has the wrong number of rows: %" PetscInt_FMT ", expected %" PetscInt_FMT, numRows, numIndices);
7878 
7879       PetscCall(DMGetWorkArray(dm, newNumIndices * numCols, MPIU_SCALAR, &newNewValues));
7880       // row-major to column-major conversion, left multiplication becomes right multiplication
7881       PetscCallBLAS("BLASgemm", BLASgemm_("N", "T", &M, &N, &K, &a, newValues, &M, modMat, &N, &b, newNewValues, &M));
7882       if (newValues != values) PetscCall(DMRestoreWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &newValues));
7883       newValues = newNewValues;
7884     }
7885     *outValues = (PetscScalar *)newValues;
7886     PetscCall(DMRestoreWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &modMat));
7887   }
7888   PetscFunctionReturn(PETSC_SUCCESS);
7889 }
7890 
7891 PETSC_INTERN PetscErrorCode DMPlexAnchorsModifyMat(DM dm, PetscSection section, PetscInt numPoints, PetscInt numIndices, const PetscInt points[], const PetscInt ***perms, const PetscScalar values[], PetscInt *outNumPoints, PetscInt *outNumIndices, PetscInt *outPoints[], PetscScalar *outValues[], PetscInt offsets[], PetscBool multiplyLeft)
7892 {
7893   PetscFunctionBegin;
7894   PetscCall(DMPlexAnchorsModifyMat_Internal(dm, section, numPoints, numIndices, points, perms, numIndices, numIndices, values, outNumPoints, outNumIndices, outPoints, outValues, offsets, PETSC_TRUE, multiplyLeft));
7895   PetscFunctionReturn(PETSC_SUCCESS);
7896 }
7897 
7898 static PetscErrorCode DMPlexGetClosureIndicesSize_Internal(DM dm, PetscSection section, PetscInt point, PetscInt *closureSize)
7899 {
7900   /* Closure ordering */
7901   PetscSection    clSection;
7902   IS              clPoints;
7903   const PetscInt *clp;
7904   PetscInt       *points;
7905   PetscInt        Ncl, Ni = 0;
7906 
7907   PetscFunctionBeginHot;
7908   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &Ncl, &points, &clSection, &clPoints, &clp));
7909   for (PetscInt p = 0; p < Ncl * 2; p += 2) {
7910     PetscInt dof;
7911 
7912     PetscCall(PetscSectionGetDof(section, points[p], &dof));
7913     Ni += dof;
7914   }
7915   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7916   *closureSize = Ni;
7917   PetscFunctionReturn(PETSC_SUCCESS);
7918 }
7919 
7920 static PetscErrorCode DMPlexGetClosureIndices_Internal(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numRows, PetscInt *numCols, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[], PetscBool multiplyRight, PetscBool multiplyLeft)
7921 {
7922   /* Closure ordering */
7923   PetscSection    clSection;
7924   IS              clPoints;
7925   const PetscInt *clp;
7926   PetscInt       *points;
7927   const PetscInt *clperm = NULL;
7928   /* Dof permutation and sign flips */
7929   const PetscInt    **perms[32] = {NULL};
7930   const PetscScalar **flips[32] = {NULL};
7931   PetscScalar        *valCopy   = NULL;
7932   /* Hanging node constraints */
7933   PetscInt    *pointsC = NULL;
7934   PetscScalar *valuesC = NULL;
7935   PetscInt     NclC, NiC;
7936 
7937   PetscInt *idx;
7938   PetscInt  Nf, Ncl, Ni = 0, offsets[32], p, f;
7939   PetscBool isLocal = (section == idxSection) ? PETSC_TRUE : PETSC_FALSE;
7940   PetscInt  idxStart, idxEnd;
7941   PetscInt  nRows, nCols;
7942 
7943   PetscFunctionBeginHot;
7944   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7945   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7946   PetscValidHeaderSpecific(idxSection, PETSC_SECTION_CLASSID, 3);
7947   PetscAssertPointer(numRows, 6);
7948   PetscAssertPointer(numCols, 7);
7949   if (indices) PetscAssertPointer(indices, 8);
7950   if (outOffsets) PetscAssertPointer(outOffsets, 9);
7951   if (values) PetscAssertPointer(values, 10);
7952   PetscCall(PetscSectionGetNumFields(section, &Nf));
7953   PetscCheck(Nf <= 31, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", Nf);
7954   PetscCall(PetscArrayzero(offsets, 32));
7955   /* 1) Get points in closure */
7956   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &Ncl, &points, &clSection, &clPoints, &clp));
7957   if (useClPerm) {
7958     PetscInt depth, clsize;
7959     PetscCall(DMPlexGetPointDepth(dm, point, &depth));
7960     for (clsize = 0, p = 0; p < Ncl; p++) {
7961       PetscInt dof;
7962       PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
7963       clsize += dof;
7964     }
7965     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
7966   }
7967   /* 2) Get number of indices on these points and field offsets from section */
7968   for (p = 0; p < Ncl * 2; p += 2) {
7969     PetscInt dof, fdof;
7970 
7971     PetscCall(PetscSectionGetDof(section, points[p], &dof));
7972     for (f = 0; f < Nf; ++f) {
7973       PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
7974       offsets[f + 1] += fdof;
7975     }
7976     Ni += dof;
7977   }
7978   if (*numRows == -1) *numRows = Ni;
7979   if (*numCols == -1) *numCols = Ni;
7980   nRows = *numRows;
7981   nCols = *numCols;
7982   for (f = 1; f < Nf; ++f) offsets[f + 1] += offsets[f];
7983   PetscCheck(!Nf || offsets[Nf] == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, offsets[Nf], Ni);
7984   /* 3) Get symmetries and sign flips. Apply sign flips to values if passed in (only works for square values matrix) */
7985   if (multiplyRight) PetscCheck(nCols == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Expected %" PetscInt_FMT " columns, got %" PetscInt_FMT, Ni, nCols);
7986   if (multiplyLeft) PetscCheck(nRows == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Expected %" PetscInt_FMT " rows, got %" PetscInt_FMT, Ni, nRows);
7987   for (f = 0; f < PetscMax(1, Nf); ++f) {
7988     if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7989     else PetscCall(PetscSectionGetPointSyms(section, Ncl, points, &perms[f], &flips[f]));
7990     /* may need to apply sign changes to the element matrix */
7991     if (values && flips[f]) {
7992       PetscInt foffset = offsets[f];
7993 
7994       for (p = 0; p < Ncl; ++p) {
7995         PetscInt           pnt  = points[2 * p], fdof;
7996         const PetscScalar *flip = flips[f] ? flips[f][p] : NULL;
7997 
7998         if (!Nf) PetscCall(PetscSectionGetDof(section, pnt, &fdof));
7999         else PetscCall(PetscSectionGetFieldDof(section, pnt, f, &fdof));
8000         if (flip) {
8001           PetscInt i, j, k;
8002 
8003           if (!valCopy) {
8004             PetscCall(DMGetWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
8005             for (j = 0; j < Ni * Ni; ++j) valCopy[j] = (*values)[j];
8006             *values = valCopy;
8007           }
8008           for (i = 0; i < fdof; ++i) {
8009             PetscScalar fval = flip[i];
8010 
8011             if (multiplyRight) {
8012               for (k = 0; k < nRows; ++k) { valCopy[Ni * k + (foffset + i)] *= fval; }
8013             }
8014             if (multiplyLeft) {
8015               for (k = 0; k < nCols; ++k) { valCopy[nCols * (foffset + i) + k] *= fval; }
8016             }
8017           }
8018         }
8019         foffset += fdof;
8020       }
8021     }
8022   }
8023   /* 4) Apply hanging node constraints. Get new symmetries and replace all storage with constrained storage */
8024   PetscCall(DMPlexAnchorsModifyMat_Internal(dm, section, Ncl, Ni, points, perms, nRows, nCols, values ? *values : NULL, &NclC, &NiC, &pointsC, values ? &valuesC : NULL, offsets, multiplyRight, multiplyLeft));
8025   if (NclC) {
8026     if (multiplyRight) { *numCols = nCols = NiC; }
8027     if (multiplyLeft) { *numRows = nRows = NiC; }
8028     if (valCopy) PetscCall(DMRestoreWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
8029     for (f = 0; f < PetscMax(1, Nf); ++f) {
8030       if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
8031       else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
8032     }
8033     for (f = 0; f < PetscMax(1, Nf); ++f) {
8034       if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, NclC, pointsC, &perms[f], &flips[f]));
8035       else PetscCall(PetscSectionGetPointSyms(section, NclC, pointsC, &perms[f], &flips[f]));
8036     }
8037     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
8038     Ncl    = NclC;
8039     Ni     = NiC;
8040     points = pointsC;
8041     if (values) *values = valuesC;
8042   }
8043   /* 5) Calculate indices */
8044   PetscCall(DMGetWorkArray(dm, Ni, MPIU_INT, &idx));
8045   PetscCall(PetscSectionGetChart(idxSection, &idxStart, &idxEnd));
8046   if (Nf) {
8047     PetscInt  idxOff;
8048     PetscBool useFieldOffsets;
8049 
8050     if (outOffsets) {
8051       for (f = 0; f <= Nf; f++) outOffsets[f] = offsets[f];
8052     }
8053     PetscCall(PetscSectionGetUseFieldOffsets(idxSection, &useFieldOffsets));
8054     if (useFieldOffsets) {
8055       for (p = 0; p < Ncl; ++p) {
8056         const PetscInt pnt = points[p * 2];
8057 
8058         PetscCall(DMPlexGetIndicesPointFieldsSplit_Internal(section, idxSection, pnt, offsets, perms, p, clperm, idx));
8059       }
8060     } else {
8061       for (p = 0; p < Ncl; ++p) {
8062         const PetscInt pnt = points[p * 2];
8063 
8064         if (pnt < idxStart || pnt >= idxEnd) continue;
8065         PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
8066         /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
8067          * not (at the time of this writing) have fields set. They probably should, in which case we would pass the
8068          * global section. */
8069         PetscCall(DMPlexGetIndicesPointFields_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, offsets, PETSC_FALSE, perms, p, clperm, idx));
8070       }
8071     }
8072   } else {
8073     PetscInt off = 0, idxOff;
8074 
8075     for (p = 0; p < Ncl; ++p) {
8076       const PetscInt  pnt  = points[p * 2];
8077       const PetscInt *perm = perms[0] ? perms[0][p] : NULL;
8078 
8079       if (pnt < idxStart || pnt >= idxEnd) continue;
8080       PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
8081       /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
8082        * not (at the time of this writing) have fields set. They probably should, in which case we would pass the global section. */
8083       PetscCall(DMPlexGetIndicesPoint_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, &off, PETSC_FALSE, perm, clperm, idx));
8084     }
8085   }
8086   /* 6) Cleanup */
8087   for (f = 0; f < PetscMax(1, Nf); ++f) {
8088     if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
8089     else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
8090   }
8091   if (NclC) {
8092     PetscCall(DMRestoreWorkArray(dm, NclC * 2, MPIU_INT, &pointsC));
8093   } else {
8094     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
8095   }
8096 
8097   if (indices) *indices = idx;
8098   PetscFunctionReturn(PETSC_SUCCESS);
8099 }
8100 
8101 /*@C
8102   DMPlexGetClosureIndices - Gets the global dof indices associated with the closure of the given point within the provided sections.
8103 
8104   Not collective
8105 
8106   Input Parameters:
8107 + dm         - The `DM`
8108 . section    - The `PetscSection` describing the points (a local section)
8109 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
8110 . point      - The point defining the closure
8111 - useClPerm  - Use the closure point permutation if available
8112 
8113   Output Parameters:
8114 + numIndices - The number of dof indices in the closure of point with the input sections
8115 . indices    - The dof indices
8116 . outOffsets - Array to write the field offsets into, or `NULL`
8117 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or `NULL`
8118 
8119   Level: advanced
8120 
8121   Notes:
8122   Call `DMPlexRestoreClosureIndices()` to free allocated memory
8123 
8124   If `idxSection` is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
8125   of those indices is not significant.  If `idxSection` is local, the constrained dofs will yield the involution -(idx+1)
8126   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
8127   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when `idxSection` == section, otherwise global
8128   indices (with the above semantics) are implied.
8129 
8130 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`,
8131           `PetscSection`, `DMGetGlobalSection()`
8132 @*/
8133 PetscErrorCode DMPlexGetClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
8134 {
8135   PetscInt numRows = -1, numCols = -1;
8136 
8137   PetscFunctionBeginHot;
8138   PetscCall(DMPlexGetClosureIndices_Internal(dm, section, idxSection, point, useClPerm, &numRows, &numCols, indices, outOffsets, values, PETSC_TRUE, PETSC_TRUE));
8139   PetscCheck(numRows == numCols, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Symmetric matrix transformation produces rectangular dimensions (%" PetscInt_FMT ", %" PetscInt_FMT ")", numRows, numCols);
8140   *numIndices = numRows;
8141   PetscFunctionReturn(PETSC_SUCCESS);
8142 }
8143 
8144 /*@C
8145   DMPlexRestoreClosureIndices - Restores the global dof indices associated with the closure of the given point within the provided sections.
8146 
8147   Not collective
8148 
8149   Input Parameters:
8150 + dm         - The `DM`
8151 . section    - The `PetscSection` describing the points (a local section)
8152 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
8153 . point      - The point defining the closure
8154 - useClPerm  - Use the closure point permutation if available
8155 
8156   Output Parameters:
8157 + numIndices - The number of dof indices in the closure of point with the input sections
8158 . indices    - The dof indices
8159 . outOffsets - Array to write the field offsets into, or `NULL`
8160 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or `NULL`
8161 
8162   Level: advanced
8163 
8164   Notes:
8165   If values were modified, the user is responsible for calling `DMRestoreWorkArray`(dm, 0, `MPIU_SCALAR`, &values).
8166 
8167   If idxSection is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
8168   of those indices is not significant.  If idxSection is local, the constrained dofs will yield the involution -(idx+1)
8169   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
8170   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when idxSection == section, otherwise global
8171   indices (with the above semantics) are implied.
8172 
8173 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`, `DMGetGlobalSection()`
8174 @*/
8175 PetscErrorCode DMPlexRestoreClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
8176 {
8177   PetscFunctionBegin;
8178   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8179   PetscAssertPointer(indices, 7);
8180   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, indices));
8181   PetscFunctionReturn(PETSC_SUCCESS);
8182 }
8183 
8184 PetscErrorCode DMPlexMatSetClosure_Internal(DM dm, PetscSection section, PetscSection globalSection, PetscBool useClPerm, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8185 {
8186   DM_Plex           *mesh = (DM_Plex *)dm->data;
8187   PetscInt          *indices;
8188   PetscInt           numIndices;
8189   const PetscScalar *valuesOrig = values;
8190   PetscErrorCode     ierr;
8191 
8192   PetscFunctionBegin;
8193   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8194   if (!section) PetscCall(DMGetLocalSection(dm, &section));
8195   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
8196   if (!globalSection) PetscCall(DMGetGlobalSection(dm, &globalSection));
8197   PetscValidHeaderSpecific(globalSection, PETSC_SECTION_CLASSID, 3);
8198   PetscValidHeaderSpecific(A, MAT_CLASSID, 5);
8199 
8200   PetscCall(DMPlexGetClosureIndices(dm, section, globalSection, point, useClPerm, &numIndices, &indices, NULL, (PetscScalar **)&values));
8201 
8202   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndices, indices, 0, NULL, values));
8203   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8204   ierr = MatSetValues(A, numIndices, indices, numIndices, indices, values, mode);
8205   if (ierr) {
8206     PetscMPIInt rank;
8207 
8208     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8209     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8210     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndices, indices, 0, NULL, values));
8211     PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
8212     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
8213     SETERRQ(PetscObjectComm((PetscObject)dm), ierr, "Not possible to set matrix values");
8214   }
8215   if (mesh->printFEM > 1) {
8216     PetscInt i;
8217     PetscCall(PetscPrintf(PETSC_COMM_SELF, "  Indices:"));
8218     for (i = 0; i < numIndices; ++i) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, indices[i]));
8219     PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8220   }
8221 
8222   PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
8223   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
8224   PetscFunctionReturn(PETSC_SUCCESS);
8225 }
8226 
8227 /*@C
8228   DMPlexMatSetClosure - Set an array of the values on the closure of 'point'
8229 
8230   Not collective
8231 
8232   Input Parameters:
8233 + dm            - The `DM`
8234 . section       - The section describing the layout in `v`, or `NULL` to use the default section
8235 . globalSection - The section describing the layout in `v`, or `NULL` to use the default global section
8236 . A             - The matrix
8237 . point         - The point in the `DM`
8238 . values        - The array of values
8239 - mode          - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
8240 
8241   Level: intermediate
8242 
8243 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosureGeneral()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
8244 @*/
8245 PetscErrorCode DMPlexMatSetClosure(DM dm, PetscSection section, PetscSection globalSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8246 {
8247   PetscFunctionBegin;
8248   PetscCall(DMPlexMatSetClosure_Internal(dm, section, globalSection, PETSC_TRUE, A, point, values, mode));
8249   PetscFunctionReturn(PETSC_SUCCESS);
8250 }
8251 
8252 /*@C
8253   DMPlexMatSetClosureGeneral - Set an array of the values on the closure of 'point' using a different row and column section
8254 
8255   Not collective
8256 
8257   Input Parameters:
8258 + dmRow            - The `DM` for the row fields
8259 . sectionRow       - The section describing the layout, or `NULL` to use the default section in `dmRow`
8260 . useRowPerm       - The flag to use the closure permutation of the `dmRow` if available
8261 . globalSectionRow - The section describing the layout, or `NULL` to use the default global section in `dmRow`
8262 . dmCol            - The `DM` for the column fields
8263 . sectionCol       - The section describing the layout, or `NULL` to use the default section in `dmCol`
8264 . useColPerm       - The flag to use the closure permutation of the `dmCol` if available
8265 . globalSectionCol - The section describing the layout, or `NULL` to use the default global section in `dmCol`
8266 . A                - The matrix
8267 . point            - The point in the `DM`
8268 . values           - The array of values
8269 - mode             - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
8270 
8271   Level: intermediate
8272 
8273 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosure()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
8274 @*/
8275 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)
8276 {
8277   DM_Plex           *mesh = (DM_Plex *)dmRow->data;
8278   PetscInt          *indicesRow, *indicesCol;
8279   PetscInt           numIndicesRow = -1, numIndicesCol = -1;
8280   const PetscScalar *valuesV0 = values, *valuesV1, *valuesV2;
8281 
8282   PetscErrorCode ierr;
8283 
8284   PetscFunctionBegin;
8285   PetscValidHeaderSpecific(dmRow, DM_CLASSID, 1);
8286   if (!sectionRow) PetscCall(DMGetLocalSection(dmRow, &sectionRow));
8287   PetscValidHeaderSpecific(sectionRow, PETSC_SECTION_CLASSID, 2);
8288   if (!globalSectionRow) PetscCall(DMGetGlobalSection(dmRow, &globalSectionRow));
8289   PetscValidHeaderSpecific(globalSectionRow, PETSC_SECTION_CLASSID, 3);
8290   PetscValidHeaderSpecific(dmCol, DM_CLASSID, 5);
8291   if (!sectionCol) PetscCall(DMGetLocalSection(dmCol, &sectionCol));
8292   PetscValidHeaderSpecific(sectionCol, PETSC_SECTION_CLASSID, 6);
8293   if (!globalSectionCol) PetscCall(DMGetGlobalSection(dmCol, &globalSectionCol));
8294   PetscValidHeaderSpecific(globalSectionCol, PETSC_SECTION_CLASSID, 7);
8295   PetscValidHeaderSpecific(A, MAT_CLASSID, 9);
8296 
8297   PetscCall(DMPlexGetClosureIndicesSize_Internal(dmRow, sectionRow, point, &numIndicesRow));
8298   PetscCall(DMPlexGetClosureIndicesSize_Internal(dmCol, sectionCol, point, &numIndicesCol));
8299   valuesV1 = valuesV0;
8300   PetscCall(DMPlexGetClosureIndices_Internal(dmRow, sectionRow, globalSectionRow, point, useRowPerm, &numIndicesRow, &numIndicesCol, &indicesRow, NULL, (PetscScalar **)&valuesV1, PETSC_FALSE, PETSC_TRUE));
8301   valuesV2 = valuesV1;
8302   PetscCall(DMPlexGetClosureIndices_Internal(dmCol, sectionCol, globalSectionCol, point, useColPerm, &numIndicesRow, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&valuesV2, PETSC_TRUE, PETSC_FALSE));
8303 
8304   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, valuesV2));
8305   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8306   ierr = MatSetValues(A, numIndicesRow, indicesRow, numIndicesCol, indicesCol, valuesV2, mode);
8307   if (ierr) {
8308     PetscMPIInt rank;
8309 
8310     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8311     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8312     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
8313     PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesRow, NULL, (PetscScalar **)&valuesV2));
8314     PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&valuesV1));
8315     if (valuesV2 != valuesV1) PetscCall(DMRestoreWorkArray(dmCol, 0, MPIU_SCALAR, &valuesV2));
8316     if (valuesV1 != valuesV0) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &valuesV1));
8317   }
8318 
8319   PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, useColPerm, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&valuesV2));
8320   PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, useRowPerm, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&valuesV1));
8321   if (valuesV2 != valuesV1) PetscCall(DMRestoreWorkArray(dmCol, 0, MPIU_SCALAR, &valuesV2));
8322   if (valuesV1 != valuesV0) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &valuesV1));
8323   PetscFunctionReturn(PETSC_SUCCESS);
8324 }
8325 
8326 PetscErrorCode DMPlexMatSetClosureRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8327 {
8328   DM_Plex        *mesh    = (DM_Plex *)dmf->data;
8329   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
8330   PetscInt       *cpoints = NULL;
8331   PetscInt       *findices, *cindices;
8332   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
8333   PetscInt        foffsets[32], coffsets[32];
8334   DMPolytopeType  ct;
8335   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
8336   PetscErrorCode  ierr;
8337 
8338   PetscFunctionBegin;
8339   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
8340   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
8341   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
8342   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
8343   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
8344   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
8345   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
8346   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
8347   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
8348   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
8349   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
8350   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
8351   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
8352   PetscCall(PetscArrayzero(foffsets, 32));
8353   PetscCall(PetscArrayzero(coffsets, 32));
8354   /* Column indices */
8355   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8356   maxFPoints = numCPoints;
8357   /* Compress out points not in the section */
8358   /*   TODO: Squeeze out points with 0 dof as well */
8359   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
8360   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
8361     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
8362       cpoints[q * 2]     = cpoints[p];
8363       cpoints[q * 2 + 1] = cpoints[p + 1];
8364       ++q;
8365     }
8366   }
8367   numCPoints = q;
8368   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
8369     PetscInt fdof;
8370 
8371     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
8372     if (!dof) continue;
8373     for (f = 0; f < numFields; ++f) {
8374       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
8375       coffsets[f + 1] += fdof;
8376     }
8377     numCIndices += dof;
8378   }
8379   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
8380   /* Row indices */
8381   PetscCall(DMPlexGetCellType(dmc, point, &ct));
8382   {
8383     DMPlexTransform tr;
8384     DMPolytopeType *rct;
8385     PetscInt       *rsize, *rcone, *rornt, Nt;
8386 
8387     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8388     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8389     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8390     numSubcells = rsize[Nt - 1];
8391     PetscCall(DMPlexTransformDestroy(&tr));
8392   }
8393   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8394   for (r = 0, q = 0; r < numSubcells; ++r) {
8395     /* TODO Map from coarse to fine cells */
8396     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8397     /* Compress out points not in the section */
8398     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8399     for (p = 0; p < numFPoints * 2; p += 2) {
8400       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8401         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8402         if (!dof) continue;
8403         for (s = 0; s < q; ++s)
8404           if (fpoints[p] == ftotpoints[s * 2]) break;
8405         if (s < q) continue;
8406         ftotpoints[q * 2]     = fpoints[p];
8407         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8408         ++q;
8409       }
8410     }
8411     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8412   }
8413   numFPoints = q;
8414   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8415     PetscInt fdof;
8416 
8417     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8418     if (!dof) continue;
8419     for (f = 0; f < numFields; ++f) {
8420       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8421       foffsets[f + 1] += fdof;
8422     }
8423     numFIndices += dof;
8424   }
8425   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8426 
8427   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8428   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8429   PetscCall(DMGetWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8430   PetscCall(DMGetWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8431   if (numFields) {
8432     const PetscInt **permsF[32] = {NULL};
8433     const PetscInt **permsC[32] = {NULL};
8434 
8435     for (f = 0; f < numFields; f++) {
8436       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8437       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8438     }
8439     for (p = 0; p < numFPoints; p++) {
8440       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8441       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8442     }
8443     for (p = 0; p < numCPoints; p++) {
8444       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8445       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8446     }
8447     for (f = 0; f < numFields; f++) {
8448       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8449       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8450     }
8451   } else {
8452     const PetscInt **permsF = NULL;
8453     const PetscInt **permsC = NULL;
8454 
8455     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8456     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8457     for (p = 0, off = 0; p < numFPoints; p++) {
8458       const PetscInt *perm = permsF ? permsF[p] : NULL;
8459 
8460       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8461       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8462     }
8463     for (p = 0, off = 0; p < numCPoints; p++) {
8464       const PetscInt *perm = permsC ? permsC[p] : NULL;
8465 
8466       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8467       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8468     }
8469     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8470     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8471   }
8472   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8473   /* TODO: flips */
8474   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8475   ierr = MatSetValues(A, numFIndices, findices, numCIndices, cindices, values, mode);
8476   if (ierr) {
8477     PetscMPIInt rank;
8478 
8479     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8480     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8481     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8482     PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8483     PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8484   }
8485   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8486   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8487   PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8488   PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8489   PetscFunctionReturn(PETSC_SUCCESS);
8490 }
8491 
8492 PetscErrorCode DMPlexMatGetClosureIndicesRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, PetscInt point, PetscInt cindices[], PetscInt findices[])
8493 {
8494   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
8495   PetscInt       *cpoints      = NULL;
8496   PetscInt        foffsets[32] = {0}, coffsets[32] = {0};
8497   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
8498   DMPolytopeType  ct;
8499   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
8500 
8501   PetscFunctionBegin;
8502   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
8503   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
8504   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
8505   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
8506   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
8507   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
8508   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
8509   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
8510   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
8511   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
8512   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
8513   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
8514   /* Column indices */
8515   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8516   maxFPoints = numCPoints;
8517   /* Compress out points not in the section */
8518   /*   TODO: Squeeze out points with 0 dof as well */
8519   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
8520   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
8521     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
8522       cpoints[q * 2]     = cpoints[p];
8523       cpoints[q * 2 + 1] = cpoints[p + 1];
8524       ++q;
8525     }
8526   }
8527   numCPoints = q;
8528   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
8529     PetscInt fdof;
8530 
8531     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
8532     if (!dof) continue;
8533     for (f = 0; f < numFields; ++f) {
8534       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
8535       coffsets[f + 1] += fdof;
8536     }
8537     numCIndices += dof;
8538   }
8539   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
8540   /* Row indices */
8541   PetscCall(DMPlexGetCellType(dmc, point, &ct));
8542   {
8543     DMPlexTransform tr;
8544     DMPolytopeType *rct;
8545     PetscInt       *rsize, *rcone, *rornt, Nt;
8546 
8547     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8548     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8549     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8550     numSubcells = rsize[Nt - 1];
8551     PetscCall(DMPlexTransformDestroy(&tr));
8552   }
8553   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8554   for (r = 0, q = 0; r < numSubcells; ++r) {
8555     /* TODO Map from coarse to fine cells */
8556     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8557     /* Compress out points not in the section */
8558     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8559     for (p = 0; p < numFPoints * 2; p += 2) {
8560       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8561         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8562         if (!dof) continue;
8563         for (s = 0; s < q; ++s)
8564           if (fpoints[p] == ftotpoints[s * 2]) break;
8565         if (s < q) continue;
8566         ftotpoints[q * 2]     = fpoints[p];
8567         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8568         ++q;
8569       }
8570     }
8571     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8572   }
8573   numFPoints = q;
8574   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8575     PetscInt fdof;
8576 
8577     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8578     if (!dof) continue;
8579     for (f = 0; f < numFields; ++f) {
8580       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8581       foffsets[f + 1] += fdof;
8582     }
8583     numFIndices += dof;
8584   }
8585   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8586 
8587   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8588   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8589   if (numFields) {
8590     const PetscInt **permsF[32] = {NULL};
8591     const PetscInt **permsC[32] = {NULL};
8592 
8593     for (f = 0; f < numFields; f++) {
8594       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8595       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8596     }
8597     for (p = 0; p < numFPoints; p++) {
8598       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8599       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8600     }
8601     for (p = 0; p < numCPoints; p++) {
8602       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8603       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8604     }
8605     for (f = 0; f < numFields; f++) {
8606       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8607       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8608     }
8609   } else {
8610     const PetscInt **permsF = NULL;
8611     const PetscInt **permsC = NULL;
8612 
8613     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8614     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8615     for (p = 0, off = 0; p < numFPoints; p++) {
8616       const PetscInt *perm = permsF ? permsF[p] : NULL;
8617 
8618       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8619       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8620     }
8621     for (p = 0, off = 0; p < numCPoints; p++) {
8622       const PetscInt *perm = permsC ? permsC[p] : NULL;
8623 
8624       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8625       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8626     }
8627     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8628     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8629   }
8630   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8631   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8632   PetscFunctionReturn(PETSC_SUCCESS);
8633 }
8634 
8635 /*@
8636   DMPlexGetVTKCellHeight - Returns the height in the DAG used to determine which points are cells (normally 0)
8637 
8638   Input Parameter:
8639 . dm - The `DMPLEX` object
8640 
8641   Output Parameter:
8642 . cellHeight - The height of a cell
8643 
8644   Level: developer
8645 
8646 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetVTKCellHeight()`
8647 @*/
8648 PetscErrorCode DMPlexGetVTKCellHeight(DM dm, PetscInt *cellHeight)
8649 {
8650   DM_Plex *mesh = (DM_Plex *)dm->data;
8651 
8652   PetscFunctionBegin;
8653   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8654   PetscAssertPointer(cellHeight, 2);
8655   *cellHeight = mesh->vtkCellHeight;
8656   PetscFunctionReturn(PETSC_SUCCESS);
8657 }
8658 
8659 /*@
8660   DMPlexSetVTKCellHeight - Sets the height in the DAG used to determine which points are cells (normally 0)
8661 
8662   Input Parameters:
8663 + dm         - The `DMPLEX` object
8664 - cellHeight - The height of a cell
8665 
8666   Level: developer
8667 
8668 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetVTKCellHeight()`
8669 @*/
8670 PetscErrorCode DMPlexSetVTKCellHeight(DM dm, PetscInt cellHeight)
8671 {
8672   DM_Plex *mesh = (DM_Plex *)dm->data;
8673 
8674   PetscFunctionBegin;
8675   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8676   mesh->vtkCellHeight = cellHeight;
8677   PetscFunctionReturn(PETSC_SUCCESS);
8678 }
8679 
8680 /*@
8681   DMPlexGetCellTypeStratum - Get the range of cells of a given celltype
8682 
8683   Input Parameters:
8684 + dm - The `DMPLEX` object
8685 - ct - The `DMPolytopeType` of the cell
8686 
8687   Output Parameters:
8688 + start - The first cell of this type, or `NULL`
8689 - end   - The upper bound on this celltype, or `NULL`
8690 
8691   Level: advanced
8692 
8693 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexConstructGhostCells()`, `DMPlexGetDepthStratum()`, `DMPlexGetHeightStratum()`
8694 @*/
8695 PetscErrorCode DMPlexGetCellTypeStratum(DM dm, DMPolytopeType ct, PetscInt *start, PetscInt *end)
8696 {
8697   DM_Plex *mesh = (DM_Plex *)dm->data;
8698   DMLabel  label;
8699   PetscInt pStart, pEnd;
8700 
8701   PetscFunctionBegin;
8702   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8703   if (start) {
8704     PetscAssertPointer(start, 3);
8705     *start = 0;
8706   }
8707   if (end) {
8708     PetscAssertPointer(end, 4);
8709     *end = 0;
8710   }
8711   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8712   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
8713   if (mesh->tr) {
8714     PetscCall(DMPlexTransformGetCellTypeStratum(mesh->tr, ct, start, end));
8715   } else {
8716     PetscCall(DMPlexGetCellTypeLabel(dm, &label));
8717     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named celltype was found");
8718     PetscCall(DMLabelGetStratumBounds(label, ct, start, end));
8719   }
8720   PetscFunctionReturn(PETSC_SUCCESS);
8721 }
8722 
8723 PetscErrorCode DMPlexCreateNumbering_Plex(DM dm, PetscInt pStart, PetscInt pEnd, PetscInt shift, PetscInt *globalSize, PetscSF sf, IS *numbering)
8724 {
8725   PetscSection section, globalSection;
8726   PetscInt    *numbers, p;
8727 
8728   PetscFunctionBegin;
8729   if (PetscDefined(USE_DEBUG)) PetscCall(DMPlexCheckPointSF(dm, sf, PETSC_TRUE));
8730   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
8731   PetscCall(PetscSectionSetChart(section, pStart, pEnd));
8732   for (p = pStart; p < pEnd; ++p) PetscCall(PetscSectionSetDof(section, p, 1));
8733   PetscCall(PetscSectionSetUp(section));
8734   PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &globalSection));
8735   PetscCall(PetscMalloc1(pEnd - pStart, &numbers));
8736   for (p = pStart; p < pEnd; ++p) {
8737     PetscCall(PetscSectionGetOffset(globalSection, p, &numbers[p - pStart]));
8738     if (numbers[p - pStart] < 0) numbers[p - pStart] -= shift;
8739     else numbers[p - pStart] += shift;
8740   }
8741   PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), pEnd - pStart, numbers, PETSC_OWN_POINTER, numbering));
8742   if (globalSize) {
8743     PetscLayout layout;
8744     PetscCall(PetscSectionGetPointLayout(PetscObjectComm((PetscObject)dm), globalSection, &layout));
8745     PetscCall(PetscLayoutGetSize(layout, globalSize));
8746     PetscCall(PetscLayoutDestroy(&layout));
8747   }
8748   PetscCall(PetscSectionDestroy(&section));
8749   PetscCall(PetscSectionDestroy(&globalSection));
8750   PetscFunctionReturn(PETSC_SUCCESS);
8751 }
8752 
8753 PetscErrorCode DMPlexCreateCellNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalCellNumbers)
8754 {
8755   PetscInt cellHeight, cStart, cEnd;
8756 
8757   PetscFunctionBegin;
8758   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8759   if (includeHybrid) PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8760   else PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd));
8761   PetscCall(DMPlexCreateNumbering_Plex(dm, cStart, cEnd, 0, NULL, dm->sf, globalCellNumbers));
8762   PetscFunctionReturn(PETSC_SUCCESS);
8763 }
8764 
8765 /*@
8766   DMPlexGetCellNumbering - Get a global cell numbering for all cells on this process
8767 
8768   Input Parameter:
8769 . dm - The `DMPLEX` object
8770 
8771   Output Parameter:
8772 . globalCellNumbers - Global cell numbers for all cells on this process
8773 
8774   Level: developer
8775 
8776 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetVertexNumbering()`
8777 @*/
8778 PetscErrorCode DMPlexGetCellNumbering(DM dm, IS *globalCellNumbers)
8779 {
8780   DM_Plex *mesh = (DM_Plex *)dm->data;
8781 
8782   PetscFunctionBegin;
8783   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8784   if (!mesh->globalCellNumbers) PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_FALSE, &mesh->globalCellNumbers));
8785   *globalCellNumbers = mesh->globalCellNumbers;
8786   PetscFunctionReturn(PETSC_SUCCESS);
8787 }
8788 
8789 PetscErrorCode DMPlexCreateVertexNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalVertexNumbers)
8790 {
8791   PetscInt vStart, vEnd;
8792 
8793   PetscFunctionBegin;
8794   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8795   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8796   PetscCall(DMPlexCreateNumbering_Plex(dm, vStart, vEnd, 0, NULL, dm->sf, globalVertexNumbers));
8797   PetscFunctionReturn(PETSC_SUCCESS);
8798 }
8799 
8800 /*@
8801   DMPlexGetVertexNumbering - Get a global vertex numbering for all vertices on this process
8802 
8803   Input Parameter:
8804 . dm - The `DMPLEX` object
8805 
8806   Output Parameter:
8807 . globalVertexNumbers - Global vertex numbers for all vertices on this process
8808 
8809   Level: developer
8810 
8811 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
8812 @*/
8813 PetscErrorCode DMPlexGetVertexNumbering(DM dm, IS *globalVertexNumbers)
8814 {
8815   DM_Plex *mesh = (DM_Plex *)dm->data;
8816 
8817   PetscFunctionBegin;
8818   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8819   if (!mesh->globalVertexNumbers) PetscCall(DMPlexCreateVertexNumbering_Internal(dm, PETSC_FALSE, &mesh->globalVertexNumbers));
8820   *globalVertexNumbers = mesh->globalVertexNumbers;
8821   PetscFunctionReturn(PETSC_SUCCESS);
8822 }
8823 
8824 /*@
8825   DMPlexCreatePointNumbering - Create a global numbering for all points.
8826 
8827   Collective
8828 
8829   Input Parameter:
8830 . dm - The `DMPLEX` object
8831 
8832   Output Parameter:
8833 . globalPointNumbers - Global numbers for all points on this process
8834 
8835   Level: developer
8836 
8837   Notes:
8838   The point numbering `IS` is parallel, with local portion indexed by local points (see `DMGetLocalSection()`). The global
8839   points are taken as stratified, with each MPI rank owning a contiguous subset of each stratum. In the IS, owned points
8840   will have their non-negative value while points owned by different ranks will be involuted -(idx+1). As an example,
8841   consider a parallel mesh in which the first two elements and first two vertices are owned by rank 0.
8842 
8843   The partitioned mesh is
8844   ```
8845   (2)--0--(3)--1--(4)    (1)--0--(2)
8846   ```
8847   and its global numbering is
8848   ```
8849   (3)--0--(4)--1--(5)--2--(6)
8850   ```
8851   Then the global numbering is provided as
8852   ```
8853   [0] Number of indices in set 5
8854   [0] 0 0
8855   [0] 1 1
8856   [0] 2 3
8857   [0] 3 4
8858   [0] 4 -6
8859   [1] Number of indices in set 3
8860   [1] 0 2
8861   [1] 1 5
8862   [1] 2 6
8863   ```
8864 
8865 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
8866 @*/
8867 PetscErrorCode DMPlexCreatePointNumbering(DM dm, IS *globalPointNumbers)
8868 {
8869   IS        nums[4];
8870   PetscInt  depths[4], gdepths[4], starts[4];
8871   PetscInt  depth, d, shift = 0;
8872   PetscBool empty = PETSC_FALSE;
8873 
8874   PetscFunctionBegin;
8875   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8876   PetscCall(DMPlexGetDepth(dm, &depth));
8877   // For unstratified meshes use dim instead of depth
8878   if (depth < 0) PetscCall(DMGetDimension(dm, &depth));
8879   // If any stratum is empty, we must mark all empty
8880   for (d = 0; d <= depth; ++d) {
8881     PetscInt end;
8882 
8883     depths[d] = depth - d;
8884     PetscCall(DMPlexGetDepthStratum(dm, depths[d], &starts[d], &end));
8885     if (!(starts[d] - end)) empty = PETSC_TRUE;
8886   }
8887   if (empty)
8888     for (d = 0; d <= depth; ++d) {
8889       depths[d] = -1;
8890       starts[d] = -1;
8891     }
8892   else PetscCall(PetscSortIntWithArray(depth + 1, starts, depths));
8893   PetscCall(MPIU_Allreduce(depths, gdepths, depth + 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
8894   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]);
8895   // Note here that 'shift' is collective, so that the numbering is stratified by depth
8896   for (d = 0; d <= depth; ++d) {
8897     PetscInt pStart, pEnd, gsize;
8898 
8899     PetscCall(DMPlexGetDepthStratum(dm, gdepths[d], &pStart, &pEnd));
8900     PetscCall(DMPlexCreateNumbering_Plex(dm, pStart, pEnd, shift, &gsize, dm->sf, &nums[d]));
8901     shift += gsize;
8902   }
8903   PetscCall(ISConcatenate(PETSC_COMM_SELF, depth + 1, nums, globalPointNumbers));
8904   for (d = 0; d <= depth; ++d) PetscCall(ISDestroy(&nums[d]));
8905   PetscFunctionReturn(PETSC_SUCCESS);
8906 }
8907 
8908 /*@
8909   DMPlexCreateEdgeNumbering - Create a global numbering for edges.
8910 
8911   Collective
8912 
8913   Input Parameter:
8914 . dm - The `DMPLEX` object
8915 
8916   Output Parameter:
8917 . globalEdgeNumbers - Global numbers for all edges on this process
8918 
8919   Level: developer
8920 
8921   Notes:
8922   The point numbering `IS` is parallel, with local portion indexed by local points (see `DMGetLocalSection()`). In the IS, owned edges will have their non-negative value while edges owned by different ranks will be involuted -(idx+1).
8923 
8924 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`, `DMPlexGetVertexNumbering()`, `DMPlexCreatePointNumbering()`
8925 @*/
8926 PetscErrorCode DMPlexCreateEdgeNumbering(DM dm, IS *globalEdgeNumbers)
8927 {
8928   PetscSF  sf;
8929   PetscInt eStart, eEnd;
8930 
8931   PetscFunctionBegin;
8932   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8933   PetscCall(DMGetPointSF(dm, &sf));
8934   PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
8935   PetscCall(DMPlexCreateNumbering_Plex(dm, eStart, eEnd, 0, NULL, sf, globalEdgeNumbers));
8936   PetscFunctionReturn(PETSC_SUCCESS);
8937 }
8938 
8939 /*@
8940   DMPlexCreateRankField - Create a cell field whose value is the rank of the owner
8941 
8942   Input Parameter:
8943 . dm - The `DMPLEX` object
8944 
8945   Output Parameter:
8946 . ranks - The rank field
8947 
8948   Options Database Key:
8949 . -dm_partition_view - Adds the rank field into the `DM` output from `-dm_view` using the same viewer
8950 
8951   Level: intermediate
8952 
8953 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`
8954 @*/
8955 PetscErrorCode DMPlexCreateRankField(DM dm, Vec *ranks)
8956 {
8957   DM             rdm;
8958   PetscFE        fe;
8959   PetscScalar   *r;
8960   PetscMPIInt    rank;
8961   DMPolytopeType ct;
8962   PetscInt       dim, cStart, cEnd, c;
8963   PetscBool      simplex;
8964 
8965   PetscFunctionBeginUser;
8966   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8967   PetscAssertPointer(ranks, 2);
8968   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
8969   PetscCall(DMClone(dm, &rdm));
8970   PetscCall(DMGetDimension(rdm, &dim));
8971   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
8972   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
8973   simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct) + 1 ? PETSC_TRUE : PETSC_FALSE;
8974   PetscCall(PetscFECreateDefault(PETSC_COMM_SELF, dim, 1, simplex, "PETSc___rank_", -1, &fe));
8975   PetscCall(PetscObjectSetName((PetscObject)fe, "rank"));
8976   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
8977   PetscCall(PetscFEDestroy(&fe));
8978   PetscCall(DMCreateDS(rdm));
8979   PetscCall(DMCreateGlobalVector(rdm, ranks));
8980   PetscCall(PetscObjectSetName((PetscObject)*ranks, "partition"));
8981   PetscCall(VecGetArray(*ranks, &r));
8982   for (c = cStart; c < cEnd; ++c) {
8983     PetscScalar *lr;
8984 
8985     PetscCall(DMPlexPointGlobalRef(rdm, c, r, &lr));
8986     if (lr) *lr = rank;
8987   }
8988   PetscCall(VecRestoreArray(*ranks, &r));
8989   PetscCall(DMDestroy(&rdm));
8990   PetscFunctionReturn(PETSC_SUCCESS);
8991 }
8992 
8993 /*@
8994   DMPlexCreateLabelField - Create a field whose value is the label value for that point
8995 
8996   Input Parameters:
8997 + dm    - The `DMPLEX`
8998 - label - The `DMLabel`
8999 
9000   Output Parameter:
9001 . val - The label value field
9002 
9003   Options Database Key:
9004 . -dm_label_view - Adds the label value field into the `DM` output from `-dm_view` using the same viewer
9005 
9006   Level: intermediate
9007 
9008 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`
9009 @*/
9010 PetscErrorCode DMPlexCreateLabelField(DM dm, DMLabel label, Vec *val)
9011 {
9012   DM             rdm, plex;
9013   Vec            lval;
9014   PetscSection   section;
9015   PetscFE        fe;
9016   PetscScalar   *v;
9017   PetscInt       dim, pStart, pEnd, p, cStart;
9018   DMPolytopeType ct;
9019   char           name[PETSC_MAX_PATH_LEN];
9020   const char    *lname, *prefix;
9021 
9022   PetscFunctionBeginUser;
9023   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9024   PetscAssertPointer(label, 2);
9025   PetscAssertPointer(val, 3);
9026   PetscCall(DMClone(dm, &rdm));
9027   PetscCall(DMConvert(rdm, DMPLEX, &plex));
9028   PetscCall(DMPlexGetHeightStratum(plex, 0, &cStart, NULL));
9029   PetscCall(DMPlexGetCellType(plex, cStart, &ct));
9030   PetscCall(DMDestroy(&plex));
9031   PetscCall(DMGetDimension(rdm, &dim));
9032   PetscCall(DMGetOptionsPrefix(dm, &prefix));
9033   PetscCall(PetscObjectGetName((PetscObject)label, &lname));
9034   PetscCall(PetscSNPrintf(name, sizeof(name), "%s%s_", prefix ? prefix : "", lname));
9035   PetscCall(PetscFECreateByCell(PETSC_COMM_SELF, dim, 1, ct, name, -1, &fe));
9036   PetscCall(PetscObjectSetName((PetscObject)fe, ""));
9037   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
9038   PetscCall(PetscFEDestroy(&fe));
9039   PetscCall(DMCreateDS(rdm));
9040   PetscCall(DMCreateGlobalVector(rdm, val));
9041   PetscCall(DMCreateLocalVector(rdm, &lval));
9042   PetscCall(PetscObjectSetName((PetscObject)*val, lname));
9043   PetscCall(DMGetLocalSection(rdm, &section));
9044   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
9045   PetscCall(VecGetArray(lval, &v));
9046   for (p = pStart; p < pEnd; ++p) {
9047     PetscInt cval, dof, off;
9048 
9049     PetscCall(PetscSectionGetDof(section, p, &dof));
9050     if (!dof) continue;
9051     PetscCall(DMLabelGetValue(label, p, &cval));
9052     PetscCall(PetscSectionGetOffset(section, p, &off));
9053     for (PetscInt d = 0; d < dof; d++) v[off + d] = cval;
9054   }
9055   PetscCall(VecRestoreArray(lval, &v));
9056   PetscCall(DMLocalToGlobal(rdm, lval, INSERT_VALUES, *val));
9057   PetscCall(VecDestroy(&lval));
9058   PetscCall(DMDestroy(&rdm));
9059   PetscFunctionReturn(PETSC_SUCCESS);
9060 }
9061 
9062 /*@
9063   DMPlexCheckSymmetry - Check that the adjacency information in the mesh is symmetric.
9064 
9065   Input Parameter:
9066 . dm - The `DMPLEX` object
9067 
9068   Level: developer
9069 
9070   Notes:
9071   This is a useful diagnostic when creating meshes programmatically.
9072 
9073   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9074 
9075 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9076 @*/
9077 PetscErrorCode DMPlexCheckSymmetry(DM dm)
9078 {
9079   PetscSection    coneSection, supportSection;
9080   const PetscInt *cone, *support;
9081   PetscInt        coneSize, c, supportSize, s;
9082   PetscInt        pStart, pEnd, p, pp, csize, ssize;
9083   PetscBool       storagecheck = PETSC_TRUE;
9084 
9085   PetscFunctionBegin;
9086   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9087   PetscCall(DMViewFromOptions(dm, NULL, "-sym_dm_view"));
9088   PetscCall(DMPlexGetConeSection(dm, &coneSection));
9089   PetscCall(DMPlexGetSupportSection(dm, &supportSection));
9090   /* Check that point p is found in the support of its cone points, and vice versa */
9091   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
9092   for (p = pStart; p < pEnd; ++p) {
9093     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
9094     PetscCall(DMPlexGetCone(dm, p, &cone));
9095     for (c = 0; c < coneSize; ++c) {
9096       PetscBool dup = PETSC_FALSE;
9097       PetscInt  d;
9098       for (d = c - 1; d >= 0; --d) {
9099         if (cone[c] == cone[d]) {
9100           dup = PETSC_TRUE;
9101           break;
9102         }
9103       }
9104       PetscCall(DMPlexGetSupportSize(dm, cone[c], &supportSize));
9105       PetscCall(DMPlexGetSupport(dm, cone[c], &support));
9106       for (s = 0; s < supportSize; ++s) {
9107         if (support[s] == p) break;
9108       }
9109       if ((s >= supportSize) || (dup && (support[s + 1] != p))) {
9110         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", p));
9111         for (s = 0; s < coneSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[s]));
9112         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9113         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", cone[c]));
9114         for (s = 0; s < supportSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[s]));
9115         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9116         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]);
9117         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in support of cone point %" PetscInt_FMT, p, cone[c]);
9118       }
9119     }
9120     PetscCall(DMPlexGetTreeParent(dm, p, &pp, NULL));
9121     if (p != pp) {
9122       storagecheck = PETSC_FALSE;
9123       continue;
9124     }
9125     PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
9126     PetscCall(DMPlexGetSupport(dm, p, &support));
9127     for (s = 0; s < supportSize; ++s) {
9128       PetscCall(DMPlexGetConeSize(dm, support[s], &coneSize));
9129       PetscCall(DMPlexGetCone(dm, support[s], &cone));
9130       for (c = 0; c < coneSize; ++c) {
9131         PetscCall(DMPlexGetTreeParent(dm, cone[c], &pp, NULL));
9132         if (cone[c] != pp) {
9133           c = 0;
9134           break;
9135         }
9136         if (cone[c] == p) break;
9137       }
9138       if (c >= coneSize) {
9139         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", p));
9140         for (c = 0; c < supportSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[c]));
9141         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9142         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", support[s]));
9143         for (c = 0; c < coneSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[c]));
9144         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9145         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in cone of support point %" PetscInt_FMT, p, support[s]);
9146       }
9147     }
9148   }
9149   if (storagecheck) {
9150     PetscCall(PetscSectionGetStorageSize(coneSection, &csize));
9151     PetscCall(PetscSectionGetStorageSize(supportSection, &ssize));
9152     PetscCheck(csize == ssize, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Total cone size %" PetscInt_FMT " != Total support size %" PetscInt_FMT, csize, ssize);
9153   }
9154   PetscFunctionReturn(PETSC_SUCCESS);
9155 }
9156 
9157 /*
9158   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.
9159 */
9160 static PetscErrorCode DMPlexCellUnsplitVertices_Private(DM dm, PetscInt c, DMPolytopeType ct, PetscInt *unsplit)
9161 {
9162   DMPolytopeType  cct;
9163   PetscInt        ptpoints[4];
9164   const PetscInt *cone, *ccone, *ptcone;
9165   PetscInt        coneSize, cp, cconeSize, ccp, npt = 0, pt;
9166 
9167   PetscFunctionBegin;
9168   *unsplit = 0;
9169   switch (ct) {
9170   case DM_POLYTOPE_POINT_PRISM_TENSOR:
9171     ptpoints[npt++] = c;
9172     break;
9173   case DM_POLYTOPE_SEG_PRISM_TENSOR:
9174     PetscCall(DMPlexGetCone(dm, c, &cone));
9175     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9176     for (cp = 0; cp < coneSize; ++cp) {
9177       PetscCall(DMPlexGetCellType(dm, cone[cp], &cct));
9178       if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) ptpoints[npt++] = cone[cp];
9179     }
9180     break;
9181   case DM_POLYTOPE_TRI_PRISM_TENSOR:
9182   case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9183     PetscCall(DMPlexGetCone(dm, c, &cone));
9184     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9185     for (cp = 0; cp < coneSize; ++cp) {
9186       PetscCall(DMPlexGetCone(dm, cone[cp], &ccone));
9187       PetscCall(DMPlexGetConeSize(dm, cone[cp], &cconeSize));
9188       for (ccp = 0; ccp < cconeSize; ++ccp) {
9189         PetscCall(DMPlexGetCellType(dm, ccone[ccp], &cct));
9190         if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) {
9191           PetscInt p;
9192           for (p = 0; p < npt; ++p)
9193             if (ptpoints[p] == ccone[ccp]) break;
9194           if (p == npt) ptpoints[npt++] = ccone[ccp];
9195         }
9196       }
9197     }
9198     break;
9199   default:
9200     break;
9201   }
9202   for (pt = 0; pt < npt; ++pt) {
9203     PetscCall(DMPlexGetCone(dm, ptpoints[pt], &ptcone));
9204     if (ptcone[0] == ptcone[1]) ++(*unsplit);
9205   }
9206   PetscFunctionReturn(PETSC_SUCCESS);
9207 }
9208 
9209 /*@
9210   DMPlexCheckSkeleton - Check that each cell has the correct number of vertices
9211 
9212   Input Parameters:
9213 + dm         - The `DMPLEX` object
9214 - cellHeight - Normally 0
9215 
9216   Level: developer
9217 
9218   Notes:
9219   This is a useful diagnostic when creating meshes programmatically.
9220   Currently applicable only to homogeneous simplex or tensor meshes.
9221 
9222   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9223 
9224 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9225 @*/
9226 PetscErrorCode DMPlexCheckSkeleton(DM dm, PetscInt cellHeight)
9227 {
9228   DMPlexInterpolatedFlag interp;
9229   DMPolytopeType         ct;
9230   PetscInt               vStart, vEnd, cStart, cEnd, c;
9231 
9232   PetscFunctionBegin;
9233   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9234   PetscCall(DMPlexIsInterpolated(dm, &interp));
9235   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9236   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
9237   for (c = cStart; c < cEnd; ++c) {
9238     PetscInt *closure = NULL;
9239     PetscInt  coneSize, closureSize, cl, Nv = 0;
9240 
9241     PetscCall(DMPlexGetCellType(dm, c, &ct));
9242     PetscCheck((PetscInt)ct >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell %" PetscInt_FMT " has no cell type", c);
9243     if (ct == DM_POLYTOPE_UNKNOWN) continue;
9244     if (interp == DMPLEX_INTERPOLATED_FULL) {
9245       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9246       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));
9247     }
9248     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9249     for (cl = 0; cl < closureSize * 2; cl += 2) {
9250       const PetscInt p = closure[cl];
9251       if ((p >= vStart) && (p < vEnd)) ++Nv;
9252     }
9253     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9254     /* Special Case: Tensor faces with identified vertices */
9255     if (Nv < DMPolytopeTypeGetNumVertices(ct)) {
9256       PetscInt unsplit;
9257 
9258       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9259       if (Nv + unsplit == DMPolytopeTypeGetNumVertices(ct)) continue;
9260     }
9261     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));
9262   }
9263   PetscFunctionReturn(PETSC_SUCCESS);
9264 }
9265 
9266 /*@
9267   DMPlexCheckFaces - Check that the faces of each cell give a vertex order this is consistent with what we expect from the cell type
9268 
9269   Collective
9270 
9271   Input Parameters:
9272 + dm         - The `DMPLEX` object
9273 - cellHeight - Normally 0
9274 
9275   Level: developer
9276 
9277   Notes:
9278   This is a useful diagnostic when creating meshes programmatically.
9279   This routine is only relevant for meshes that are fully interpolated across all ranks.
9280   It will error out if a partially interpolated mesh is given on some rank.
9281   It will do nothing for locally uninterpolated mesh (as there is nothing to check).
9282 
9283   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9284 
9285 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMPlexGetVTKCellHeight()`, `DMSetFromOptions()`
9286 @*/
9287 PetscErrorCode DMPlexCheckFaces(DM dm, PetscInt cellHeight)
9288 {
9289   PetscInt               dim, depth, vStart, vEnd, cStart, cEnd, c, h;
9290   DMPlexInterpolatedFlag interpEnum;
9291 
9292   PetscFunctionBegin;
9293   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9294   PetscCall(DMPlexIsInterpolatedCollective(dm, &interpEnum));
9295   if (interpEnum == DMPLEX_INTERPOLATED_NONE) PetscFunctionReturn(PETSC_SUCCESS);
9296   if (interpEnum != DMPLEX_INTERPOLATED_FULL) {
9297     PetscCall(PetscPrintf(PetscObjectComm((PetscObject)dm), "DMPlexCheckFaces() warning: Mesh is only partially interpolated, this is currently not supported"));
9298     PetscFunctionReturn(PETSC_SUCCESS);
9299   }
9300 
9301   PetscCall(DMGetDimension(dm, &dim));
9302   PetscCall(DMPlexGetDepth(dm, &depth));
9303   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
9304   for (h = cellHeight; h < PetscMin(depth, dim); ++h) {
9305     PetscCall(DMPlexGetHeightStratum(dm, h, &cStart, &cEnd));
9306     for (c = cStart; c < cEnd; ++c) {
9307       const PetscInt       *cone, *ornt, *faceSizes, *faces;
9308       const DMPolytopeType *faceTypes;
9309       DMPolytopeType        ct;
9310       PetscInt              numFaces, coneSize, f;
9311       PetscInt             *closure = NULL, closureSize, cl, numCorners = 0, fOff = 0, unsplit;
9312 
9313       PetscCall(DMPlexGetCellType(dm, c, &ct));
9314       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9315       if (unsplit) continue;
9316       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9317       PetscCall(DMPlexGetCone(dm, c, &cone));
9318       PetscCall(DMPlexGetConeOrientation(dm, c, &ornt));
9319       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9320       for (cl = 0; cl < closureSize * 2; cl += 2) {
9321         const PetscInt p = closure[cl];
9322         if ((p >= vStart) && (p < vEnd)) closure[numCorners++] = p;
9323       }
9324       PetscCall(DMPlexGetRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
9325       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);
9326       for (f = 0; f < numFaces; ++f) {
9327         DMPolytopeType fct;
9328         PetscInt      *fclosure = NULL, fclosureSize, cl, fnumCorners = 0, v;
9329 
9330         PetscCall(DMPlexGetCellType(dm, cone[f], &fct));
9331         PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[f], ornt[f], PETSC_TRUE, &fclosureSize, &fclosure));
9332         for (cl = 0; cl < fclosureSize * 2; cl += 2) {
9333           const PetscInt p = fclosure[cl];
9334           if ((p >= vStart) && (p < vEnd)) fclosure[fnumCorners++] = p;
9335         }
9336         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]);
9337         for (v = 0; v < fnumCorners; ++v) {
9338           if (fclosure[v] != faces[fOff + v]) {
9339             PetscInt v1;
9340 
9341             PetscCall(PetscPrintf(PETSC_COMM_SELF, "face closure:"));
9342             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, fclosure[v1]));
9343             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\ncell face:"));
9344             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, faces[fOff + v1]));
9345             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9346             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]);
9347           }
9348         }
9349         PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[f], PETSC_TRUE, &fclosureSize, &fclosure));
9350         fOff += faceSizes[f];
9351       }
9352       PetscCall(DMPlexRestoreRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
9353       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9354     }
9355   }
9356   PetscFunctionReturn(PETSC_SUCCESS);
9357 }
9358 
9359 /*@
9360   DMPlexCheckGeometry - Check the geometry of mesh cells
9361 
9362   Input Parameter:
9363 . dm - The `DMPLEX` object
9364 
9365   Level: developer
9366 
9367   Notes:
9368   This is a useful diagnostic when creating meshes programmatically.
9369 
9370   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9371 
9372 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9373 @*/
9374 PetscErrorCode DMPlexCheckGeometry(DM dm)
9375 {
9376   Vec       coordinates;
9377   PetscReal detJ, J[9], refVol = 1.0;
9378   PetscReal vol;
9379   PetscInt  dim, depth, dE, d, cStart, cEnd, c;
9380 
9381   PetscFunctionBegin;
9382   PetscCall(DMGetDimension(dm, &dim));
9383   PetscCall(DMGetCoordinateDim(dm, &dE));
9384   if (dim != dE) PetscFunctionReturn(PETSC_SUCCESS);
9385   PetscCall(DMPlexGetDepth(dm, &depth));
9386   for (d = 0; d < dim; ++d) refVol *= 2.0;
9387   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
9388   /* Make sure local coordinates are created, because that step is collective */
9389   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
9390   if (!coordinates) PetscFunctionReturn(PETSC_SUCCESS);
9391   for (c = cStart; c < cEnd; ++c) {
9392     DMPolytopeType ct;
9393     PetscInt       unsplit;
9394     PetscBool      ignoreZeroVol = PETSC_FALSE;
9395 
9396     PetscCall(DMPlexGetCellType(dm, c, &ct));
9397     switch (ct) {
9398     case DM_POLYTOPE_SEG_PRISM_TENSOR:
9399     case DM_POLYTOPE_TRI_PRISM_TENSOR:
9400     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9401       ignoreZeroVol = PETSC_TRUE;
9402       break;
9403     default:
9404       break;
9405     }
9406     switch (ct) {
9407     case DM_POLYTOPE_TRI_PRISM:
9408     case DM_POLYTOPE_TRI_PRISM_TENSOR:
9409     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9410     case DM_POLYTOPE_PYRAMID:
9411       continue;
9412     default:
9413       break;
9414     }
9415     PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9416     if (unsplit) continue;
9417     PetscCall(DMPlexComputeCellGeometryFEM(dm, c, NULL, NULL, J, NULL, &detJ));
9418     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);
9419     PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FEM Volume %g\n", c, (double)(detJ * refVol)));
9420     /* This should work with periodicity since DG coordinates should be used */
9421     if (depth > 1) {
9422       PetscCall(DMPlexComputeCellGeometryFVM(dm, c, &vol, NULL, NULL));
9423       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);
9424       PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FVM Volume %g\n", c, (double)vol));
9425     }
9426   }
9427   PetscFunctionReturn(PETSC_SUCCESS);
9428 }
9429 
9430 /*@
9431   DMPlexCheckPointSF - Check that several necessary conditions are met for the point `PetscSF` of this plex.
9432 
9433   Collective
9434 
9435   Input Parameters:
9436 + dm              - The `DMPLEX` object
9437 . pointSF         - The `PetscSF`, or `NULL` for `PointSF` attached to `DM`
9438 - allowExtraRoots - Flag to allow extra points not present in the `DM`
9439 
9440   Level: developer
9441 
9442   Notes:
9443   This is mainly intended for debugging/testing purposes.
9444 
9445   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9446 
9447   Extra roots can come from periodic cuts, where additional points appear on the boundary
9448 
9449 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMGetPointSF()`, `DMSetFromOptions()`
9450 @*/
9451 PetscErrorCode DMPlexCheckPointSF(DM dm, PetscSF pointSF, PetscBool allowExtraRoots)
9452 {
9453   PetscInt           l, nleaves, nroots, overlap;
9454   const PetscInt    *locals;
9455   const PetscSFNode *remotes;
9456   PetscBool          distributed;
9457   MPI_Comm           comm;
9458   PetscMPIInt        rank;
9459 
9460   PetscFunctionBegin;
9461   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9462   if (pointSF) PetscValidHeaderSpecific(pointSF, PETSCSF_CLASSID, 2);
9463   else pointSF = dm->sf;
9464   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9465   PetscCheck(pointSF, comm, PETSC_ERR_ARG_WRONGSTATE, "DMPlex must have Point SF attached");
9466   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9467   {
9468     PetscMPIInt mpiFlag;
9469 
9470     PetscCallMPI(MPI_Comm_compare(comm, PetscObjectComm((PetscObject)pointSF), &mpiFlag));
9471     PetscCheck(mpiFlag == MPI_CONGRUENT || mpiFlag == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "DM and Point SF have different communicators (flag %d)", mpiFlag);
9472   }
9473   PetscCall(PetscSFGetGraph(pointSF, &nroots, &nleaves, &locals, &remotes));
9474   PetscCall(DMPlexIsDistributed(dm, &distributed));
9475   if (!distributed) {
9476     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);
9477     PetscFunctionReturn(PETSC_SUCCESS);
9478   }
9479   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);
9480   PetscCall(DMPlexGetOverlap(dm, &overlap));
9481 
9482   /* Check SF graph is compatible with DMPlex chart */
9483   {
9484     PetscInt pStart, pEnd, maxLeaf;
9485 
9486     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
9487     PetscCall(PetscSFGetLeafRange(pointSF, NULL, &maxLeaf));
9488     PetscCheck(allowExtraRoots || pEnd - pStart == nroots, PETSC_COMM_SELF, PETSC_ERR_PLIB, "pEnd - pStart = %" PetscInt_FMT " != nroots = %" PetscInt_FMT, pEnd - pStart, nroots);
9489     PetscCheck(maxLeaf < pEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "maxLeaf = %" PetscInt_FMT " >= pEnd = %" PetscInt_FMT, maxLeaf, pEnd);
9490   }
9491 
9492   /* Check Point SF has no local points referenced */
9493   for (l = 0; l < nleaves; l++) {
9494     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);
9495   }
9496 
9497   /* Check there are no cells in interface */
9498   if (!overlap) {
9499     PetscInt cellHeight, cStart, cEnd;
9500 
9501     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9502     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9503     for (l = 0; l < nleaves; ++l) {
9504       const PetscInt point = locals ? locals[l] : l;
9505 
9506       PetscCheck(point < cStart || point >= cEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " which is a cell", point);
9507     }
9508   }
9509 
9510   /* If some point is in interface, then all its cone points must be also in interface (either as leaves or roots) */
9511   {
9512     const PetscInt *rootdegree;
9513 
9514     PetscCall(PetscSFComputeDegreeBegin(pointSF, &rootdegree));
9515     PetscCall(PetscSFComputeDegreeEnd(pointSF, &rootdegree));
9516     for (l = 0; l < nleaves; ++l) {
9517       const PetscInt  point = locals ? locals[l] : l;
9518       const PetscInt *cone;
9519       PetscInt        coneSize, c, idx;
9520 
9521       PetscCall(DMPlexGetConeSize(dm, point, &coneSize));
9522       PetscCall(DMPlexGetCone(dm, point, &cone));
9523       for (c = 0; c < coneSize; ++c) {
9524         if (!rootdegree[cone[c]]) {
9525           if (locals) {
9526             PetscCall(PetscFindInt(cone[c], nleaves, locals, &idx));
9527           } else {
9528             idx = (cone[c] < nleaves) ? cone[c] : -1;
9529           }
9530           PetscCheck(idx >= 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " but not %" PetscInt_FMT " from its cone", point, cone[c]);
9531         }
9532       }
9533     }
9534   }
9535   PetscFunctionReturn(PETSC_SUCCESS);
9536 }
9537 
9538 /*@
9539   DMPlexCheckOrphanVertices - Check that no vertices are disconnected from the mesh, unless the mesh only consists of disconnected vertices.
9540 
9541   Collective
9542 
9543   Input Parameter:
9544 . dm - The `DMPLEX` object
9545 
9546   Level: developer
9547 
9548   Notes:
9549   This is mainly intended for debugging/testing purposes.
9550 
9551   Other cell types which are disconnected would be caught by the symmetry and face checks.
9552 
9553   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9554 
9555 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCheck()`, `DMSetFromOptions()`
9556 @*/
9557 PetscErrorCode DMPlexCheckOrphanVertices(DM dm)
9558 {
9559   PetscInt pStart, pEnd, vStart, vEnd;
9560 
9561   PetscFunctionBegin;
9562   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
9563   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
9564   if (pStart == vStart && pEnd == vEnd) PetscFunctionReturn(PETSC_SUCCESS);
9565   for (PetscInt v = vStart; v < vEnd; ++v) {
9566     PetscInt suppSize;
9567 
9568     PetscCall(DMPlexGetSupportSize(dm, v, &suppSize));
9569     PetscCheck(suppSize, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Vertex %" PetscInt_FMT " is disconnected from the mesh", v);
9570   }
9571   PetscFunctionReturn(PETSC_SUCCESS);
9572 }
9573 
9574 /*@
9575   DMPlexCheck - Perform various checks of `DMPLEX` sanity
9576 
9577   Input Parameter:
9578 . dm - The `DMPLEX` object
9579 
9580   Level: developer
9581 
9582   Notes:
9583   This is a useful diagnostic when creating meshes programmatically.
9584 
9585   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9586 
9587   Currently does not include `DMPlexCheckCellShape()`.
9588 
9589 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9590 @*/
9591 PetscErrorCode DMPlexCheck(DM dm)
9592 {
9593   PetscInt cellHeight;
9594 
9595   PetscFunctionBegin;
9596   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9597   PetscCall(DMPlexCheckSymmetry(dm));
9598   PetscCall(DMPlexCheckSkeleton(dm, cellHeight));
9599   PetscCall(DMPlexCheckFaces(dm, cellHeight));
9600   PetscCall(DMPlexCheckGeometry(dm));
9601   PetscCall(DMPlexCheckPointSF(dm, NULL, PETSC_FALSE));
9602   PetscCall(DMPlexCheckInterfaceCones(dm));
9603   PetscCall(DMPlexCheckOrphanVertices(dm));
9604   PetscFunctionReturn(PETSC_SUCCESS);
9605 }
9606 
9607 typedef struct cell_stats {
9608   PetscReal min, max, sum, squaresum;
9609   PetscInt  count;
9610 } cell_stats_t;
9611 
9612 static void MPIAPI cell_stats_reduce(void *a, void *b, int *len, MPI_Datatype *datatype)
9613 {
9614   PetscInt i, N = *len;
9615 
9616   for (i = 0; i < N; i++) {
9617     cell_stats_t *A = (cell_stats_t *)a;
9618     cell_stats_t *B = (cell_stats_t *)b;
9619 
9620     B->min = PetscMin(A->min, B->min);
9621     B->max = PetscMax(A->max, B->max);
9622     B->sum += A->sum;
9623     B->squaresum += A->squaresum;
9624     B->count += A->count;
9625   }
9626 }
9627 
9628 /*@
9629   DMPlexCheckCellShape - Checks the Jacobian of the mapping from reference to real cells and computes some minimal statistics.
9630 
9631   Collective
9632 
9633   Input Parameters:
9634 + dm        - The `DMPLEX` object
9635 . output    - If true, statistics will be displayed on `stdout`
9636 - condLimit - Display all cells above this condition number, or `PETSC_DETERMINE` for no cell output
9637 
9638   Level: developer
9639 
9640   Notes:
9641   This is mainly intended for debugging/testing purposes.
9642 
9643   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9644 
9645 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexComputeOrthogonalQuality()`
9646 @*/
9647 PetscErrorCode DMPlexCheckCellShape(DM dm, PetscBool output, PetscReal condLimit)
9648 {
9649   DM           dmCoarse;
9650   cell_stats_t stats, globalStats;
9651   MPI_Comm     comm = PetscObjectComm((PetscObject)dm);
9652   PetscReal   *J, *invJ, min = 0, max = 0, mean = 0, stdev = 0;
9653   PetscReal    limit = condLimit > 0 ? condLimit : PETSC_MAX_REAL;
9654   PetscInt     cdim, cStart, cEnd, c, eStart, eEnd, count = 0;
9655   PetscMPIInt  rank, size;
9656 
9657   PetscFunctionBegin;
9658   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9659   stats.min = PETSC_MAX_REAL;
9660   stats.max = PETSC_MIN_REAL;
9661   stats.sum = stats.squaresum = 0.;
9662   stats.count                 = 0;
9663 
9664   PetscCallMPI(MPI_Comm_size(comm, &size));
9665   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9666   PetscCall(DMGetCoordinateDim(dm, &cdim));
9667   PetscCall(PetscMalloc2(PetscSqr(cdim), &J, PetscSqr(cdim), &invJ));
9668   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
9669   PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
9670   for (c = cStart; c < cEnd; c++) {
9671     PetscInt  i;
9672     PetscReal frobJ = 0., frobInvJ = 0., cond2, cond, detJ;
9673 
9674     PetscCall(DMPlexComputeCellGeometryAffineFEM(dm, c, NULL, J, invJ, &detJ));
9675     PetscCheck(detJ >= 0.0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Mesh cell %" PetscInt_FMT " is inverted", c);
9676     for (i = 0; i < PetscSqr(cdim); ++i) {
9677       frobJ += J[i] * J[i];
9678       frobInvJ += invJ[i] * invJ[i];
9679     }
9680     cond2 = frobJ * frobInvJ;
9681     cond  = PetscSqrtReal(cond2);
9682 
9683     stats.min = PetscMin(stats.min, cond);
9684     stats.max = PetscMax(stats.max, cond);
9685     stats.sum += cond;
9686     stats.squaresum += cond2;
9687     stats.count++;
9688     if (output && cond > limit) {
9689       PetscSection coordSection;
9690       Vec          coordsLocal;
9691       PetscScalar *coords = NULL;
9692       PetscInt     Nv, d, clSize, cl, *closure = NULL;
9693 
9694       PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
9695       PetscCall(DMGetCoordinateSection(dm, &coordSection));
9696       PetscCall(DMPlexVecGetClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9697       PetscCall(PetscSynchronizedPrintf(comm, "[%d] Cell %" PetscInt_FMT " cond %g\n", rank, c, (double)cond));
9698       for (i = 0; i < Nv / cdim; ++i) {
9699         PetscCall(PetscSynchronizedPrintf(comm, "  Vertex %" PetscInt_FMT ": (", i));
9700         for (d = 0; d < cdim; ++d) {
9701           if (d > 0) PetscCall(PetscSynchronizedPrintf(comm, ", "));
9702           PetscCall(PetscSynchronizedPrintf(comm, "%g", (double)PetscRealPart(coords[i * cdim + d])));
9703         }
9704         PetscCall(PetscSynchronizedPrintf(comm, ")\n"));
9705       }
9706       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9707       for (cl = 0; cl < clSize * 2; cl += 2) {
9708         const PetscInt edge = closure[cl];
9709 
9710         if ((edge >= eStart) && (edge < eEnd)) {
9711           PetscReal len;
9712 
9713           PetscCall(DMPlexComputeCellGeometryFVM(dm, edge, &len, NULL, NULL));
9714           PetscCall(PetscSynchronizedPrintf(comm, "  Edge %" PetscInt_FMT ": length %g\n", edge, (double)len));
9715         }
9716       }
9717       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9718       PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9719     }
9720   }
9721   if (output) PetscCall(PetscSynchronizedFlush(comm, NULL));
9722 
9723   if (size > 1) {
9724     PetscMPIInt  blockLengths[2] = {4, 1};
9725     MPI_Aint     blockOffsets[2] = {offsetof(cell_stats_t, min), offsetof(cell_stats_t, count)};
9726     MPI_Datatype blockTypes[2]   = {MPIU_REAL, MPIU_INT}, statType;
9727     MPI_Op       statReduce;
9728 
9729     PetscCallMPI(MPI_Type_create_struct(2, blockLengths, blockOffsets, blockTypes, &statType));
9730     PetscCallMPI(MPI_Type_commit(&statType));
9731     PetscCallMPI(MPI_Op_create(cell_stats_reduce, PETSC_TRUE, &statReduce));
9732     PetscCallMPI(MPI_Reduce(&stats, &globalStats, 1, statType, statReduce, 0, comm));
9733     PetscCallMPI(MPI_Op_free(&statReduce));
9734     PetscCallMPI(MPI_Type_free(&statType));
9735   } else {
9736     PetscCall(PetscArraycpy(&globalStats, &stats, 1));
9737   }
9738   if (rank == 0) {
9739     count = globalStats.count;
9740     min   = globalStats.min;
9741     max   = globalStats.max;
9742     mean  = globalStats.sum / globalStats.count;
9743     stdev = globalStats.count > 1 ? PetscSqrtReal(PetscMax((globalStats.squaresum - globalStats.count * mean * mean) / (globalStats.count - 1), 0)) : 0.0;
9744   }
9745 
9746   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));
9747   PetscCall(PetscFree2(J, invJ));
9748 
9749   PetscCall(DMGetCoarseDM(dm, &dmCoarse));
9750   if (dmCoarse) {
9751     PetscBool isplex;
9752 
9753     PetscCall(PetscObjectTypeCompare((PetscObject)dmCoarse, DMPLEX, &isplex));
9754     if (isplex) PetscCall(DMPlexCheckCellShape(dmCoarse, output, condLimit));
9755   }
9756   PetscFunctionReturn(PETSC_SUCCESS);
9757 }
9758 
9759 /*@
9760   DMPlexComputeOrthogonalQuality - Compute cell-wise orthogonal quality mesh statistic. Optionally tags all cells with
9761   orthogonal quality below given tolerance.
9762 
9763   Collective
9764 
9765   Input Parameters:
9766 + dm   - The `DMPLEX` object
9767 . fv   - Optional `PetscFV` object for pre-computed cell/face centroid information
9768 - atol - [0, 1] Absolute tolerance for tagging cells.
9769 
9770   Output Parameters:
9771 + OrthQual      - `Vec` containing orthogonal quality per cell
9772 - OrthQualLabel - `DMLabel` tagging cells below atol with `DM_ADAPT_REFINE`
9773 
9774   Options Database Keys:
9775 + -dm_plex_orthogonal_quality_label_view - view OrthQualLabel if label is requested. Currently only `PETSCVIEWERASCII` is supported.
9776 - -dm_plex_orthogonal_quality_vec_view   - view OrthQual vector.
9777 
9778   Level: intermediate
9779 
9780   Notes:
9781   Orthogonal quality is given by the following formula\:
9782 
9783   $ \min \left[ \frac{A_i \cdot f_i}{\|A_i\| \|f_i\|} , \frac{A_i \cdot c_i}{\|A_i\| \|c_i\|} \right]$
9784 
9785   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
9786   is the vector from the current cells centroid to the centroid of its i'th neighbor (which shares a face with the
9787   current cell). This computes the vector similarity between each cell face and its corresponding neighbor centroid by
9788   calculating the cosine of the angle between these vectors.
9789 
9790   Orthogonal quality ranges from 1 (best) to 0 (worst).
9791 
9792   This routine is mainly useful for FVM, however is not restricted to only FVM. The `PetscFV` object is optionally used to check for
9793   pre-computed FVM cell data, but if it is not passed in then this data will be computed.
9794 
9795   Cells are tagged if they have an orthogonal quality less than or equal to the absolute tolerance.
9796 
9797 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCheckCellShape()`, `DMCreateLabel()`, `PetscFV`, `DMLabel`, `Vec`
9798 @*/
9799 PetscErrorCode DMPlexComputeOrthogonalQuality(DM dm, PetscFV fv, PetscReal atol, Vec *OrthQual, DMLabel *OrthQualLabel)
9800 {
9801   PetscInt               nc, cellHeight, cStart, cEnd, cell, cellIter = 0;
9802   PetscInt              *idx;
9803   PetscScalar           *oqVals;
9804   const PetscScalar     *cellGeomArr, *faceGeomArr;
9805   PetscReal             *ci, *fi, *Ai;
9806   MPI_Comm               comm;
9807   Vec                    cellgeom, facegeom;
9808   DM                     dmFace, dmCell;
9809   IS                     glob;
9810   ISLocalToGlobalMapping ltog;
9811   PetscViewer            vwr;
9812 
9813   PetscFunctionBegin;
9814   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9815   if (fv) PetscValidHeaderSpecific(fv, PETSCFV_CLASSID, 2);
9816   PetscAssertPointer(OrthQual, 4);
9817   PetscCheck(atol >= 0.0 && atol <= 1.0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Absolute tolerance %g not in [0,1]", (double)atol);
9818   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9819   PetscCall(DMGetDimension(dm, &nc));
9820   PetscCheck(nc >= 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must have dimension >= 2 (current %" PetscInt_FMT ")", nc);
9821   {
9822     DMPlexInterpolatedFlag interpFlag;
9823 
9824     PetscCall(DMPlexIsInterpolated(dm, &interpFlag));
9825     if (interpFlag != DMPLEX_INTERPOLATED_FULL) {
9826       PetscMPIInt rank;
9827 
9828       PetscCallMPI(MPI_Comm_rank(comm, &rank));
9829       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must be fully interpolated, DM on rank %d is not fully interpolated", rank);
9830     }
9831   }
9832   if (OrthQualLabel) {
9833     PetscAssertPointer(OrthQualLabel, 5);
9834     PetscCall(DMCreateLabel(dm, "Orthogonal_Quality"));
9835     PetscCall(DMGetLabel(dm, "Orthogonal_Quality", OrthQualLabel));
9836   } else {
9837     *OrthQualLabel = NULL;
9838   }
9839   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9840   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9841   PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_TRUE, &glob));
9842   PetscCall(ISLocalToGlobalMappingCreateIS(glob, &ltog));
9843   PetscCall(ISLocalToGlobalMappingSetType(ltog, ISLOCALTOGLOBALMAPPINGHASH));
9844   PetscCall(VecCreate(comm, OrthQual));
9845   PetscCall(VecSetType(*OrthQual, VECSTANDARD));
9846   PetscCall(VecSetSizes(*OrthQual, cEnd - cStart, PETSC_DETERMINE));
9847   PetscCall(VecSetLocalToGlobalMapping(*OrthQual, ltog));
9848   PetscCall(VecSetUp(*OrthQual));
9849   PetscCall(ISDestroy(&glob));
9850   PetscCall(ISLocalToGlobalMappingDestroy(&ltog));
9851   PetscCall(DMPlexGetDataFVM(dm, fv, &cellgeom, &facegeom, NULL));
9852   PetscCall(VecGetArrayRead(cellgeom, &cellGeomArr));
9853   PetscCall(VecGetArrayRead(facegeom, &faceGeomArr));
9854   PetscCall(VecGetDM(cellgeom, &dmCell));
9855   PetscCall(VecGetDM(facegeom, &dmFace));
9856   PetscCall(PetscMalloc5(cEnd - cStart, &idx, cEnd - cStart, &oqVals, nc, &ci, nc, &fi, nc, &Ai));
9857   for (cell = cStart; cell < cEnd; cellIter++, cell++) {
9858     PetscInt         cellneigh, cellneighiter = 0, adjSize = PETSC_DETERMINE;
9859     PetscInt         cellarr[2], *adj = NULL;
9860     PetscScalar     *cArr, *fArr;
9861     PetscReal        minvalc = 1.0, minvalf = 1.0;
9862     PetscFVCellGeom *cg;
9863 
9864     idx[cellIter] = cell - cStart;
9865     cellarr[0]    = cell;
9866     /* Make indexing into cellGeom easier */
9867     PetscCall(DMPlexPointLocalRead(dmCell, cell, cellGeomArr, &cg));
9868     PetscCall(DMPlexGetAdjacency_Internal(dm, cell, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &adjSize, &adj));
9869     /* Technically 1 too big, but easier than fiddling with empty adjacency array */
9870     PetscCall(PetscCalloc2(adjSize, &cArr, adjSize, &fArr));
9871     for (cellneigh = 0; cellneigh < adjSize; cellneighiter++, cellneigh++) {
9872       PetscInt         i;
9873       const PetscInt   neigh  = adj[cellneigh];
9874       PetscReal        normci = 0, normfi = 0, normai = 0;
9875       PetscFVCellGeom *cgneigh;
9876       PetscFVFaceGeom *fg;
9877 
9878       /* Don't count ourselves in the neighbor list */
9879       if (neigh == cell) continue;
9880       PetscCall(DMPlexPointLocalRead(dmCell, neigh, cellGeomArr, &cgneigh));
9881       cellarr[1] = neigh;
9882       {
9883         PetscInt        numcovpts;
9884         const PetscInt *covpts;
9885 
9886         PetscCall(DMPlexGetMeet(dm, 2, cellarr, &numcovpts, &covpts));
9887         PetscCall(DMPlexPointLocalRead(dmFace, covpts[0], faceGeomArr, &fg));
9888         PetscCall(DMPlexRestoreMeet(dm, 2, cellarr, &numcovpts, &covpts));
9889       }
9890 
9891       /* Compute c_i, f_i and their norms */
9892       for (i = 0; i < nc; i++) {
9893         ci[i] = cgneigh->centroid[i] - cg->centroid[i];
9894         fi[i] = fg->centroid[i] - cg->centroid[i];
9895         Ai[i] = fg->normal[i];
9896         normci += PetscPowReal(ci[i], 2);
9897         normfi += PetscPowReal(fi[i], 2);
9898         normai += PetscPowReal(Ai[i], 2);
9899       }
9900       normci = PetscSqrtReal(normci);
9901       normfi = PetscSqrtReal(normfi);
9902       normai = PetscSqrtReal(normai);
9903 
9904       /* Normalize and compute for each face-cell-normal pair */
9905       for (i = 0; i < nc; i++) {
9906         ci[i] = ci[i] / normci;
9907         fi[i] = fi[i] / normfi;
9908         Ai[i] = Ai[i] / normai;
9909         /* PetscAbs because I don't know if normals are guaranteed to point out */
9910         cArr[cellneighiter] += PetscAbs(Ai[i] * ci[i]);
9911         fArr[cellneighiter] += PetscAbs(Ai[i] * fi[i]);
9912       }
9913       if (PetscRealPart(cArr[cellneighiter]) < minvalc) minvalc = PetscRealPart(cArr[cellneighiter]);
9914       if (PetscRealPart(fArr[cellneighiter]) < minvalf) minvalf = PetscRealPart(fArr[cellneighiter]);
9915     }
9916     PetscCall(PetscFree(adj));
9917     PetscCall(PetscFree2(cArr, fArr));
9918     /* Defer to cell if they're equal */
9919     oqVals[cellIter] = PetscMin(minvalf, minvalc);
9920     if (OrthQualLabel) {
9921       if (PetscRealPart(oqVals[cellIter]) <= atol) PetscCall(DMLabelSetValue(*OrthQualLabel, cell, DM_ADAPT_REFINE));
9922     }
9923   }
9924   PetscCall(VecSetValuesLocal(*OrthQual, cEnd - cStart, idx, oqVals, INSERT_VALUES));
9925   PetscCall(VecAssemblyBegin(*OrthQual));
9926   PetscCall(VecAssemblyEnd(*OrthQual));
9927   PetscCall(VecRestoreArrayRead(cellgeom, &cellGeomArr));
9928   PetscCall(VecRestoreArrayRead(facegeom, &faceGeomArr));
9929   PetscCall(PetscOptionsCreateViewer(comm, NULL, NULL, "-dm_plex_orthogonal_quality_label_view", &vwr, NULL, NULL));
9930   if (OrthQualLabel) {
9931     if (vwr) PetscCall(DMLabelView(*OrthQualLabel, vwr));
9932   }
9933   PetscCall(PetscFree5(idx, oqVals, ci, fi, Ai));
9934   PetscCall(PetscViewerDestroy(&vwr));
9935   PetscCall(VecViewFromOptions(*OrthQual, NULL, "-dm_plex_orthogonal_quality_vec_view"));
9936   PetscFunctionReturn(PETSC_SUCCESS);
9937 }
9938 
9939 /* this is here instead of DMGetOutputDM because output DM still has constraints in the local indices that affect
9940  * interpolator construction */
9941 static PetscErrorCode DMGetFullDM(DM dm, DM *odm)
9942 {
9943   PetscSection section, newSection, gsection;
9944   PetscSF      sf;
9945   PetscBool    hasConstraints, ghasConstraints;
9946 
9947   PetscFunctionBegin;
9948   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9949   PetscAssertPointer(odm, 2);
9950   PetscCall(DMGetLocalSection(dm, &section));
9951   PetscCall(PetscSectionHasConstraints(section, &hasConstraints));
9952   PetscCall(MPIU_Allreduce(&hasConstraints, &ghasConstraints, 1, MPIU_BOOL, MPI_LOR, PetscObjectComm((PetscObject)dm)));
9953   if (!ghasConstraints) {
9954     PetscCall(PetscObjectReference((PetscObject)dm));
9955     *odm = dm;
9956     PetscFunctionReturn(PETSC_SUCCESS);
9957   }
9958   PetscCall(DMClone(dm, odm));
9959   PetscCall(DMCopyFields(dm, *odm));
9960   PetscCall(DMGetLocalSection(*odm, &newSection));
9961   PetscCall(DMGetPointSF(*odm, &sf));
9962   PetscCall(PetscSectionCreateGlobalSection(newSection, sf, PETSC_TRUE, PETSC_TRUE, PETSC_FALSE, &gsection));
9963   PetscCall(DMSetGlobalSection(*odm, gsection));
9964   PetscCall(PetscSectionDestroy(&gsection));
9965   PetscFunctionReturn(PETSC_SUCCESS);
9966 }
9967 
9968 static PetscErrorCode DMCreateAffineInterpolationCorrection_Plex(DM dmc, DM dmf, Vec *shift)
9969 {
9970   DM        dmco, dmfo;
9971   Mat       interpo;
9972   Vec       rscale;
9973   Vec       cglobalo, clocal;
9974   Vec       fglobal, fglobalo, flocal;
9975   PetscBool regular;
9976 
9977   PetscFunctionBegin;
9978   PetscCall(DMGetFullDM(dmc, &dmco));
9979   PetscCall(DMGetFullDM(dmf, &dmfo));
9980   PetscCall(DMSetCoarseDM(dmfo, dmco));
9981   PetscCall(DMPlexGetRegularRefinement(dmf, &regular));
9982   PetscCall(DMPlexSetRegularRefinement(dmfo, regular));
9983   PetscCall(DMCreateInterpolation(dmco, dmfo, &interpo, &rscale));
9984   PetscCall(DMCreateGlobalVector(dmco, &cglobalo));
9985   PetscCall(DMCreateLocalVector(dmc, &clocal));
9986   PetscCall(VecSet(cglobalo, 0.));
9987   PetscCall(VecSet(clocal, 0.));
9988   PetscCall(DMCreateGlobalVector(dmf, &fglobal));
9989   PetscCall(DMCreateGlobalVector(dmfo, &fglobalo));
9990   PetscCall(DMCreateLocalVector(dmf, &flocal));
9991   PetscCall(VecSet(fglobal, 0.));
9992   PetscCall(VecSet(fglobalo, 0.));
9993   PetscCall(VecSet(flocal, 0.));
9994   PetscCall(DMPlexInsertBoundaryValues(dmc, PETSC_TRUE, clocal, 0., NULL, NULL, NULL));
9995   PetscCall(DMLocalToGlobalBegin(dmco, clocal, INSERT_VALUES, cglobalo));
9996   PetscCall(DMLocalToGlobalEnd(dmco, clocal, INSERT_VALUES, cglobalo));
9997   PetscCall(MatMult(interpo, cglobalo, fglobalo));
9998   PetscCall(DMGlobalToLocalBegin(dmfo, fglobalo, INSERT_VALUES, flocal));
9999   PetscCall(DMGlobalToLocalEnd(dmfo, fglobalo, INSERT_VALUES, flocal));
10000   PetscCall(DMLocalToGlobalBegin(dmf, flocal, INSERT_VALUES, fglobal));
10001   PetscCall(DMLocalToGlobalEnd(dmf, flocal, INSERT_VALUES, fglobal));
10002   *shift = fglobal;
10003   PetscCall(VecDestroy(&flocal));
10004   PetscCall(VecDestroy(&fglobalo));
10005   PetscCall(VecDestroy(&clocal));
10006   PetscCall(VecDestroy(&cglobalo));
10007   PetscCall(VecDestroy(&rscale));
10008   PetscCall(MatDestroy(&interpo));
10009   PetscCall(DMDestroy(&dmfo));
10010   PetscCall(DMDestroy(&dmco));
10011   PetscFunctionReturn(PETSC_SUCCESS);
10012 }
10013 
10014 PETSC_INTERN PetscErrorCode DMInterpolateSolution_Plex(DM coarse, DM fine, Mat interp, Vec coarseSol, Vec fineSol)
10015 {
10016   PetscObject shifto;
10017   Vec         shift;
10018 
10019   PetscFunctionBegin;
10020   if (!interp) {
10021     Vec rscale;
10022 
10023     PetscCall(DMCreateInterpolation(coarse, fine, &interp, &rscale));
10024     PetscCall(VecDestroy(&rscale));
10025   } else {
10026     PetscCall(PetscObjectReference((PetscObject)interp));
10027   }
10028   PetscCall(PetscObjectQuery((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", &shifto));
10029   if (!shifto) {
10030     PetscCall(DMCreateAffineInterpolationCorrection_Plex(coarse, fine, &shift));
10031     PetscCall(PetscObjectCompose((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", (PetscObject)shift));
10032     shifto = (PetscObject)shift;
10033     PetscCall(VecDestroy(&shift));
10034   }
10035   shift = (Vec)shifto;
10036   PetscCall(MatInterpolate(interp, coarseSol, fineSol));
10037   PetscCall(VecAXPY(fineSol, 1.0, shift));
10038   PetscCall(MatDestroy(&interp));
10039   PetscFunctionReturn(PETSC_SUCCESS);
10040 }
10041 
10042 /* Pointwise interpolation
10043      Just code FEM for now
10044      u^f = I u^c
10045      sum_k u^f_k phi^f_k = I sum_j u^c_j phi^c_j
10046      u^f_i = sum_j psi^f_i I phi^c_j u^c_j
10047      I_{ij} = psi^f_i phi^c_j
10048 */
10049 PetscErrorCode DMCreateInterpolation_Plex(DM dmCoarse, DM dmFine, Mat *interpolation, Vec *scaling)
10050 {
10051   PetscSection gsc, gsf;
10052   PetscInt     m, n;
10053   void        *ctx;
10054   DM           cdm;
10055   PetscBool    regular, ismatis, isRefined = dmCoarse->data == dmFine->data ? PETSC_FALSE : PETSC_TRUE;
10056 
10057   PetscFunctionBegin;
10058   PetscCall(DMGetGlobalSection(dmFine, &gsf));
10059   PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
10060   PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
10061   PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
10062 
10063   PetscCall(PetscStrcmp(dmCoarse->mattype, MATIS, &ismatis));
10064   PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), interpolation));
10065   PetscCall(MatSetSizes(*interpolation, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
10066   PetscCall(MatSetType(*interpolation, ismatis ? MATAIJ : dmCoarse->mattype));
10067   PetscCall(DMGetApplicationContext(dmFine, &ctx));
10068 
10069   PetscCall(DMGetCoarseDM(dmFine, &cdm));
10070   PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
10071   if (!isRefined || (regular && cdm == dmCoarse)) PetscCall(DMPlexComputeInterpolatorNested(dmCoarse, dmFine, isRefined, *interpolation, ctx));
10072   else PetscCall(DMPlexComputeInterpolatorGeneral(dmCoarse, dmFine, *interpolation, ctx));
10073   PetscCall(MatViewFromOptions(*interpolation, NULL, "-interp_mat_view"));
10074   if (scaling) {
10075     /* Use naive scaling */
10076     PetscCall(DMCreateInterpolationScale(dmCoarse, dmFine, *interpolation, scaling));
10077   }
10078   PetscFunctionReturn(PETSC_SUCCESS);
10079 }
10080 
10081 PetscErrorCode DMCreateInjection_Plex(DM dmCoarse, DM dmFine, Mat *mat)
10082 {
10083   VecScatter ctx;
10084 
10085   PetscFunctionBegin;
10086   PetscCall(DMPlexComputeInjectorFEM(dmCoarse, dmFine, &ctx, NULL));
10087   PetscCall(MatCreateScatter(PetscObjectComm((PetscObject)ctx), ctx, mat));
10088   PetscCall(VecScatterDestroy(&ctx));
10089   PetscFunctionReturn(PETSC_SUCCESS);
10090 }
10091 
10092 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[])
10093 {
10094   const PetscInt Nc = uOff[1] - uOff[0];
10095   PetscInt       c;
10096   for (c = 0; c < Nc; ++c) g0[c * Nc + c] = 1.0;
10097 }
10098 
10099 PetscErrorCode DMCreateMassMatrixLumped_Plex(DM dm, Vec *mass)
10100 {
10101   DM           dmc;
10102   PetscDS      ds;
10103   Vec          ones, locmass;
10104   IS           cellIS;
10105   PetscFormKey key;
10106   PetscInt     depth;
10107 
10108   PetscFunctionBegin;
10109   PetscCall(DMClone(dm, &dmc));
10110   PetscCall(DMCopyDisc(dm, dmc));
10111   PetscCall(DMGetDS(dmc, &ds));
10112   PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
10113   PetscCall(DMCreateGlobalVector(dmc, mass));
10114   PetscCall(DMGetLocalVector(dmc, &ones));
10115   PetscCall(DMGetLocalVector(dmc, &locmass));
10116   PetscCall(DMPlexGetDepth(dmc, &depth));
10117   PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
10118   PetscCall(VecSet(locmass, 0.0));
10119   PetscCall(VecSet(ones, 1.0));
10120   key.label = NULL;
10121   key.value = 0;
10122   key.field = 0;
10123   key.part  = 0;
10124   PetscCall(DMPlexComputeJacobian_Action_Internal(dmc, key, cellIS, 0.0, 0.0, ones, NULL, ones, locmass, NULL));
10125   PetscCall(ISDestroy(&cellIS));
10126   PetscCall(VecSet(*mass, 0.0));
10127   PetscCall(DMLocalToGlobalBegin(dmc, locmass, ADD_VALUES, *mass));
10128   PetscCall(DMLocalToGlobalEnd(dmc, locmass, ADD_VALUES, *mass));
10129   PetscCall(DMRestoreLocalVector(dmc, &ones));
10130   PetscCall(DMRestoreLocalVector(dmc, &locmass));
10131   PetscCall(DMDestroy(&dmc));
10132   PetscFunctionReturn(PETSC_SUCCESS);
10133 }
10134 
10135 PetscErrorCode DMCreateMassMatrix_Plex(DM dmCoarse, DM dmFine, Mat *mass)
10136 {
10137   PetscSection gsc, gsf;
10138   PetscInt     m, n;
10139   void        *ctx;
10140   DM           cdm;
10141   PetscBool    regular;
10142 
10143   PetscFunctionBegin;
10144   if (dmFine == dmCoarse) {
10145     DM            dmc;
10146     PetscDS       ds;
10147     PetscWeakForm wf;
10148     Vec           u;
10149     IS            cellIS;
10150     PetscFormKey  key;
10151     PetscInt      depth;
10152 
10153     PetscCall(DMClone(dmFine, &dmc));
10154     PetscCall(DMCopyDisc(dmFine, dmc));
10155     PetscCall(DMGetDS(dmc, &ds));
10156     PetscCall(PetscDSGetWeakForm(ds, &wf));
10157     PetscCall(PetscWeakFormClear(wf));
10158     PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
10159     PetscCall(DMCreateMatrix(dmc, mass));
10160     PetscCall(DMGetLocalVector(dmc, &u));
10161     PetscCall(DMPlexGetDepth(dmc, &depth));
10162     PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
10163     PetscCall(MatZeroEntries(*mass));
10164     key.label = NULL;
10165     key.value = 0;
10166     key.field = 0;
10167     key.part  = 0;
10168     PetscCall(DMPlexComputeJacobian_Internal(dmc, key, cellIS, 0.0, 0.0, u, NULL, *mass, *mass, NULL));
10169     PetscCall(ISDestroy(&cellIS));
10170     PetscCall(DMRestoreLocalVector(dmc, &u));
10171     PetscCall(DMDestroy(&dmc));
10172   } else {
10173     PetscCall(DMGetGlobalSection(dmFine, &gsf));
10174     PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
10175     PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
10176     PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
10177 
10178     PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), mass));
10179     PetscCall(MatSetSizes(*mass, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
10180     PetscCall(MatSetType(*mass, dmCoarse->mattype));
10181     PetscCall(DMGetApplicationContext(dmFine, &ctx));
10182 
10183     PetscCall(DMGetCoarseDM(dmFine, &cdm));
10184     PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
10185     if (regular && cdm == dmCoarse) PetscCall(DMPlexComputeMassMatrixNested(dmCoarse, dmFine, *mass, ctx));
10186     else PetscCall(DMPlexComputeMassMatrixGeneral(dmCoarse, dmFine, *mass, ctx));
10187   }
10188   PetscCall(MatViewFromOptions(*mass, NULL, "-mass_mat_view"));
10189   PetscFunctionReturn(PETSC_SUCCESS);
10190 }
10191 
10192 /*@
10193   DMPlexGetRegularRefinement - Get the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
10194 
10195   Input Parameter:
10196 . dm - The `DMPLEX` object
10197 
10198   Output Parameter:
10199 . regular - The flag
10200 
10201   Level: intermediate
10202 
10203 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetRegularRefinement()`
10204 @*/
10205 PetscErrorCode DMPlexGetRegularRefinement(DM dm, PetscBool *regular)
10206 {
10207   PetscFunctionBegin;
10208   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10209   PetscAssertPointer(regular, 2);
10210   *regular = ((DM_Plex *)dm->data)->regularRefinement;
10211   PetscFunctionReturn(PETSC_SUCCESS);
10212 }
10213 
10214 /*@
10215   DMPlexSetRegularRefinement - Set the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
10216 
10217   Input Parameters:
10218 + dm      - The `DMPLEX` object
10219 - regular - The flag
10220 
10221   Level: intermediate
10222 
10223 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetRegularRefinement()`
10224 @*/
10225 PetscErrorCode DMPlexSetRegularRefinement(DM dm, PetscBool regular)
10226 {
10227   PetscFunctionBegin;
10228   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10229   ((DM_Plex *)dm->data)->regularRefinement = regular;
10230   PetscFunctionReturn(PETSC_SUCCESS);
10231 }
10232 
10233 /*@
10234   DMPlexGetAnchors - Get the layout of the anchor (point-to-point) constraints.  Typically, the user will not have to
10235   call DMPlexGetAnchors() directly: if there are anchors, then `DMPlexGetAnchors()` is called during `DMGetDefaultConstraints()`.
10236 
10237   Not Collective
10238 
10239   Input Parameter:
10240 . dm - The `DMPLEX` object
10241 
10242   Output Parameters:
10243 + anchorSection - If not `NULL`, set to the section describing which points anchor the constrained points.
10244 - anchorIS      - If not `NULL`, set to the list of anchors indexed by `anchorSection`
10245 
10246   Level: intermediate
10247 
10248 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`, `IS`, `PetscSection`
10249 @*/
10250 PetscErrorCode DMPlexGetAnchors(DM dm, PetscSection *anchorSection, IS *anchorIS)
10251 {
10252   DM_Plex *plex = (DM_Plex *)dm->data;
10253 
10254   PetscFunctionBegin;
10255   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10256   if (!plex->anchorSection && !plex->anchorIS && plex->createanchors) PetscCall((*plex->createanchors)(dm));
10257   if (anchorSection) *anchorSection = plex->anchorSection;
10258   if (anchorIS) *anchorIS = plex->anchorIS;
10259   PetscFunctionReturn(PETSC_SUCCESS);
10260 }
10261 
10262 /*@
10263   DMPlexSetAnchors - Set the layout of the local anchor (point-to-point) constraints.
10264 
10265   Collective
10266 
10267   Input Parameters:
10268 + dm            - The `DMPLEX` object
10269 . anchorSection - The section that describes the mapping from constrained points to the anchor points listed in anchorIS.
10270                   Must have a local communicator (`PETSC_COMM_SELF` or derivative).
10271 - anchorIS      - The list of all anchor points.  Must have a local communicator (`PETSC_COMM_SELF` or derivative).
10272 
10273   Level: intermediate
10274 
10275   Notes:
10276   Unlike boundary conditions, when a point's degrees of freedom in a section are constrained to
10277   an outside value, the anchor constraints set a point's degrees of freedom to be a linear
10278   combination of other points' degrees of freedom.
10279 
10280   After specifying the layout of constraints with `DMPlexSetAnchors()`, one specifies the constraints by calling
10281   `DMGetDefaultConstraints()` and filling in the entries in the constraint matrix.
10282 
10283   The reference counts of `anchorSection` and `anchorIS` are incremented.
10284 
10285 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`
10286 @*/
10287 PetscErrorCode DMPlexSetAnchors(DM dm, PetscSection anchorSection, IS anchorIS)
10288 {
10289   DM_Plex    *plex = (DM_Plex *)dm->data;
10290   PetscMPIInt result;
10291 
10292   PetscFunctionBegin;
10293   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10294   if (anchorSection) {
10295     PetscValidHeaderSpecific(anchorSection, PETSC_SECTION_CLASSID, 2);
10296     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorSection), &result));
10297     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor section must have local communicator");
10298   }
10299   if (anchorIS) {
10300     PetscValidHeaderSpecific(anchorIS, IS_CLASSID, 3);
10301     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorIS), &result));
10302     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor IS must have local communicator");
10303   }
10304 
10305   PetscCall(PetscObjectReference((PetscObject)anchorSection));
10306   PetscCall(PetscSectionDestroy(&plex->anchorSection));
10307   plex->anchorSection = anchorSection;
10308 
10309   PetscCall(PetscObjectReference((PetscObject)anchorIS));
10310   PetscCall(ISDestroy(&plex->anchorIS));
10311   plex->anchorIS = anchorIS;
10312 
10313   if (PetscUnlikelyDebug(anchorIS && anchorSection)) {
10314     PetscInt        size, a, pStart, pEnd;
10315     const PetscInt *anchors;
10316 
10317     PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
10318     PetscCall(ISGetLocalSize(anchorIS, &size));
10319     PetscCall(ISGetIndices(anchorIS, &anchors));
10320     for (a = 0; a < size; a++) {
10321       PetscInt p;
10322 
10323       p = anchors[a];
10324       if (p >= pStart && p < pEnd) {
10325         PetscInt dof;
10326 
10327         PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
10328         if (dof) {
10329           PetscCall(ISRestoreIndices(anchorIS, &anchors));
10330           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Point %" PetscInt_FMT " cannot be constrained and an anchor", p);
10331         }
10332       }
10333     }
10334     PetscCall(ISRestoreIndices(anchorIS, &anchors));
10335   }
10336   /* reset the generic constraints */
10337   PetscCall(DMSetDefaultConstraints(dm, NULL, NULL, NULL));
10338   PetscFunctionReturn(PETSC_SUCCESS);
10339 }
10340 
10341 static PetscErrorCode DMPlexCreateConstraintSection_Anchors(DM dm, PetscSection section, PetscSection *cSec)
10342 {
10343   PetscSection anchorSection;
10344   PetscInt     pStart, pEnd, sStart, sEnd, p, dof, numFields, f;
10345 
10346   PetscFunctionBegin;
10347   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10348   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
10349   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, cSec));
10350   PetscCall(PetscSectionGetNumFields(section, &numFields));
10351   if (numFields) {
10352     PetscInt f;
10353     PetscCall(PetscSectionSetNumFields(*cSec, numFields));
10354 
10355     for (f = 0; f < numFields; f++) {
10356       PetscInt numComp;
10357 
10358       PetscCall(PetscSectionGetFieldComponents(section, f, &numComp));
10359       PetscCall(PetscSectionSetFieldComponents(*cSec, f, numComp));
10360     }
10361   }
10362   PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
10363   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
10364   pStart = PetscMax(pStart, sStart);
10365   pEnd   = PetscMin(pEnd, sEnd);
10366   pEnd   = PetscMax(pStart, pEnd);
10367   PetscCall(PetscSectionSetChart(*cSec, pStart, pEnd));
10368   for (p = pStart; p < pEnd; p++) {
10369     PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
10370     if (dof) {
10371       PetscCall(PetscSectionGetDof(section, p, &dof));
10372       PetscCall(PetscSectionSetDof(*cSec, p, dof));
10373       for (f = 0; f < numFields; f++) {
10374         PetscCall(PetscSectionGetFieldDof(section, p, f, &dof));
10375         PetscCall(PetscSectionSetFieldDof(*cSec, p, f, dof));
10376       }
10377     }
10378   }
10379   PetscCall(PetscSectionSetUp(*cSec));
10380   PetscCall(PetscObjectSetName((PetscObject)*cSec, "Constraint Section"));
10381   PetscFunctionReturn(PETSC_SUCCESS);
10382 }
10383 
10384 static PetscErrorCode DMPlexCreateConstraintMatrix_Anchors(DM dm, PetscSection section, PetscSection cSec, Mat *cMat)
10385 {
10386   PetscSection    aSec;
10387   PetscInt        pStart, pEnd, p, sStart, sEnd, dof, aDof, aOff, off, nnz, annz, m, n, q, a, offset, *i, *j;
10388   const PetscInt *anchors;
10389   PetscInt        numFields, f;
10390   IS              aIS;
10391   MatType         mtype;
10392   PetscBool       iscuda, iskokkos;
10393 
10394   PetscFunctionBegin;
10395   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10396   PetscCall(PetscSectionGetStorageSize(cSec, &m));
10397   PetscCall(PetscSectionGetStorageSize(section, &n));
10398   PetscCall(MatCreate(PETSC_COMM_SELF, cMat));
10399   PetscCall(MatSetSizes(*cMat, m, n, m, n));
10400   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJCUSPARSE, &iscuda));
10401   if (!iscuda) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJCUSPARSE, &iscuda));
10402   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJKOKKOS, &iskokkos));
10403   if (!iskokkos) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJKOKKOS, &iskokkos));
10404   if (iscuda) mtype = MATSEQAIJCUSPARSE;
10405   else if (iskokkos) mtype = MATSEQAIJKOKKOS;
10406   else mtype = MATSEQAIJ;
10407   PetscCall(MatSetType(*cMat, mtype));
10408   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
10409   PetscCall(ISGetIndices(aIS, &anchors));
10410   /* cSec will be a subset of aSec and section */
10411   PetscCall(PetscSectionGetChart(cSec, &pStart, &pEnd));
10412   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
10413   PetscCall(PetscMalloc1(m + 1, &i));
10414   i[0] = 0;
10415   PetscCall(PetscSectionGetNumFields(section, &numFields));
10416   for (p = pStart; p < pEnd; p++) {
10417     PetscInt rDof, rOff, r;
10418 
10419     PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10420     if (!rDof) continue;
10421     PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10422     if (numFields) {
10423       for (f = 0; f < numFields; f++) {
10424         annz = 0;
10425         for (r = 0; r < rDof; r++) {
10426           a = anchors[rOff + r];
10427           if (a < sStart || a >= sEnd) continue;
10428           PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
10429           annz += aDof;
10430         }
10431         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
10432         PetscCall(PetscSectionGetFieldOffset(cSec, p, f, &off));
10433         for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
10434       }
10435     } else {
10436       annz = 0;
10437       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10438       for (q = 0; q < dof; q++) {
10439         a = anchors[rOff + q];
10440         if (a < sStart || a >= sEnd) continue;
10441         PetscCall(PetscSectionGetDof(section, a, &aDof));
10442         annz += aDof;
10443       }
10444       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10445       PetscCall(PetscSectionGetOffset(cSec, p, &off));
10446       for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
10447     }
10448   }
10449   nnz = i[m];
10450   PetscCall(PetscMalloc1(nnz, &j));
10451   offset = 0;
10452   for (p = pStart; p < pEnd; p++) {
10453     if (numFields) {
10454       for (f = 0; f < numFields; f++) {
10455         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
10456         for (q = 0; q < dof; q++) {
10457           PetscInt rDof, rOff, r;
10458           PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10459           PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10460           for (r = 0; r < rDof; r++) {
10461             PetscInt s;
10462 
10463             a = anchors[rOff + r];
10464             if (a < sStart || a >= sEnd) continue;
10465             PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
10466             PetscCall(PetscSectionGetFieldOffset(section, a, f, &aOff));
10467             for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
10468           }
10469         }
10470       }
10471     } else {
10472       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10473       for (q = 0; q < dof; q++) {
10474         PetscInt rDof, rOff, r;
10475         PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10476         PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10477         for (r = 0; r < rDof; r++) {
10478           PetscInt s;
10479 
10480           a = anchors[rOff + r];
10481           if (a < sStart || a >= sEnd) continue;
10482           PetscCall(PetscSectionGetDof(section, a, &aDof));
10483           PetscCall(PetscSectionGetOffset(section, a, &aOff));
10484           for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
10485         }
10486       }
10487     }
10488   }
10489   PetscCall(MatSeqAIJSetPreallocationCSR(*cMat, i, j, NULL));
10490   PetscCall(PetscFree(i));
10491   PetscCall(PetscFree(j));
10492   PetscCall(ISRestoreIndices(aIS, &anchors));
10493   PetscFunctionReturn(PETSC_SUCCESS);
10494 }
10495 
10496 PetscErrorCode DMCreateDefaultConstraints_Plex(DM dm)
10497 {
10498   DM_Plex     *plex = (DM_Plex *)dm->data;
10499   PetscSection anchorSection, section, cSec;
10500   Mat          cMat;
10501 
10502   PetscFunctionBegin;
10503   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10504   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
10505   if (anchorSection) {
10506     PetscInt Nf;
10507 
10508     PetscCall(DMGetLocalSection(dm, &section));
10509     PetscCall(DMPlexCreateConstraintSection_Anchors(dm, section, &cSec));
10510     PetscCall(DMPlexCreateConstraintMatrix_Anchors(dm, section, cSec, &cMat));
10511     PetscCall(DMGetNumFields(dm, &Nf));
10512     if (Nf && plex->computeanchormatrix) PetscCall((*plex->computeanchormatrix)(dm, section, cSec, cMat));
10513     PetscCall(DMSetDefaultConstraints(dm, cSec, cMat, NULL));
10514     PetscCall(PetscSectionDestroy(&cSec));
10515     PetscCall(MatDestroy(&cMat));
10516   }
10517   PetscFunctionReturn(PETSC_SUCCESS);
10518 }
10519 
10520 PetscErrorCode DMCreateSubDomainDM_Plex(DM dm, DMLabel label, PetscInt value, IS *is, DM *subdm)
10521 {
10522   IS           subis;
10523   PetscSection section, subsection;
10524 
10525   PetscFunctionBegin;
10526   PetscCall(DMGetLocalSection(dm, &section));
10527   PetscCheck(section, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set default section for DM before splitting subdomain");
10528   PetscCheck(subdm, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set output subDM for splitting subdomain");
10529   /* Create subdomain */
10530   PetscCall(DMPlexFilter(dm, label, value, PETSC_FALSE, PETSC_FALSE, NULL, subdm));
10531   /* Create submodel */
10532   PetscCall(DMPlexGetSubpointIS(*subdm, &subis));
10533   PetscCall(PetscSectionCreateSubmeshSection(section, subis, &subsection));
10534   PetscCall(DMSetLocalSection(*subdm, subsection));
10535   PetscCall(PetscSectionDestroy(&subsection));
10536   PetscCall(DMCopyDisc(dm, *subdm));
10537   /* Create map from submodel to global model */
10538   if (is) {
10539     PetscSection    sectionGlobal, subsectionGlobal;
10540     IS              spIS;
10541     const PetscInt *spmap;
10542     PetscInt       *subIndices;
10543     PetscInt        subSize = 0, subOff = 0, pStart, pEnd, p;
10544     PetscInt        Nf, f, bs = -1, bsLocal[2], bsMinMax[2];
10545 
10546     PetscCall(DMPlexGetSubpointIS(*subdm, &spIS));
10547     PetscCall(ISGetIndices(spIS, &spmap));
10548     PetscCall(PetscSectionGetNumFields(section, &Nf));
10549     PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
10550     PetscCall(DMGetGlobalSection(*subdm, &subsectionGlobal));
10551     PetscCall(PetscSectionGetChart(subsection, &pStart, &pEnd));
10552     for (p = pStart; p < pEnd; ++p) {
10553       PetscInt gdof, pSubSize = 0;
10554 
10555       PetscCall(PetscSectionGetDof(sectionGlobal, p, &gdof));
10556       if (gdof > 0) {
10557         for (f = 0; f < Nf; ++f) {
10558           PetscInt fdof, fcdof;
10559 
10560           PetscCall(PetscSectionGetFieldDof(subsection, p, f, &fdof));
10561           PetscCall(PetscSectionGetFieldConstraintDof(subsection, p, f, &fcdof));
10562           pSubSize += fdof - fcdof;
10563         }
10564         subSize += pSubSize;
10565         if (pSubSize) {
10566           if (bs < 0) {
10567             bs = pSubSize;
10568           } else if (bs != pSubSize) {
10569             /* Layout does not admit a pointwise block size */
10570             bs = 1;
10571           }
10572         }
10573       }
10574     }
10575     /* Must have same blocksize on all procs (some might have no points) */
10576     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs;
10577     bsLocal[1] = bs;
10578     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
10579     if (bsMinMax[0] != bsMinMax[1]) {
10580       bs = 1;
10581     } else {
10582       bs = bsMinMax[0];
10583     }
10584     PetscCall(PetscMalloc1(subSize, &subIndices));
10585     for (p = pStart; p < pEnd; ++p) {
10586       PetscInt gdof, goff;
10587 
10588       PetscCall(PetscSectionGetDof(subsectionGlobal, p, &gdof));
10589       if (gdof > 0) {
10590         const PetscInt point = spmap[p];
10591 
10592         PetscCall(PetscSectionGetOffset(sectionGlobal, point, &goff));
10593         for (f = 0; f < Nf; ++f) {
10594           PetscInt fdof, fcdof, fc, f2, poff = 0;
10595 
10596           /* Can get rid of this loop by storing field information in the global section */
10597           for (f2 = 0; f2 < f; ++f2) {
10598             PetscCall(PetscSectionGetFieldDof(section, p, f2, &fdof));
10599             PetscCall(PetscSectionGetFieldConstraintDof(section, p, f2, &fcdof));
10600             poff += fdof - fcdof;
10601           }
10602           PetscCall(PetscSectionGetFieldDof(section, p, f, &fdof));
10603           PetscCall(PetscSectionGetFieldConstraintDof(section, p, f, &fcdof));
10604           for (fc = 0; fc < fdof - fcdof; ++fc, ++subOff) subIndices[subOff] = goff + poff + fc;
10605         }
10606       }
10607     }
10608     PetscCall(ISRestoreIndices(spIS, &spmap));
10609     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), subSize, subIndices, PETSC_OWN_POINTER, is));
10610     if (bs > 1) {
10611       /* We need to check that the block size does not come from non-contiguous fields */
10612       PetscInt i, j, set = 1;
10613       for (i = 0; i < subSize; i += bs) {
10614         for (j = 0; j < bs; ++j) {
10615           if (subIndices[i + j] != subIndices[i] + j) {
10616             set = 0;
10617             break;
10618           }
10619         }
10620       }
10621       if (set) PetscCall(ISSetBlockSize(*is, bs));
10622     }
10623     /* Attach nullspace */
10624     for (f = 0; f < Nf; ++f) {
10625       (*subdm)->nullspaceConstructors[f] = dm->nullspaceConstructors[f];
10626       if ((*subdm)->nullspaceConstructors[f]) break;
10627     }
10628     if (f < Nf) {
10629       MatNullSpace nullSpace;
10630       PetscCall((*(*subdm)->nullspaceConstructors[f])(*subdm, f, f, &nullSpace));
10631 
10632       PetscCall(PetscObjectCompose((PetscObject)*is, "nullspace", (PetscObject)nullSpace));
10633       PetscCall(MatNullSpaceDestroy(&nullSpace));
10634     }
10635   }
10636   PetscFunctionReturn(PETSC_SUCCESS);
10637 }
10638 
10639 /*@
10640   DMPlexMonitorThroughput - Report the cell throughput of FE integration
10641 
10642   Input Parameters:
10643 + dm    - The `DM`
10644 - dummy - unused argument
10645 
10646   Options Database Key:
10647 . -dm_plex_monitor_throughput - Activate the monitor
10648 
10649   Level: developer
10650 
10651 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexCreate()`
10652 @*/
10653 PetscErrorCode DMPlexMonitorThroughput(DM dm, void *dummy)
10654 {
10655   PetscLogHandler default_handler;
10656 
10657   PetscFunctionBegin;
10658   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10659   PetscCall(PetscLogGetDefaultHandler(&default_handler));
10660   if (default_handler) {
10661     PetscLogEvent      event;
10662     PetscEventPerfInfo eventInfo;
10663     PetscReal          cellRate, flopRate;
10664     PetscInt           cStart, cEnd, Nf, N;
10665     const char        *name;
10666 
10667     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
10668     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
10669     PetscCall(DMGetNumFields(dm, &Nf));
10670     PetscCall(PetscLogEventGetId("DMPlexResidualFE", &event));
10671     PetscCall(PetscLogEventGetPerfInfo(PETSC_DEFAULT, event, &eventInfo));
10672     N        = (cEnd - cStart) * Nf * eventInfo.count;
10673     flopRate = eventInfo.flops / eventInfo.time;
10674     cellRate = N / eventInfo.time;
10675     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)));
10676   } else {
10677     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.");
10678   }
10679   PetscFunctionReturn(PETSC_SUCCESS);
10680 }
10681