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