xref: /petsc/src/dm/impls/plex/plex.c (revision c3e1cf4fec6e4cdb4c624a48b65371f606a80f7e)
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   PetscBool       found = PETSC_FALSE;
89   PetscInt        Nct, cS = PETSC_INT_MAX, cE = 0;
90 
91   PetscFunctionBegin;
92   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
93   PetscCall(DMLabelGetValueIS(ctLabel, &valueIS));
94   PetscCall(ISGetLocalSize(valueIS, &Nct));
95   PetscCall(ISGetIndices(valueIS, &ctypes));
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     found = PETSC_TRUE;
114   }
115   if (!Nct || !found) cS = cE = 0;
116   PetscCall(ISDestroy(&valueIS));
117   // Reset label for fast lookup
118   PetscCall(DMLabelMakeAllInvalid_Internal(ctLabel));
119   if (cStart) *cStart = cS;
120   if (cEnd) *cEnd = cE;
121   PetscFunctionReturn(PETSC_SUCCESS);
122 }
123 
124 PetscErrorCode DMPlexGetFieldType_Internal(DM dm, PetscSection section, PetscInt field, PetscInt *sStart, PetscInt *sEnd, PetscViewerVTKFieldType *ft)
125 {
126   PetscInt cdim, pStart, pEnd, vStart, vEnd, cStart, cEnd;
127   PetscInt vcdof[2] = {0, 0}, globalvcdof[2];
128 
129   PetscFunctionBegin;
130   *ft = PETSC_VTK_INVALID;
131   PetscCall(DMGetCoordinateDim(dm, &cdim));
132   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
133   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
134   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
135   if (field >= 0) {
136     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, vStart, field, &vcdof[0]));
137     if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, cStart, field, &vcdof[1]));
138   } else {
139     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetDof(section, vStart, &vcdof[0]));
140     if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetDof(section, cStart, &vcdof[1]));
141   }
142   PetscCallMPI(MPIU_Allreduce(vcdof, globalvcdof, 2, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
143   if (globalvcdof[0]) {
144     *sStart = vStart;
145     *sEnd   = vEnd;
146     if (globalvcdof[0] == cdim) *ft = PETSC_VTK_POINT_VECTOR_FIELD;
147     else *ft = PETSC_VTK_POINT_FIELD;
148   } else if (globalvcdof[1]) {
149     *sStart = cStart;
150     *sEnd   = cEnd;
151     if (globalvcdof[1] == cdim) *ft = PETSC_VTK_CELL_VECTOR_FIELD;
152     else *ft = PETSC_VTK_CELL_FIELD;
153   } else {
154     if (field >= 0) {
155       const char *fieldname;
156 
157       PetscCall(PetscSectionGetFieldName(section, field, &fieldname));
158       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section field %" PetscInt_FMT " \"%s\"\n", field, fieldname));
159     } else {
160       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section\n"));
161     }
162   }
163   PetscFunctionReturn(PETSC_SUCCESS);
164 }
165 
166 /*@
167   DMPlexVecView1D - Plot many 1D solutions on the same line graph
168 
169   Collective
170 
171   Input Parameters:
172 + dm     - The `DMPLEX` object
173 . n      - The number of vectors
174 . u      - The array of local vectors
175 - viewer - The `PetscViewer`
176 
177   Level: advanced
178 
179 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `VecViewFromOptions()`, `VecView()`
180 @*/
181 PetscErrorCode DMPlexVecView1D(DM dm, PetscInt n, Vec u[], PetscViewer viewer)
182 {
183   PetscDS            ds;
184   PetscDraw          draw = NULL;
185   PetscDrawLG        lg;
186   Vec                coordinates;
187   const PetscScalar *coords, **sol;
188   PetscReal         *vals;
189   PetscInt          *Nc;
190   PetscInt           Nf, f, c, Nl, l, i, vStart, vEnd, v;
191   char             **names;
192 
193   PetscFunctionBegin;
194   PetscCall(DMGetDS(dm, &ds));
195   PetscCall(PetscDSGetNumFields(ds, &Nf));
196   PetscCall(PetscDSGetTotalComponents(ds, &Nl));
197   PetscCall(PetscDSGetComponents(ds, &Nc));
198 
199   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
200   if (!draw) PetscFunctionReturn(PETSC_SUCCESS);
201   PetscCall(PetscDrawLGCreate(draw, n * Nl, &lg));
202 
203   PetscCall(PetscMalloc3(n, &sol, n * Nl, &names, n * Nl, &vals));
204   for (i = 0, l = 0; i < n; ++i) {
205     const char *vname;
206 
207     PetscCall(PetscObjectGetName((PetscObject)u[i], &vname));
208     for (f = 0; f < Nf; ++f) {
209       PetscObject disc;
210       const char *fname;
211       char        tmpname[PETSC_MAX_PATH_LEN];
212 
213       PetscCall(PetscDSGetDiscretization(ds, f, &disc));
214       /* TODO Create names for components */
215       for (c = 0; c < Nc[f]; ++c, ++l) {
216         PetscCall(PetscObjectGetName(disc, &fname));
217         PetscCall(PetscStrncpy(tmpname, vname, sizeof(tmpname)));
218         PetscCall(PetscStrlcat(tmpname, ":", sizeof(tmpname)));
219         PetscCall(PetscStrlcat(tmpname, fname, sizeof(tmpname)));
220         PetscCall(PetscStrallocpy(tmpname, &names[l]));
221       }
222     }
223   }
224   PetscCall(PetscDrawLGSetLegend(lg, (const char *const *)names));
225   /* Just add P_1 support for now */
226   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
227   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
228   PetscCall(VecGetArrayRead(coordinates, &coords));
229   for (i = 0; i < n; ++i) PetscCall(VecGetArrayRead(u[i], &sol[i]));
230   for (v = vStart; v < vEnd; ++v) {
231     PetscScalar *x, *svals;
232 
233     PetscCall(DMPlexPointLocalRead(dm, v, coords, &x));
234     for (i = 0; i < n; ++i) {
235       PetscCall(DMPlexPointLocalRead(dm, v, sol[i], &svals));
236       for (l = 0; l < Nl; ++l) vals[i * Nl + l] = PetscRealPart(svals[l]);
237     }
238     PetscCall(PetscDrawLGAddCommonPoint(lg, PetscRealPart(x[0]), vals));
239   }
240   PetscCall(VecRestoreArrayRead(coordinates, &coords));
241   for (i = 0; i < n; ++i) PetscCall(VecRestoreArrayRead(u[i], &sol[i]));
242   for (l = 0; l < n * Nl; ++l) PetscCall(PetscFree(names[l]));
243   PetscCall(PetscFree3(sol, names, vals));
244 
245   PetscCall(PetscDrawLGDraw(lg));
246   PetscCall(PetscDrawLGDestroy(&lg));
247   PetscFunctionReturn(PETSC_SUCCESS);
248 }
249 
250 static PetscErrorCode VecView_Plex_Local_Draw_1D(Vec u, PetscViewer viewer)
251 {
252   DM dm;
253 
254   PetscFunctionBegin;
255   PetscCall(VecGetDM(u, &dm));
256   PetscCall(DMPlexVecView1D(dm, 1, &u, viewer));
257   PetscFunctionReturn(PETSC_SUCCESS);
258 }
259 
260 static PetscErrorCode VecView_Plex_Local_Draw_2D(Vec v, PetscViewer viewer)
261 {
262   DM                 dm;
263   PetscSection       s;
264   PetscDraw          draw, popup;
265   DM                 cdm;
266   PetscSection       coordSection;
267   Vec                coordinates;
268   const PetscScalar *array;
269   PetscReal          lbound[3], ubound[3];
270   PetscReal          vbound[2], time;
271   PetscBool          flg;
272   PetscInt           dim, Nf, f, Nc, comp, vStart, vEnd, cStart, cEnd, c, N, level, step, w = 0;
273   const char        *name;
274   char               title[PETSC_MAX_PATH_LEN];
275 
276   PetscFunctionBegin;
277   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
278   PetscCall(VecGetDM(v, &dm));
279   PetscCall(DMGetCoordinateDim(dm, &dim));
280   PetscCall(DMGetLocalSection(dm, &s));
281   PetscCall(PetscSectionGetNumFields(s, &Nf));
282   PetscCall(DMGetCoarsenLevel(dm, &level));
283   PetscCall(DMGetCoordinateDM(dm, &cdm));
284   PetscCall(DMGetLocalSection(cdm, &coordSection));
285   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
286   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
287   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
288 
289   PetscCall(PetscObjectGetName((PetscObject)v, &name));
290   PetscCall(DMGetOutputSequenceNumber(dm, &step, &time));
291 
292   PetscCall(VecGetLocalSize(coordinates, &N));
293   PetscCall(DMGetBoundingBox(dm, lbound, ubound));
294   PetscCall(PetscDrawClear(draw));
295 
296   /* Could implement something like DMDASelectFields() */
297   for (f = 0; f < Nf; ++f) {
298     DM          fdm = dm;
299     Vec         fv  = v;
300     IS          fis;
301     char        prefix[PETSC_MAX_PATH_LEN];
302     const char *fname;
303 
304     PetscCall(PetscSectionGetFieldComponents(s, f, &Nc));
305     PetscCall(PetscSectionGetFieldName(s, f, &fname));
306 
307     if (v->hdr.prefix) PetscCall(PetscStrncpy(prefix, v->hdr.prefix, sizeof(prefix)));
308     else prefix[0] = '\0';
309     if (Nf > 1) {
310       PetscCall(DMCreateSubDM(dm, 1, &f, &fis, &fdm));
311       PetscCall(VecGetSubVector(v, fis, &fv));
312       PetscCall(PetscStrlcat(prefix, fname, sizeof(prefix)));
313       PetscCall(PetscStrlcat(prefix, "_", sizeof(prefix)));
314     }
315     for (comp = 0; comp < Nc; ++comp, ++w) {
316       PetscInt nmax = 2;
317 
318       PetscCall(PetscViewerDrawGetDraw(viewer, w, &draw));
319       if (Nc > 1) PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s_%" PetscInt_FMT " Step: %" PetscInt_FMT " Time: %.4g", name, fname, comp, step, (double)time));
320       else PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s Step: %" PetscInt_FMT " Time: %.4g", name, fname, step, (double)time));
321       PetscCall(PetscDrawSetTitle(draw, title));
322 
323       /* TODO Get max and min only for this component */
324       PetscCall(PetscOptionsGetRealArray(NULL, prefix, "-vec_view_bounds", vbound, &nmax, &flg));
325       if (!flg) {
326         PetscCall(VecMin(fv, NULL, &vbound[0]));
327         PetscCall(VecMax(fv, NULL, &vbound[1]));
328         if (vbound[1] <= vbound[0]) vbound[1] = vbound[0] + 1.0;
329       }
330 
331       PetscCall(PetscDrawGetPopup(draw, &popup));
332       PetscCall(PetscDrawScalePopup(popup, vbound[0], vbound[1]));
333       PetscCall(PetscDrawSetCoordinates(draw, lbound[0], lbound[1], ubound[0], ubound[1]));
334       PetscCall(VecGetArrayRead(fv, &array));
335       for (c = cStart; c < cEnd; ++c) {
336         DMPolytopeType     ct;
337         PetscScalar       *coords = NULL, *a = NULL;
338         const PetscScalar *coords_arr;
339         PetscBool          isDG;
340         PetscInt           numCoords;
341         int                color[4] = {-1, -1, -1, -1};
342 
343         PetscCall(DMPlexGetCellType(dm, c, &ct));
344         PetscCall(DMPlexPointLocalRead(fdm, c, array, &a));
345         if (a) {
346           color[0] = PetscDrawRealToColor(PetscRealPart(a[comp]), vbound[0], vbound[1]);
347           color[1] = color[2] = color[3] = color[0];
348         } else {
349           PetscScalar *vals = NULL;
350           PetscInt     numVals, va;
351 
352           PetscCall(DMPlexVecGetClosure(fdm, NULL, fv, c, &numVals, &vals));
353           if (!numVals) {
354             PetscCall(DMPlexVecRestoreClosure(fdm, NULL, fv, c, &numVals, &vals));
355             continue;
356           }
357           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);
358           switch (numVals / Nc) {
359           case 1: /* P1 Clamped Segment Prism */
360           case 2: /* P1 Segment Prism, P2 Clamped Segment Prism */
361             PetscCheck(ct == DM_POLYTOPE_SEG_PRISM_TENSOR, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell should be a tensor segment, but it is a %s", DMPolytopeTypes[ct]);
362             for (va = 0; va < numVals / Nc; ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp]), vbound[0], vbound[1]);
363             break;
364           case 3: /* P1 Triangle */
365           case 4: /* P1 Quadrangle */
366             PetscCheck(ct == DM_POLYTOPE_TRIANGLE || ct == DM_POLYTOPE_QUADRILATERAL || ct == DM_POLYTOPE_SEG_PRISM_TENSOR, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell should be a triangle or quad, but it is a %s", DMPolytopeTypes[ct]);
367             for (va = 0; va < numVals / Nc; ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp]), vbound[0], vbound[1]);
368             break;
369           case 6: /* P2 Triangle */
370           case 8: /* P2 Quadrangle */
371             PetscCheck(ct == DM_POLYTOPE_TRIANGLE || ct == DM_POLYTOPE_QUADRILATERAL || ct == DM_POLYTOPE_SEG_PRISM_TENSOR, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell should be a triangle or quad, but it is a %s", DMPolytopeTypes[ct]);
372             for (va = 0; va < numVals / (Nc * 2); ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp + numVals / (Nc * 2)]), vbound[0], vbound[1]);
373             break;
374           default:
375             SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of values for cell closure %" PetscInt_FMT " cannot be handled", numVals / Nc);
376           }
377           PetscCall(DMPlexVecRestoreClosure(fdm, NULL, fv, c, &numVals, &vals));
378         }
379         PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
380         switch (numCoords) {
381         case 6:
382         case 12: /* Localized triangle */
383           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]));
384           break;
385         case 8:
386         case 16: /* Localized quadrilateral */
387           if (ct == DM_POLYTOPE_SEG_PRISM_TENSOR) {
388             PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscMax(color[0], color[1])));
389           } else {
390             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]));
391             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]));
392           }
393           break;
394         default:
395           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells with %" PetscInt_FMT " coordinates", numCoords);
396         }
397         PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
398       }
399       PetscCall(VecRestoreArrayRead(fv, &array));
400       PetscCall(PetscDrawFlush(draw));
401       PetscCall(PetscDrawPause(draw));
402       PetscCall(PetscDrawSave(draw));
403     }
404     if (Nf > 1) {
405       PetscCall(VecRestoreSubVector(v, fis, &fv));
406       PetscCall(ISDestroy(&fis));
407       PetscCall(DMDestroy(&fdm));
408     }
409   }
410   PetscFunctionReturn(PETSC_SUCCESS);
411 }
412 
413 static PetscErrorCode VecView_Plex_Local_Draw(Vec v, PetscViewer viewer)
414 {
415   DM        dm;
416   PetscDraw draw;
417   PetscInt  dim;
418   PetscBool isnull;
419 
420   PetscFunctionBegin;
421   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
422   PetscCall(PetscDrawIsNull(draw, &isnull));
423   if (isnull) PetscFunctionReturn(PETSC_SUCCESS);
424 
425   PetscCall(VecGetDM(v, &dm));
426   PetscCall(DMGetCoordinateDim(dm, &dim));
427   switch (dim) {
428   case 1:
429     PetscCall(VecView_Plex_Local_Draw_1D(v, viewer));
430     break;
431   case 2:
432     PetscCall(VecView_Plex_Local_Draw_2D(v, viewer));
433     break;
434   default:
435     SETERRQ(PetscObjectComm((PetscObject)v), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT ". Try PETSCVIEWERGLVIS", dim);
436   }
437   PetscFunctionReturn(PETSC_SUCCESS);
438 }
439 
440 static PetscErrorCode VecView_Plex_Local_VTK(Vec v, PetscViewer viewer)
441 {
442   DM                      dm;
443   Vec                     locv;
444   const char             *name;
445   PetscSection            section;
446   PetscInt                pStart, pEnd;
447   PetscInt                numFields;
448   PetscViewerVTKFieldType ft;
449 
450   PetscFunctionBegin;
451   PetscCall(VecGetDM(v, &dm));
452   PetscCall(DMCreateLocalVector(dm, &locv)); /* VTK viewer requires exclusive ownership of the vector */
453   PetscCall(PetscObjectGetName((PetscObject)v, &name));
454   PetscCall(PetscObjectSetName((PetscObject)locv, name));
455   PetscCall(VecCopy(v, locv));
456   PetscCall(DMGetLocalSection(dm, &section));
457   PetscCall(PetscSectionGetNumFields(section, &numFields));
458   if (!numFields) {
459     PetscCall(DMPlexGetFieldType_Internal(dm, section, PETSC_DETERMINE, &pStart, &pEnd, &ft));
460     PetscCall(PetscViewerVTKAddField(viewer, (PetscObject)dm, DMPlexVTKWriteAll, PETSC_DEFAULT, ft, PETSC_TRUE, (PetscObject)locv));
461   } else {
462     PetscInt f;
463 
464     for (f = 0; f < numFields; f++) {
465       PetscCall(DMPlexGetFieldType_Internal(dm, section, f, &pStart, &pEnd, &ft));
466       if (ft == PETSC_VTK_INVALID) continue;
467       PetscCall(PetscObjectReference((PetscObject)locv));
468       PetscCall(PetscViewerVTKAddField(viewer, (PetscObject)dm, DMPlexVTKWriteAll, f, ft, PETSC_TRUE, (PetscObject)locv));
469     }
470     PetscCall(VecDestroy(&locv));
471   }
472   PetscFunctionReturn(PETSC_SUCCESS);
473 }
474 
475 PetscErrorCode VecView_Plex_Local(Vec v, PetscViewer viewer)
476 {
477   DM        dm;
478   PetscBool isvtk, ishdf5, isdraw, isglvis, iscgns;
479 
480   PetscFunctionBegin;
481   PetscCall(VecGetDM(v, &dm));
482   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
483   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
484   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
485   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
486   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
487   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
488   if (isvtk || ishdf5 || isdraw || isglvis || iscgns) {
489     PetscInt    i, numFields;
490     PetscObject fe;
491     PetscBool   fem  = PETSC_FALSE;
492     Vec         locv = v;
493     const char *name;
494     PetscInt    step;
495     PetscReal   time;
496 
497     PetscCall(DMGetNumFields(dm, &numFields));
498     for (i = 0; i < numFields; i++) {
499       PetscCall(DMGetField(dm, i, NULL, &fe));
500       if (fe->classid == PETSCFE_CLASSID) {
501         fem = PETSC_TRUE;
502         break;
503       }
504     }
505     if (fem) {
506       PetscObject isZero;
507 
508       PetscCall(DMGetLocalVector(dm, &locv));
509       PetscCall(PetscObjectGetName((PetscObject)v, &name));
510       PetscCall(PetscObjectSetName((PetscObject)locv, name));
511       PetscCall(PetscObjectQuery((PetscObject)v, "__Vec_bc_zero__", &isZero));
512       PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", isZero));
513       PetscCall(VecCopy(v, locv));
514       PetscCall(DMGetOutputSequenceNumber(dm, NULL, &time));
515       PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_TRUE, locv, time, NULL, NULL, NULL));
516     }
517     if (isvtk) {
518       PetscCall(VecView_Plex_Local_VTK(locv, viewer));
519     } else if (ishdf5) {
520 #if defined(PETSC_HAVE_HDF5)
521       PetscCall(VecView_Plex_Local_HDF5_Internal(locv, viewer));
522 #else
523       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
524 #endif
525     } else if (isdraw) {
526       PetscCall(VecView_Plex_Local_Draw(locv, viewer));
527     } else if (isglvis) {
528       PetscCall(DMGetOutputSequenceNumber(dm, &step, NULL));
529       PetscCall(PetscViewerGLVisSetSnapId(viewer, step));
530       PetscCall(VecView_GLVis(locv, viewer));
531     } else if (iscgns) {
532 #if defined(PETSC_HAVE_CGNS)
533       PetscCall(VecView_Plex_Local_CGNS(locv, viewer));
534 #else
535       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "CGNS not supported in this build.\nPlease reconfigure using --download-cgns");
536 #endif
537     }
538     if (fem) {
539       PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", NULL));
540       PetscCall(DMRestoreLocalVector(dm, &locv));
541     }
542   } else {
543     PetscBool isseq;
544 
545     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
546     if (isseq) PetscCall(VecView_Seq(v, viewer));
547     else PetscCall(VecView_MPI(v, viewer));
548   }
549   PetscFunctionReturn(PETSC_SUCCESS);
550 }
551 
552 PetscErrorCode VecView_Plex(Vec v, PetscViewer viewer)
553 {
554   DM        dm;
555   PetscBool isvtk, ishdf5, isdraw, isglvis, isexodusii, iscgns;
556 
557   PetscFunctionBegin;
558   PetscCall(VecGetDM(v, &dm));
559   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
560   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
561   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
562   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
563   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
564   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
565   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodusii));
566   if (isvtk || isdraw || isglvis || iscgns) {
567     Vec         locv;
568     PetscObject isZero;
569     const char *name;
570 
571     PetscCall(DMGetLocalVector(dm, &locv));
572     PetscCall(PetscObjectGetName((PetscObject)v, &name));
573     PetscCall(PetscObjectSetName((PetscObject)locv, name));
574     PetscCall(DMGlobalToLocalBegin(dm, v, INSERT_VALUES, locv));
575     PetscCall(DMGlobalToLocalEnd(dm, v, INSERT_VALUES, locv));
576     PetscCall(PetscObjectQuery((PetscObject)v, "__Vec_bc_zero__", &isZero));
577     PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", isZero));
578     PetscCall(VecView_Plex_Local(locv, viewer));
579     PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", NULL));
580     PetscCall(DMRestoreLocalVector(dm, &locv));
581     /* Call flush for proper logging of VecView timings */
582     if (isvtk) PetscCall(PetscViewerFlush(viewer));
583   } else if (ishdf5) {
584 #if defined(PETSC_HAVE_HDF5)
585     PetscCall(VecView_Plex_HDF5_Internal(v, viewer));
586 #else
587     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
588 #endif
589   } else if (isexodusii) {
590 #if defined(PETSC_HAVE_EXODUSII)
591     PetscCall(VecView_PlexExodusII_Internal(v, viewer));
592 #else
593     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
594 #endif
595   } else {
596     PetscBool isseq;
597 
598     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
599     if (isseq) PetscCall(VecView_Seq(v, viewer));
600     else PetscCall(VecView_MPI(v, viewer));
601   }
602   PetscFunctionReturn(PETSC_SUCCESS);
603 }
604 
605 PetscErrorCode VecView_Plex_Native(Vec originalv, PetscViewer viewer)
606 {
607   DM                dm;
608   MPI_Comm          comm;
609   PetscViewerFormat format;
610   Vec               v;
611   PetscBool         isvtk, ishdf5;
612 
613   PetscFunctionBegin;
614   PetscCall(VecGetDM(originalv, &dm));
615   PetscCall(PetscObjectGetComm((PetscObject)originalv, &comm));
616   PetscCheck(dm, comm, PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
617   PetscCall(PetscViewerGetFormat(viewer, &format));
618   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
619   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
620   if (format == PETSC_VIEWER_NATIVE) {
621     /* Natural ordering is the common case for DMDA, NATIVE means plain vector, for PLEX is the opposite */
622     /* this need a better fix */
623     if (dm->useNatural) {
624       if (dm->sfNatural) {
625         const char *vecname;
626         PetscInt    n, nroots;
627 
628         PetscCall(VecGetLocalSize(originalv, &n));
629         PetscCall(PetscSFGetGraph(dm->sfNatural, &nroots, NULL, NULL, NULL));
630         if (n == nroots) {
631           PetscCall(DMPlexCreateNaturalVector(dm, &v));
632           PetscCall(DMPlexGlobalToNaturalBegin(dm, originalv, v));
633           PetscCall(DMPlexGlobalToNaturalEnd(dm, originalv, v));
634           PetscCall(PetscObjectGetName((PetscObject)originalv, &vecname));
635           PetscCall(PetscObjectSetName((PetscObject)v, vecname));
636         } else SETERRQ(comm, PETSC_ERR_ARG_WRONG, "DM global to natural SF only handles global vectors");
637       } else SETERRQ(comm, PETSC_ERR_ARG_WRONGSTATE, "DM global to natural SF was not created");
638     } else v = originalv;
639   } else v = originalv;
640 
641   if (ishdf5) {
642 #if defined(PETSC_HAVE_HDF5)
643     PetscCall(VecView_Plex_HDF5_Native_Internal(v, viewer));
644 #else
645     SETERRQ(comm, PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
646 #endif
647   } else if (isvtk) {
648     SETERRQ(comm, PETSC_ERR_SUP, "VTK format does not support viewing in natural order. Please switch to HDF5.");
649   } else {
650     PetscBool isseq;
651 
652     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
653     if (isseq) PetscCall(VecView_Seq(v, viewer));
654     else PetscCall(VecView_MPI(v, viewer));
655   }
656   if (v != originalv) PetscCall(VecDestroy(&v));
657   PetscFunctionReturn(PETSC_SUCCESS);
658 }
659 
660 PetscErrorCode VecLoad_Plex_Local(Vec v, PetscViewer viewer)
661 {
662   DM        dm;
663   PetscBool ishdf5;
664 
665   PetscFunctionBegin;
666   PetscCall(VecGetDM(v, &dm));
667   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
668   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
669   if (ishdf5) {
670     DM          dmBC;
671     Vec         gv;
672     const char *name;
673 
674     PetscCall(DMGetOutputDM(dm, &dmBC));
675     PetscCall(DMGetGlobalVector(dmBC, &gv));
676     PetscCall(PetscObjectGetName((PetscObject)v, &name));
677     PetscCall(PetscObjectSetName((PetscObject)gv, name));
678     PetscCall(VecLoad_Default(gv, viewer));
679     PetscCall(DMGlobalToLocalBegin(dmBC, gv, INSERT_VALUES, v));
680     PetscCall(DMGlobalToLocalEnd(dmBC, gv, INSERT_VALUES, v));
681     PetscCall(DMRestoreGlobalVector(dmBC, &gv));
682   } else PetscCall(VecLoad_Default(v, viewer));
683   PetscFunctionReturn(PETSC_SUCCESS);
684 }
685 
686 PetscErrorCode VecLoad_Plex(Vec v, PetscViewer viewer)
687 {
688   DM        dm;
689   PetscBool ishdf5, isexodusii;
690 
691   PetscFunctionBegin;
692   PetscCall(VecGetDM(v, &dm));
693   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
694   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
695   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodusii));
696   if (ishdf5) {
697 #if defined(PETSC_HAVE_HDF5)
698     PetscCall(VecLoad_Plex_HDF5_Internal(v, viewer));
699 #else
700     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
701 #endif
702   } else if (isexodusii) {
703 #if defined(PETSC_HAVE_EXODUSII)
704     PetscCall(VecLoad_PlexExodusII_Internal(v, viewer));
705 #else
706     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
707 #endif
708   } else PetscCall(VecLoad_Default(v, viewer));
709   PetscFunctionReturn(PETSC_SUCCESS);
710 }
711 
712 PetscErrorCode VecLoad_Plex_Native(Vec originalv, PetscViewer viewer)
713 {
714   DM                dm;
715   PetscViewerFormat format;
716   PetscBool         ishdf5;
717 
718   PetscFunctionBegin;
719   PetscCall(VecGetDM(originalv, &dm));
720   PetscCheck(dm, PetscObjectComm((PetscObject)originalv), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
721   PetscCall(PetscViewerGetFormat(viewer, &format));
722   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
723   if (format == PETSC_VIEWER_NATIVE) {
724     if (dm->useNatural) {
725       if (dm->sfNatural) {
726         if (ishdf5) {
727 #if defined(PETSC_HAVE_HDF5)
728           Vec         v;
729           const char *vecname;
730 
731           PetscCall(DMPlexCreateNaturalVector(dm, &v));
732           PetscCall(PetscObjectGetName((PetscObject)originalv, &vecname));
733           PetscCall(PetscObjectSetName((PetscObject)v, vecname));
734           PetscCall(VecLoad_Plex_HDF5_Native_Internal(v, viewer));
735           PetscCall(DMPlexNaturalToGlobalBegin(dm, v, originalv));
736           PetscCall(DMPlexNaturalToGlobalEnd(dm, v, originalv));
737           PetscCall(VecDestroy(&v));
738 #else
739           SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
740 #endif
741         } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Reading in natural order is not supported for anything but HDF5.");
742       }
743     } else PetscCall(VecLoad_Default(originalv, viewer));
744   }
745   PetscFunctionReturn(PETSC_SUCCESS);
746 }
747 
748 PETSC_UNUSED static PetscErrorCode DMPlexView_Ascii_Geometry(DM dm, PetscViewer viewer)
749 {
750   PetscSection       coordSection;
751   Vec                coordinates;
752   DMLabel            depthLabel, celltypeLabel;
753   const char        *name[4];
754   const PetscScalar *a;
755   PetscInt           dim, pStart, pEnd, cStart, cEnd, c;
756 
757   PetscFunctionBegin;
758   PetscCall(DMGetDimension(dm, &dim));
759   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
760   PetscCall(DMGetCoordinateSection(dm, &coordSection));
761   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
762   PetscCall(DMPlexGetCellTypeLabel(dm, &celltypeLabel));
763   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
764   PetscCall(PetscSectionGetChart(coordSection, &pStart, &pEnd));
765   PetscCall(VecGetArrayRead(coordinates, &a));
766   name[0]       = "vertex";
767   name[1]       = "edge";
768   name[dim - 1] = "face";
769   name[dim]     = "cell";
770   for (c = cStart; c < cEnd; ++c) {
771     PetscInt *closure = NULL;
772     PetscInt  closureSize, cl, ct;
773 
774     PetscCall(DMLabelGetValue(celltypeLabel, c, &ct));
775     PetscCall(PetscViewerASCIIPrintf(viewer, "Geometry for cell %" PetscInt_FMT " polytope type %s:\n", c, DMPolytopeTypes[ct]));
776     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
777     PetscCall(PetscViewerASCIIPushTab(viewer));
778     for (cl = 0; cl < closureSize * 2; cl += 2) {
779       PetscInt point = closure[cl], depth, dof, off, d, p;
780 
781       if ((point < pStart) || (point >= pEnd)) continue;
782       PetscCall(PetscSectionGetDof(coordSection, point, &dof));
783       if (!dof) continue;
784       PetscCall(DMLabelGetValue(depthLabel, point, &depth));
785       PetscCall(PetscSectionGetOffset(coordSection, point, &off));
786       PetscCall(PetscViewerASCIIPrintf(viewer, "%s %" PetscInt_FMT " coords:", name[depth], point));
787       for (p = 0; p < dof / dim; ++p) {
788         PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
789         for (d = 0; d < dim; ++d) {
790           if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
791           PetscCall(PetscViewerASCIIPrintf(viewer, "%g", (double)PetscRealPart(a[off + p * dim + d])));
792         }
793         PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
794       }
795       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
796     }
797     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
798     PetscCall(PetscViewerASCIIPopTab(viewer));
799   }
800   PetscCall(VecRestoreArrayRead(coordinates, &a));
801   PetscFunctionReturn(PETSC_SUCCESS);
802 }
803 
804 typedef enum {
805   CS_CARTESIAN,
806   CS_POLAR,
807   CS_CYLINDRICAL,
808   CS_SPHERICAL
809 } CoordSystem;
810 const char *CoordSystems[] = {"cartesian", "polar", "cylindrical", "spherical", "CoordSystem", "CS_", NULL};
811 
812 static PetscErrorCode DMPlexView_Ascii_Coordinates(PetscViewer viewer, CoordSystem cs, PetscInt dim, const PetscScalar x[])
813 {
814   PetscInt i;
815 
816   PetscFunctionBegin;
817   if (dim > 3) {
818     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double)PetscRealPart(x[i])));
819   } else {
820     PetscReal coords[3], trcoords[3] = {0., 0., 0.};
821 
822     for (i = 0; i < dim; ++i) coords[i] = PetscRealPart(x[i]);
823     switch (cs) {
824     case CS_CARTESIAN:
825       for (i = 0; i < dim; ++i) trcoords[i] = coords[i];
826       break;
827     case CS_POLAR:
828       PetscCheck(dim == 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Polar coordinates are for 2 dimension, not %" PetscInt_FMT, dim);
829       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
830       trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
831       break;
832     case CS_CYLINDRICAL:
833       PetscCheck(dim == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cylindrical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
834       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
835       trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
836       trcoords[2] = coords[2];
837       break;
838     case CS_SPHERICAL:
839       PetscCheck(dim == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Spherical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
840       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]) + PetscSqr(coords[2]));
841       trcoords[1] = PetscAtan2Real(PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1])), coords[2]);
842       trcoords[2] = PetscAtan2Real(coords[1], coords[0]);
843       break;
844     }
845     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double)trcoords[i]));
846   }
847   PetscFunctionReturn(PETSC_SUCCESS);
848 }
849 
850 static PetscErrorCode DMPlexView_Ascii(DM dm, PetscViewer viewer)
851 {
852   DM_Plex          *mesh = (DM_Plex *)dm->data;
853   DM                cdm, cdmCell;
854   PetscSection      coordSection, coordSectionCell;
855   Vec               coordinates, coordinatesCell;
856   PetscViewerFormat format;
857 
858   PetscFunctionBegin;
859   PetscCall(PetscViewerGetFormat(viewer, &format));
860   if (format == PETSC_VIEWER_ASCII_INFO_DETAIL) {
861     const char *name;
862     PetscInt    dim, cellHeight, maxConeSize, maxSupportSize;
863     PetscInt    pStart, pEnd, p, numLabels, l;
864     PetscMPIInt rank, size;
865 
866     PetscCall(DMGetCoordinateDM(dm, &cdm));
867     PetscCall(DMGetCoordinateSection(dm, &coordSection));
868     PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
869     PetscCall(DMGetCellCoordinateDM(dm, &cdmCell));
870     PetscCall(DMGetCellCoordinateSection(dm, &coordSectionCell));
871     PetscCall(DMGetCellCoordinatesLocal(dm, &coordinatesCell));
872     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
873     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
874     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
875     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
876     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
877     PetscCall(DMGetDimension(dm, &dim));
878     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
879     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
880     else PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
881     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
882     PetscCall(PetscViewerASCIIPrintf(viewer, "Supports:\n"));
883     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
884     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max support size: %" PetscInt_FMT "\n", rank, maxSupportSize));
885     for (p = pStart; p < pEnd; ++p) {
886       PetscInt dof, off, s;
887 
888       PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
889       PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
890       for (s = off; s < off + dof; ++s) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d]: %" PetscInt_FMT " ----> %" PetscInt_FMT "\n", rank, p, mesh->supports[s]));
891     }
892     PetscCall(PetscViewerFlush(viewer));
893     PetscCall(PetscViewerASCIIPrintf(viewer, "Cones:\n"));
894     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max cone size: %" PetscInt_FMT "\n", rank, maxConeSize));
895     for (p = pStart; p < pEnd; ++p) {
896       PetscInt dof, off, c;
897 
898       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
899       PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
900       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]));
901     }
902     PetscCall(PetscViewerFlush(viewer));
903     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
904     if (coordSection && coordinates) {
905       CoordSystem        cs = CS_CARTESIAN;
906       const PetscScalar *array, *arrayCell = NULL;
907       PetscInt           Nf, Nc, pvStart, pvEnd, pcStart = PETSC_INT_MAX, pcEnd = PETSC_INT_MIN, pStart, pEnd, p;
908       PetscMPIInt        rank;
909       const char        *name;
910 
911       PetscCall(PetscOptionsGetEnum(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_coord_system", CoordSystems, (PetscEnum *)&cs, NULL));
912       PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)viewer), &rank));
913       PetscCall(PetscSectionGetNumFields(coordSection, &Nf));
914       PetscCheck(Nf == 1, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Coordinate section should have 1 field, not %" PetscInt_FMT, Nf);
915       PetscCall(PetscSectionGetFieldComponents(coordSection, 0, &Nc));
916       PetscCall(PetscSectionGetChart(coordSection, &pvStart, &pvEnd));
917       if (coordSectionCell) PetscCall(PetscSectionGetChart(coordSectionCell, &pcStart, &pcEnd));
918       pStart = PetscMin(pvStart, pcStart);
919       pEnd   = PetscMax(pvEnd, pcEnd);
920       PetscCall(PetscObjectGetName((PetscObject)coordinates, &name));
921       PetscCall(PetscViewerASCIIPrintf(viewer, "%s with %" PetscInt_FMT " fields\n", name, Nf));
922       PetscCall(PetscViewerASCIIPrintf(viewer, "  field 0 with %" PetscInt_FMT " components\n", Nc));
923       if (cs != CS_CARTESIAN) PetscCall(PetscViewerASCIIPrintf(viewer, "  output coordinate system: %s\n", CoordSystems[cs]));
924 
925       PetscCall(VecGetArrayRead(coordinates, &array));
926       if (coordinatesCell) PetscCall(VecGetArrayRead(coordinatesCell, &arrayCell));
927       PetscCall(PetscViewerASCIIPushSynchronized(viewer));
928       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "Process %d:\n", rank));
929       for (p = pStart; p < pEnd; ++p) {
930         PetscInt dof, off;
931 
932         if (p >= pvStart && p < pvEnd) {
933           PetscCall(PetscSectionGetDof(coordSection, p, &dof));
934           PetscCall(PetscSectionGetOffset(coordSection, p, &off));
935           if (dof) {
936             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dof %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
937             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &array[off]));
938             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
939           }
940         }
941         if (cdmCell && p >= pcStart && p < pcEnd) {
942           PetscCall(PetscSectionGetDof(coordSectionCell, p, &dof));
943           PetscCall(PetscSectionGetOffset(coordSectionCell, p, &off));
944           if (dof) {
945             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dof %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
946             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &arrayCell[off]));
947             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
948           }
949         }
950       }
951       PetscCall(PetscViewerFlush(viewer));
952       PetscCall(PetscViewerASCIIPopSynchronized(viewer));
953       PetscCall(VecRestoreArrayRead(coordinates, &array));
954       if (coordinatesCell) PetscCall(VecRestoreArrayRead(coordinatesCell, &arrayCell));
955     }
956     PetscCall(DMGetNumLabels(dm, &numLabels));
957     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
958     for (l = 0; l < numLabels; ++l) {
959       DMLabel     label;
960       PetscBool   isdepth;
961       const char *name;
962 
963       PetscCall(DMGetLabelName(dm, l, &name));
964       PetscCall(PetscStrcmp(name, "depth", &isdepth));
965       if (isdepth) continue;
966       PetscCall(DMGetLabel(dm, name, &label));
967       PetscCall(DMLabelView(label, viewer));
968     }
969     if (size > 1) {
970       PetscSF sf;
971 
972       PetscCall(DMGetPointSF(dm, &sf));
973       PetscCall(PetscSFView(sf, viewer));
974     }
975     if (mesh->periodic.face_sfs)
976       for (PetscInt i = 0; i < mesh->periodic.num_face_sfs; i++) PetscCall(PetscSFView(mesh->periodic.face_sfs[i], viewer));
977     PetscCall(PetscViewerFlush(viewer));
978   } else if (format == PETSC_VIEWER_ASCII_LATEX) {
979     const char  *name, *color;
980     const char  *defcolors[3]  = {"gray", "orange", "green"};
981     const char  *deflcolors[4] = {"blue", "cyan", "red", "magenta"};
982     char         lname[PETSC_MAX_PATH_LEN];
983     PetscReal    scale      = 2.0;
984     PetscReal    tikzscale  = 1.0;
985     PetscBool    useNumbers = PETSC_TRUE, drawNumbers[4], drawColors[4], useLabels, useColors, plotEdges, drawHasse = PETSC_FALSE;
986     double       tcoords[3];
987     PetscScalar *coords;
988     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;
989     PetscMPIInt  rank, size;
990     char       **names, **colors, **lcolors;
991     PetscBool    flg, lflg;
992     PetscBT      wp = NULL;
993     PetscInt     pEnd, pStart;
994 
995     PetscCall(DMGetCoordinateDM(dm, &cdm));
996     PetscCall(DMGetCoordinateSection(dm, &coordSection));
997     PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
998     PetscCall(DMGetCellCoordinateDM(dm, &cdmCell));
999     PetscCall(DMGetCellCoordinateSection(dm, &coordSectionCell));
1000     PetscCall(DMGetCellCoordinatesLocal(dm, &coordinatesCell));
1001     PetscCall(DMGetDimension(dm, &dim));
1002     PetscCall(DMPlexGetDepth(dm, &depth));
1003     PetscCall(DMGetNumLabels(dm, &numLabels));
1004     numLabels  = PetscMax(numLabels, 10);
1005     numColors  = 10;
1006     numLColors = 10;
1007     PetscCall(PetscCalloc3(numLabels, &names, numColors, &colors, numLColors, &lcolors));
1008     PetscCall(PetscOptionsGetReal(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_scale", &scale, NULL));
1009     PetscCall(PetscOptionsGetReal(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_tikzscale", &tikzscale, NULL));
1010     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_numbers", &useNumbers, NULL));
1011     for (d = 0; d < 4; ++d) drawNumbers[d] = useNumbers;
1012     for (d = 0; d < 4; ++d) drawColors[d] = PETSC_TRUE;
1013     n = 4;
1014     PetscCall(PetscOptionsGetBoolArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_numbers_depth", drawNumbers, &n, &flg));
1015     PetscCheck(!flg || n == dim + 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim + 1);
1016     n = 4;
1017     PetscCall(PetscOptionsGetBoolArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_colors_depth", drawColors, &n, &flg));
1018     PetscCheck(!flg || n == dim + 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim + 1);
1019     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_labels", names, &numLabels, &useLabels));
1020     if (!useLabels) numLabels = 0;
1021     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_colors", colors, &numColors, &useColors));
1022     if (!useColors) {
1023       numColors = 3;
1024       for (c = 0; c < numColors; ++c) PetscCall(PetscStrallocpy(defcolors[c], &colors[c]));
1025     }
1026     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_lcolors", lcolors, &numLColors, &useColors));
1027     if (!useColors) {
1028       numLColors = 4;
1029       for (c = 0; c < numLColors; ++c) PetscCall(PetscStrallocpy(deflcolors[c], &lcolors[c]));
1030     }
1031     PetscCall(PetscOptionsGetString(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_label_filter", lname, sizeof(lname), &lflg));
1032     plotEdges = (PetscBool)(depth > 1 && drawNumbers[1] && dim < 3);
1033     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_edges", &plotEdges, &flg));
1034     PetscCheck(!flg || !plotEdges || depth >= dim, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Mesh must be interpolated");
1035     if (depth < dim) plotEdges = PETSC_FALSE;
1036     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_hasse", &drawHasse, NULL));
1037 
1038     /* filter points with labelvalue != labeldefaultvalue */
1039     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
1040     PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1041     PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
1042     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1043     PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd));
1044     if (lflg) {
1045       DMLabel lbl;
1046 
1047       PetscCall(DMGetLabel(dm, lname, &lbl));
1048       if (lbl) {
1049         PetscInt val, defval;
1050 
1051         PetscCall(DMLabelGetDefaultValue(lbl, &defval));
1052         PetscCall(PetscBTCreate(pEnd - pStart, &wp));
1053         for (c = pStart; c < pEnd; c++) {
1054           PetscInt *closure = NULL;
1055           PetscInt  closureSize;
1056 
1057           PetscCall(DMLabelGetValue(lbl, c, &val));
1058           if (val == defval) continue;
1059 
1060           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1061           for (p = 0; p < closureSize * 2; p += 2) PetscCall(PetscBTSet(wp, closure[p] - pStart));
1062           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1063         }
1064       }
1065     }
1066 
1067     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1068     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
1069     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1070     PetscCall(PetscViewerASCIIPrintf(viewer, "\
1071 \\documentclass[tikz]{standalone}\n\n\
1072 \\usepackage{pgflibraryshapes}\n\
1073 \\usetikzlibrary{backgrounds}\n\
1074 \\usetikzlibrary{arrows}\n\
1075 \\begin{document}\n"));
1076     if (size > 1) {
1077       PetscCall(PetscViewerASCIIPrintf(viewer, "%s for process ", name));
1078       for (p = 0; p < size; ++p) {
1079         if (p) PetscCall(PetscViewerASCIIPrintf(viewer, (p == size - 1) ? ", and " : ", "));
1080         PetscCall(PetscViewerASCIIPrintf(viewer, "{\\textcolor{%s}%" PetscInt_FMT "}", colors[p % numColors], p));
1081       }
1082       PetscCall(PetscViewerASCIIPrintf(viewer, ".\n\n\n"));
1083     }
1084     if (drawHasse) {
1085       PetscInt maxStratum = PetscMax(vEnd - vStart, PetscMax(eEnd - eStart, PetscMax(fEnd - fStart, cEnd - cStart)));
1086 
1087       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vStart}{%" PetscInt_FMT "}\n", vStart));
1088       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vEnd}{%" PetscInt_FMT "}\n", vEnd - 1));
1089       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numVertices}{%" PetscInt_FMT "}\n", vEnd - vStart));
1090       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vShift}{%.2f}\n", 3 + (maxStratum - (vEnd - vStart)) / 2.));
1091       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eStart}{%" PetscInt_FMT "}\n", eStart));
1092       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eEnd}{%" PetscInt_FMT "}\n", eEnd - 1));
1093       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eShift}{%.2f}\n", 3 + (maxStratum - (eEnd - eStart)) / 2.));
1094       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numEdges}{%" PetscInt_FMT "}\n", eEnd - eStart));
1095       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\fStart}{%" PetscInt_FMT "}\n", fStart));
1096       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\fEnd}{%" PetscInt_FMT "}\n", fEnd - 1));
1097       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\fShift}{%.2f}\n", 3 + (maxStratum - (fEnd - fStart)) / 2.));
1098       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numFaces}{%" PetscInt_FMT "}\n", fEnd - fStart));
1099       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cStart}{%" PetscInt_FMT "}\n", cStart));
1100       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cEnd}{%" PetscInt_FMT "}\n", cEnd - 1));
1101       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numCells}{%" PetscInt_FMT "}\n", cEnd - cStart));
1102       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cShift}{%.2f}\n", 3 + (maxStratum - (cEnd - cStart)) / 2.));
1103     }
1104     PetscCall(PetscViewerASCIIPrintf(viewer, "\\begin{tikzpicture}[scale = %g,font=\\fontsize{8}{8}\\selectfont]\n", (double)tikzscale));
1105 
1106     /* Plot vertices */
1107     PetscCall(VecGetArray(coordinates, &coords));
1108     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
1109     for (v = vStart; v < vEnd; ++v) {
1110       PetscInt  off, dof, d;
1111       PetscBool isLabeled = PETSC_FALSE;
1112 
1113       if (wp && !PetscBTLookup(wp, v - pStart)) continue;
1114       PetscCall(PetscSectionGetDof(coordSection, v, &dof));
1115       PetscCall(PetscSectionGetOffset(coordSection, v, &off));
1116       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1117       PetscCheck(dof <= 3, PETSC_COMM_SELF, PETSC_ERR_PLIB, "coordSection vertex %" PetscInt_FMT " has dof %" PetscInt_FMT " > 3", v, dof);
1118       for (d = 0; d < dof; ++d) {
1119         tcoords[d] = (double)(scale * PetscRealPart(coords[off + d]));
1120         tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1121       }
1122       /* Rotate coordinates since PGF makes z point out of the page instead of up */
1123       if (dim == 3) {
1124         PetscReal tmp = tcoords[1];
1125         tcoords[1]    = tcoords[2];
1126         tcoords[2]    = -tmp;
1127       }
1128       for (d = 0; d < dof; ++d) {
1129         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1130         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)tcoords[d]));
1131       }
1132       if (drawHasse) color = colors[0 % numColors];
1133       else color = colors[rank % numColors];
1134       for (l = 0; l < numLabels; ++l) {
1135         PetscInt val;
1136         PetscCall(DMGetLabelValue(dm, names[l], v, &val));
1137         if (val >= 0) {
1138           color     = lcolors[l % numLColors];
1139           isLabeled = PETSC_TRUE;
1140           break;
1141         }
1142       }
1143       if (drawNumbers[0]) {
1144         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", v, rank, color, v));
1145       } else if (drawColors[0]) {
1146         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", v, rank, !isLabeled ? 1 : 2, color));
1147       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", v, rank));
1148     }
1149     PetscCall(VecRestoreArray(coordinates, &coords));
1150     PetscCall(PetscViewerFlush(viewer));
1151     /* Plot edges */
1152     if (plotEdges) {
1153       PetscCall(VecGetArray(coordinates, &coords));
1154       PetscCall(PetscViewerASCIIPrintf(viewer, "\\path\n"));
1155       for (e = eStart; e < eEnd; ++e) {
1156         const PetscInt *cone;
1157         PetscInt        coneSize, offA, offB, dof, d;
1158 
1159         if (wp && !PetscBTLookup(wp, e - pStart)) continue;
1160         PetscCall(DMPlexGetConeSize(dm, e, &coneSize));
1161         PetscCheck(coneSize == 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Edge %" PetscInt_FMT " cone should have two vertices, not %" PetscInt_FMT, e, coneSize);
1162         PetscCall(DMPlexGetCone(dm, e, &cone));
1163         PetscCall(PetscSectionGetDof(coordSection, cone[0], &dof));
1164         PetscCall(PetscSectionGetOffset(coordSection, cone[0], &offA));
1165         PetscCall(PetscSectionGetOffset(coordSection, cone[1], &offB));
1166         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "("));
1167         for (d = 0; d < dof; ++d) {
1168           tcoords[d] = (double)(0.5 * scale * PetscRealPart(coords[offA + d] + coords[offB + d]));
1169           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1170         }
1171         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1172         if (dim == 3) {
1173           PetscReal tmp = tcoords[1];
1174           tcoords[1]    = tcoords[2];
1175           tcoords[2]    = -tmp;
1176         }
1177         for (d = 0; d < dof; ++d) {
1178           if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1179           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)tcoords[d]));
1180         }
1181         if (drawHasse) color = colors[1 % numColors];
1182         else color = colors[rank % numColors];
1183         for (l = 0; l < numLabels; ++l) {
1184           PetscInt val;
1185           PetscCall(DMGetLabelValue(dm, names[l], e, &val));
1186           if (val >= 0) {
1187             color = lcolors[l % numLColors];
1188             break;
1189           }
1190         }
1191         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "} --\n", e, rank, color, e));
1192       }
1193       PetscCall(VecRestoreArray(coordinates, &coords));
1194       PetscCall(PetscViewerFlush(viewer));
1195       PetscCall(PetscViewerASCIIPrintf(viewer, "(0,0);\n"));
1196     }
1197     /* Plot cells */
1198     if (dim == 3 || !drawNumbers[1]) {
1199       for (e = eStart; e < eEnd; ++e) {
1200         const PetscInt *cone;
1201 
1202         if (wp && !PetscBTLookup(wp, e - pStart)) continue;
1203         color = colors[rank % numColors];
1204         for (l = 0; l < numLabels; ++l) {
1205           PetscInt val;
1206           PetscCall(DMGetLabelValue(dm, names[l], e, &val));
1207           if (val >= 0) {
1208             color = lcolors[l % numLColors];
1209             break;
1210           }
1211         }
1212         PetscCall(DMPlexGetCone(dm, e, &cone));
1213         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", color, cone[0], rank, cone[1], rank));
1214       }
1215     } else {
1216       DMPolytopeType ct;
1217 
1218       /* Drawing a 2D polygon */
1219       for (c = cStart; c < cEnd; ++c) {
1220         if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1221         PetscCall(DMPlexGetCellType(dm, c, &ct));
1222         if (DMPolytopeTypeIsHybrid(ct)) {
1223           const PetscInt *cone;
1224           PetscInt        coneSize, e;
1225 
1226           PetscCall(DMPlexGetCone(dm, c, &cone));
1227           PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
1228           for (e = 0; e < coneSize; ++e) {
1229             const PetscInt *econe;
1230 
1231             PetscCall(DMPlexGetCone(dm, cone[e], &econe));
1232             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));
1233           }
1234         } else {
1235           PetscInt *closure = NULL;
1236           PetscInt  closureSize, Nv = 0, v;
1237 
1238           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1239           for (p = 0; p < closureSize * 2; p += 2) {
1240             const PetscInt point = closure[p];
1241 
1242             if ((point >= vStart) && (point < vEnd)) closure[Nv++] = point;
1243           }
1244           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] ", colors[rank % numColors]));
1245           for (v = 0; v <= Nv; ++v) {
1246             const PetscInt vertex = closure[v % Nv];
1247 
1248             if (v > 0) {
1249               if (plotEdges) {
1250                 const PetscInt *edge;
1251                 PetscInt        endpoints[2], ne;
1252 
1253                 endpoints[0] = closure[v - 1];
1254                 endpoints[1] = vertex;
1255                 PetscCall(DMPlexGetJoin(dm, 2, endpoints, &ne, &edge));
1256                 PetscCheck(ne == 1, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Could not find edge for vertices %" PetscInt_FMT ", %" PetscInt_FMT, endpoints[0], endpoints[1]);
1257                 PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- (%" PetscInt_FMT "_%d) -- ", edge[0], rank));
1258                 PetscCall(DMPlexRestoreJoin(dm, 2, endpoints, &ne, &edge));
1259               } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- "));
1260             }
1261             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "(%" PetscInt_FMT "_%d)", vertex, rank));
1262           }
1263           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ";\n"));
1264           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1265         }
1266       }
1267     }
1268     for (c = cStart; c < cEnd; ++c) {
1269       double             ccoords[3] = {0.0, 0.0, 0.0};
1270       PetscBool          isLabeled  = PETSC_FALSE;
1271       PetscScalar       *cellCoords = NULL;
1272       const PetscScalar *array;
1273       PetscInt           numCoords, cdim, d;
1274       PetscBool          isDG;
1275 
1276       if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1277       PetscCall(DMGetCoordinateDim(dm, &cdim));
1278       PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1279       PetscCheck(!(numCoords % cdim), PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "coordinate dim %" PetscInt_FMT " does not divide numCoords %" PetscInt_FMT, cdim, numCoords);
1280       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1281       for (p = 0; p < numCoords / cdim; ++p) {
1282         for (d = 0; d < cdim; ++d) {
1283           tcoords[d] = (double)(scale * PetscRealPart(cellCoords[p * cdim + d]));
1284           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1285         }
1286         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1287         if (cdim == 3) {
1288           PetscReal tmp = tcoords[1];
1289           tcoords[1]    = tcoords[2];
1290           tcoords[2]    = -tmp;
1291         }
1292         for (d = 0; d < dim; ++d) ccoords[d] += tcoords[d];
1293       }
1294       for (d = 0; d < cdim; ++d) ccoords[d] /= (numCoords / cdim);
1295       PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1296       for (d = 0; d < cdim; ++d) {
1297         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1298         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)ccoords[d]));
1299       }
1300       if (drawHasse) color = colors[depth % numColors];
1301       else color = colors[rank % numColors];
1302       for (l = 0; l < numLabels; ++l) {
1303         PetscInt val;
1304         PetscCall(DMGetLabelValue(dm, names[l], c, &val));
1305         if (val >= 0) {
1306           color     = lcolors[l % numLColors];
1307           isLabeled = PETSC_TRUE;
1308           break;
1309         }
1310       }
1311       if (drawNumbers[dim]) {
1312         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", c, rank, color, c));
1313       } else if (drawColors[dim]) {
1314         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", c, rank, !isLabeled ? 1 : 2, color));
1315       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", c, rank));
1316     }
1317     if (drawHasse) {
1318       int height = 0;
1319 
1320       color = colors[depth % numColors];
1321       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Cells\n"));
1322       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\c in {\\cStart,...,\\cEnd}\n"));
1323       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1324       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\c_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\cShift+\\c-\\cStart,%d) {\\c};\n", rank, color, height++));
1325       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1326 
1327       if (depth > 2) {
1328         color = colors[1 % numColors];
1329         PetscCall(PetscViewerASCIIPrintf(viewer, "%% Faces\n"));
1330         PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\f in {\\fStart,...,\\fEnd}\n"));
1331         PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1332         PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\f_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\fShift+\\f-\\fStart,%d) {\\f};\n", rank, color, height++));
1333         PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1334       }
1335 
1336       color = colors[1 % numColors];
1337       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Edges\n"));
1338       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\e in {\\eStart,...,\\eEnd}\n"));
1339       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1340       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\e_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\eShift+\\e-\\eStart,%d) {\\e};\n", rank, color, height++));
1341       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1342 
1343       color = colors[0 % numColors];
1344       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Vertices\n"));
1345       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\v in {\\vStart,...,\\vEnd}\n"));
1346       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1347       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\v_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\vShift+\\v-\\vStart,%d) {\\v};\n", rank, color, height++));
1348       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1349 
1350       for (p = pStart; p < pEnd; ++p) {
1351         const PetscInt *cone;
1352         PetscInt        coneSize, cp;
1353 
1354         PetscCall(DMPlexGetCone(dm, p, &cone));
1355         PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
1356         for (cp = 0; cp < coneSize; ++cp) PetscCall(PetscViewerASCIIPrintf(viewer, "\\draw[->, shorten >=1pt] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", cone[cp], rank, p, rank));
1357       }
1358     }
1359     PetscCall(PetscViewerFlush(viewer));
1360     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
1361     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{tikzpicture}\n"));
1362     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{document}\n"));
1363     for (l = 0; l < numLabels; ++l) PetscCall(PetscFree(names[l]));
1364     for (c = 0; c < numColors; ++c) PetscCall(PetscFree(colors[c]));
1365     for (c = 0; c < numLColors; ++c) PetscCall(PetscFree(lcolors[c]));
1366     PetscCall(PetscFree3(names, colors, lcolors));
1367     PetscCall(PetscBTDestroy(&wp));
1368   } else if (format == PETSC_VIEWER_LOAD_BALANCE) {
1369     Vec                    cown, acown;
1370     VecScatter             sct;
1371     ISLocalToGlobalMapping g2l;
1372     IS                     gid, acis;
1373     MPI_Comm               comm, ncomm = MPI_COMM_NULL;
1374     MPI_Group              ggroup, ngroup;
1375     PetscScalar           *array, nid;
1376     const PetscInt        *idxs;
1377     PetscInt              *idxs2, *start, *adjacency, *work;
1378     PetscInt64             lm[3], gm[3];
1379     PetscInt               i, c, cStart, cEnd, cum, numVertices, ect, ectn, cellHeight;
1380     PetscMPIInt            d1, d2, rank;
1381 
1382     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1383     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1384 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1385     PetscCallMPI(MPI_Comm_split_type(comm, MPI_COMM_TYPE_SHARED, rank, MPI_INFO_NULL, &ncomm));
1386 #endif
1387     if (ncomm != MPI_COMM_NULL) {
1388       PetscCallMPI(MPI_Comm_group(comm, &ggroup));
1389       PetscCallMPI(MPI_Comm_group(ncomm, &ngroup));
1390       d1 = 0;
1391       PetscCallMPI(MPI_Group_translate_ranks(ngroup, 1, &d1, ggroup, &d2));
1392       nid = d2;
1393       PetscCallMPI(MPI_Group_free(&ggroup));
1394       PetscCallMPI(MPI_Group_free(&ngroup));
1395       PetscCallMPI(MPI_Comm_free(&ncomm));
1396     } else nid = 0.0;
1397 
1398     /* Get connectivity */
1399     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1400     PetscCall(DMPlexCreatePartitionerGraph(dm, cellHeight, &numVertices, &start, &adjacency, &gid));
1401 
1402     /* filter overlapped local cells */
1403     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
1404     PetscCall(ISGetIndices(gid, &idxs));
1405     PetscCall(ISGetLocalSize(gid, &cum));
1406     PetscCall(PetscMalloc1(cum, &idxs2));
1407     for (c = cStart, cum = 0; c < cEnd; c++) {
1408       if (idxs[c - cStart] < 0) continue;
1409       idxs2[cum++] = idxs[c - cStart];
1410     }
1411     PetscCall(ISRestoreIndices(gid, &idxs));
1412     PetscCheck(numVertices == cum, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Unexpected %" PetscInt_FMT " != %" PetscInt_FMT, numVertices, cum);
1413     PetscCall(ISDestroy(&gid));
1414     PetscCall(ISCreateGeneral(comm, numVertices, idxs2, PETSC_OWN_POINTER, &gid));
1415 
1416     /* support for node-aware cell locality */
1417     PetscCall(ISCreateGeneral(comm, start[numVertices], adjacency, PETSC_USE_POINTER, &acis));
1418     PetscCall(VecCreateSeq(PETSC_COMM_SELF, start[numVertices], &acown));
1419     PetscCall(VecCreateMPI(comm, numVertices, PETSC_DECIDE, &cown));
1420     PetscCall(VecGetArray(cown, &array));
1421     for (c = 0; c < numVertices; c++) array[c] = nid;
1422     PetscCall(VecRestoreArray(cown, &array));
1423     PetscCall(VecScatterCreate(cown, acis, acown, NULL, &sct));
1424     PetscCall(VecScatterBegin(sct, cown, acown, INSERT_VALUES, SCATTER_FORWARD));
1425     PetscCall(VecScatterEnd(sct, cown, acown, INSERT_VALUES, SCATTER_FORWARD));
1426     PetscCall(ISDestroy(&acis));
1427     PetscCall(VecScatterDestroy(&sct));
1428     PetscCall(VecDestroy(&cown));
1429 
1430     /* compute edgeCut */
1431     for (c = 0, cum = 0; c < numVertices; c++) cum = PetscMax(cum, start[c + 1] - start[c]);
1432     PetscCall(PetscMalloc1(cum, &work));
1433     PetscCall(ISLocalToGlobalMappingCreateIS(gid, &g2l));
1434     PetscCall(ISLocalToGlobalMappingSetType(g2l, ISLOCALTOGLOBALMAPPINGHASH));
1435     PetscCall(ISDestroy(&gid));
1436     PetscCall(VecGetArray(acown, &array));
1437     for (c = 0, ect = 0, ectn = 0; c < numVertices; c++) {
1438       PetscInt totl;
1439 
1440       totl = start[c + 1] - start[c];
1441       PetscCall(ISGlobalToLocalMappingApply(g2l, IS_GTOLM_MASK, totl, adjacency + start[c], NULL, work));
1442       for (i = 0; i < totl; i++) {
1443         if (work[i] < 0) {
1444           ect += 1;
1445           ectn += (array[i + start[c]] != nid) ? 0 : 1;
1446         }
1447       }
1448     }
1449     PetscCall(PetscFree(work));
1450     PetscCall(VecRestoreArray(acown, &array));
1451     lm[0] = numVertices > 0 ? numVertices : PETSC_INT_MAX;
1452     lm[1] = -numVertices;
1453     PetscCallMPI(MPIU_Allreduce(lm, gm, 2, MPIU_INT64, MPI_MIN, comm));
1454     PetscCall(PetscViewerASCIIPrintf(viewer, "  Cell balance: %.2f (max %" PetscInt_FMT ", min %" PetscInt_FMT, -((double)gm[1]) / ((double)gm[0]), -(PetscInt)gm[1], (PetscInt)gm[0]));
1455     lm[0] = ect;                     /* edgeCut */
1456     lm[1] = ectn;                    /* node-aware edgeCut */
1457     lm[2] = numVertices > 0 ? 0 : 1; /* empty processes */
1458     PetscCallMPI(MPIU_Allreduce(lm, gm, 3, MPIU_INT64, MPI_SUM, comm));
1459     PetscCall(PetscViewerASCIIPrintf(viewer, ", empty %" PetscInt_FMT ")\n", (PetscInt)gm[2]));
1460 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1461     PetscCall(PetscViewerASCIIPrintf(viewer, "  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n", (PetscInt)(gm[0] / 2), gm[0] ? ((double)gm[1]) / ((double)gm[0]) : 1.));
1462 #else
1463     PetscCall(PetscViewerASCIIPrintf(viewer, "  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n", (PetscInt)(gm[0] / 2), 0.0));
1464 #endif
1465     PetscCall(ISLocalToGlobalMappingDestroy(&g2l));
1466     PetscCall(PetscFree(start));
1467     PetscCall(PetscFree(adjacency));
1468     PetscCall(VecDestroy(&acown));
1469   } else {
1470     const char    *name;
1471     PetscInt      *sizes, *hybsizes, *ghostsizes;
1472     PetscInt       locDepth, depth, cellHeight, dim, d;
1473     PetscInt       pStart, pEnd, p, gcStart, gcEnd, gcNum;
1474     PetscInt       numLabels, l, maxSize = 17;
1475     DMPolytopeType ct0 = DM_POLYTOPE_UNKNOWN;
1476     MPI_Comm       comm;
1477     PetscMPIInt    size, rank;
1478 
1479     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1480     PetscCallMPI(MPI_Comm_size(comm, &size));
1481     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1482     PetscCall(DMGetDimension(dm, &dim));
1483     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1484     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1485     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
1486     else PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
1487     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
1488     PetscCall(DMPlexGetDepth(dm, &locDepth));
1489     PetscCallMPI(MPIU_Allreduce(&locDepth, &depth, 1, MPIU_INT, MPI_MAX, comm));
1490     PetscCall(DMPlexGetCellTypeStratum(dm, DM_POLYTOPE_FV_GHOST, &gcStart, &gcEnd));
1491     gcNum = gcEnd - gcStart;
1492     if (size < maxSize) PetscCall(PetscCalloc3(size, &sizes, size, &hybsizes, size, &ghostsizes));
1493     else PetscCall(PetscCalloc3(3, &sizes, 3, &hybsizes, 3, &ghostsizes));
1494     for (d = 0; d <= depth; d++) {
1495       PetscInt Nc[2] = {0, 0}, ict;
1496 
1497       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
1498       if (pStart < pEnd) PetscCall(DMPlexGetCellType(dm, pStart, &ct0));
1499       ict = ct0;
1500       PetscCallMPI(MPI_Bcast(&ict, 1, MPIU_INT, 0, comm));
1501       ct0 = (DMPolytopeType)ict;
1502       for (p = pStart; p < pEnd; ++p) {
1503         DMPolytopeType ct;
1504 
1505         PetscCall(DMPlexGetCellType(dm, p, &ct));
1506         if (ct == ct0) ++Nc[0];
1507         else ++Nc[1];
1508       }
1509       if (size < maxSize) {
1510         PetscCallMPI(MPI_Gather(&Nc[0], 1, MPIU_INT, sizes, 1, MPIU_INT, 0, comm));
1511         PetscCallMPI(MPI_Gather(&Nc[1], 1, MPIU_INT, hybsizes, 1, MPIU_INT, 0, comm));
1512         if (d == depth) PetscCallMPI(MPI_Gather(&gcNum, 1, MPIU_INT, ghostsizes, 1, MPIU_INT, 0, comm));
1513         PetscCall(PetscViewerASCIIPrintf(viewer, "  Number of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1514         for (p = 0; p < size; ++p) {
1515           if (rank == 0) {
1516             PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT, sizes[p] + hybsizes[p]));
1517             if (hybsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT ")", hybsizes[p]));
1518             if (ghostsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "]", ghostsizes[p]));
1519           }
1520         }
1521       } else {
1522         PetscInt locMinMax[2];
1523 
1524         locMinMax[0] = Nc[0] + Nc[1];
1525         locMinMax[1] = Nc[0] + Nc[1];
1526         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, sizes));
1527         locMinMax[0] = Nc[1];
1528         locMinMax[1] = Nc[1];
1529         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, hybsizes));
1530         if (d == depth) {
1531           locMinMax[0] = gcNum;
1532           locMinMax[1] = gcNum;
1533           PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, ghostsizes));
1534         }
1535         PetscCall(PetscViewerASCIIPrintf(viewer, "  Min/Max of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1536         PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT "/%" PetscInt_FMT, sizes[0], sizes[1]));
1537         if (hybsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT "/%" PetscInt_FMT ")", hybsizes[0], hybsizes[1]));
1538         if (ghostsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "/%" PetscInt_FMT "]", ghostsizes[0], ghostsizes[1]));
1539       }
1540       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
1541     }
1542     PetscCall(PetscFree3(sizes, hybsizes, ghostsizes));
1543     {
1544       const PetscReal *maxCell;
1545       const PetscReal *L;
1546       PetscBool        localized;
1547 
1548       PetscCall(DMGetPeriodicity(dm, &maxCell, NULL, &L));
1549       PetscCall(DMGetCoordinatesLocalized(dm, &localized));
1550       if (L || localized) {
1551         PetscCall(PetscViewerASCIIPrintf(viewer, "Periodic mesh"));
1552         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1553         if (L) {
1554           PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
1555           for (d = 0; d < dim; ++d) {
1556             if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1557             PetscCall(PetscViewerASCIIPrintf(viewer, "%s", L[d] > 0.0 ? "PERIODIC" : "NONE"));
1558           }
1559           PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
1560         }
1561         PetscCall(PetscViewerASCIIPrintf(viewer, " coordinates %s\n", localized ? "localized" : "not localized"));
1562         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1563       }
1564     }
1565     PetscCall(DMGetNumLabels(dm, &numLabels));
1566     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
1567     for (l = 0; l < numLabels; ++l) {
1568       DMLabel     label;
1569       const char *name;
1570       PetscInt   *values;
1571       PetscInt    numValues, v;
1572 
1573       PetscCall(DMGetLabelName(dm, l, &name));
1574       PetscCall(DMGetLabel(dm, name, &label));
1575       PetscCall(DMLabelGetNumValues(label, &numValues));
1576       PetscCall(PetscViewerASCIIPrintf(viewer, "  %s: %" PetscInt_FMT " strata with value/size (", name, numValues));
1577 
1578       { // Extract array of DMLabel values so it can be sorted
1579         IS              is_values;
1580         const PetscInt *is_values_local = NULL;
1581 
1582         PetscCall(DMLabelGetValueIS(label, &is_values));
1583         PetscCall(ISGetIndices(is_values, &is_values_local));
1584         PetscCall(PetscMalloc1(numValues, &values));
1585         PetscCall(PetscArraycpy(values, is_values_local, numValues));
1586         PetscCall(PetscSortInt(numValues, values));
1587         PetscCall(ISRestoreIndices(is_values, &is_values_local));
1588         PetscCall(ISDestroy(&is_values));
1589       }
1590       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1591       for (v = 0; v < numValues; ++v) {
1592         PetscInt size;
1593 
1594         PetscCall(DMLabelGetStratumSize(label, values[v], &size));
1595         if (v > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1596         PetscCall(PetscViewerASCIIPrintf(viewer, "%" PetscInt_FMT " (%" PetscInt_FMT ")", values[v], size));
1597       }
1598       PetscCall(PetscViewerASCIIPrintf(viewer, ")\n"));
1599       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1600       PetscCall(PetscFree(values));
1601     }
1602     {
1603       char    **labelNames;
1604       PetscInt  Nl = numLabels;
1605       PetscBool flg;
1606 
1607       PetscCall(PetscMalloc1(Nl, &labelNames));
1608       PetscCall(PetscOptionsGetStringArray(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_plex_view_labels", labelNames, &Nl, &flg));
1609       for (l = 0; l < Nl; ++l) {
1610         DMLabel label;
1611 
1612         PetscCall(DMHasLabel(dm, labelNames[l], &flg));
1613         if (flg) {
1614           PetscCall(DMGetLabel(dm, labelNames[l], &label));
1615           PetscCall(DMLabelView(label, viewer));
1616         }
1617         PetscCall(PetscFree(labelNames[l]));
1618       }
1619       PetscCall(PetscFree(labelNames));
1620     }
1621     /* If no fields are specified, people do not want to see adjacency */
1622     if (dm->Nf) {
1623       PetscInt f;
1624 
1625       for (f = 0; f < dm->Nf; ++f) {
1626         const char *name;
1627 
1628         PetscCall(PetscObjectGetName(dm->fields[f].disc, &name));
1629         if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Field %s:\n", name));
1630         PetscCall(PetscViewerASCIIPushTab(viewer));
1631         if (dm->fields[f].label) PetscCall(DMLabelView(dm->fields[f].label, viewer));
1632         if (dm->fields[f].adjacency[0]) {
1633           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM++\n"));
1634           else PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM\n"));
1635         } else {
1636           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FEM\n"));
1637           else PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FUNKY\n"));
1638         }
1639         PetscCall(PetscViewerASCIIPopTab(viewer));
1640       }
1641     }
1642     PetscCall(DMGetCoarseDM(dm, &cdm));
1643     if (cdm) {
1644       PetscCall(PetscViewerASCIIPushTab(viewer));
1645       PetscCall(PetscViewerASCIIPrintf(viewer, "Defined by transform from:\n"));
1646       PetscCall(DMPlexView_Ascii(cdm, viewer));
1647       PetscCall(PetscViewerASCIIPopTab(viewer));
1648     }
1649   }
1650   PetscFunctionReturn(PETSC_SUCCESS);
1651 }
1652 
1653 static PetscErrorCode DMPlexDrawCell(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[])
1654 {
1655   DMPolytopeType ct;
1656   PetscMPIInt    rank;
1657   PetscInt       cdim;
1658 
1659   PetscFunctionBegin;
1660   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1661   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1662   PetscCall(DMGetCoordinateDim(dm, &cdim));
1663   switch (ct) {
1664   case DM_POLYTOPE_SEGMENT:
1665   case DM_POLYTOPE_POINT_PRISM_TENSOR:
1666     switch (cdim) {
1667     case 1: {
1668       const PetscReal y  = 0.5;  /* TODO Put it in the middle of the viewport */
1669       const PetscReal dy = 0.05; /* TODO Make it a fraction of the total length */
1670 
1671       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y, PetscRealPart(coords[1]), y, PETSC_DRAW_BLACK));
1672       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y + dy, PetscRealPart(coords[0]), y - dy, PETSC_DRAW_BLACK));
1673       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[1]), y + dy, PetscRealPart(coords[1]), y - dy, PETSC_DRAW_BLACK));
1674     } break;
1675     case 2: {
1676       const PetscReal dx = (PetscRealPart(coords[3]) - PetscRealPart(coords[1]));
1677       const PetscReal dy = (PetscRealPart(coords[2]) - PetscRealPart(coords[0]));
1678       const PetscReal l  = 0.1 / PetscSqrtReal(dx * dx + dy * dy);
1679 
1680       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1681       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));
1682       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));
1683     } break;
1684     default:
1685       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of dimension %" PetscInt_FMT, cdim);
1686     }
1687     break;
1688   case DM_POLYTOPE_TRIANGLE:
1689     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));
1690     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1691     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1692     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1693     break;
1694   case DM_POLYTOPE_QUADRILATERAL:
1695     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));
1696     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));
1697     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1698     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1699     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_BLACK));
1700     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1701     break;
1702   case DM_POLYTOPE_SEG_PRISM_TENSOR:
1703     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));
1704     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));
1705     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1706     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_BLACK));
1707     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1708     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1709     break;
1710   case DM_POLYTOPE_FV_GHOST:
1711     break;
1712   default:
1713     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1714   }
1715   PetscFunctionReturn(PETSC_SUCCESS);
1716 }
1717 
1718 static PetscErrorCode DrawPolygon_Private(DM dm, PetscDraw draw, PetscInt cell, PetscInt Nv, const PetscReal refVertices[], const PetscScalar coords[], PetscInt edgeDiv, PetscReal refCoords[], PetscReal edgeCoords[])
1719 {
1720   PetscReal   centroid[2] = {0., 0.};
1721   PetscMPIInt rank;
1722   PetscMPIInt fillColor;
1723 
1724   PetscFunctionBegin;
1725   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1726   fillColor = PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2;
1727   for (PetscInt v = 0; v < Nv; ++v) {
1728     centroid[0] += PetscRealPart(coords[v * 2 + 0]) / Nv;
1729     centroid[1] += PetscRealPart(coords[v * 2 + 1]) / Nv;
1730   }
1731   for (PetscInt e = 0; e < Nv; ++e) {
1732     refCoords[0] = refVertices[e * 2 + 0];
1733     refCoords[1] = refVertices[e * 2 + 1];
1734     for (PetscInt d = 1; d <= edgeDiv; ++d) {
1735       refCoords[d * 2 + 0] = refCoords[0] + (refVertices[(e + 1) % Nv * 2 + 0] - refCoords[0]) * d / edgeDiv;
1736       refCoords[d * 2 + 1] = refCoords[1] + (refVertices[(e + 1) % Nv * 2 + 1] - refCoords[1]) * d / edgeDiv;
1737     }
1738     PetscCall(DMPlexReferenceToCoordinates(dm, cell, edgeDiv + 1, refCoords, edgeCoords));
1739     for (PetscInt d = 0; d < edgeDiv; ++d) {
1740       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));
1741       PetscCall(PetscDrawLine(draw, edgeCoords[d * 2 + 0], edgeCoords[d * 2 + 1], edgeCoords[(d + 1) * 2 + 0], edgeCoords[(d + 1) * 2 + 1], PETSC_DRAW_BLACK));
1742     }
1743   }
1744   PetscFunctionReturn(PETSC_SUCCESS);
1745 }
1746 
1747 static PetscErrorCode DMPlexDrawCellHighOrder(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[], PetscInt edgeDiv, PetscReal refCoords[], PetscReal edgeCoords[])
1748 {
1749   DMPolytopeType ct;
1750 
1751   PetscFunctionBegin;
1752   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1753   switch (ct) {
1754   case DM_POLYTOPE_TRIANGLE: {
1755     PetscReal refVertices[6] = {-1., -1., 1., -1., -1., 1.};
1756 
1757     PetscCall(DrawPolygon_Private(dm, draw, cell, 3, refVertices, coords, edgeDiv, refCoords, edgeCoords));
1758   } break;
1759   case DM_POLYTOPE_QUADRILATERAL: {
1760     PetscReal refVertices[8] = {-1., -1., 1., -1., 1., 1., -1., 1.};
1761 
1762     PetscCall(DrawPolygon_Private(dm, draw, cell, 4, refVertices, coords, edgeDiv, refCoords, edgeCoords));
1763   } break;
1764   default:
1765     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1766   }
1767   PetscFunctionReturn(PETSC_SUCCESS);
1768 }
1769 
1770 static PetscErrorCode DMPlexView_Draw(DM dm, PetscViewer viewer)
1771 {
1772   PetscDraw    draw;
1773   DM           cdm;
1774   PetscSection coordSection;
1775   Vec          coordinates;
1776   PetscReal    xyl[3], xyr[3];
1777   PetscReal   *refCoords, *edgeCoords;
1778   PetscBool    isnull, drawAffine;
1779   PetscInt     dim, vStart, vEnd, cStart, cEnd, c, cDegree, edgeDiv;
1780 
1781   PetscFunctionBegin;
1782   PetscCall(DMGetCoordinateDim(dm, &dim));
1783   PetscCheck(dim <= 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT, dim);
1784   PetscCall(DMGetCoordinateDegree_Internal(dm, &cDegree));
1785   drawAffine = cDegree > 1 ? PETSC_FALSE : PETSC_TRUE;
1786   edgeDiv    = cDegree + 1;
1787   PetscCall(PetscOptionsGetBool(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_view_draw_affine", &drawAffine, NULL));
1788   if (!drawAffine) PetscCall(PetscMalloc2((edgeDiv + 1) * dim, &refCoords, (edgeDiv + 1) * dim, &edgeCoords));
1789   PetscCall(DMGetCoordinateDM(dm, &cdm));
1790   PetscCall(DMGetLocalSection(cdm, &coordSection));
1791   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1792   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1793   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1794 
1795   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
1796   PetscCall(PetscDrawIsNull(draw, &isnull));
1797   if (isnull) PetscFunctionReturn(PETSC_SUCCESS);
1798   PetscCall(PetscDrawSetTitle(draw, "Mesh"));
1799 
1800   PetscCall(DMGetBoundingBox(dm, xyl, xyr));
1801   PetscCall(PetscDrawSetCoordinates(draw, xyl[0], xyl[1], xyr[0], xyr[1]));
1802   PetscCall(PetscDrawClear(draw));
1803 
1804   for (c = cStart; c < cEnd; ++c) {
1805     PetscScalar       *coords = NULL;
1806     const PetscScalar *coords_arr;
1807     PetscInt           numCoords;
1808     PetscBool          isDG;
1809 
1810     PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
1811     if (drawAffine) PetscCall(DMPlexDrawCell(dm, draw, c, coords));
1812     else PetscCall(DMPlexDrawCellHighOrder(dm, draw, c, coords, edgeDiv, refCoords, edgeCoords));
1813     PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
1814   }
1815   if (!drawAffine) PetscCall(PetscFree2(refCoords, edgeCoords));
1816   PetscCall(PetscDrawFlush(draw));
1817   PetscCall(PetscDrawPause(draw));
1818   PetscCall(PetscDrawSave(draw));
1819   PetscFunctionReturn(PETSC_SUCCESS);
1820 }
1821 
1822 static PetscErrorCode DMPlexCreateHighOrderSurrogate_Internal(DM dm, DM *hdm)
1823 {
1824   DM           odm = dm, rdm = dm, cdm;
1825   PetscFE      fe;
1826   PetscSpace   sp;
1827   PetscClassId id;
1828   PetscInt     degree;
1829   PetscBool    hoView = PETSC_TRUE;
1830 
1831   PetscFunctionBegin;
1832   PetscObjectOptionsBegin((PetscObject)dm);
1833   PetscCall(PetscOptionsBool("-dm_plex_high_order_view", "Subsample to view meshes with high order coordinates", "DMPlexCreateHighOrderSurrogate_Internal", hoView, &hoView, NULL));
1834   PetscOptionsEnd();
1835   PetscCall(PetscObjectReference((PetscObject)dm));
1836   *hdm = dm;
1837   if (!hoView) PetscFunctionReturn(PETSC_SUCCESS);
1838   PetscCall(DMGetCoordinateDM(dm, &cdm));
1839   PetscCall(DMGetField(cdm, 0, NULL, (PetscObject *)&fe));
1840   PetscCall(PetscObjectGetClassId((PetscObject)fe, &id));
1841   if (id != PETSCFE_CLASSID) PetscFunctionReturn(PETSC_SUCCESS);
1842   PetscCall(PetscFEGetBasisSpace(fe, &sp));
1843   PetscCall(PetscSpaceGetDegree(sp, &degree, NULL));
1844   for (PetscInt r = 0, rd = PetscCeilReal(((PetscReal)degree) / 2.); r < (PetscInt)PetscCeilReal(PetscLog2Real(degree)); ++r, rd = PetscCeilReal(((PetscReal)rd) / 2.)) {
1845     DM  cdm, rcdm;
1846     Mat In;
1847     Vec cl, rcl;
1848 
1849     PetscCall(DMRefine(odm, PetscObjectComm((PetscObject)odm), &rdm));
1850     PetscCall(DMPlexCreateCoordinateSpace(rdm, rd, PETSC_FALSE, NULL));
1851     PetscCall(PetscObjectSetName((PetscObject)rdm, "Refined Mesh with Linear Coordinates"));
1852     PetscCall(DMGetCoordinateDM(odm, &cdm));
1853     PetscCall(DMGetCoordinateDM(rdm, &rcdm));
1854     PetscCall(DMGetCoordinatesLocal(odm, &cl));
1855     PetscCall(DMGetCoordinatesLocal(rdm, &rcl));
1856     PetscCall(DMSetCoarseDM(rcdm, cdm));
1857     PetscCall(DMCreateInterpolation(cdm, rcdm, &In, NULL));
1858     PetscCall(MatMult(In, cl, rcl));
1859     PetscCall(MatDestroy(&In));
1860     PetscCall(DMSetCoordinatesLocal(rdm, rcl));
1861     PetscCall(DMDestroy(&odm));
1862     odm = rdm;
1863   }
1864   *hdm = rdm;
1865   PetscFunctionReturn(PETSC_SUCCESS);
1866 }
1867 
1868 #if defined(PETSC_HAVE_EXODUSII)
1869   #include <exodusII.h>
1870   #include <petscviewerexodusii.h>
1871 #endif
1872 
1873 PetscErrorCode DMView_Plex(DM dm, PetscViewer viewer)
1874 {
1875   PetscBool iascii, ishdf5, isvtk, isdraw, flg, isglvis, isexodus, iscgns;
1876   char      name[PETSC_MAX_PATH_LEN];
1877 
1878   PetscFunctionBegin;
1879   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1880   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1881   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &iascii));
1882   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
1883   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1884   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
1885   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
1886   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodus));
1887   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
1888   if (iascii) {
1889     PetscViewerFormat format;
1890     PetscCall(PetscViewerGetFormat(viewer, &format));
1891     if (format == PETSC_VIEWER_ASCII_GLVIS) PetscCall(DMPlexView_GLVis(dm, viewer));
1892     else PetscCall(DMPlexView_Ascii(dm, viewer));
1893   } else if (ishdf5) {
1894 #if defined(PETSC_HAVE_HDF5)
1895     PetscCall(DMPlexView_HDF5_Internal(dm, viewer));
1896 #else
1897     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1898 #endif
1899   } else if (isvtk) {
1900     PetscCall(DMPlexVTKWriteAll((PetscObject)dm, viewer));
1901   } else if (isdraw) {
1902     DM hdm;
1903 
1904     PetscCall(DMPlexCreateHighOrderSurrogate_Internal(dm, &hdm));
1905     PetscCall(DMPlexView_Draw(hdm, viewer));
1906     PetscCall(DMDestroy(&hdm));
1907   } else if (isglvis) {
1908     PetscCall(DMPlexView_GLVis(dm, viewer));
1909 #if defined(PETSC_HAVE_EXODUSII)
1910   } else if (isexodus) {
1911     /*
1912       exodusII requires that all sets be part of exactly one cell set.
1913       If the dm does not have a "Cell Sets" label defined, we create one
1914       with ID 1, containing all cells.
1915       Note that if the Cell Sets label is defined but does not cover all cells,
1916       we may still have a problem. This should probably be checked here or in the viewer;
1917     */
1918     PetscInt numCS;
1919     PetscCall(DMGetLabelSize(dm, "Cell Sets", &numCS));
1920     if (!numCS) {
1921       PetscInt cStart, cEnd, c;
1922       PetscCall(DMCreateLabel(dm, "Cell Sets"));
1923       PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1924       for (c = cStart; c < cEnd; ++c) PetscCall(DMSetLabelValue(dm, "Cell Sets", c, 1));
1925     }
1926     PetscCall(DMView_PlexExodusII(dm, viewer));
1927 #endif
1928 #if defined(PETSC_HAVE_CGNS)
1929   } else if (iscgns) {
1930     PetscCall(DMView_PlexCGNS(dm, viewer));
1931 #endif
1932   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex writing", ((PetscObject)viewer)->type_name);
1933   /* Optionally view the partition */
1934   PetscCall(PetscOptionsHasName(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_partition_view", &flg));
1935   if (flg) {
1936     Vec ranks;
1937     PetscCall(DMPlexCreateRankField(dm, &ranks));
1938     PetscCall(VecView(ranks, viewer));
1939     PetscCall(VecDestroy(&ranks));
1940   }
1941   /* Optionally view a label */
1942   PetscCall(PetscOptionsGetString(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_label_view", name, sizeof(name), &flg));
1943   if (flg) {
1944     DMLabel label;
1945     Vec     val;
1946 
1947     PetscCall(DMGetLabel(dm, name, &label));
1948     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Label %s provided to -dm_label_view does not exist in this DM", name);
1949     PetscCall(DMPlexCreateLabelField(dm, label, &val));
1950     PetscCall(VecView(val, viewer));
1951     PetscCall(VecDestroy(&val));
1952   }
1953   PetscFunctionReturn(PETSC_SUCCESS);
1954 }
1955 
1956 /*@
1957   DMPlexTopologyView - Saves a `DMPLEX` topology into a file
1958 
1959   Collective
1960 
1961   Input Parameters:
1962 + dm     - The `DM` whose topology is to be saved
1963 - viewer - The `PetscViewer` to save it in
1964 
1965   Level: advanced
1966 
1967 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexTopologyLoad()`, `PetscViewer`
1968 @*/
1969 PetscErrorCode DMPlexTopologyView(DM dm, PetscViewer viewer)
1970 {
1971   PetscBool ishdf5;
1972 
1973   PetscFunctionBegin;
1974   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1975   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1976   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1977   PetscCall(PetscLogEventBegin(DMPLEX_TopologyView, viewer, 0, 0, 0));
1978   if (ishdf5) {
1979 #if defined(PETSC_HAVE_HDF5)
1980     PetscViewerFormat format;
1981     PetscCall(PetscViewerGetFormat(viewer, &format));
1982     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
1983       IS globalPointNumbering;
1984 
1985       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
1986       PetscCall(DMPlexTopologyView_HDF5_Internal(dm, globalPointNumbering, viewer));
1987       PetscCall(ISDestroy(&globalPointNumbering));
1988     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
1989 #else
1990     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1991 #endif
1992   }
1993   PetscCall(PetscLogEventEnd(DMPLEX_TopologyView, viewer, 0, 0, 0));
1994   PetscFunctionReturn(PETSC_SUCCESS);
1995 }
1996 
1997 /*@
1998   DMPlexCoordinatesView - Saves `DMPLEX` coordinates into a file
1999 
2000   Collective
2001 
2002   Input Parameters:
2003 + dm     - The `DM` whose coordinates are to be saved
2004 - viewer - The `PetscViewer` for saving
2005 
2006   Level: advanced
2007 
2008 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexLabelsView()`, `DMPlexCoordinatesLoad()`, `PetscViewer`
2009 @*/
2010 PetscErrorCode DMPlexCoordinatesView(DM dm, PetscViewer viewer)
2011 {
2012   PetscBool ishdf5;
2013 
2014   PetscFunctionBegin;
2015   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2016   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2017   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2018   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
2019   if (ishdf5) {
2020 #if defined(PETSC_HAVE_HDF5)
2021     PetscViewerFormat format;
2022     PetscCall(PetscViewerGetFormat(viewer, &format));
2023     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2024       PetscCall(DMPlexCoordinatesView_HDF5_Internal(dm, viewer));
2025     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
2026 #else
2027     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2028 #endif
2029   }
2030   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
2031   PetscFunctionReturn(PETSC_SUCCESS);
2032 }
2033 
2034 /*@
2035   DMPlexLabelsView - Saves `DMPLEX` labels into a file
2036 
2037   Collective
2038 
2039   Input Parameters:
2040 + dm     - The `DM` whose labels are to be saved
2041 - viewer - The `PetscViewer` for saving
2042 
2043   Level: advanced
2044 
2045 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsLoad()`, `PetscViewer`
2046 @*/
2047 PetscErrorCode DMPlexLabelsView(DM dm, PetscViewer viewer)
2048 {
2049   PetscBool ishdf5;
2050 
2051   PetscFunctionBegin;
2052   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2053   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2054   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2055   PetscCall(PetscLogEventBegin(DMPLEX_LabelsView, viewer, 0, 0, 0));
2056   if (ishdf5) {
2057 #if defined(PETSC_HAVE_HDF5)
2058     IS                globalPointNumbering;
2059     PetscViewerFormat format;
2060 
2061     PetscCall(PetscViewerGetFormat(viewer, &format));
2062     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2063       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
2064       PetscCall(DMPlexLabelsView_HDF5_Internal(dm, globalPointNumbering, viewer));
2065       PetscCall(ISDestroy(&globalPointNumbering));
2066     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2067 #else
2068     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2069 #endif
2070   }
2071   PetscCall(PetscLogEventEnd(DMPLEX_LabelsView, viewer, 0, 0, 0));
2072   PetscFunctionReturn(PETSC_SUCCESS);
2073 }
2074 
2075 /*@
2076   DMPlexSectionView - Saves a section associated with a `DMPLEX`
2077 
2078   Collective
2079 
2080   Input Parameters:
2081 + dm        - The `DM` that contains the topology on which the section to be saved is defined
2082 . viewer    - The `PetscViewer` for saving
2083 - sectiondm - The `DM` that contains the section to be saved, can be `NULL`
2084 
2085   Level: advanced
2086 
2087   Notes:
2088   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.
2089 
2090   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.
2091 
2092 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`, `PetscSectionView()`, `DMPlexSectionLoad()`, `PetscViewer`
2093 @*/
2094 PetscErrorCode DMPlexSectionView(DM dm, PetscViewer viewer, DM sectiondm)
2095 {
2096   PetscBool ishdf5;
2097 
2098   PetscFunctionBegin;
2099   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2100   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2101   if (!sectiondm) sectiondm = dm;
2102   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2103   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2104   PetscCall(PetscLogEventBegin(DMPLEX_SectionView, viewer, 0, 0, 0));
2105   if (ishdf5) {
2106 #if defined(PETSC_HAVE_HDF5)
2107     PetscCall(DMPlexSectionView_HDF5_Internal(dm, viewer, sectiondm));
2108 #else
2109     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2110 #endif
2111   }
2112   PetscCall(PetscLogEventEnd(DMPLEX_SectionView, viewer, 0, 0, 0));
2113   PetscFunctionReturn(PETSC_SUCCESS);
2114 }
2115 
2116 /*@
2117   DMPlexGlobalVectorView - Saves a global vector
2118 
2119   Collective
2120 
2121   Input Parameters:
2122 + dm        - The `DM` that represents the topology
2123 . viewer    - The `PetscViewer` to save data with
2124 . sectiondm - The `DM` that contains the global section on which vec is defined, can be `NULL`
2125 - vec       - The global vector to be saved
2126 
2127   Level: advanced
2128 
2129   Notes:
2130   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.
2131 
2132   Calling sequence:
2133 .vb
2134        DMCreate(PETSC_COMM_WORLD, &dm);
2135        DMSetType(dm, DMPLEX);
2136        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2137        DMClone(dm, &sectiondm);
2138        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2139        PetscSectionCreate(PETSC_COMM_WORLD, &section);
2140        DMPlexGetChart(sectiondm, &pStart, &pEnd);
2141        PetscSectionSetChart(section, pStart, pEnd);
2142        PetscSectionSetUp(section);
2143        DMSetLocalSection(sectiondm, section);
2144        PetscSectionDestroy(&section);
2145        DMGetGlobalVector(sectiondm, &vec);
2146        PetscObjectSetName((PetscObject)vec, "vec_name");
2147        DMPlexTopologyView(dm, viewer);
2148        DMPlexSectionView(dm, viewer, sectiondm);
2149        DMPlexGlobalVectorView(dm, viewer, sectiondm, vec);
2150        DMRestoreGlobalVector(sectiondm, &vec);
2151        DMDestroy(&sectiondm);
2152        DMDestroy(&dm);
2153 .ve
2154 
2155 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexLocalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2156 @*/
2157 PetscErrorCode DMPlexGlobalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2158 {
2159   PetscBool ishdf5;
2160 
2161   PetscFunctionBegin;
2162   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2163   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2164   if (!sectiondm) sectiondm = dm;
2165   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2166   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2167   /* Check consistency */
2168   {
2169     PetscSection section;
2170     PetscBool    includesConstraints;
2171     PetscInt     m, m1;
2172 
2173     PetscCall(VecGetLocalSize(vec, &m1));
2174     PetscCall(DMGetGlobalSection(sectiondm, &section));
2175     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2176     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2177     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2178     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2179   }
2180   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2181   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorView, viewer, 0, 0, 0));
2182   if (ishdf5) {
2183 #if defined(PETSC_HAVE_HDF5)
2184     PetscCall(DMPlexGlobalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2185 #else
2186     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2187 #endif
2188   }
2189   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorView, viewer, 0, 0, 0));
2190   PetscFunctionReturn(PETSC_SUCCESS);
2191 }
2192 
2193 /*@
2194   DMPlexLocalVectorView - Saves a local vector
2195 
2196   Collective
2197 
2198   Input Parameters:
2199 + dm        - The `DM` that represents the topology
2200 . viewer    - The `PetscViewer` to save data with
2201 . sectiondm - The `DM` that contains the local section on which `vec` is defined, can be `NULL`
2202 - vec       - The local vector to be saved
2203 
2204   Level: advanced
2205 
2206   Note:
2207   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.
2208 
2209   Calling sequence:
2210 .vb
2211        DMCreate(PETSC_COMM_WORLD, &dm);
2212        DMSetType(dm, DMPLEX);
2213        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2214        DMClone(dm, &sectiondm);
2215        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2216        PetscSectionCreate(PETSC_COMM_WORLD, &section);
2217        DMPlexGetChart(sectiondm, &pStart, &pEnd);
2218        PetscSectionSetChart(section, pStart, pEnd);
2219        PetscSectionSetUp(section);
2220        DMSetLocalSection(sectiondm, section);
2221        DMGetLocalVector(sectiondm, &vec);
2222        PetscObjectSetName((PetscObject)vec, "vec_name");
2223        DMPlexTopologyView(dm, viewer);
2224        DMPlexSectionView(dm, viewer, sectiondm);
2225        DMPlexLocalVectorView(dm, viewer, sectiondm, vec);
2226        DMRestoreLocalVector(sectiondm, &vec);
2227        DMDestroy(&sectiondm);
2228        DMDestroy(&dm);
2229 .ve
2230 
2231 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexGlobalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2232 @*/
2233 PetscErrorCode DMPlexLocalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2234 {
2235   PetscBool ishdf5;
2236 
2237   PetscFunctionBegin;
2238   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2239   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2240   if (!sectiondm) sectiondm = dm;
2241   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2242   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2243   /* Check consistency */
2244   {
2245     PetscSection section;
2246     PetscBool    includesConstraints;
2247     PetscInt     m, m1;
2248 
2249     PetscCall(VecGetLocalSize(vec, &m1));
2250     PetscCall(DMGetLocalSection(sectiondm, &section));
2251     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2252     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2253     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2254     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2255   }
2256   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2257   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorView, viewer, 0, 0, 0));
2258   if (ishdf5) {
2259 #if defined(PETSC_HAVE_HDF5)
2260     PetscCall(DMPlexLocalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2261 #else
2262     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2263 #endif
2264   }
2265   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorView, viewer, 0, 0, 0));
2266   PetscFunctionReturn(PETSC_SUCCESS);
2267 }
2268 
2269 PetscErrorCode DMLoad_Plex(DM dm, PetscViewer viewer)
2270 {
2271   PetscBool ishdf5;
2272 
2273   PetscFunctionBegin;
2274   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2275   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2276   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2277   if (ishdf5) {
2278 #if defined(PETSC_HAVE_HDF5)
2279     PetscViewerFormat format;
2280     PetscCall(PetscViewerGetFormat(viewer, &format));
2281     if (format == PETSC_VIEWER_HDF5_XDMF || format == PETSC_VIEWER_HDF5_VIZ) {
2282       PetscCall(DMPlexLoad_HDF5_Xdmf_Internal(dm, viewer));
2283     } else if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2284       PetscCall(DMPlexLoad_HDF5_Internal(dm, viewer));
2285     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2286     PetscFunctionReturn(PETSC_SUCCESS);
2287 #else
2288     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2289 #endif
2290   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex loading", ((PetscObject)viewer)->type_name);
2291 }
2292 
2293 /*@
2294   DMPlexTopologyLoad - Loads a topology into a `DMPLEX`
2295 
2296   Collective
2297 
2298   Input Parameters:
2299 + dm     - The `DM` into which the topology is loaded
2300 - viewer - The `PetscViewer` for the saved topology
2301 
2302   Output Parameter:
2303 . 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;
2304   `NULL` if unneeded
2305 
2306   Level: advanced
2307 
2308 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2309           `PetscViewer`, `PetscSF`
2310 @*/
2311 PetscErrorCode DMPlexTopologyLoad(DM dm, PetscViewer viewer, PetscSF *globalToLocalPointSF)
2312 {
2313   PetscBool ishdf5;
2314 
2315   PetscFunctionBegin;
2316   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2317   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2318   if (globalToLocalPointSF) PetscAssertPointer(globalToLocalPointSF, 3);
2319   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2320   PetscCall(PetscLogEventBegin(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2321   if (ishdf5) {
2322 #if defined(PETSC_HAVE_HDF5)
2323     PetscViewerFormat format;
2324     PetscCall(PetscViewerGetFormat(viewer, &format));
2325     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2326       PetscCall(DMPlexTopologyLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2327     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2328 #else
2329     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2330 #endif
2331   }
2332   PetscCall(PetscLogEventEnd(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2333   PetscFunctionReturn(PETSC_SUCCESS);
2334 }
2335 
2336 /*@
2337   DMPlexCoordinatesLoad - Loads coordinates into a `DMPLEX`
2338 
2339   Collective
2340 
2341   Input Parameters:
2342 + dm                   - The `DM` into which the coordinates are loaded
2343 . viewer               - The `PetscViewer` for the saved coordinates
2344 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad()` when loading dm from viewer
2345 
2346   Level: advanced
2347 
2348 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2349           `PetscSF`, `PetscViewer`
2350 @*/
2351 PetscErrorCode DMPlexCoordinatesLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2352 {
2353   PetscBool ishdf5;
2354 
2355   PetscFunctionBegin;
2356   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2357   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2358   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2359   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2360   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesLoad, viewer, 0, 0, 0));
2361   if (ishdf5) {
2362 #if defined(PETSC_HAVE_HDF5)
2363     PetscViewerFormat format;
2364     PetscCall(PetscViewerGetFormat(viewer, &format));
2365     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2366       PetscCall(DMPlexCoordinatesLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2367     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2368 #else
2369     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2370 #endif
2371   }
2372   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesLoad, viewer, 0, 0, 0));
2373   PetscFunctionReturn(PETSC_SUCCESS);
2374 }
2375 
2376 /*@
2377   DMPlexLabelsLoad - Loads labels into a `DMPLEX`
2378 
2379   Collective
2380 
2381   Input Parameters:
2382 + dm                   - The `DM` into which the labels are loaded
2383 . viewer               - The `PetscViewer` for the saved labels
2384 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad()` when loading `dm` from viewer
2385 
2386   Level: advanced
2387 
2388   Note:
2389   The `PetscSF` argument must not be `NULL` if the `DM` is distributed, otherwise an error occurs.
2390 
2391 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2392           `PetscSF`, `PetscViewer`
2393 @*/
2394 PetscErrorCode DMPlexLabelsLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2395 {
2396   PetscBool ishdf5;
2397 
2398   PetscFunctionBegin;
2399   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2400   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2401   if (globalToLocalPointSF) PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2402   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2403   PetscCall(PetscLogEventBegin(DMPLEX_LabelsLoad, viewer, 0, 0, 0));
2404   if (ishdf5) {
2405 #if defined(PETSC_HAVE_HDF5)
2406     PetscViewerFormat format;
2407 
2408     PetscCall(PetscViewerGetFormat(viewer, &format));
2409     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2410       PetscCall(DMPlexLabelsLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2411     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2412 #else
2413     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2414 #endif
2415   }
2416   PetscCall(PetscLogEventEnd(DMPLEX_LabelsLoad, viewer, 0, 0, 0));
2417   PetscFunctionReturn(PETSC_SUCCESS);
2418 }
2419 
2420 /*@
2421   DMPlexSectionLoad - Loads section into a `DMPLEX`
2422 
2423   Collective
2424 
2425   Input Parameters:
2426 + dm                   - The `DM` that represents the topology
2427 . viewer               - The `PetscViewer` that represents the on-disk section (sectionA)
2428 . sectiondm            - The `DM` into which the on-disk section (sectionA) is migrated, can be `NULL`
2429 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad(`) when loading dm from viewer
2430 
2431   Output Parameters:
2432 + 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)
2433 - 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)
2434 
2435   Level: advanced
2436 
2437   Notes:
2438   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.
2439 
2440   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.
2441 
2442   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.
2443 
2444   Example using 2 processes:
2445 .vb
2446   NX (number of points on dm): 4
2447   sectionA                   : the on-disk section
2448   vecA                       : a vector associated with sectionA
2449   sectionB                   : sectiondm's local section constructed in this function
2450   vecB (local)               : a vector associated with sectiondm's local section
2451   vecB (global)              : a vector associated with sectiondm's global section
2452 
2453                                      rank 0    rank 1
2454   vecA (global)                  : [.0 .4 .1 | .2 .3]        <- to be loaded in DMPlexGlobalVectorLoad() or DMPlexLocalVectorLoad()
2455   sectionA->atlasOff             :       0 2 | 1             <- loaded in PetscSectionLoad()
2456   sectionA->atlasDof             :       1 3 | 1             <- loaded in PetscSectionLoad()
2457   sectionA's global point numbers:       0 2 | 3             <- loaded in DMPlexSectionLoad()
2458   [0, NX)                        :       0 1 | 2 3           <- conceptual partition used in globalToLocalPointSF
2459   sectionB's global point numbers:     0 1 3 | 3 2           <- associated with [0, NX) by globalToLocalPointSF
2460   sectionB->atlasDof             :     1 0 1 | 1 3
2461   sectionB->atlasOff (no perm)   :     0 1 1 | 0 1
2462   vecB (local)                   :   [.0 .4] | [.4 .1 .2 .3] <- to be constructed by calling DMPlexLocalVectorLoad() with localDofSF
2463   vecB (global)                  :    [.0 .4 | .1 .2 .3]     <- to be constructed by calling DMPlexGlobalVectorLoad() with globalDofSF
2464 .ve
2465   where "|" represents a partition of loaded data, and global point 3 is assumed to be owned by rank 0.
2466 
2467 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`, `PetscSectionLoad()`, `DMPlexSectionView()`, `PetscSF`, `PetscViewer`
2468 @*/
2469 PetscErrorCode DMPlexSectionLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF globalToLocalPointSF, PetscSF *globalDofSF, PetscSF *localDofSF)
2470 {
2471   PetscBool ishdf5;
2472 
2473   PetscFunctionBegin;
2474   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2475   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2476   if (!sectiondm) sectiondm = dm;
2477   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2478   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 4);
2479   if (globalDofSF) PetscAssertPointer(globalDofSF, 5);
2480   if (localDofSF) PetscAssertPointer(localDofSF, 6);
2481   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2482   PetscCall(PetscLogEventBegin(DMPLEX_SectionLoad, viewer, 0, 0, 0));
2483   if (ishdf5) {
2484 #if defined(PETSC_HAVE_HDF5)
2485     PetscCall(DMPlexSectionLoad_HDF5_Internal(dm, viewer, sectiondm, globalToLocalPointSF, globalDofSF, localDofSF));
2486 #else
2487     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2488 #endif
2489   }
2490   PetscCall(PetscLogEventEnd(DMPLEX_SectionLoad, viewer, 0, 0, 0));
2491   PetscFunctionReturn(PETSC_SUCCESS);
2492 }
2493 
2494 /*@
2495   DMPlexGlobalVectorLoad - Loads on-disk vector data into a global vector
2496 
2497   Collective
2498 
2499   Input Parameters:
2500 + dm        - The `DM` that represents the topology
2501 . viewer    - The `PetscViewer` that represents the on-disk vector data
2502 . sectiondm - The `DM` that contains the global section on which vec is defined, can be `NULL`
2503 . sf        - The `PetscSF` that migrates the on-disk vector data into vec
2504 - vec       - The global vector to set values of
2505 
2506   Level: advanced
2507 
2508   Notes:
2509   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.
2510 
2511   Calling sequence:
2512 .vb
2513        DMCreate(PETSC_COMM_WORLD, &dm);
2514        DMSetType(dm, DMPLEX);
2515        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2516        DMPlexTopologyLoad(dm, viewer, &sfX);
2517        DMClone(dm, &sectiondm);
2518        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2519        DMPlexSectionLoad(dm, viewer, sectiondm, sfX, &gsf, NULL);
2520        DMGetGlobalVector(sectiondm, &vec);
2521        PetscObjectSetName((PetscObject)vec, "vec_name");
2522        DMPlexGlobalVectorLoad(dm, viewer, sectiondm, gsf, vec);
2523        DMRestoreGlobalVector(sectiondm, &vec);
2524        PetscSFDestroy(&gsf);
2525        PetscSFDestroy(&sfX);
2526        DMDestroy(&sectiondm);
2527        DMDestroy(&dm);
2528 .ve
2529 
2530 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexLocalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`,
2531           `PetscSF`, `PetscViewer`
2532 @*/
2533 PetscErrorCode DMPlexGlobalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2534 {
2535   PetscBool ishdf5;
2536 
2537   PetscFunctionBegin;
2538   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2539   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2540   if (!sectiondm) sectiondm = dm;
2541   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2542   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2543   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2544   /* Check consistency */
2545   {
2546     PetscSection section;
2547     PetscBool    includesConstraints;
2548     PetscInt     m, m1;
2549 
2550     PetscCall(VecGetLocalSize(vec, &m1));
2551     PetscCall(DMGetGlobalSection(sectiondm, &section));
2552     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2553     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2554     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2555     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2556   }
2557   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2558   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorLoad, viewer, 0, 0, 0));
2559   if (ishdf5) {
2560 #if defined(PETSC_HAVE_HDF5)
2561     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2562 #else
2563     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2564 #endif
2565   }
2566   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorLoad, viewer, 0, 0, 0));
2567   PetscFunctionReturn(PETSC_SUCCESS);
2568 }
2569 
2570 /*@
2571   DMPlexLocalVectorLoad - Loads on-disk vector data into a local vector
2572 
2573   Collective
2574 
2575   Input Parameters:
2576 + dm        - The `DM` that represents the topology
2577 . viewer    - The `PetscViewer` that represents the on-disk vector data
2578 . sectiondm - The `DM` that contains the local section on which vec is defined, can be `NULL`
2579 . sf        - The `PetscSF` that migrates the on-disk vector data into vec
2580 - vec       - The local vector to set values of
2581 
2582   Level: advanced
2583 
2584   Notes:
2585   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.
2586 
2587   Calling sequence:
2588 .vb
2589        DMCreate(PETSC_COMM_WORLD, &dm);
2590        DMSetType(dm, DMPLEX);
2591        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2592        DMPlexTopologyLoad(dm, viewer, &sfX);
2593        DMClone(dm, &sectiondm);
2594        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2595        DMPlexSectionLoad(dm, viewer, sectiondm, sfX, NULL, &lsf);
2596        DMGetLocalVector(sectiondm, &vec);
2597        PetscObjectSetName((PetscObject)vec, "vec_name");
2598        DMPlexLocalVectorLoad(dm, viewer, sectiondm, lsf, vec);
2599        DMRestoreLocalVector(sectiondm, &vec);
2600        PetscSFDestroy(&lsf);
2601        PetscSFDestroy(&sfX);
2602        DMDestroy(&sectiondm);
2603        DMDestroy(&dm);
2604 .ve
2605 
2606 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`,
2607           `PetscSF`, `PetscViewer`
2608 @*/
2609 PetscErrorCode DMPlexLocalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2610 {
2611   PetscBool ishdf5;
2612 
2613   PetscFunctionBegin;
2614   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2615   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2616   if (!sectiondm) sectiondm = dm;
2617   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2618   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2619   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2620   /* Check consistency */
2621   {
2622     PetscSection section;
2623     PetscBool    includesConstraints;
2624     PetscInt     m, m1;
2625 
2626     PetscCall(VecGetLocalSize(vec, &m1));
2627     PetscCall(DMGetLocalSection(sectiondm, &section));
2628     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2629     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2630     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2631     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2632   }
2633   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2634   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorLoad, viewer, 0, 0, 0));
2635   if (ishdf5) {
2636 #if defined(PETSC_HAVE_HDF5)
2637     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2638 #else
2639     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2640 #endif
2641   }
2642   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorLoad, viewer, 0, 0, 0));
2643   PetscFunctionReturn(PETSC_SUCCESS);
2644 }
2645 
2646 PetscErrorCode DMDestroy_Plex(DM dm)
2647 {
2648   DM_Plex *mesh = (DM_Plex *)dm->data;
2649 
2650   PetscFunctionBegin;
2651   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMSetUpGLVisViewer_C", NULL));
2652   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertBoundaryValues_C", NULL));
2653   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMCreateNeumannOverlap_C", NULL));
2654   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMInterpolateSolution_C", NULL));
2655   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertTimeDerivativeBoundaryValues_C", NULL));
2656   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", NULL));
2657   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeGetDefault_C", NULL));
2658   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeSetDefault_C", NULL));
2659   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "MatComputeNeumannOverlap_C", NULL));
2660   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderGetDefault_C", NULL));
2661   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderSetDefault_C", NULL));
2662   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionGetDefault_C", NULL));
2663   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionSetDefault_C", NULL));
2664   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionGetType_C", NULL));
2665   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionSetType_C", NULL));
2666   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", NULL));
2667   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexSetOverlap_C", NULL));
2668   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetUseCeed_C", NULL));
2669   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexSetUseCeed_C", NULL));
2670   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMGetIsoperiodicPointSF_C", NULL));
2671   if (--mesh->refct > 0) PetscFunctionReturn(PETSC_SUCCESS);
2672   PetscCall(PetscSectionDestroy(&mesh->coneSection));
2673   PetscCall(PetscFree(mesh->cones));
2674   PetscCall(PetscFree(mesh->coneOrientations));
2675   PetscCall(PetscSectionDestroy(&mesh->supportSection));
2676   PetscCall(PetscSectionDestroy(&mesh->subdomainSection));
2677   PetscCall(PetscFree(mesh->supports));
2678   PetscCall(PetscFree(mesh->cellTypes));
2679   PetscCall(DMPlexTransformDestroy(&mesh->tr));
2680   PetscCall(PetscFree(mesh->tetgenOpts));
2681   PetscCall(PetscFree(mesh->triangleOpts));
2682   PetscCall(PetscFree(mesh->transformType));
2683   PetscCall(PetscFree(mesh->distributionName));
2684   PetscCall(PetscPartitionerDestroy(&mesh->partitioner));
2685   PetscCall(DMLabelDestroy(&mesh->subpointMap));
2686   PetscCall(ISDestroy(&mesh->subpointIS));
2687   PetscCall(ISDestroy(&mesh->globalVertexNumbers));
2688   PetscCall(ISDestroy(&mesh->globalCellNumbers));
2689   if (mesh->periodic.face_sfs) {
2690     for (PetscInt i = 0; i < mesh->periodic.num_face_sfs; i++) PetscCall(PetscSFDestroy(&mesh->periodic.face_sfs[i]));
2691     PetscCall(PetscFree(mesh->periodic.face_sfs));
2692   }
2693   PetscCall(PetscSFDestroy(&mesh->periodic.composed_sf));
2694   if (mesh->periodic.periodic_points) {
2695     for (PetscInt i = 0; i < mesh->periodic.num_face_sfs; i++) PetscCall(ISDestroy(&mesh->periodic.periodic_points[i]));
2696     PetscCall(PetscFree(mesh->periodic.periodic_points));
2697   }
2698   if (mesh->periodic.transform) PetscCall(PetscFree(mesh->periodic.transform));
2699   PetscCall(PetscSectionDestroy(&mesh->anchorSection));
2700   PetscCall(ISDestroy(&mesh->anchorIS));
2701   PetscCall(PetscSectionDestroy(&mesh->parentSection));
2702   PetscCall(PetscFree(mesh->parents));
2703   PetscCall(PetscFree(mesh->childIDs));
2704   PetscCall(PetscSectionDestroy(&mesh->childSection));
2705   PetscCall(PetscFree(mesh->children));
2706   PetscCall(DMDestroy(&mesh->referenceTree));
2707   PetscCall(PetscGridHashDestroy(&mesh->lbox));
2708   PetscCall(PetscFree(mesh->neighbors));
2709   if (mesh->metricCtx) PetscCall(PetscFree(mesh->metricCtx));
2710   if (mesh->nonempty_comm != MPI_COMM_NULL && mesh->nonempty_comm != MPI_COMM_SELF) PetscCallMPI(MPI_Comm_free(&mesh->nonempty_comm));
2711   /* This was originally freed in DMDestroy(), but that prevents reference counting of backend objects */
2712   PetscCall(PetscFree(mesh));
2713   PetscFunctionReturn(PETSC_SUCCESS);
2714 }
2715 
2716 PetscErrorCode DMCreateMatrix_Plex(DM dm, Mat *J)
2717 {
2718   PetscSection           sectionGlobal, sectionLocal;
2719   PetscInt               bs = -1, mbs;
2720   PetscInt               localSize, localStart = 0;
2721   PetscBool              isShell, isBlock, isSeqBlock, isMPIBlock, isSymBlock, isSymSeqBlock, isSymMPIBlock, isMatIS;
2722   MatType                mtype;
2723   ISLocalToGlobalMapping ltog;
2724 
2725   PetscFunctionBegin;
2726   PetscCall(MatInitializePackage());
2727   mtype = dm->mattype;
2728   PetscCall(DMGetLocalSection(dm, &sectionLocal));
2729   PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
2730   /* PetscCall(PetscSectionGetStorageSize(sectionGlobal, &localSize)); */
2731   PetscCall(PetscSectionGetConstrainedStorageSize(sectionGlobal, &localSize));
2732   PetscCallMPI(MPI_Exscan(&localSize, &localStart, 1, MPIU_INT, MPI_SUM, PetscObjectComm((PetscObject)dm)));
2733   PetscCall(MatCreate(PetscObjectComm((PetscObject)dm), J));
2734   PetscCall(MatSetSizes(*J, localSize, localSize, PETSC_DETERMINE, PETSC_DETERMINE));
2735   PetscCall(MatSetType(*J, mtype));
2736   PetscCall(MatSetFromOptions(*J));
2737   PetscCall(MatGetBlockSize(*J, &mbs));
2738   if (mbs > 1) bs = mbs;
2739   PetscCall(PetscStrcmp(mtype, MATSHELL, &isShell));
2740   PetscCall(PetscStrcmp(mtype, MATBAIJ, &isBlock));
2741   PetscCall(PetscStrcmp(mtype, MATSEQBAIJ, &isSeqBlock));
2742   PetscCall(PetscStrcmp(mtype, MATMPIBAIJ, &isMPIBlock));
2743   PetscCall(PetscStrcmp(mtype, MATSBAIJ, &isSymBlock));
2744   PetscCall(PetscStrcmp(mtype, MATSEQSBAIJ, &isSymSeqBlock));
2745   PetscCall(PetscStrcmp(mtype, MATMPISBAIJ, &isSymMPIBlock));
2746   PetscCall(PetscStrcmp(mtype, MATIS, &isMatIS));
2747   if (!isShell) {
2748     // There are three states with pblocks, since block starts can have no dofs:
2749     // UNKNOWN) New Block:   An open block has been signalled by pblocks[p] == 1
2750     // TRUE)    Block Start: The first entry in a block has been added
2751     // FALSE)   Block Add:   An additional block entry has been added, since pblocks[p] == 0
2752     PetscBT         blst;
2753     PetscBool3      bstate     = PETSC_BOOL3_UNKNOWN;
2754     PetscBool       fillMatrix = (PetscBool)(!dm->prealloc_only && !isMatIS);
2755     const PetscInt *perm       = NULL;
2756     PetscInt       *dnz, *onz, *dnzu, *onzu, bsLocal[2], bsMinMax[2], *pblocks;
2757     PetscInt        pStart, pEnd, dof, cdof, num_fields;
2758 
2759     PetscCall(DMGetLocalToGlobalMapping(dm, &ltog));
2760     PetscCall(PetscSectionGetBlockStarts(sectionLocal, &blst));
2761     if (sectionLocal->perm) PetscCall(ISGetIndices(sectionLocal->perm, &perm));
2762 
2763     PetscCall(PetscCalloc1(localSize, &pblocks));
2764     PetscCall(PetscSectionGetChart(sectionGlobal, &pStart, &pEnd));
2765     PetscCall(PetscSectionGetNumFields(sectionGlobal, &num_fields));
2766     // We need to process in the permuted order to get block sizes right
2767     for (PetscInt point = pStart; point < pEnd; ++point) {
2768       const PetscInt p = perm ? perm[point] : point;
2769 
2770       switch (dm->blocking_type) {
2771       case DM_BLOCKING_TOPOLOGICAL_POINT: { // One block per topological point
2772         PetscInt bdof, offset;
2773 
2774         PetscCall(PetscSectionGetDof(sectionGlobal, p, &dof));
2775         PetscCall(PetscSectionGetOffset(sectionGlobal, p, &offset));
2776         PetscCall(PetscSectionGetConstraintDof(sectionGlobal, p, &cdof));
2777         if (blst && PetscBTLookup(blst, p)) bstate = PETSC_BOOL3_UNKNOWN;
2778         if (dof > 0) {
2779           // State change
2780           if (bstate == PETSC_BOOL3_UNKNOWN) bstate = PETSC_BOOL3_TRUE;
2781           else if (bstate == PETSC_BOOL3_TRUE && blst && !PetscBTLookup(blst, p)) bstate = PETSC_BOOL3_FALSE;
2782 
2783           for (PetscInt i = 0; i < dof - cdof; ++i) pblocks[offset - localStart + i] = dof - cdof;
2784           // Signal block concatenation
2785           if (bstate == PETSC_BOOL3_FALSE && dof - cdof) pblocks[offset - localStart] = -(dof - cdof);
2786         }
2787         dof  = dof < 0 ? -(dof + 1) : dof;
2788         bdof = cdof && (dof - cdof) ? 1 : dof;
2789         if (dof) {
2790           if (bs < 0) {
2791             bs = bdof;
2792           } else if (bs != bdof) {
2793             bs = 1;
2794           }
2795         }
2796       } break;
2797       case DM_BLOCKING_FIELD_NODE: {
2798         for (PetscInt field = 0; field < num_fields; field++) {
2799           PetscInt num_comp, bdof, offset;
2800           PetscCall(PetscSectionGetFieldComponents(sectionGlobal, field, &num_comp));
2801           PetscCall(PetscSectionGetFieldDof(sectionGlobal, p, field, &dof));
2802           if (dof < 0) continue;
2803           PetscCall(PetscSectionGetFieldOffset(sectionGlobal, p, field, &offset));
2804           PetscCall(PetscSectionGetFieldConstraintDof(sectionGlobal, p, field, &cdof));
2805           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);
2806           PetscInt num_nodes = dof / num_comp;
2807           for (PetscInt i = 0; i < dof - cdof; i++) pblocks[offset - localStart + i] = (dof - cdof) / num_nodes;
2808           // Handle possibly constant block size (unlikely)
2809           bdof = cdof && (dof - cdof) ? 1 : dof;
2810           if (dof) {
2811             if (bs < 0) {
2812               bs = bdof;
2813             } else if (bs != bdof) {
2814               bs = 1;
2815             }
2816           }
2817         }
2818       } break;
2819       }
2820     }
2821     if (sectionLocal->perm) PetscCall(ISRestoreIndices(sectionLocal->perm, &perm));
2822     /* Must have same blocksize on all procs (some might have no points) */
2823     bsLocal[0] = bs < 0 ? PETSC_INT_MAX : bs;
2824     bsLocal[1] = bs;
2825     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
2826     if (bsMinMax[0] != bsMinMax[1]) bs = 1;
2827     else bs = bsMinMax[0];
2828     bs = PetscMax(1, bs);
2829     PetscCall(MatSetLocalToGlobalMapping(*J, ltog, ltog));
2830     if (dm->prealloc_skip) { // User will likely use MatSetPreallocationCOO(), but still set structural parameters
2831       PetscCall(MatSetBlockSize(*J, bs));
2832       PetscCall(MatSetUp(*J));
2833     } else {
2834       PetscCall(PetscCalloc4(localSize / bs, &dnz, localSize / bs, &onz, localSize / bs, &dnzu, localSize / bs, &onzu));
2835       PetscCall(DMPlexPreallocateOperator(dm, bs, dnz, onz, dnzu, onzu, *J, fillMatrix));
2836       PetscCall(PetscFree4(dnz, onz, dnzu, onzu));
2837     }
2838     if (pblocks) { // Consolidate blocks
2839       PetscInt nblocks = 0;
2840       pblocks[0]       = PetscAbs(pblocks[0]);
2841       for (PetscInt i = 0; i < localSize; i += PetscMax(1, pblocks[i])) {
2842         if (pblocks[i] == 0) continue;
2843         // Negative block size indicates the blocks should be concatenated
2844         if (pblocks[i] < 0) {
2845           pblocks[i] = -pblocks[i];
2846           pblocks[nblocks - 1] += pblocks[i];
2847         } else {
2848           pblocks[nblocks++] = pblocks[i]; // nblocks always <= i
2849         }
2850         for (PetscInt j = 1; j < pblocks[i]; j++)
2851           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);
2852       }
2853       PetscCall(MatSetVariableBlockSizes(*J, nblocks, pblocks));
2854     }
2855     PetscCall(PetscFree(pblocks));
2856   }
2857   PetscCall(MatSetDM(*J, dm));
2858   PetscFunctionReturn(PETSC_SUCCESS);
2859 }
2860 
2861 /*@
2862   DMPlexGetSubdomainSection - Returns the section associated with the subdomain
2863 
2864   Not Collective
2865 
2866   Input Parameter:
2867 . dm - The `DMPLEX`
2868 
2869   Output Parameter:
2870 . subsection - The subdomain section
2871 
2872   Level: developer
2873 
2874 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `PetscSection`
2875 @*/
2876 PetscErrorCode DMPlexGetSubdomainSection(DM dm, PetscSection *subsection)
2877 {
2878   DM_Plex *mesh = (DM_Plex *)dm->data;
2879 
2880   PetscFunctionBegin;
2881   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2882   if (!mesh->subdomainSection) {
2883     PetscSection section;
2884     PetscSF      sf;
2885 
2886     PetscCall(PetscSFCreate(PETSC_COMM_SELF, &sf));
2887     PetscCall(DMGetLocalSection(dm, &section));
2888     PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_TRUE, PETSC_FALSE, PETSC_TRUE, &mesh->subdomainSection));
2889     PetscCall(PetscSFDestroy(&sf));
2890   }
2891   *subsection = mesh->subdomainSection;
2892   PetscFunctionReturn(PETSC_SUCCESS);
2893 }
2894 
2895 /*@
2896   DMPlexGetChart - Return the interval for all mesh points [`pStart`, `pEnd`)
2897 
2898   Not Collective
2899 
2900   Input Parameter:
2901 . dm - The `DMPLEX`
2902 
2903   Output Parameters:
2904 + pStart - The first mesh point
2905 - pEnd   - The upper bound for mesh points
2906 
2907   Level: beginner
2908 
2909 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetChart()`
2910 @*/
2911 PetscErrorCode DMPlexGetChart(DM dm, PetscInt *pStart, PetscInt *pEnd)
2912 {
2913   DM_Plex *mesh = (DM_Plex *)dm->data;
2914 
2915   PetscFunctionBegin;
2916   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2917   if (mesh->tr) PetscCall(DMPlexTransformGetChart(mesh->tr, pStart, pEnd));
2918   else PetscCall(PetscSectionGetChart(mesh->coneSection, pStart, pEnd));
2919   PetscFunctionReturn(PETSC_SUCCESS);
2920 }
2921 
2922 /*@
2923   DMPlexSetChart - Set the interval for all mesh points [`pStart`, `pEnd`)
2924 
2925   Not Collective
2926 
2927   Input Parameters:
2928 + dm     - The `DMPLEX`
2929 . pStart - The first mesh point
2930 - pEnd   - The upper bound for mesh points
2931 
2932   Level: beginner
2933 
2934 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetChart()`
2935 @*/
2936 PetscErrorCode DMPlexSetChart(DM dm, PetscInt pStart, PetscInt pEnd)
2937 {
2938   DM_Plex *mesh = (DM_Plex *)dm->data;
2939 
2940   PetscFunctionBegin;
2941   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2942   PetscCall(PetscSectionSetChart(mesh->coneSection, pStart, pEnd));
2943   PetscCall(PetscSectionSetChart(mesh->supportSection, pStart, pEnd));
2944   PetscCall(PetscFree(mesh->cellTypes));
2945   PetscFunctionReturn(PETSC_SUCCESS);
2946 }
2947 
2948 /*@
2949   DMPlexGetConeSize - Return the number of in-edges for this point in the DAG
2950 
2951   Not Collective
2952 
2953   Input Parameters:
2954 + dm - The `DMPLEX`
2955 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
2956 
2957   Output Parameter:
2958 . size - The cone size for point `p`
2959 
2960   Level: beginner
2961 
2962 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
2963 @*/
2964 PetscErrorCode DMPlexGetConeSize(DM dm, PetscInt p, PetscInt *size)
2965 {
2966   DM_Plex *mesh = (DM_Plex *)dm->data;
2967 
2968   PetscFunctionBegin;
2969   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2970   PetscAssertPointer(size, 3);
2971   if (mesh->tr) PetscCall(DMPlexTransformGetConeSize(mesh->tr, p, size));
2972   else PetscCall(PetscSectionGetDof(mesh->coneSection, p, size));
2973   PetscFunctionReturn(PETSC_SUCCESS);
2974 }
2975 
2976 /*@
2977   DMPlexSetConeSize - Set the number of in-edges for this point in the DAG
2978 
2979   Not Collective
2980 
2981   Input Parameters:
2982 + dm   - The `DMPLEX`
2983 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
2984 - size - The cone size for point `p`
2985 
2986   Level: beginner
2987 
2988   Note:
2989   This should be called after `DMPlexSetChart()`.
2990 
2991 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetCone()`, `DMPlexCreate()`, `DMPlexGetConeSize()`, `DMPlexSetChart()`
2992 @*/
2993 PetscErrorCode DMPlexSetConeSize(DM dm, PetscInt p, PetscInt size)
2994 {
2995   DM_Plex *mesh = (DM_Plex *)dm->data;
2996 
2997   PetscFunctionBegin;
2998   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2999   PetscCheck(!mesh->tr, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Cannot call DMPlexSetConeSize() on a mesh with a transform defined.");
3000   PetscCall(PetscSectionSetDof(mesh->coneSection, p, size));
3001   PetscFunctionReturn(PETSC_SUCCESS);
3002 }
3003 
3004 /*@C
3005   DMPlexGetCone - Return the points on the in-edges for this point in the DAG
3006 
3007   Not Collective
3008 
3009   Input Parameters:
3010 + dm - The `DMPLEX`
3011 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3012 
3013   Output Parameter:
3014 . cone - An array of points which are on the in-edges for point `p`, the length of `cone` is the result of `DMPlexGetConeSize()`
3015 
3016   Level: beginner
3017 
3018   Fortran Notes:
3019   `cone` must be declared with
3020 .vb
3021   PetscInt, pointer :: cone(:)
3022 .ve
3023 
3024   You must also call `DMPlexRestoreCone()` after you finish using the array.
3025   `DMPlexRestoreCone()` is not needed/available in C.
3026 
3027 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSize()`, `DMPlexSetCone()`, `DMPlexGetConeTuple()`, `DMPlexSetChart()`, `DMPlexRestoreCone()`
3028 @*/
3029 PetscErrorCode DMPlexGetCone(DM dm, PetscInt p, const PetscInt *cone[])
3030 {
3031   DM_Plex *mesh = (DM_Plex *)dm->data;
3032   PetscInt off;
3033 
3034   PetscFunctionBegin;
3035   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3036   PetscAssertPointer(cone, 3);
3037   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3038   *cone = PetscSafePointerPlusOffset(mesh->cones, off);
3039   PetscFunctionReturn(PETSC_SUCCESS);
3040 }
3041 
3042 /*@
3043   DMPlexGetConeTuple - Return the points on the in-edges of several points in the DAG
3044 
3045   Not Collective
3046 
3047   Input Parameters:
3048 + dm - The `DMPLEX`
3049 - p  - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3050 
3051   Output Parameters:
3052 + pConesSection - `PetscSection` describing the layout of `pCones`
3053 - pCones        - An `IS` containing the points which are on the in-edges for the point set `p`
3054 
3055   Level: intermediate
3056 
3057 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeRecursive()`, `DMPlexSetChart()`, `PetscSection`, `IS`
3058 @*/
3059 PetscErrorCode DMPlexGetConeTuple(DM dm, IS p, PetscSection *pConesSection, IS *pCones)
3060 {
3061   PetscSection cs, newcs;
3062   PetscInt    *cones;
3063   PetscInt    *newarr = NULL;
3064   PetscInt     n;
3065 
3066   PetscFunctionBegin;
3067   PetscCall(DMPlexGetCones(dm, &cones));
3068   PetscCall(DMPlexGetConeSection(dm, &cs));
3069   PetscCall(PetscSectionExtractDofsFromArray(cs, MPIU_INT, cones, p, &newcs, pCones ? ((void **)&newarr) : NULL));
3070   if (pConesSection) *pConesSection = newcs;
3071   if (pCones) {
3072     PetscCall(PetscSectionGetStorageSize(newcs, &n));
3073     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)p), n, newarr, PETSC_OWN_POINTER, pCones));
3074   }
3075   PetscFunctionReturn(PETSC_SUCCESS);
3076 }
3077 
3078 /*@
3079   DMPlexGetConeRecursiveVertices - Expand each given point into its cone points and do that recursively until we end up just with vertices.
3080 
3081   Not Collective
3082 
3083   Input Parameters:
3084 + dm     - The `DMPLEX`
3085 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3086 
3087   Output Parameter:
3088 . expandedPoints - An `IS` containing the of vertices recursively expanded from input points
3089 
3090   Level: advanced
3091 
3092   Notes:
3093   Like `DMPlexGetConeRecursive()` but returns only the 0-depth `IS` (i.e. vertices only) and no sections.
3094 
3095   There is no corresponding Restore function, just call `ISDestroy()` on the returned `IS` to deallocate.
3096 
3097 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexRestoreConeRecursive()`,
3098           `DMPlexGetDepth()`, `IS`
3099 @*/
3100 PetscErrorCode DMPlexGetConeRecursiveVertices(DM dm, IS points, IS *expandedPoints)
3101 {
3102   IS      *expandedPointsAll;
3103   PetscInt depth;
3104 
3105   PetscFunctionBegin;
3106   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3107   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
3108   PetscAssertPointer(expandedPoints, 3);
3109   PetscCall(DMPlexGetConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
3110   *expandedPoints = expandedPointsAll[0];
3111   PetscCall(PetscObjectReference((PetscObject)expandedPointsAll[0]));
3112   PetscCall(DMPlexRestoreConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
3113   PetscFunctionReturn(PETSC_SUCCESS);
3114 }
3115 
3116 /*@
3117   DMPlexGetConeRecursive - Expand each given point into its cone points and do that recursively until we end up just with vertices
3118   (DAG points of depth 0, i.e., without cones).
3119 
3120   Not Collective
3121 
3122   Input Parameters:
3123 + dm     - The `DMPLEX`
3124 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3125 
3126   Output Parameters:
3127 + depth          - (optional) Size of the output arrays, equal to `DMPLEX` depth, returned by `DMPlexGetDepth()`
3128 . expandedPoints - (optional) An array of index sets with recursively expanded cones
3129 - sections       - (optional) An array of sections which describe mappings from points to their cone points
3130 
3131   Level: advanced
3132 
3133   Notes:
3134   Like `DMPlexGetConeTuple()` but recursive.
3135 
3136   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.
3137   For example, for d=0 it contains only vertices, for d=1 it can contain vertices and edges, etc.
3138 
3139   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\:
3140   (1) DAG points in `expandedPoints`[d+1] with `depth` d+1 to their cone points in `expandedPoints`[d];
3141   (2) DAG points in `expandedPoints`[d+1] with `depth` in [0,d] to the same points in `expandedPoints`[d].
3142 
3143 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexRestoreConeRecursive()`, `DMPlexGetConeRecursiveVertices()`,
3144           `DMPlexGetDepth()`, `PetscSection`, `IS`
3145 @*/
3146 PetscErrorCode DMPlexGetConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
3147 {
3148   const PetscInt *arr0 = NULL, *cone = NULL;
3149   PetscInt       *arr = NULL, *newarr = NULL;
3150   PetscInt        d, depth_, i, n, newn, cn, co, start, end;
3151   IS             *expandedPoints_;
3152   PetscSection   *sections_;
3153 
3154   PetscFunctionBegin;
3155   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3156   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
3157   if (depth) PetscAssertPointer(depth, 3);
3158   if (expandedPoints) PetscAssertPointer(expandedPoints, 4);
3159   if (sections) PetscAssertPointer(sections, 5);
3160   PetscCall(ISGetLocalSize(points, &n));
3161   PetscCall(ISGetIndices(points, &arr0));
3162   PetscCall(DMPlexGetDepth(dm, &depth_));
3163   PetscCall(PetscCalloc1(depth_, &expandedPoints_));
3164   PetscCall(PetscCalloc1(depth_, &sections_));
3165   arr = (PetscInt *)arr0; /* this is ok because first generation of arr is not modified */
3166   for (d = depth_ - 1; d >= 0; d--) {
3167     PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &sections_[d]));
3168     PetscCall(PetscSectionSetChart(sections_[d], 0, n));
3169     for (i = 0; i < n; i++) {
3170       PetscCall(DMPlexGetDepthStratum(dm, d + 1, &start, &end));
3171       if (arr[i] >= start && arr[i] < end) {
3172         PetscCall(DMPlexGetConeSize(dm, arr[i], &cn));
3173         PetscCall(PetscSectionSetDof(sections_[d], i, cn));
3174       } else {
3175         PetscCall(PetscSectionSetDof(sections_[d], i, 1));
3176       }
3177     }
3178     PetscCall(PetscSectionSetUp(sections_[d]));
3179     PetscCall(PetscSectionGetStorageSize(sections_[d], &newn));
3180     PetscCall(PetscMalloc1(newn, &newarr));
3181     for (i = 0; i < n; i++) {
3182       PetscCall(PetscSectionGetDof(sections_[d], i, &cn));
3183       PetscCall(PetscSectionGetOffset(sections_[d], i, &co));
3184       if (cn > 1) {
3185         PetscCall(DMPlexGetCone(dm, arr[i], &cone));
3186         PetscCall(PetscMemcpy(&newarr[co], cone, cn * sizeof(PetscInt)));
3187       } else {
3188         newarr[co] = arr[i];
3189       }
3190     }
3191     PetscCall(ISCreateGeneral(PETSC_COMM_SELF, newn, newarr, PETSC_OWN_POINTER, &expandedPoints_[d]));
3192     arr = newarr;
3193     n   = newn;
3194   }
3195   PetscCall(ISRestoreIndices(points, &arr0));
3196   *depth = depth_;
3197   if (expandedPoints) *expandedPoints = expandedPoints_;
3198   else {
3199     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&expandedPoints_[d]));
3200     PetscCall(PetscFree(expandedPoints_));
3201   }
3202   if (sections) *sections = sections_;
3203   else {
3204     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&sections_[d]));
3205     PetscCall(PetscFree(sections_));
3206   }
3207   PetscFunctionReturn(PETSC_SUCCESS);
3208 }
3209 
3210 /*@
3211   DMPlexRestoreConeRecursive - Deallocates arrays created by `DMPlexGetConeRecursive()`
3212 
3213   Not Collective
3214 
3215   Input Parameters:
3216 + dm     - The `DMPLEX`
3217 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3218 
3219   Output Parameters:
3220 + depth          - (optional) Size of the output arrays, equal to `DMPLEX` depth, returned by `DMPlexGetDepth()`
3221 . expandedPoints - (optional) An array of recursively expanded cones
3222 - sections       - (optional) An array of sections which describe mappings from points to their cone points
3223 
3224   Level: advanced
3225 
3226   Note:
3227   See `DMPlexGetConeRecursive()`
3228 
3229 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexGetConeRecursiveVertices()`,
3230           `DMPlexGetDepth()`, `IS`, `PetscSection`
3231 @*/
3232 PetscErrorCode DMPlexRestoreConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
3233 {
3234   PetscInt d, depth_;
3235 
3236   PetscFunctionBegin;
3237   PetscCall(DMPlexGetDepth(dm, &depth_));
3238   PetscCheck(!depth || *depth == depth_, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "depth changed since last call to DMPlexGetConeRecursive");
3239   if (depth) *depth = 0;
3240   if (expandedPoints) {
3241     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&((*expandedPoints)[d])));
3242     PetscCall(PetscFree(*expandedPoints));
3243   }
3244   if (sections) {
3245     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&((*sections)[d])));
3246     PetscCall(PetscFree(*sections));
3247   }
3248   PetscFunctionReturn(PETSC_SUCCESS);
3249 }
3250 
3251 /*@
3252   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
3253 
3254   Not Collective
3255 
3256   Input Parameters:
3257 + dm   - The `DMPLEX`
3258 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3259 - cone - An array of points which are on the in-edges for point `p`, its length must have been previously provided with `DMPlexSetConeSize()`
3260 
3261   Level: beginner
3262 
3263   Note:
3264   This should be called after all calls to `DMPlexSetConeSize()` and `DMSetUp()`.
3265 
3266 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`, `DMPlexSetSupport()`, `DMPlexSetSupportSize()`
3267 @*/
3268 PetscErrorCode DMPlexSetCone(DM dm, PetscInt p, const PetscInt cone[])
3269 {
3270   DM_Plex *mesh = (DM_Plex *)dm->data;
3271   PetscInt dof, off, c;
3272 
3273   PetscFunctionBegin;
3274   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3275   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3276   if (dof) PetscAssertPointer(cone, 3);
3277   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3278   if (PetscDefined(USE_DEBUG)) {
3279     PetscInt pStart, pEnd;
3280     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3281     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);
3282     for (c = 0; c < dof; ++c) {
3283       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);
3284       mesh->cones[off + c] = cone[c];
3285     }
3286   } else {
3287     for (c = 0; c < dof; ++c) mesh->cones[off + c] = cone[c];
3288   }
3289   PetscFunctionReturn(PETSC_SUCCESS);
3290 }
3291 
3292 /*@C
3293   DMPlexGetConeOrientation - Return the orientations on the in-edges for this point in the DAG
3294 
3295   Not Collective
3296 
3297   Input Parameters:
3298 + dm - The `DMPLEX`
3299 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3300 
3301   Output Parameter:
3302 . coneOrientation - An array of orientations which are on the in-edges for point `p`. An orientation is an
3303                     integer giving the prescription for cone traversal. Its length is given by the result of `DMPlexSetConeSize()`
3304 
3305   Level: beginner
3306 
3307   Note:
3308   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3309   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3310   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3311   with the identity.
3312 
3313   Fortran Notes:
3314   You must call `DMPlexRestoreConeOrientation()` after you finish using the returned array.
3315   `DMPlexRestoreConeOrientation()` is not needed/available in C.
3316 
3317 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetConeSize()`, `DMPolytopeTypeComposeOrientation()`, `DMPolytopeTypeComposeOrientationInv()`,
3318           `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetCone()`, `DMPlexSetChart()`
3319 @*/
3320 PetscErrorCode DMPlexGetConeOrientation(DM dm, PetscInt p, const PetscInt *coneOrientation[])
3321 {
3322   DM_Plex *mesh = (DM_Plex *)dm->data;
3323   PetscInt off;
3324 
3325   PetscFunctionBegin;
3326   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3327   if (PetscDefined(USE_DEBUG)) {
3328     PetscInt dof;
3329     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3330     if (dof) PetscAssertPointer(coneOrientation, 3);
3331   }
3332   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3333 
3334   *coneOrientation = &mesh->coneOrientations[off];
3335   PetscFunctionReturn(PETSC_SUCCESS);
3336 }
3337 
3338 /*@
3339   DMPlexSetConeOrientation - Set the orientations on the in-edges for this point in the DAG
3340 
3341   Not Collective
3342 
3343   Input Parameters:
3344 + dm              - The `DMPLEX`
3345 . p               - The point, which must lie in the chart set with `DMPlexSetChart()`
3346 - coneOrientation - An array of orientations. Its length is given by the result of `DMPlexSetConeSize()`
3347 
3348   Level: beginner
3349 
3350   Notes:
3351   This should be called after all calls to `DMPlexSetConeSize()` and `DMSetUp()`.
3352 
3353   The meaning of coneOrientation is detailed in `DMPlexGetConeOrientation()`.
3354 
3355 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetConeOrientation()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3356 @*/
3357 PetscErrorCode DMPlexSetConeOrientation(DM dm, PetscInt p, const PetscInt coneOrientation[])
3358 {
3359   DM_Plex *mesh = (DM_Plex *)dm->data;
3360   PetscInt pStart, pEnd;
3361   PetscInt dof, off, c;
3362 
3363   PetscFunctionBegin;
3364   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3365   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3366   if (dof) PetscAssertPointer(coneOrientation, 3);
3367   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3368   if (PetscDefined(USE_DEBUG)) {
3369     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3370     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);
3371     for (c = 0; c < dof; ++c) {
3372       PetscInt cdof, o = coneOrientation[c];
3373 
3374       PetscCall(PetscSectionGetDof(mesh->coneSection, mesh->cones[off + c], &cdof));
3375       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);
3376       mesh->coneOrientations[off + c] = o;
3377     }
3378   } else {
3379     for (c = 0; c < dof; ++c) mesh->coneOrientations[off + c] = coneOrientation[c];
3380   }
3381   PetscFunctionReturn(PETSC_SUCCESS);
3382 }
3383 
3384 /*@
3385   DMPlexInsertCone - Insert a point into the in-edges for the point p in the DAG
3386 
3387   Not Collective
3388 
3389   Input Parameters:
3390 + dm        - The `DMPLEX`
3391 . p         - The point, which must lie in the chart set with `DMPlexSetChart()`
3392 . conePos   - The local index in the cone where the point should be put
3393 - conePoint - The mesh point to insert
3394 
3395   Level: beginner
3396 
3397 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3398 @*/
3399 PetscErrorCode DMPlexInsertCone(DM dm, PetscInt p, PetscInt conePos, PetscInt conePoint)
3400 {
3401   DM_Plex *mesh = (DM_Plex *)dm->data;
3402   PetscInt pStart, pEnd;
3403   PetscInt dof, off;
3404 
3405   PetscFunctionBegin;
3406   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3407   if (PetscDefined(USE_DEBUG)) {
3408     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3409     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);
3410     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);
3411     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3412     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);
3413   }
3414   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3415   mesh->cones[off + conePos] = conePoint;
3416   PetscFunctionReturn(PETSC_SUCCESS);
3417 }
3418 
3419 /*@
3420   DMPlexInsertConeOrientation - Insert a point orientation for the in-edge for the point p in the DAG
3421 
3422   Not Collective
3423 
3424   Input Parameters:
3425 + dm              - The `DMPLEX`
3426 . p               - The point, which must lie in the chart set with `DMPlexSetChart()`
3427 . conePos         - The local index in the cone where the point should be put
3428 - coneOrientation - The point orientation to insert
3429 
3430   Level: beginner
3431 
3432   Note:
3433   The meaning of coneOrientation values is detailed in `DMPlexGetConeOrientation()`.
3434 
3435 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3436 @*/
3437 PetscErrorCode DMPlexInsertConeOrientation(DM dm, PetscInt p, PetscInt conePos, PetscInt coneOrientation)
3438 {
3439   DM_Plex *mesh = (DM_Plex *)dm->data;
3440   PetscInt pStart, pEnd;
3441   PetscInt dof, off;
3442 
3443   PetscFunctionBegin;
3444   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3445   if (PetscDefined(USE_DEBUG)) {
3446     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3447     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);
3448     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3449     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);
3450   }
3451   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3452   mesh->coneOrientations[off + conePos] = coneOrientation;
3453   PetscFunctionReturn(PETSC_SUCCESS);
3454 }
3455 
3456 /*@C
3457   DMPlexGetOrientedCone - Return the points and orientations on the in-edges for this point in the DAG
3458 
3459   Not collective
3460 
3461   Input Parameters:
3462 + dm - The DMPlex
3463 - p  - The point, which must lie in the chart set with DMPlexSetChart()
3464 
3465   Output Parameters:
3466 + cone - An array of points which are on the in-edges for point `p`
3467 - ornt - An array of orientations which are on the in-edges for point `p`. An orientation is an
3468          integer giving the prescription for cone traversal.
3469 
3470   Level: beginner
3471 
3472   Notes:
3473   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3474   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3475   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3476   with the identity.
3477 
3478   You must also call `DMPlexRestoreOrientedCone()` after you finish using the returned array.
3479 
3480   Fortran Notes:
3481   `cone` and `ornt` must be declared with
3482 .vb
3483   PetscInt, pointer :: cone(:)
3484   PetscInt, pointer :: ornt(:)
3485 .ve
3486 
3487 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreOrientedCone()`, `DMPlexGetConeSize()`, `DMPlexGetCone()`, `DMPlexGetChart()`
3488 @*/
3489 PetscErrorCode DMPlexGetOrientedCone(DM dm, PetscInt p, const PetscInt *cone[], const PetscInt *ornt[])
3490 {
3491   DM_Plex *mesh = (DM_Plex *)dm->data;
3492 
3493   PetscFunctionBegin;
3494   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3495   if (mesh->tr) {
3496     PetscCall(DMPlexTransformGetCone(mesh->tr, p, cone, ornt));
3497   } else {
3498     PetscInt off;
3499     if (PetscDefined(USE_DEBUG)) {
3500       PetscInt dof;
3501       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3502       if (dof) {
3503         if (cone) PetscAssertPointer(cone, 3);
3504         if (ornt) PetscAssertPointer(ornt, 4);
3505       }
3506     }
3507     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3508     if (cone) *cone = PetscSafePointerPlusOffset(mesh->cones, off);
3509     if (ornt) *ornt = PetscSafePointerPlusOffset(mesh->coneOrientations, off);
3510   }
3511   PetscFunctionReturn(PETSC_SUCCESS);
3512 }
3513 
3514 /*@C
3515   DMPlexRestoreOrientedCone - Restore the points and orientations on the in-edges for this point in the DAG obtained with `DMPlexGetOrientedCone()`
3516 
3517   Not Collective
3518 
3519   Input Parameters:
3520 + dm   - The DMPlex
3521 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3522 . cone - An array of points which are on the in-edges for point p
3523 - ornt - An array of orientations which are on the in-edges for point `p`. An orientation is an
3524          integer giving the prescription for cone traversal.
3525 
3526   Level: beginner
3527 
3528 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetOrientedCone()`, `DMPlexGetConeSize()`, `DMPlexGetCone()`, `DMPlexGetChart()`
3529 @*/
3530 PetscErrorCode DMPlexRestoreOrientedCone(DM dm, PetscInt p, const PetscInt *cone[], const PetscInt *ornt[])
3531 {
3532   DM_Plex *mesh = (DM_Plex *)dm->data;
3533 
3534   PetscFunctionBegin;
3535   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3536   if (mesh->tr) PetscCall(DMPlexTransformRestoreCone(mesh->tr, p, cone, ornt));
3537   PetscFunctionReturn(PETSC_SUCCESS);
3538 }
3539 
3540 /*@
3541   DMPlexGetSupportSize - Return the number of out-edges for this point in the DAG
3542 
3543   Not Collective
3544 
3545   Input Parameters:
3546 + dm - The `DMPLEX`
3547 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3548 
3549   Output Parameter:
3550 . size - The support size for point `p`
3551 
3552   Level: beginner
3553 
3554 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`, `DMPlexGetConeSize()`
3555 @*/
3556 PetscErrorCode DMPlexGetSupportSize(DM dm, PetscInt p, PetscInt *size)
3557 {
3558   DM_Plex *mesh = (DM_Plex *)dm->data;
3559 
3560   PetscFunctionBegin;
3561   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3562   PetscAssertPointer(size, 3);
3563   PetscCall(PetscSectionGetDof(mesh->supportSection, p, size));
3564   PetscFunctionReturn(PETSC_SUCCESS);
3565 }
3566 
3567 /*@
3568   DMPlexSetSupportSize - Set the number of out-edges for this point in the DAG
3569 
3570   Not Collective
3571 
3572   Input Parameters:
3573 + dm   - The `DMPLEX`
3574 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3575 - size - The support size for point `p`
3576 
3577   Level: beginner
3578 
3579   Note:
3580   This should be called after `DMPlexSetChart()`.
3581 
3582 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetSupportSize()`, `DMPlexSetChart()`
3583 @*/
3584 PetscErrorCode DMPlexSetSupportSize(DM dm, PetscInt p, PetscInt size)
3585 {
3586   DM_Plex *mesh = (DM_Plex *)dm->data;
3587 
3588   PetscFunctionBegin;
3589   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3590   PetscCall(PetscSectionSetDof(mesh->supportSection, p, size));
3591   PetscFunctionReturn(PETSC_SUCCESS);
3592 }
3593 
3594 /*@C
3595   DMPlexGetSupport - Return the points on the out-edges for this point in the DAG
3596 
3597   Not Collective
3598 
3599   Input Parameters:
3600 + dm - The `DMPLEX`
3601 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3602 
3603   Output Parameter:
3604 . support - An array of points which are on the out-edges for point `p`, its length is that obtained from `DMPlexGetSupportSize()`
3605 
3606   Level: beginner
3607 
3608   Fortran Notes:
3609   `support` must be declared with
3610 .vb
3611   PetscInt, pointer :: support(:)
3612 .ve
3613 
3614   You must also call `DMPlexRestoreSupport()` after you finish using the returned array.
3615   `DMPlexRestoreSupport()` is not needed/available in C.
3616 
3617 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSupportSize()`, `DMPlexSetSupport()`, `DMPlexGetCone()`, `DMPlexSetChart()`
3618 @*/
3619 PetscErrorCode DMPlexGetSupport(DM dm, PetscInt p, const PetscInt *support[])
3620 {
3621   DM_Plex *mesh = (DM_Plex *)dm->data;
3622   PetscInt off;
3623 
3624   PetscFunctionBegin;
3625   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3626   PetscAssertPointer(support, 3);
3627   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3628   *support = PetscSafePointerPlusOffset(mesh->supports, off);
3629   PetscFunctionReturn(PETSC_SUCCESS);
3630 }
3631 
3632 /*@
3633   DMPlexSetSupport - Set the points on the out-edges for this point in the DAG, that is the list of points that this point covers
3634 
3635   Not Collective
3636 
3637   Input Parameters:
3638 + dm      - The `DMPLEX`
3639 . p       - The point, which must lie in the chart set with `DMPlexSetChart()`
3640 - support - An array of points which are on the out-edges for point `p`, its length is that obtained from `DMPlexGetSupportSize()`
3641 
3642   Level: beginner
3643 
3644   Note:
3645   This should be called after all calls to `DMPlexSetSupportSize()` and `DMSetUp()`.
3646 
3647 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetCone()`, `DMPlexSetConeSize()`, `DMPlexCreate()`, `DMPlexGetSupport()`, `DMPlexSetChart()`, `DMPlexSetSupportSize()`, `DMSetUp()`
3648 @*/
3649 PetscErrorCode DMPlexSetSupport(DM dm, PetscInt p, const PetscInt support[])
3650 {
3651   DM_Plex *mesh = (DM_Plex *)dm->data;
3652   PetscInt pStart, pEnd;
3653   PetscInt dof, off, c;
3654 
3655   PetscFunctionBegin;
3656   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3657   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3658   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3659   if (dof) PetscAssertPointer(support, 3);
3660   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3661   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);
3662   for (c = 0; c < dof; ++c) {
3663     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);
3664     mesh->supports[off + c] = support[c];
3665   }
3666   PetscFunctionReturn(PETSC_SUCCESS);
3667 }
3668 
3669 /*@
3670   DMPlexInsertSupport - Insert a point into the out-edges for the point p in the DAG
3671 
3672   Not Collective
3673 
3674   Input Parameters:
3675 + dm           - The `DMPLEX`
3676 . p            - The point, which must lie in the chart set with `DMPlexSetChart()`
3677 . supportPos   - The local index in the cone where the point should be put
3678 - supportPoint - The mesh point to insert
3679 
3680   Level: beginner
3681 
3682 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3683 @*/
3684 PetscErrorCode DMPlexInsertSupport(DM dm, PetscInt p, PetscInt supportPos, PetscInt supportPoint)
3685 {
3686   DM_Plex *mesh = (DM_Plex *)dm->data;
3687   PetscInt pStart, pEnd;
3688   PetscInt dof, off;
3689 
3690   PetscFunctionBegin;
3691   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3692   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3693   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3694   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3695   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);
3696   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);
3697   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);
3698   mesh->supports[off + supportPos] = supportPoint;
3699   PetscFunctionReturn(PETSC_SUCCESS);
3700 }
3701 
3702 /* Converts an orientation o in the current numbering to the previous scheme used in Plex */
3703 PetscInt DMPolytopeConvertNewOrientation_Internal(DMPolytopeType ct, PetscInt o)
3704 {
3705   switch (ct) {
3706   case DM_POLYTOPE_SEGMENT:
3707     if (o == -1) return -2;
3708     break;
3709   case DM_POLYTOPE_TRIANGLE:
3710     if (o == -3) return -1;
3711     if (o == -2) return -3;
3712     if (o == -1) return -2;
3713     break;
3714   case DM_POLYTOPE_QUADRILATERAL:
3715     if (o == -4) return -2;
3716     if (o == -3) return -1;
3717     if (o == -2) return -4;
3718     if (o == -1) return -3;
3719     break;
3720   default:
3721     return o;
3722   }
3723   return o;
3724 }
3725 
3726 /* Converts an orientation o in the previous scheme used in Plex to the current numbering */
3727 PetscInt DMPolytopeConvertOldOrientation_Internal(DMPolytopeType ct, PetscInt o)
3728 {
3729   switch (ct) {
3730   case DM_POLYTOPE_SEGMENT:
3731     if ((o == -2) || (o == 1)) return -1;
3732     if (o == -1) return 0;
3733     break;
3734   case DM_POLYTOPE_TRIANGLE:
3735     if (o == -3) return -2;
3736     if (o == -2) return -1;
3737     if (o == -1) return -3;
3738     break;
3739   case DM_POLYTOPE_QUADRILATERAL:
3740     if (o == -4) return -2;
3741     if (o == -3) return -1;
3742     if (o == -2) return -4;
3743     if (o == -1) return -3;
3744     break;
3745   default:
3746     return o;
3747   }
3748   return o;
3749 }
3750 
3751 /* Takes in a mesh whose orientations are in the previous scheme and converts them all to the current numbering */
3752 PetscErrorCode DMPlexConvertOldOrientations_Internal(DM dm)
3753 {
3754   PetscInt pStart, pEnd, p;
3755 
3756   PetscFunctionBegin;
3757   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3758   for (p = pStart; p < pEnd; ++p) {
3759     const PetscInt *cone, *ornt;
3760     PetscInt        coneSize, c;
3761 
3762     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
3763     PetscCall(DMPlexGetCone(dm, p, &cone));
3764     PetscCall(DMPlexGetConeOrientation(dm, p, &ornt));
3765     for (c = 0; c < coneSize; ++c) {
3766       DMPolytopeType ct;
3767       const PetscInt o = ornt[c];
3768 
3769       PetscCall(DMPlexGetCellType(dm, cone[c], &ct));
3770       switch (ct) {
3771       case DM_POLYTOPE_SEGMENT:
3772         if ((o == -2) || (o == 1)) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3773         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, 0));
3774         break;
3775       case DM_POLYTOPE_TRIANGLE:
3776         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3777         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3778         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3779         break;
3780       case DM_POLYTOPE_QUADRILATERAL:
3781         if (o == -4) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3782         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3783         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -4));
3784         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3785         break;
3786       default:
3787         break;
3788       }
3789     }
3790   }
3791   PetscFunctionReturn(PETSC_SUCCESS);
3792 }
3793 
3794 static inline PetscErrorCode DMPlexGetTransitiveClosure_Hot_Private(DM dm, PetscInt p, PetscBool useCone, PetscInt *size, const PetscInt *arr[], const PetscInt *ornt[])
3795 {
3796   DM_Plex *mesh = (DM_Plex *)dm->data;
3797 
3798   PetscFunctionBeginHot;
3799   if (PetscDefined(USE_DEBUG) || mesh->tr) {
3800     if (useCone) {
3801       PetscCall(DMPlexGetConeSize(dm, p, size));
3802       PetscCall(DMPlexGetOrientedCone(dm, p, arr, ornt));
3803     } else {
3804       PetscCall(DMPlexGetSupportSize(dm, p, size));
3805       PetscCall(DMPlexGetSupport(dm, p, arr));
3806     }
3807   } else {
3808     if (useCone) {
3809       const PetscSection s   = mesh->coneSection;
3810       const PetscInt     ps  = p - s->pStart;
3811       const PetscInt     off = s->atlasOff[ps];
3812 
3813       *size = s->atlasDof[ps];
3814       *arr  = mesh->cones + off;
3815       *ornt = mesh->coneOrientations + off;
3816     } else {
3817       const PetscSection s   = mesh->supportSection;
3818       const PetscInt     ps  = p - s->pStart;
3819       const PetscInt     off = s->atlasOff[ps];
3820 
3821       *size = s->atlasDof[ps];
3822       *arr  = mesh->supports + off;
3823     }
3824   }
3825   PetscFunctionReturn(PETSC_SUCCESS);
3826 }
3827 
3828 static inline PetscErrorCode DMPlexRestoreTransitiveClosure_Hot_Private(DM dm, PetscInt p, PetscBool useCone, PetscInt *size, const PetscInt *arr[], const PetscInt *ornt[])
3829 {
3830   DM_Plex *mesh = (DM_Plex *)dm->data;
3831 
3832   PetscFunctionBeginHot;
3833   if (PetscDefined(USE_DEBUG) || mesh->tr) {
3834     if (useCone) PetscCall(DMPlexRestoreOrientedCone(dm, p, arr, ornt));
3835   }
3836   PetscFunctionReturn(PETSC_SUCCESS);
3837 }
3838 
3839 static PetscErrorCode DMPlexGetTransitiveClosure_Depth1_Private(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3840 {
3841   DMPolytopeType  ct = DM_POLYTOPE_UNKNOWN;
3842   PetscInt       *closure;
3843   const PetscInt *tmp = NULL, *tmpO = NULL;
3844   PetscInt        off = 0, tmpSize, t;
3845 
3846   PetscFunctionBeginHot;
3847   if (ornt) {
3848     PetscCall(DMPlexGetCellType(dm, p, &ct));
3849     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;
3850   }
3851   if (*points) {
3852     closure = *points;
3853   } else {
3854     PetscInt maxConeSize, maxSupportSize;
3855     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3856     PetscCall(DMGetWorkArray(dm, 2 * (PetscMax(maxConeSize, maxSupportSize) + 1), MPIU_INT, &closure));
3857   }
3858   PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, p, useCone, &tmpSize, &tmp, &tmpO));
3859   if (ct == DM_POLYTOPE_UNKNOWN) {
3860     closure[off++] = p;
3861     closure[off++] = 0;
3862     for (t = 0; t < tmpSize; ++t) {
3863       closure[off++] = tmp[t];
3864       closure[off++] = tmpO ? tmpO[t] : 0;
3865     }
3866   } else {
3867     const PetscInt *arr = DMPolytopeTypeGetArrangement(ct, ornt);
3868 
3869     /* We assume that cells with a valid type have faces with a valid type */
3870     closure[off++] = p;
3871     closure[off++] = ornt;
3872     for (t = 0; t < tmpSize; ++t) {
3873       DMPolytopeType ft;
3874 
3875       PetscCall(DMPlexGetCellType(dm, tmp[t], &ft));
3876       closure[off++] = tmp[arr[t]];
3877       closure[off++] = tmpO ? DMPolytopeTypeComposeOrientation(ft, ornt, tmpO[t]) : 0;
3878     }
3879   }
3880   PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, p, useCone, &tmpSize, &tmp, &tmpO));
3881   if (numPoints) *numPoints = tmpSize + 1;
3882   if (points) *points = closure;
3883   PetscFunctionReturn(PETSC_SUCCESS);
3884 }
3885 
3886 /* We need a special tensor version because we want to allow duplicate points in the endcaps for hybrid cells */
3887 static PetscErrorCode DMPlexTransitiveClosure_Tensor_Internal(DM dm, PetscInt point, DMPolytopeType ct, PetscInt o, PetscBool useCone, PetscInt *numPoints, PetscInt **points)
3888 {
3889   const PetscInt *arr = DMPolytopeTypeGetArrangement(ct, o);
3890   const PetscInt *cone, *ornt;
3891   PetscInt       *pts, *closure = NULL;
3892   DMPolytopeType  ft;
3893   PetscInt        maxConeSize, maxSupportSize, coneSeries, supportSeries, maxSize;
3894   PetscInt        dim, coneSize, c, d, clSize, cl;
3895 
3896   PetscFunctionBeginHot;
3897   PetscCall(DMGetDimension(dm, &dim));
3898   PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, point, PETSC_TRUE, &coneSize, &cone, &ornt));
3899   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3900   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, dim + 1) - 1) / (maxConeSize - 1)) : dim + 1;
3901   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, dim + 1) - 1) / (maxSupportSize - 1)) : dim + 1;
3902   maxSize       = PetscMax(coneSeries, supportSeries);
3903   if (*points) {
3904     pts = *points;
3905   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &pts));
3906   c        = 0;
3907   pts[c++] = point;
3908   pts[c++] = o;
3909   PetscCall(DMPlexGetCellType(dm, cone[arr[0 * 2 + 0]], &ft));
3910   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[0 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[0 * 2 + 1], ornt[0]), useCone, &clSize, &closure));
3911   for (cl = 0; cl < clSize * 2; cl += 2) {
3912     pts[c++] = closure[cl];
3913     pts[c++] = closure[cl + 1];
3914   }
3915   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[1 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[1 * 2 + 1], ornt[1]), useCone, &clSize, &closure));
3916   for (cl = 0; cl < clSize * 2; cl += 2) {
3917     pts[c++] = closure[cl];
3918     pts[c++] = closure[cl + 1];
3919   }
3920   PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[0], useCone, &clSize, &closure));
3921   for (d = 2; d < coneSize; ++d) {
3922     PetscCall(DMPlexGetCellType(dm, cone[arr[d * 2 + 0]], &ft));
3923     pts[c++] = cone[arr[d * 2 + 0]];
3924     pts[c++] = DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]);
3925   }
3926   PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, point, PETSC_TRUE, &coneSize, &cone, &ornt));
3927   if (dim >= 3) {
3928     for (d = 2; d < coneSize; ++d) {
3929       const PetscInt  fpoint = cone[arr[d * 2 + 0]];
3930       const PetscInt *fcone, *fornt;
3931       PetscInt        fconeSize, fc, i;
3932 
3933       PetscCall(DMPlexGetCellType(dm, fpoint, &ft));
3934       const PetscInt *farr = DMPolytopeTypeGetArrangement(ft, DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]));
3935       PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, fpoint, PETSC_TRUE, &fconeSize, &fcone, &fornt));
3936       for (fc = 0; fc < fconeSize; ++fc) {
3937         const PetscInt cp = fcone[farr[fc * 2 + 0]];
3938         const PetscInt co = farr[fc * 2 + 1];
3939 
3940         for (i = 0; i < c; i += 2)
3941           if (pts[i] == cp) break;
3942         if (i == c) {
3943           PetscCall(DMPlexGetCellType(dm, cp, &ft));
3944           pts[c++] = cp;
3945           pts[c++] = DMPolytopeTypeComposeOrientation(ft, co, fornt[farr[fc * 2 + 0]]);
3946         }
3947       }
3948       PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, fpoint, PETSC_TRUE, &fconeSize, &fcone, &fornt));
3949     }
3950   }
3951   *numPoints = c / 2;
3952   *points    = pts;
3953   PetscFunctionReturn(PETSC_SUCCESS);
3954 }
3955 
3956 PetscErrorCode DMPlexGetTransitiveClosure_Internal(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3957 {
3958   DMPolytopeType ct;
3959   PetscInt      *closure, *fifo;
3960   PetscInt       closureSize = 0, fifoStart = 0, fifoSize = 0;
3961   PetscInt       maxConeSize, maxSupportSize, coneSeries, supportSeries;
3962   PetscInt       depth, maxSize;
3963 
3964   PetscFunctionBeginHot;
3965   PetscCall(DMPlexGetDepth(dm, &depth));
3966   if (depth == 1) {
3967     PetscCall(DMPlexGetTransitiveClosure_Depth1_Private(dm, p, ornt, useCone, numPoints, points));
3968     PetscFunctionReturn(PETSC_SUCCESS);
3969   }
3970   PetscCall(DMPlexGetCellType(dm, p, &ct));
3971   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;
3972   if (DMPolytopeTypeIsHybrid(ct) && ct != DM_POLYTOPE_POINT_PRISM_TENSOR) {
3973     PetscCall(DMPlexTransitiveClosure_Tensor_Internal(dm, p, ct, ornt, useCone, numPoints, points));
3974     PetscFunctionReturn(PETSC_SUCCESS);
3975   }
3976   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3977   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, depth + 1) - 1) / (maxConeSize - 1)) : depth + 1;
3978   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, depth + 1) - 1) / (maxSupportSize - 1)) : depth + 1;
3979   maxSize       = PetscMax(coneSeries, supportSeries);
3980   PetscCall(DMGetWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
3981   if (*points) {
3982     closure = *points;
3983   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &closure));
3984   closure[closureSize++] = p;
3985   closure[closureSize++] = ornt;
3986   fifo[fifoSize++]       = p;
3987   fifo[fifoSize++]       = ornt;
3988   fifo[fifoSize++]       = ct;
3989   /* Should kick out early when depth is reached, rather than checking all vertices for empty cones */
3990   while (fifoSize - fifoStart) {
3991     const PetscInt       q    = fifo[fifoStart++];
3992     const PetscInt       o    = fifo[fifoStart++];
3993     const DMPolytopeType qt   = (DMPolytopeType)fifo[fifoStart++];
3994     const PetscInt      *qarr = DMPolytopeTypeGetArrangement(qt, o);
3995     const PetscInt      *tmp, *tmpO = NULL;
3996     PetscInt             tmpSize, t;
3997 
3998     if (PetscDefined(USE_DEBUG)) {
3999       PetscInt nO = DMPolytopeTypeGetNumArrangements(qt) / 2;
4000       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);
4001     }
4002     PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, q, useCone, &tmpSize, &tmp, &tmpO));
4003     for (t = 0; t < tmpSize; ++t) {
4004       const PetscInt ip = useCone && qarr ? qarr[t * 2] : t;
4005       const PetscInt io = useCone && qarr ? qarr[t * 2 + 1] : 0;
4006       const PetscInt cp = tmp[ip];
4007       PetscCall(DMPlexGetCellType(dm, cp, &ct));
4008       const PetscInt co = tmpO ? DMPolytopeTypeComposeOrientation(ct, io, tmpO[ip]) : 0;
4009       PetscInt       c;
4010 
4011       /* Check for duplicate */
4012       for (c = 0; c < closureSize; c += 2) {
4013         if (closure[c] == cp) break;
4014       }
4015       if (c == closureSize) {
4016         closure[closureSize++] = cp;
4017         closure[closureSize++] = co;
4018         fifo[fifoSize++]       = cp;
4019         fifo[fifoSize++]       = co;
4020         fifo[fifoSize++]       = ct;
4021       }
4022     }
4023     PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, q, useCone, &tmpSize, &tmp, &tmpO));
4024   }
4025   PetscCall(DMRestoreWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
4026   if (numPoints) *numPoints = closureSize / 2;
4027   if (points) *points = closure;
4028   PetscFunctionReturn(PETSC_SUCCESS);
4029 }
4030 
4031 /*@C
4032   DMPlexGetTransitiveClosure - Return the points on the transitive closure of the in-edges or out-edges for this point in the DAG
4033 
4034   Not Collective
4035 
4036   Input Parameters:
4037 + dm      - The `DMPLEX`
4038 . p       - The mesh point
4039 - useCone - `PETSC_TRUE` for the closure, otherwise return the star
4040 
4041   Input/Output Parameter:
4042 . points - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...];
4043            if *points is `NULL` on input, internal storage will be returned, use `DMPlexRestoreTransitiveClosure()`,
4044            otherwise the provided array is used to hold the values
4045 
4046   Output Parameter:
4047 . numPoints - The number of points in the closure, so `points` is of size 2*`numPoints`
4048 
4049   Level: beginner
4050 
4051   Note:
4052   If using internal storage (points is `NULL` on input), each call overwrites the last output.
4053 
4054   Fortran Notes:
4055   `points` must be declared with
4056 .vb
4057   PetscInt, pointer :: points(:)
4058 .ve
4059   and is always allocated by the function.
4060 
4061   The `numPoints` argument is not present in the Fortran binding.
4062 
4063 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
4064 @*/
4065 PetscErrorCode DMPlexGetTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
4066 {
4067   PetscFunctionBeginHot;
4068   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4069   if (numPoints) PetscAssertPointer(numPoints, 4);
4070   if (points) PetscAssertPointer(points, 5);
4071   if (PetscDefined(USE_DEBUG)) {
4072     PetscInt pStart, pEnd;
4073     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4074     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);
4075   }
4076   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, p, 0, useCone, numPoints, points));
4077   PetscFunctionReturn(PETSC_SUCCESS);
4078 }
4079 
4080 /*@C
4081   DMPlexRestoreTransitiveClosure - Restore the array of points on the transitive closure of the in-edges or out-edges for this point in the DAG
4082 
4083   Not Collective
4084 
4085   Input Parameters:
4086 + dm        - The `DMPLEX`
4087 . p         - The mesh point
4088 . useCone   - `PETSC_TRUE` for the closure, otherwise return the star
4089 . numPoints - The number of points in the closure, so points[] is of size 2*`numPoints`
4090 - points    - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...]
4091 
4092   Level: beginner
4093 
4094   Note:
4095   If not using internal storage (points is not `NULL` on input), this call is unnecessary
4096 
4097 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
4098 @*/
4099 PetscErrorCode DMPlexRestoreTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
4100 {
4101   PetscFunctionBeginHot;
4102   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4103   if (numPoints) *numPoints = 0;
4104   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, points));
4105   PetscFunctionReturn(PETSC_SUCCESS);
4106 }
4107 
4108 /*@
4109   DMPlexGetMaxSizes - Return the maximum number of in-edges (cone) and out-edges (support) for any point in the DAG
4110 
4111   Not Collective
4112 
4113   Input Parameter:
4114 . dm - The `DMPLEX`
4115 
4116   Output Parameters:
4117 + maxConeSize    - The maximum number of in-edges
4118 - maxSupportSize - The maximum number of out-edges
4119 
4120   Level: beginner
4121 
4122 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
4123 @*/
4124 PetscErrorCode DMPlexGetMaxSizes(DM dm, PetscInt *maxConeSize, PetscInt *maxSupportSize)
4125 {
4126   DM_Plex *mesh = (DM_Plex *)dm->data;
4127 
4128   PetscFunctionBegin;
4129   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4130   if (maxConeSize) PetscCall(PetscSectionGetMaxDof(mesh->coneSection, maxConeSize));
4131   if (maxSupportSize) PetscCall(PetscSectionGetMaxDof(mesh->supportSection, maxSupportSize));
4132   PetscFunctionReturn(PETSC_SUCCESS);
4133 }
4134 
4135 PetscErrorCode DMSetUp_Plex(DM dm)
4136 {
4137   DM_Plex *mesh = (DM_Plex *)dm->data;
4138   PetscInt size, maxSupportSize;
4139 
4140   PetscFunctionBegin;
4141   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4142   PetscCall(PetscSectionSetUp(mesh->coneSection));
4143   PetscCall(PetscSectionGetStorageSize(mesh->coneSection, &size));
4144   PetscCall(PetscMalloc1(size, &mesh->cones));
4145   PetscCall(PetscCalloc1(size, &mesh->coneOrientations));
4146   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
4147   if (maxSupportSize) {
4148     PetscCall(PetscSectionSetUp(mesh->supportSection));
4149     PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &size));
4150     PetscCall(PetscMalloc1(size, &mesh->supports));
4151   }
4152   PetscFunctionReturn(PETSC_SUCCESS);
4153 }
4154 
4155 PetscErrorCode DMCreateSubDM_Plex(DM dm, PetscInt numFields, const PetscInt fields[], IS *is, DM *subdm)
4156 {
4157   PetscFunctionBegin;
4158   if (subdm) PetscCall(DMClone(dm, subdm));
4159   PetscCall(DMCreateSectionSubDM(dm, numFields, fields, NULL, NULL, is, subdm));
4160   if (subdm) (*subdm)->useNatural = dm->useNatural;
4161   if (dm->useNatural && dm->sfMigration) {
4162     PetscSF sfNatural;
4163 
4164     (*subdm)->sfMigration = dm->sfMigration;
4165     PetscCall(PetscObjectReference((PetscObject)dm->sfMigration));
4166     PetscCall(DMPlexCreateGlobalToNaturalSF(*subdm, NULL, (*subdm)->sfMigration, &sfNatural));
4167     (*subdm)->sfNatural = sfNatural;
4168   }
4169   PetscFunctionReturn(PETSC_SUCCESS);
4170 }
4171 
4172 PetscErrorCode DMCreateSuperDM_Plex(DM dms[], PetscInt len, IS **is, DM *superdm)
4173 {
4174   PetscInt i = 0;
4175 
4176   PetscFunctionBegin;
4177   PetscCall(DMClone(dms[0], superdm));
4178   PetscCall(DMCreateSectionSuperDM(dms, len, is, superdm));
4179   (*superdm)->useNatural = PETSC_FALSE;
4180   for (i = 0; i < len; i++) {
4181     if (dms[i]->useNatural && dms[i]->sfMigration) {
4182       PetscSF sfNatural;
4183 
4184       (*superdm)->sfMigration = dms[i]->sfMigration;
4185       PetscCall(PetscObjectReference((PetscObject)dms[i]->sfMigration));
4186       (*superdm)->useNatural = PETSC_TRUE;
4187       PetscCall(DMPlexCreateGlobalToNaturalSF(*superdm, NULL, (*superdm)->sfMigration, &sfNatural));
4188       (*superdm)->sfNatural = sfNatural;
4189       break;
4190     }
4191   }
4192   PetscFunctionReturn(PETSC_SUCCESS);
4193 }
4194 
4195 /*@
4196   DMPlexSymmetrize - Create support (out-edge) information from cone (in-edge) information
4197 
4198   Not Collective
4199 
4200   Input Parameter:
4201 . dm - The `DMPLEX`
4202 
4203   Level: beginner
4204 
4205   Note:
4206   This should be called after all calls to `DMPlexSetCone()`
4207 
4208 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMPlexSetCone()`
4209 @*/
4210 PetscErrorCode DMPlexSymmetrize(DM dm)
4211 {
4212   DM_Plex  *mesh = (DM_Plex *)dm->data;
4213   PetscInt *offsets;
4214   PetscInt  supportSize;
4215   PetscInt  pStart, pEnd, p;
4216 
4217   PetscFunctionBegin;
4218   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4219   PetscCheck(!mesh->supports, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONGSTATE, "Supports were already setup in this DMPlex");
4220   PetscCall(PetscLogEventBegin(DMPLEX_Symmetrize, dm, 0, 0, 0));
4221   /* Calculate support sizes */
4222   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4223   for (p = pStart; p < pEnd; ++p) {
4224     PetscInt dof, off, c;
4225 
4226     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
4227     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
4228     for (c = off; c < off + dof; ++c) PetscCall(PetscSectionAddDof(mesh->supportSection, mesh->cones[c], 1));
4229   }
4230   PetscCall(PetscSectionSetUp(mesh->supportSection));
4231   /* Calculate supports */
4232   PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &supportSize));
4233   PetscCall(PetscMalloc1(supportSize, &mesh->supports));
4234   PetscCall(PetscCalloc1(pEnd - pStart, &offsets));
4235   for (p = pStart; p < pEnd; ++p) {
4236     PetscInt dof, off, c;
4237 
4238     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
4239     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
4240     for (c = off; c < off + dof; ++c) {
4241       const PetscInt q = mesh->cones[c];
4242       PetscInt       offS;
4243 
4244       PetscCall(PetscSectionGetOffset(mesh->supportSection, q, &offS));
4245 
4246       mesh->supports[offS + offsets[q]] = p;
4247       ++offsets[q];
4248     }
4249   }
4250   PetscCall(PetscFree(offsets));
4251   PetscCall(PetscLogEventEnd(DMPLEX_Symmetrize, dm, 0, 0, 0));
4252   PetscFunctionReturn(PETSC_SUCCESS);
4253 }
4254 
4255 static PetscErrorCode DMPlexCreateDepthStratum(DM dm, DMLabel label, PetscInt depth, PetscInt pStart, PetscInt pEnd)
4256 {
4257   IS stratumIS;
4258 
4259   PetscFunctionBegin;
4260   if (pStart >= pEnd) PetscFunctionReturn(PETSC_SUCCESS);
4261   if (PetscDefined(USE_DEBUG)) {
4262     PetscInt  qStart, qEnd, numLevels, level;
4263     PetscBool overlap = PETSC_FALSE;
4264     PetscCall(DMLabelGetNumValues(label, &numLevels));
4265     for (level = 0; level < numLevels; level++) {
4266       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4267       if ((pStart >= qStart && pStart < qEnd) || (pEnd > qStart && pEnd <= qEnd)) {
4268         overlap = PETSC_TRUE;
4269         break;
4270       }
4271     }
4272     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);
4273   }
4274   PetscCall(ISCreateStride(PETSC_COMM_SELF, pEnd - pStart, pStart, 1, &stratumIS));
4275   PetscCall(DMLabelSetStratumIS(label, depth, stratumIS));
4276   PetscCall(ISDestroy(&stratumIS));
4277   PetscFunctionReturn(PETSC_SUCCESS);
4278 }
4279 
4280 static PetscErrorCode DMPlexStratify_CellType_Private(DM dm, DMLabel label)
4281 {
4282   PetscInt *pMin, *pMax;
4283   PetscInt  pStart, pEnd;
4284   PetscInt  dmin = PETSC_INT_MAX, dmax = PETSC_INT_MIN;
4285 
4286   PetscFunctionBegin;
4287   {
4288     DMLabel label2;
4289 
4290     PetscCall(DMPlexGetCellTypeLabel(dm, &label2));
4291     PetscCall(PetscObjectViewFromOptions((PetscObject)label2, NULL, "-ct_view"));
4292   }
4293   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4294   for (PetscInt p = pStart; p < pEnd; ++p) {
4295     DMPolytopeType ct;
4296 
4297     PetscCall(DMPlexGetCellType(dm, p, &ct));
4298     dmin = PetscMin(DMPolytopeTypeGetDim(ct), dmin);
4299     dmax = PetscMax(DMPolytopeTypeGetDim(ct), dmax);
4300   }
4301   PetscCall(PetscMalloc2(dmax + 1, &pMin, dmax + 1, &pMax));
4302   for (PetscInt d = dmin; d <= dmax; ++d) {
4303     pMin[d] = PETSC_INT_MAX;
4304     pMax[d] = PETSC_INT_MIN;
4305   }
4306   for (PetscInt p = pStart; p < pEnd; ++p) {
4307     DMPolytopeType ct;
4308     PetscInt       d;
4309 
4310     PetscCall(DMPlexGetCellType(dm, p, &ct));
4311     d       = DMPolytopeTypeGetDim(ct);
4312     pMin[d] = PetscMin(p, pMin[d]);
4313     pMax[d] = PetscMax(p, pMax[d]);
4314   }
4315   for (PetscInt d = dmin; d <= dmax; ++d) {
4316     if (pMin[d] > pMax[d]) continue;
4317     PetscCall(DMPlexCreateDepthStratum(dm, label, d, pMin[d], pMax[d] + 1));
4318   }
4319   PetscCall(PetscFree2(pMin, pMax));
4320   PetscFunctionReturn(PETSC_SUCCESS);
4321 }
4322 
4323 static PetscErrorCode DMPlexStratify_Topological_Private(DM dm, DMLabel label)
4324 {
4325   PetscInt pStart, pEnd;
4326   PetscInt numRoots = 0, numLeaves = 0;
4327 
4328   PetscFunctionBegin;
4329   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4330   {
4331     /* Initialize roots and count leaves */
4332     PetscInt sMin = PETSC_INT_MAX;
4333     PetscInt sMax = PETSC_INT_MIN;
4334     PetscInt coneSize, supportSize;
4335 
4336     for (PetscInt p = pStart; p < pEnd; ++p) {
4337       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4338       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
4339       if (!coneSize && supportSize) {
4340         sMin = PetscMin(p, sMin);
4341         sMax = PetscMax(p, sMax);
4342         ++numRoots;
4343       } else if (!supportSize && coneSize) {
4344         ++numLeaves;
4345       } else if (!supportSize && !coneSize) {
4346         /* Isolated points */
4347         sMin = PetscMin(p, sMin);
4348         sMax = PetscMax(p, sMax);
4349       }
4350     }
4351     PetscCall(DMPlexCreateDepthStratum(dm, label, 0, sMin, sMax + 1));
4352   }
4353 
4354   if (numRoots + numLeaves == (pEnd - pStart)) {
4355     PetscInt sMin = PETSC_INT_MAX;
4356     PetscInt sMax = PETSC_INT_MIN;
4357     PetscInt coneSize, supportSize;
4358 
4359     for (PetscInt p = pStart; p < pEnd; ++p) {
4360       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4361       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
4362       if (!supportSize && coneSize) {
4363         sMin = PetscMin(p, sMin);
4364         sMax = PetscMax(p, sMax);
4365       }
4366     }
4367     PetscCall(DMPlexCreateDepthStratum(dm, label, 1, sMin, sMax + 1));
4368   } else {
4369     PetscInt level = 0;
4370     PetscInt qStart, qEnd;
4371 
4372     PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4373     while (qEnd > qStart) {
4374       PetscInt sMin = PETSC_INT_MAX;
4375       PetscInt sMax = PETSC_INT_MIN;
4376 
4377       for (PetscInt q = qStart; q < qEnd; ++q) {
4378         const PetscInt *support;
4379         PetscInt        supportSize;
4380 
4381         PetscCall(DMPlexGetSupportSize(dm, q, &supportSize));
4382         PetscCall(DMPlexGetSupport(dm, q, &support));
4383         for (PetscInt s = 0; s < supportSize; ++s) {
4384           sMin = PetscMin(support[s], sMin);
4385           sMax = PetscMax(support[s], sMax);
4386         }
4387       }
4388       PetscCall(DMLabelGetNumValues(label, &level));
4389       PetscCall(DMPlexCreateDepthStratum(dm, label, level, sMin, sMax + 1));
4390       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4391     }
4392   }
4393   PetscFunctionReturn(PETSC_SUCCESS);
4394 }
4395 
4396 /*@
4397   DMPlexStratify - Computes the strata for all points in the `DMPLEX`
4398 
4399   Collective
4400 
4401   Input Parameter:
4402 . dm - The `DMPLEX`
4403 
4404   Level: beginner
4405 
4406   Notes:
4407   The strata group all points of the same grade, and this function calculates the strata. This
4408   grade can be seen as the height (or depth) of the point in the DAG.
4409 
4410   The DAG for most topologies is a graded poset (https://en.wikipedia.org/wiki/Graded_poset), and
4411   can be illustrated by a Hasse Diagram (https://en.wikipedia.org/wiki/Hasse_diagram).
4412   Concretely, `DMPlexStratify()` creates a new label named "depth" containing the depth in the DAG of each point. For cell-vertex
4413   meshes, vertices are depth 0 and cells are depth 1. For fully interpolated meshes, depth 0 for vertices, 1 for edges, and so on
4414   until cells have depth equal to the dimension of the mesh. The depth label can be accessed through `DMPlexGetDepthLabel()` or `DMPlexGetDepthStratum()`, or
4415   manually via `DMGetLabel()`.  The height is defined implicitly by height = maxDimension - depth, and can be accessed
4416   via `DMPlexGetHeightStratum()`.  For example, cells have height 0 and faces have height 1.
4417 
4418   The depth of a point is calculated by executing a breadth-first search (BFS) on the DAG. This could produce surprising results
4419   if run on a partially interpolated mesh, meaning one that had some edges and faces, but not others. For example, suppose that
4420   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
4421   to interpolate only that one (e0), so that
4422 .vb
4423   cone(c0) = {e0, v2}
4424   cone(e0) = {v0, v1}
4425 .ve
4426   If `DMPlexStratify()` is run on this mesh, it will give depths
4427 .vb
4428    depth 0 = {v0, v1, v2}
4429    depth 1 = {e0, c0}
4430 .ve
4431   where the triangle has been given depth 1, instead of 2, because it is reachable from vertex v2.
4432 
4433   `DMPlexStratify()` should be called after all calls to `DMPlexSymmetrize()`
4434 
4435 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexComputeCellTypes()`
4436 @*/
4437 PetscErrorCode DMPlexStratify(DM dm)
4438 {
4439   DM_Plex  *mesh = (DM_Plex *)dm->data;
4440   DMLabel   label;
4441   PetscBool flg = PETSC_FALSE;
4442 
4443   PetscFunctionBegin;
4444   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4445   PetscCall(PetscLogEventBegin(DMPLEX_Stratify, dm, 0, 0, 0));
4446 
4447   // Create depth label
4448   PetscCall(DMRemoveLabel(dm, "depth", NULL));
4449   PetscCall(DMCreateLabel(dm, "depth"));
4450   PetscCall(DMPlexGetDepthLabel(dm, &label));
4451 
4452   PetscCall(PetscOptionsGetBool(NULL, dm->hdr.prefix, "-dm_plex_stratify_celltype", &flg, NULL));
4453   if (flg) PetscCall(DMPlexStratify_CellType_Private(dm, label));
4454   else PetscCall(DMPlexStratify_Topological_Private(dm, label));
4455 
4456   { /* just in case there is an empty process */
4457     PetscInt numValues, maxValues = 0, v;
4458 
4459     PetscCall(DMLabelGetNumValues(label, &numValues));
4460     PetscCallMPI(MPIU_Allreduce(&numValues, &maxValues, 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
4461     for (v = numValues; v < maxValues; v++) PetscCall(DMLabelAddStratum(label, v));
4462   }
4463   PetscCall(PetscObjectStateGet((PetscObject)label, &mesh->depthState));
4464   PetscCall(PetscLogEventEnd(DMPLEX_Stratify, dm, 0, 0, 0));
4465   PetscFunctionReturn(PETSC_SUCCESS);
4466 }
4467 
4468 PetscErrorCode DMPlexComputeCellType_Internal(DM dm, PetscInt p, PetscInt pdepth, DMPolytopeType *pt)
4469 {
4470   DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4471   PetscInt       dim, depth, pheight, coneSize;
4472 
4473   PetscFunctionBeginHot;
4474   PetscCall(DMGetDimension(dm, &dim));
4475   PetscCall(DMPlexGetDepth(dm, &depth));
4476   PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4477   pheight = depth - pdepth;
4478   if (depth <= 1) {
4479     switch (pdepth) {
4480     case 0:
4481       ct = DM_POLYTOPE_POINT;
4482       break;
4483     case 1:
4484       switch (coneSize) {
4485       case 2:
4486         ct = DM_POLYTOPE_SEGMENT;
4487         break;
4488       case 3:
4489         ct = DM_POLYTOPE_TRIANGLE;
4490         break;
4491       case 4:
4492         switch (dim) {
4493         case 2:
4494           ct = DM_POLYTOPE_QUADRILATERAL;
4495           break;
4496         case 3:
4497           ct = DM_POLYTOPE_TETRAHEDRON;
4498           break;
4499         default:
4500           break;
4501         }
4502         break;
4503       case 5:
4504         ct = DM_POLYTOPE_PYRAMID;
4505         break;
4506       case 6:
4507         ct = DM_POLYTOPE_TRI_PRISM_TENSOR;
4508         break;
4509       case 8:
4510         ct = DM_POLYTOPE_HEXAHEDRON;
4511         break;
4512       default:
4513         break;
4514       }
4515     }
4516   } else {
4517     if (pdepth == 0) {
4518       ct = DM_POLYTOPE_POINT;
4519     } else if (pheight == 0) {
4520       switch (dim) {
4521       case 1:
4522         switch (coneSize) {
4523         case 2:
4524           ct = DM_POLYTOPE_SEGMENT;
4525           break;
4526         default:
4527           break;
4528         }
4529         break;
4530       case 2:
4531         switch (coneSize) {
4532         case 3:
4533           ct = DM_POLYTOPE_TRIANGLE;
4534           break;
4535         case 4:
4536           ct = DM_POLYTOPE_QUADRILATERAL;
4537           break;
4538         default:
4539           break;
4540         }
4541         break;
4542       case 3:
4543         switch (coneSize) {
4544         case 4:
4545           ct = DM_POLYTOPE_TETRAHEDRON;
4546           break;
4547         case 5: {
4548           const PetscInt *cone;
4549           PetscInt        faceConeSize;
4550 
4551           PetscCall(DMPlexGetCone(dm, p, &cone));
4552           PetscCall(DMPlexGetConeSize(dm, cone[0], &faceConeSize));
4553           switch (faceConeSize) {
4554           case 3:
4555             ct = DM_POLYTOPE_TRI_PRISM_TENSOR;
4556             break;
4557           case 4:
4558             ct = DM_POLYTOPE_PYRAMID;
4559             break;
4560           }
4561         } break;
4562         case 6:
4563           ct = DM_POLYTOPE_HEXAHEDRON;
4564           break;
4565         default:
4566           break;
4567         }
4568         break;
4569       default:
4570         break;
4571       }
4572     } else if (pheight > 0) {
4573       switch (coneSize) {
4574       case 2:
4575         ct = DM_POLYTOPE_SEGMENT;
4576         break;
4577       case 3:
4578         ct = DM_POLYTOPE_TRIANGLE;
4579         break;
4580       case 4:
4581         ct = DM_POLYTOPE_QUADRILATERAL;
4582         break;
4583       default:
4584         break;
4585       }
4586     }
4587   }
4588   *pt = ct;
4589   PetscFunctionReturn(PETSC_SUCCESS);
4590 }
4591 
4592 /*@
4593   DMPlexComputeCellTypes - Infer the polytope type of every cell using its dimension and cone size.
4594 
4595   Collective
4596 
4597   Input Parameter:
4598 . dm - The `DMPLEX`
4599 
4600   Level: developer
4601 
4602   Note:
4603   This function is normally called automatically when a cell type is requested. It creates an
4604   internal `DMLabel` named "celltype" which can be directly accessed using `DMGetLabel()`. A user may disable
4605   automatic creation by creating the label manually, using `DMCreateLabel`(dm, "celltype").
4606 
4607   `DMPlexComputeCellTypes()` should be called after all calls to `DMPlexSymmetrize()` and `DMPlexStratify()`
4608 
4609 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexStratify()`, `DMGetLabel()`, `DMCreateLabel()`
4610 @*/
4611 PetscErrorCode DMPlexComputeCellTypes(DM dm)
4612 {
4613   DM_Plex *mesh;
4614   DMLabel  ctLabel;
4615   PetscInt pStart, pEnd, p;
4616 
4617   PetscFunctionBegin;
4618   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4619   mesh = (DM_Plex *)dm->data;
4620   PetscCall(DMCreateLabel(dm, "celltype"));
4621   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
4622   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4623   PetscCall(PetscFree(mesh->cellTypes));
4624   PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
4625   for (p = pStart; p < pEnd; ++p) {
4626     DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4627     PetscInt       pdepth;
4628 
4629     PetscCall(DMPlexGetPointDepth(dm, p, &pdepth));
4630     PetscCall(DMPlexComputeCellType_Internal(dm, p, pdepth, &ct));
4631     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]);
4632     PetscCall(DMLabelSetValue(ctLabel, p, ct));
4633     mesh->cellTypes[p - pStart].value_as_uint8 = (uint8_t)ct;
4634   }
4635   PetscCall(PetscObjectStateGet((PetscObject)ctLabel, &mesh->celltypeState));
4636   PetscCall(PetscObjectViewFromOptions((PetscObject)ctLabel, NULL, "-dm_plex_celltypes_view"));
4637   PetscFunctionReturn(PETSC_SUCCESS);
4638 }
4639 
4640 /*@C
4641   DMPlexGetJoin - Get an array for the join of the set of points
4642 
4643   Not Collective
4644 
4645   Input Parameters:
4646 + dm        - The `DMPLEX` object
4647 . numPoints - The number of input points for the join
4648 - points    - The input points
4649 
4650   Output Parameters:
4651 + numCoveredPoints - The number of points in the join
4652 - coveredPoints    - The points in the join
4653 
4654   Level: intermediate
4655 
4656   Note:
4657   Currently, this is restricted to a single level join
4658 
4659   Fortran Notes:
4660   `converedPoints` must be declared with
4661 .vb
4662   PetscInt, pointer :: coveredPints(:)
4663 .ve
4664 
4665   The `numCoveredPoints` argument is not present in the Fortran binding.
4666 
4667 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4668 @*/
4669 PetscErrorCode DMPlexGetJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt *coveredPoints[])
4670 {
4671   DM_Plex  *mesh = (DM_Plex *)dm->data;
4672   PetscInt *join[2];
4673   PetscInt  joinSize, i = 0;
4674   PetscInt  dof, off, p, c, m;
4675   PetscInt  maxSupportSize;
4676 
4677   PetscFunctionBegin;
4678   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4679   PetscAssertPointer(points, 3);
4680   PetscAssertPointer(numCoveredPoints, 4);
4681   PetscAssertPointer(coveredPoints, 5);
4682   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
4683   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[0]));
4684   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[1]));
4685   /* Copy in support of first point */
4686   PetscCall(PetscSectionGetDof(mesh->supportSection, points[0], &dof));
4687   PetscCall(PetscSectionGetOffset(mesh->supportSection, points[0], &off));
4688   for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = mesh->supports[off + joinSize];
4689   /* Check each successive support */
4690   for (p = 1; p < numPoints; ++p) {
4691     PetscInt newJoinSize = 0;
4692 
4693     PetscCall(PetscSectionGetDof(mesh->supportSection, points[p], &dof));
4694     PetscCall(PetscSectionGetOffset(mesh->supportSection, points[p], &off));
4695     for (c = 0; c < dof; ++c) {
4696       const PetscInt point = mesh->supports[off + c];
4697 
4698       for (m = 0; m < joinSize; ++m) {
4699         if (point == join[i][m]) {
4700           join[1 - i][newJoinSize++] = point;
4701           break;
4702         }
4703       }
4704     }
4705     joinSize = newJoinSize;
4706     i        = 1 - i;
4707   }
4708   *numCoveredPoints = joinSize;
4709   *coveredPoints    = join[i];
4710   PetscCall(DMRestoreWorkArray(dm, maxSupportSize, MPIU_INT, &join[1 - i]));
4711   PetscFunctionReturn(PETSC_SUCCESS);
4712 }
4713 
4714 /*@C
4715   DMPlexRestoreJoin - Restore an array for the join of the set of points obtained with `DMPlexGetJoin()`
4716 
4717   Not Collective
4718 
4719   Input Parameters:
4720 + dm        - The `DMPLEX` object
4721 . numPoints - The number of input points for the join
4722 - points    - The input points
4723 
4724   Output Parameters:
4725 + numCoveredPoints - The number of points in the join
4726 - coveredPoints    - The points in the join
4727 
4728   Level: intermediate
4729 
4730   Fortran Notes:
4731   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4732 
4733 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetJoin()`, `DMPlexGetFullJoin()`, `DMPlexGetMeet()`
4734 @*/
4735 PetscErrorCode DMPlexRestoreJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt *coveredPoints[])
4736 {
4737   PetscFunctionBegin;
4738   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4739   if (points) PetscAssertPointer(points, 3);
4740   if (numCoveredPoints) PetscAssertPointer(numCoveredPoints, 4);
4741   PetscAssertPointer(coveredPoints, 5);
4742   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
4743   if (numCoveredPoints) *numCoveredPoints = 0;
4744   PetscFunctionReturn(PETSC_SUCCESS);
4745 }
4746 
4747 /*@C
4748   DMPlexGetFullJoin - Get an array for the join of the set of points
4749 
4750   Not Collective
4751 
4752   Input Parameters:
4753 + dm        - The `DMPLEX` object
4754 . numPoints - The number of input points for the join
4755 - points    - The input points, its length is `numPoints`
4756 
4757   Output Parameters:
4758 + numCoveredPoints - The number of points in the join
4759 - coveredPoints    - The points in the join, its length is `numCoveredPoints`
4760 
4761   Level: intermediate
4762 
4763   Fortran Notes:
4764   `points` and `converedPoints` must be declared with
4765 .vb
4766   PetscInt, pointer :: points(:)
4767   PetscInt, pointer :: coveredPints(:)
4768 .ve
4769 
4770   The `numCoveredPoints` argument is not present in the Fortran binding.
4771 
4772 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetJoin()`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4773 @*/
4774 PetscErrorCode DMPlexGetFullJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt *coveredPoints[])
4775 {
4776   PetscInt *offsets, **closures;
4777   PetscInt *join[2];
4778   PetscInt  depth = 0, maxSize, joinSize = 0, i = 0;
4779   PetscInt  p, d, c, m, ms;
4780 
4781   PetscFunctionBegin;
4782   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4783   PetscAssertPointer(points, 3);
4784   PetscAssertPointer(numCoveredPoints, 4);
4785   PetscAssertPointer(coveredPoints, 5);
4786 
4787   PetscCall(DMPlexGetDepth(dm, &depth));
4788   PetscCall(PetscCalloc1(numPoints, &closures));
4789   PetscCall(DMGetWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
4790   PetscCall(DMPlexGetMaxSizes(dm, NULL, &ms));
4791   maxSize = (ms > 1) ? ((PetscPowInt(ms, depth + 1) - 1) / (ms - 1)) : depth + 1;
4792   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[0]));
4793   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[1]));
4794 
4795   for (p = 0; p < numPoints; ++p) {
4796     PetscInt closureSize;
4797 
4798     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_FALSE, &closureSize, &closures[p]));
4799 
4800     offsets[p * (depth + 2) + 0] = 0;
4801     for (d = 0; d < depth + 1; ++d) {
4802       PetscInt pStart, pEnd, i;
4803 
4804       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
4805       for (i = offsets[p * (depth + 2) + d]; i < closureSize; ++i) {
4806         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
4807           offsets[p * (depth + 2) + d + 1] = i;
4808           break;
4809         }
4810       }
4811       if (i == closureSize) offsets[p * (depth + 2) + d + 1] = i;
4812     }
4813     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);
4814   }
4815   for (d = 0; d < depth + 1; ++d) {
4816     PetscInt dof;
4817 
4818     /* Copy in support of first point */
4819     dof = offsets[d + 1] - offsets[d];
4820     for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = closures[0][(offsets[d] + joinSize) * 2];
4821     /* Check each successive cone */
4822     for (p = 1; p < numPoints && joinSize; ++p) {
4823       PetscInt newJoinSize = 0;
4824 
4825       dof = offsets[p * (depth + 2) + d + 1] - offsets[p * (depth + 2) + d];
4826       for (c = 0; c < dof; ++c) {
4827         const PetscInt point = closures[p][(offsets[p * (depth + 2) + d] + c) * 2];
4828 
4829         for (m = 0; m < joinSize; ++m) {
4830           if (point == join[i][m]) {
4831             join[1 - i][newJoinSize++] = point;
4832             break;
4833           }
4834         }
4835       }
4836       joinSize = newJoinSize;
4837       i        = 1 - i;
4838     }
4839     if (joinSize) break;
4840   }
4841   *numCoveredPoints = joinSize;
4842   *coveredPoints    = join[i];
4843   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_FALSE, NULL, &closures[p]));
4844   PetscCall(PetscFree(closures));
4845   PetscCall(DMRestoreWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
4846   PetscCall(DMRestoreWorkArray(dm, ms, MPIU_INT, &join[1 - i]));
4847   PetscFunctionReturn(PETSC_SUCCESS);
4848 }
4849 
4850 /*@C
4851   DMPlexGetMeet - Get an array for the meet of the set of points
4852 
4853   Not Collective
4854 
4855   Input Parameters:
4856 + dm        - The `DMPLEX` object
4857 . numPoints - The number of input points for the meet
4858 - points    - The input points, of length `numPoints`
4859 
4860   Output Parameters:
4861 + numCoveringPoints - The number of points in the meet
4862 - coveringPoints    - The points in the meet, of length `numCoveringPoints`
4863 
4864   Level: intermediate
4865 
4866   Note:
4867   Currently, this is restricted to a single level meet
4868 
4869   Fortran Notes:
4870   `coveringPoints` must be declared with
4871 .vb
4872   PetscInt, pointer :: coveringPoints(:)
4873 .ve
4874 
4875   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4876 
4877 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
4878 @*/
4879 PetscErrorCode DMPlexGetMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveringPoints, const PetscInt *coveringPoints[])
4880 {
4881   DM_Plex  *mesh = (DM_Plex *)dm->data;
4882   PetscInt *meet[2];
4883   PetscInt  meetSize, i = 0;
4884   PetscInt  dof, off, p, c, m;
4885   PetscInt  maxConeSize;
4886 
4887   PetscFunctionBegin;
4888   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4889   PetscAssertPointer(points, 3);
4890   PetscAssertPointer(numCoveringPoints, 4);
4891   PetscAssertPointer(coveringPoints, 5);
4892   PetscCall(PetscSectionGetMaxDof(mesh->coneSection, &maxConeSize));
4893   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[0]));
4894   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[1]));
4895   /* Copy in cone of first point */
4896   PetscCall(PetscSectionGetDof(mesh->coneSection, points[0], &dof));
4897   PetscCall(PetscSectionGetOffset(mesh->coneSection, points[0], &off));
4898   for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = mesh->cones[off + meetSize];
4899   /* Check each successive cone */
4900   for (p = 1; p < numPoints; ++p) {
4901     PetscInt newMeetSize = 0;
4902 
4903     PetscCall(PetscSectionGetDof(mesh->coneSection, points[p], &dof));
4904     PetscCall(PetscSectionGetOffset(mesh->coneSection, points[p], &off));
4905     for (c = 0; c < dof; ++c) {
4906       const PetscInt point = mesh->cones[off + c];
4907 
4908       for (m = 0; m < meetSize; ++m) {
4909         if (point == meet[i][m]) {
4910           meet[1 - i][newMeetSize++] = point;
4911           break;
4912         }
4913       }
4914     }
4915     meetSize = newMeetSize;
4916     i        = 1 - i;
4917   }
4918   *numCoveringPoints = meetSize;
4919   *coveringPoints    = meet[i];
4920   PetscCall(DMRestoreWorkArray(dm, maxConeSize, MPIU_INT, &meet[1 - i]));
4921   PetscFunctionReturn(PETSC_SUCCESS);
4922 }
4923 
4924 /*@C
4925   DMPlexRestoreMeet - Restore an array for the meet of the set of points obtained with `DMPlexGetMeet()`
4926 
4927   Not Collective
4928 
4929   Input Parameters:
4930 + dm        - The `DMPLEX` object
4931 . numPoints - The number of input points for the meet
4932 - points    - The input points
4933 
4934   Output Parameters:
4935 + numCoveredPoints - The number of points in the meet
4936 - coveredPoints    - The points in the meet
4937 
4938   Level: intermediate
4939 
4940   Fortran Notes:
4941   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4942 
4943 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetMeet()`, `DMPlexGetFullMeet()`, `DMPlexGetJoin()`
4944 @*/
4945 PetscErrorCode DMPlexRestoreMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt *coveredPoints[])
4946 {
4947   PetscFunctionBegin;
4948   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4949   if (points) PetscAssertPointer(points, 3);
4950   if (numCoveredPoints) PetscAssertPointer(numCoveredPoints, 4);
4951   PetscAssertPointer(coveredPoints, 5);
4952   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
4953   if (numCoveredPoints) *numCoveredPoints = 0;
4954   PetscFunctionReturn(PETSC_SUCCESS);
4955 }
4956 
4957 /*@C
4958   DMPlexGetFullMeet - Get an array for the meet of the set of points
4959 
4960   Not Collective
4961 
4962   Input Parameters:
4963 + dm        - The `DMPLEX` object
4964 . numPoints - The number of input points for the meet
4965 - points    - The input points, of length  `numPoints`
4966 
4967   Output Parameters:
4968 + numCoveredPoints - The number of points in the meet
4969 - coveredPoints    - The points in the meet, of length  `numCoveredPoints`
4970 
4971   Level: intermediate
4972 
4973   Fortran Notes:
4974   `points` and `coveredPoints` must be declared with
4975 .vb
4976   PetscInt, pointer :: points(:)
4977   PetscInt, pointer :: coveredPoints(:)
4978 .ve
4979 
4980   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4981 
4982 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetMeet()`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
4983 @*/
4984 PetscErrorCode DMPlexGetFullMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt *coveredPoints[])
4985 {
4986   PetscInt *offsets, **closures;
4987   PetscInt *meet[2];
4988   PetscInt  height = 0, maxSize, meetSize = 0, i = 0;
4989   PetscInt  p, h, c, m, mc;
4990 
4991   PetscFunctionBegin;
4992   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4993   PetscAssertPointer(points, 3);
4994   PetscAssertPointer(numCoveredPoints, 4);
4995   PetscAssertPointer(coveredPoints, 5);
4996 
4997   PetscCall(DMPlexGetDepth(dm, &height));
4998   PetscCall(PetscMalloc1(numPoints, &closures));
4999   PetscCall(DMGetWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
5000   PetscCall(DMPlexGetMaxSizes(dm, &mc, NULL));
5001   maxSize = (mc > 1) ? ((PetscPowInt(mc, height + 1) - 1) / (mc - 1)) : height + 1;
5002   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[0]));
5003   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[1]));
5004 
5005   for (p = 0; p < numPoints; ++p) {
5006     PetscInt closureSize;
5007 
5008     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_TRUE, &closureSize, &closures[p]));
5009 
5010     offsets[p * (height + 2) + 0] = 0;
5011     for (h = 0; h < height + 1; ++h) {
5012       PetscInt pStart, pEnd, i;
5013 
5014       PetscCall(DMPlexGetHeightStratum(dm, h, &pStart, &pEnd));
5015       for (i = offsets[p * (height + 2) + h]; i < closureSize; ++i) {
5016         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
5017           offsets[p * (height + 2) + h + 1] = i;
5018           break;
5019         }
5020       }
5021       if (i == closureSize) offsets[p * (height + 2) + h + 1] = i;
5022     }
5023     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);
5024   }
5025   for (h = 0; h < height + 1; ++h) {
5026     PetscInt dof;
5027 
5028     /* Copy in cone of first point */
5029     dof = offsets[h + 1] - offsets[h];
5030     for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = closures[0][(offsets[h] + meetSize) * 2];
5031     /* Check each successive cone */
5032     for (p = 1; p < numPoints && meetSize; ++p) {
5033       PetscInt newMeetSize = 0;
5034 
5035       dof = offsets[p * (height + 2) + h + 1] - offsets[p * (height + 2) + h];
5036       for (c = 0; c < dof; ++c) {
5037         const PetscInt point = closures[p][(offsets[p * (height + 2) + h] + c) * 2];
5038 
5039         for (m = 0; m < meetSize; ++m) {
5040           if (point == meet[i][m]) {
5041             meet[1 - i][newMeetSize++] = point;
5042             break;
5043           }
5044         }
5045       }
5046       meetSize = newMeetSize;
5047       i        = 1 - i;
5048     }
5049     if (meetSize) break;
5050   }
5051   *numCoveredPoints = meetSize;
5052   *coveredPoints    = meet[i];
5053   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_TRUE, NULL, &closures[p]));
5054   PetscCall(PetscFree(closures));
5055   PetscCall(DMRestoreWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
5056   PetscCall(DMRestoreWorkArray(dm, mc, MPIU_INT, &meet[1 - i]));
5057   PetscFunctionReturn(PETSC_SUCCESS);
5058 }
5059 
5060 /*@
5061   DMPlexEqual - Determine if two `DM` have the same topology
5062 
5063   Not Collective
5064 
5065   Input Parameters:
5066 + dmA - A `DMPLEX` object
5067 - dmB - A `DMPLEX` object
5068 
5069   Output Parameter:
5070 . equal - `PETSC_TRUE` if the topologies are identical
5071 
5072   Level: intermediate
5073 
5074   Note:
5075   We are not solving graph isomorphism, so we do not permute.
5076 
5077 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCone()`
5078 @*/
5079 PetscErrorCode DMPlexEqual(DM dmA, DM dmB, PetscBool *equal)
5080 {
5081   PetscInt depth, depthB, pStart, pEnd, pStartB, pEndB, p;
5082 
5083   PetscFunctionBegin;
5084   PetscValidHeaderSpecific(dmA, DM_CLASSID, 1);
5085   PetscValidHeaderSpecific(dmB, DM_CLASSID, 2);
5086   PetscAssertPointer(equal, 3);
5087 
5088   *equal = PETSC_FALSE;
5089   PetscCall(DMPlexGetDepth(dmA, &depth));
5090   PetscCall(DMPlexGetDepth(dmB, &depthB));
5091   if (depth != depthB) PetscFunctionReturn(PETSC_SUCCESS);
5092   PetscCall(DMPlexGetChart(dmA, &pStart, &pEnd));
5093   PetscCall(DMPlexGetChart(dmB, &pStartB, &pEndB));
5094   if ((pStart != pStartB) || (pEnd != pEndB)) PetscFunctionReturn(PETSC_SUCCESS);
5095   for (p = pStart; p < pEnd; ++p) {
5096     const PetscInt *cone, *coneB, *ornt, *orntB, *support, *supportB;
5097     PetscInt        coneSize, coneSizeB, c, supportSize, supportSizeB, s;
5098 
5099     PetscCall(DMPlexGetConeSize(dmA, p, &coneSize));
5100     PetscCall(DMPlexGetCone(dmA, p, &cone));
5101     PetscCall(DMPlexGetConeOrientation(dmA, p, &ornt));
5102     PetscCall(DMPlexGetConeSize(dmB, p, &coneSizeB));
5103     PetscCall(DMPlexGetCone(dmB, p, &coneB));
5104     PetscCall(DMPlexGetConeOrientation(dmB, p, &orntB));
5105     if (coneSize != coneSizeB) PetscFunctionReturn(PETSC_SUCCESS);
5106     for (c = 0; c < coneSize; ++c) {
5107       if (cone[c] != coneB[c]) PetscFunctionReturn(PETSC_SUCCESS);
5108       if (ornt[c] != orntB[c]) PetscFunctionReturn(PETSC_SUCCESS);
5109     }
5110     PetscCall(DMPlexGetSupportSize(dmA, p, &supportSize));
5111     PetscCall(DMPlexGetSupport(dmA, p, &support));
5112     PetscCall(DMPlexGetSupportSize(dmB, p, &supportSizeB));
5113     PetscCall(DMPlexGetSupport(dmB, p, &supportB));
5114     if (supportSize != supportSizeB) PetscFunctionReturn(PETSC_SUCCESS);
5115     for (s = 0; s < supportSize; ++s) {
5116       if (support[s] != supportB[s]) PetscFunctionReturn(PETSC_SUCCESS);
5117     }
5118   }
5119   *equal = PETSC_TRUE;
5120   PetscFunctionReturn(PETSC_SUCCESS);
5121 }
5122 
5123 /*@
5124   DMPlexGetNumFaceVertices - Returns the number of vertices on a face
5125 
5126   Not Collective
5127 
5128   Input Parameters:
5129 + dm         - The `DMPLEX`
5130 . cellDim    - The cell dimension
5131 - numCorners - The number of vertices on a cell
5132 
5133   Output Parameter:
5134 . numFaceVertices - The number of vertices on a face
5135 
5136   Level: developer
5137 
5138   Note:
5139   Of course this can only work for a restricted set of symmetric shapes
5140 
5141 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCone()`
5142 @*/
5143 PetscErrorCode DMPlexGetNumFaceVertices(DM dm, PetscInt cellDim, PetscInt numCorners, PetscInt *numFaceVertices)
5144 {
5145   MPI_Comm comm;
5146 
5147   PetscFunctionBegin;
5148   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
5149   PetscAssertPointer(numFaceVertices, 4);
5150   switch (cellDim) {
5151   case 0:
5152     *numFaceVertices = 0;
5153     break;
5154   case 1:
5155     *numFaceVertices = 1;
5156     break;
5157   case 2:
5158     switch (numCorners) {
5159     case 3:                 /* triangle */
5160       *numFaceVertices = 2; /* Edge has 2 vertices */
5161       break;
5162     case 4:                 /* quadrilateral */
5163       *numFaceVertices = 2; /* Edge has 2 vertices */
5164       break;
5165     case 6:                 /* quadratic triangle, tri and quad cohesive Lagrange cells */
5166       *numFaceVertices = 3; /* Edge has 3 vertices */
5167       break;
5168     case 9:                 /* quadratic quadrilateral, quadratic quad cohesive Lagrange cells */
5169       *numFaceVertices = 3; /* Edge has 3 vertices */
5170       break;
5171     default:
5172       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
5173     }
5174     break;
5175   case 3:
5176     switch (numCorners) {
5177     case 4:                 /* tetradehdron */
5178       *numFaceVertices = 3; /* Face has 3 vertices */
5179       break;
5180     case 6:                 /* tet cohesive cells */
5181       *numFaceVertices = 4; /* Face has 4 vertices */
5182       break;
5183     case 8:                 /* hexahedron */
5184       *numFaceVertices = 4; /* Face has 4 vertices */
5185       break;
5186     case 9:                 /* tet cohesive Lagrange cells */
5187       *numFaceVertices = 6; /* Face has 6 vertices */
5188       break;
5189     case 10:                /* quadratic tetrahedron */
5190       *numFaceVertices = 6; /* Face has 6 vertices */
5191       break;
5192     case 12:                /* hex cohesive Lagrange cells */
5193       *numFaceVertices = 6; /* Face has 6 vertices */
5194       break;
5195     case 18:                /* quadratic tet cohesive Lagrange cells */
5196       *numFaceVertices = 6; /* Face has 6 vertices */
5197       break;
5198     case 27:                /* quadratic hexahedron, quadratic hex cohesive Lagrange cells */
5199       *numFaceVertices = 9; /* Face has 9 vertices */
5200       break;
5201     default:
5202       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
5203     }
5204     break;
5205   default:
5206     SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid cell dimension %" PetscInt_FMT, cellDim);
5207   }
5208   PetscFunctionReturn(PETSC_SUCCESS);
5209 }
5210 
5211 /*@
5212   DMPlexGetDepthLabel - Get the `DMLabel` recording the depth of each point
5213 
5214   Not Collective
5215 
5216   Input Parameter:
5217 . dm - The `DMPLEX` object
5218 
5219   Output Parameter:
5220 . depthLabel - The `DMLabel` recording point depth
5221 
5222   Level: developer
5223 
5224 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepth()`, `DMPlexGetHeightStratum()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`,
5225 @*/
5226 PetscErrorCode DMPlexGetDepthLabel(DM dm, DMLabel *depthLabel)
5227 {
5228   PetscFunctionBegin;
5229   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5230   PetscAssertPointer(depthLabel, 2);
5231   *depthLabel = dm->depthLabel;
5232   PetscFunctionReturn(PETSC_SUCCESS);
5233 }
5234 
5235 /*@
5236   DMPlexGetDepth - Get the depth of the DAG representing this mesh
5237 
5238   Not Collective
5239 
5240   Input Parameter:
5241 . dm - The `DMPLEX` object
5242 
5243   Output Parameter:
5244 . depth - The number of strata (breadth first levels) in the DAG
5245 
5246   Level: developer
5247 
5248   Notes:
5249   This returns maximum of point depths over all points, i.e. maximum value of the label returned by `DMPlexGetDepthLabel()`.
5250 
5251   The point depth is described more in detail in `DMPlexGetDepthStratum()`.
5252 
5253   An empty mesh gives -1.
5254 
5255 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepthLabel()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`
5256 @*/
5257 PetscErrorCode DMPlexGetDepth(DM dm, PetscInt *depth)
5258 {
5259   DM_Plex *mesh = (DM_Plex *)dm->data;
5260   DMLabel  label;
5261   PetscInt d = -1;
5262 
5263   PetscFunctionBegin;
5264   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5265   PetscAssertPointer(depth, 2);
5266   if (mesh->tr) {
5267     PetscCall(DMPlexTransformGetDepth(mesh->tr, depth));
5268   } else {
5269     PetscCall(DMPlexGetDepthLabel(dm, &label));
5270     // Allow missing depths
5271     if (label) PetscCall(DMLabelGetValueBounds(label, NULL, &d));
5272     *depth = d;
5273   }
5274   PetscFunctionReturn(PETSC_SUCCESS);
5275 }
5276 
5277 /*@
5278   DMPlexGetDepthStratum - Get the bounds [`start`, `end`) for all points at a certain depth.
5279 
5280   Not Collective
5281 
5282   Input Parameters:
5283 + dm    - The `DMPLEX` object
5284 - depth - The requested depth
5285 
5286   Output Parameters:
5287 + start - The first point at this `depth`
5288 - end   - One beyond the last point at this `depth`
5289 
5290   Level: developer
5291 
5292   Notes:
5293   Depth indexing is related to topological dimension.  Depth stratum 0 contains the lowest topological dimension points,
5294   often "vertices".  If the mesh is "interpolated" (see `DMPlexInterpolate()`), then depth stratum 1 contains the next
5295   higher dimension, e.g., "edges".
5296 
5297 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetHeightStratum()`, `DMPlexGetCellTypeStratum()`, `DMPlexGetDepth()`, `DMPlexGetDepthLabel()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`, `DMPlexInterpolate()`
5298 @*/
5299 PetscErrorCode DMPlexGetDepthStratum(DM dm, PetscInt depth, PetscInt *start, PetscInt *end)
5300 {
5301   DM_Plex *mesh = (DM_Plex *)dm->data;
5302   DMLabel  label;
5303   PetscInt pStart, pEnd;
5304 
5305   PetscFunctionBegin;
5306   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5307   if (start) {
5308     PetscAssertPointer(start, 3);
5309     *start = 0;
5310   }
5311   if (end) {
5312     PetscAssertPointer(end, 4);
5313     *end = 0;
5314   }
5315   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
5316   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
5317   if (depth < 0) {
5318     if (start) *start = pStart;
5319     if (end) *end = pEnd;
5320     PetscFunctionReturn(PETSC_SUCCESS);
5321   }
5322   if (mesh->tr) {
5323     PetscCall(DMPlexTransformGetDepthStratum(mesh->tr, depth, start, end));
5324   } else {
5325     PetscCall(DMPlexGetDepthLabel(dm, &label));
5326     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named depth was found");
5327     PetscCall(DMLabelGetStratumBounds(label, depth, start, end));
5328   }
5329   PetscFunctionReturn(PETSC_SUCCESS);
5330 }
5331 
5332 /*@
5333   DMPlexGetHeightStratum - Get the bounds [`start`, `end`) for all points at a certain height.
5334 
5335   Not Collective
5336 
5337   Input Parameters:
5338 + dm     - The `DMPLEX` object
5339 - height - The requested height
5340 
5341   Output Parameters:
5342 + start - The first point at this `height`
5343 - end   - One beyond the last point at this `height`
5344 
5345   Level: developer
5346 
5347   Notes:
5348   Height indexing is related to topological codimension.  Height stratum 0 contains the highest topological dimension
5349   points, often called "cells" or "elements".  If the mesh is "interpolated" (see `DMPlexInterpolate()`), then height
5350   stratum 1 contains the boundary of these "cells", often called "faces" or "facets".
5351 
5352 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepthStratum()`, `DMPlexGetCellTypeStratum()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
5353 @*/
5354 PetscErrorCode DMPlexGetHeightStratum(DM dm, PetscInt height, PetscInt *start, PetscInt *end)
5355 {
5356   DMLabel  label;
5357   PetscInt depth, pStart, pEnd;
5358 
5359   PetscFunctionBegin;
5360   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5361   if (start) {
5362     PetscAssertPointer(start, 3);
5363     *start = 0;
5364   }
5365   if (end) {
5366     PetscAssertPointer(end, 4);
5367     *end = 0;
5368   }
5369   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
5370   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
5371   if (height < 0) {
5372     if (start) *start = pStart;
5373     if (end) *end = pEnd;
5374     PetscFunctionReturn(PETSC_SUCCESS);
5375   }
5376   PetscCall(DMPlexGetDepthLabel(dm, &label));
5377   if (label) PetscCall(DMLabelGetNumValues(label, &depth));
5378   else PetscCall(DMGetDimension(dm, &depth));
5379   PetscCheck(depth >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Depth not yet computed");
5380   PetscCall(DMPlexGetDepthStratum(dm, depth - 1 - height, start, end));
5381   PetscFunctionReturn(PETSC_SUCCESS);
5382 }
5383 
5384 /*@
5385   DMPlexGetPointDepth - Get the `depth` of a given point
5386 
5387   Not Collective
5388 
5389   Input Parameters:
5390 + dm    - The `DMPLEX` object
5391 - point - The point
5392 
5393   Output Parameter:
5394 . depth - The depth of the `point`
5395 
5396   Level: intermediate
5397 
5398 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
5399 @*/
5400 PetscErrorCode DMPlexGetPointDepth(DM dm, PetscInt point, PetscInt *depth)
5401 {
5402   PetscFunctionBegin;
5403   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5404   PetscAssertPointer(depth, 3);
5405   PetscCall(DMLabelGetValue(dm->depthLabel, point, depth));
5406   PetscFunctionReturn(PETSC_SUCCESS);
5407 }
5408 
5409 /*@
5410   DMPlexGetPointHeight - Get the `height` of a given point
5411 
5412   Not Collective
5413 
5414   Input Parameters:
5415 + dm    - The `DMPLEX` object
5416 - point - The point
5417 
5418   Output Parameter:
5419 . height - The height of the `point`
5420 
5421   Level: intermediate
5422 
5423 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointDepth()`
5424 @*/
5425 PetscErrorCode DMPlexGetPointHeight(DM dm, PetscInt point, PetscInt *height)
5426 {
5427   PetscInt n, pDepth;
5428 
5429   PetscFunctionBegin;
5430   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5431   PetscAssertPointer(height, 3);
5432   PetscCall(DMLabelGetNumValues(dm->depthLabel, &n));
5433   PetscCall(DMLabelGetValue(dm->depthLabel, point, &pDepth));
5434   *height = n - 1 - pDepth; /* DAG depth is n-1 */
5435   PetscFunctionReturn(PETSC_SUCCESS);
5436 }
5437 
5438 /*@
5439   DMPlexGetCellTypeLabel - Get the `DMLabel` recording the polytope type of each cell
5440 
5441   Not Collective
5442 
5443   Input Parameter:
5444 . dm - The `DMPLEX` object
5445 
5446   Output Parameter:
5447 . celltypeLabel - The `DMLabel` recording cell polytope type
5448 
5449   Level: developer
5450 
5451   Note:
5452   This function will trigger automatica computation of cell types. This can be disabled by calling
5453   `DMCreateLabel`(dm, "celltype") beforehand.
5454 
5455 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMCreateLabel()`
5456 @*/
5457 PetscErrorCode DMPlexGetCellTypeLabel(DM dm, DMLabel *celltypeLabel)
5458 {
5459   PetscFunctionBegin;
5460   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5461   PetscAssertPointer(celltypeLabel, 2);
5462   if (!dm->celltypeLabel) PetscCall(DMPlexComputeCellTypes(dm));
5463   *celltypeLabel = dm->celltypeLabel;
5464   PetscFunctionReturn(PETSC_SUCCESS);
5465 }
5466 
5467 /*@
5468   DMPlexGetCellType - Get the polytope type of a given cell
5469 
5470   Not Collective
5471 
5472   Input Parameters:
5473 + dm   - The `DMPLEX` object
5474 - cell - The cell
5475 
5476   Output Parameter:
5477 . celltype - The polytope type of the cell
5478 
5479   Level: intermediate
5480 
5481 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPolytopeType`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`
5482 @*/
5483 PetscErrorCode DMPlexGetCellType(DM dm, PetscInt cell, DMPolytopeType *celltype)
5484 {
5485   DM_Plex *mesh = (DM_Plex *)dm->data;
5486   DMLabel  label;
5487   PetscInt ct;
5488 
5489   PetscFunctionBegin;
5490   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5491   PetscAssertPointer(celltype, 3);
5492   if (mesh->tr) {
5493     PetscCall(DMPlexTransformGetCellType(mesh->tr, cell, celltype));
5494   } else {
5495     PetscInt pStart, pEnd;
5496 
5497     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, NULL));
5498     if (!mesh->cellTypes) { /* XXX remove? optimize? */
5499       PetscCall(PetscSectionGetChart(mesh->coneSection, NULL, &pEnd));
5500       PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
5501       PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5502       for (PetscInt p = pStart; p < pEnd; p++) {
5503         PetscCall(DMLabelGetValue(label, p, &ct));
5504         mesh->cellTypes[p - pStart].value_as_uint8 = (uint8_t)ct;
5505       }
5506     }
5507     *celltype = (DMPolytopeType)mesh->cellTypes[cell - pStart].value_as_uint8;
5508     if (PetscDefined(USE_DEBUG)) {
5509       PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5510       PetscCall(DMLabelGetValue(label, cell, &ct));
5511       PetscCheck(ct >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Cell %" PetscInt_FMT " has not been assigned a cell type", cell);
5512       PetscCheck(ct == (PetscInt)*celltype, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid cellType for %" PetscInt_FMT ": %d != %" PetscInt_FMT, cell, (int)*celltype, ct);
5513     }
5514   }
5515   PetscFunctionReturn(PETSC_SUCCESS);
5516 }
5517 
5518 /*@
5519   DMPlexSetCellType - Set the polytope type of a given cell
5520 
5521   Not Collective
5522 
5523   Input Parameters:
5524 + dm       - The `DMPLEX` object
5525 . cell     - The cell
5526 - celltype - The polytope type of the cell
5527 
5528   Level: advanced
5529 
5530   Note:
5531   By default, cell types will be automatically computed using `DMPlexComputeCellTypes()` before this function
5532   is executed. This function will override the computed type. However, if automatic classification will not succeed
5533   and a user wants to manually specify all types, the classification must be disabled by calling
5534   DMCreateLabel(dm, "celltype") before getting or setting any cell types.
5535 
5536 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexComputeCellTypes()`, `DMCreateLabel()`
5537 @*/
5538 PetscErrorCode DMPlexSetCellType(DM dm, PetscInt cell, DMPolytopeType celltype)
5539 {
5540   DM_Plex *mesh = (DM_Plex *)dm->data;
5541   DMLabel  label;
5542   PetscInt pStart, pEnd;
5543 
5544   PetscFunctionBegin;
5545   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5546   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
5547   PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5548   PetscCall(DMLabelSetValue(label, cell, celltype));
5549   if (!mesh->cellTypes) PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
5550   mesh->cellTypes[cell - pStart].value_as_uint8 = (uint8_t)celltype;
5551   PetscFunctionReturn(PETSC_SUCCESS);
5552 }
5553 
5554 PetscErrorCode DMCreateCoordinateDM_Plex(DM dm, DM *cdm)
5555 {
5556   PetscSection section;
5557   PetscInt     maxHeight;
5558   const char  *prefix;
5559 
5560   PetscFunctionBegin;
5561   PetscCall(DMClone(dm, cdm));
5562   PetscCall(PetscObjectGetOptionsPrefix((PetscObject)dm, &prefix));
5563   PetscCall(PetscObjectSetOptionsPrefix((PetscObject)*cdm, prefix));
5564   PetscCall(PetscObjectAppendOptionsPrefix((PetscObject)*cdm, "cdm_"));
5565   PetscCall(DMPlexGetMaxProjectionHeight(dm, &maxHeight));
5566   PetscCall(DMPlexSetMaxProjectionHeight(*cdm, maxHeight));
5567   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
5568   PetscCall(DMSetLocalSection(*cdm, section));
5569   PetscCall(PetscSectionDestroy(&section));
5570 
5571   PetscCall(DMSetNumFields(*cdm, 1));
5572   PetscCall(DMCreateDS(*cdm));
5573   (*cdm)->cloneOpts = PETSC_TRUE;
5574   if (dm->setfromoptionscalled) PetscCall(DMSetFromOptions(*cdm));
5575   PetscFunctionReturn(PETSC_SUCCESS);
5576 }
5577 
5578 PetscErrorCode DMCreateCoordinateField_Plex(DM dm, DMField *field)
5579 {
5580   Vec coordsLocal, cellCoordsLocal;
5581   DM  coordsDM, cellCoordsDM;
5582 
5583   PetscFunctionBegin;
5584   *field = NULL;
5585   PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
5586   PetscCall(DMGetCoordinateDM(dm, &coordsDM));
5587   PetscCall(DMGetCellCoordinatesLocal(dm, &cellCoordsLocal));
5588   PetscCall(DMGetCellCoordinateDM(dm, &cellCoordsDM));
5589   if (coordsLocal && coordsDM) {
5590     if (cellCoordsLocal && cellCoordsDM) PetscCall(DMFieldCreateDSWithDG(coordsDM, cellCoordsDM, 0, coordsLocal, cellCoordsLocal, field));
5591     else PetscCall(DMFieldCreateDS(coordsDM, 0, coordsLocal, field));
5592   }
5593   PetscFunctionReturn(PETSC_SUCCESS);
5594 }
5595 
5596 /*@
5597   DMPlexGetConeSection - Return a section which describes the layout of cone data
5598 
5599   Not Collective
5600 
5601   Input Parameter:
5602 . dm - The `DMPLEX` object
5603 
5604   Output Parameter:
5605 . section - The `PetscSection` object
5606 
5607   Level: developer
5608 
5609 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSupportSection()`, `DMPlexGetCones()`, `DMPlexGetConeOrientations()`, `PetscSection`
5610 @*/
5611 PetscErrorCode DMPlexGetConeSection(DM dm, PetscSection *section)
5612 {
5613   DM_Plex *mesh = (DM_Plex *)dm->data;
5614 
5615   PetscFunctionBegin;
5616   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5617   if (section) *section = mesh->coneSection;
5618   PetscFunctionReturn(PETSC_SUCCESS);
5619 }
5620 
5621 /*@
5622   DMPlexGetSupportSection - Return a section which describes the layout of support data
5623 
5624   Not Collective
5625 
5626   Input Parameter:
5627 . dm - The `DMPLEX` object
5628 
5629   Output Parameter:
5630 . section - The `PetscSection` object
5631 
5632   Level: developer
5633 
5634 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `PetscSection`
5635 @*/
5636 PetscErrorCode DMPlexGetSupportSection(DM dm, PetscSection *section)
5637 {
5638   DM_Plex *mesh = (DM_Plex *)dm->data;
5639 
5640   PetscFunctionBegin;
5641   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5642   if (section) *section = mesh->supportSection;
5643   PetscFunctionReturn(PETSC_SUCCESS);
5644 }
5645 
5646 /*@C
5647   DMPlexGetCones - Return cone data
5648 
5649   Not Collective
5650 
5651   Input Parameter:
5652 . dm - The `DMPLEX` object
5653 
5654   Output Parameter:
5655 . cones - The cone for each point
5656 
5657   Level: developer
5658 
5659 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`
5660 @*/
5661 PetscErrorCode DMPlexGetCones(DM dm, PetscInt *cones[])
5662 {
5663   DM_Plex *mesh = (DM_Plex *)dm->data;
5664 
5665   PetscFunctionBegin;
5666   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5667   if (cones) *cones = mesh->cones;
5668   PetscFunctionReturn(PETSC_SUCCESS);
5669 }
5670 
5671 /*@C
5672   DMPlexGetConeOrientations - Return cone orientation data
5673 
5674   Not Collective
5675 
5676   Input Parameter:
5677 . dm - The `DMPLEX` object
5678 
5679   Output Parameter:
5680 . coneOrientations - The array of cone orientations for all points
5681 
5682   Level: developer
5683 
5684   Notes:
5685   The `PetscSection` returned by `DMPlexGetConeSection()` partitions coneOrientations into cone orientations of particular points
5686   as returned by `DMPlexGetConeOrientation()`.
5687 
5688   The meaning of coneOrientations values is detailed in `DMPlexGetConeOrientation()`.
5689 
5690 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `DMPlexGetConeOrientation()`, `PetscSection`
5691 @*/
5692 PetscErrorCode DMPlexGetConeOrientations(DM dm, PetscInt *coneOrientations[])
5693 {
5694   DM_Plex *mesh = (DM_Plex *)dm->data;
5695 
5696   PetscFunctionBegin;
5697   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5698   if (coneOrientations) *coneOrientations = mesh->coneOrientations;
5699   PetscFunctionReturn(PETSC_SUCCESS);
5700 }
5701 
5702 /* FEM Support */
5703 
5704 PetscErrorCode DMPlexGetAllCells_Internal(DM plex, IS *cellIS)
5705 {
5706   PetscInt depth;
5707 
5708   PetscFunctionBegin;
5709   PetscCall(DMPlexGetDepth(plex, &depth));
5710   PetscCall(DMGetStratumIS(plex, "dim", depth, cellIS));
5711   if (!*cellIS) PetscCall(DMGetStratumIS(plex, "depth", depth, cellIS));
5712   PetscFunctionReturn(PETSC_SUCCESS);
5713 }
5714 
5715 PetscErrorCode DMPlexGetAllFaces_Internal(DM plex, IS *faceIS)
5716 {
5717   PetscInt depth;
5718 
5719   PetscFunctionBegin;
5720   PetscCall(DMPlexGetDepth(plex, &depth));
5721   PetscCall(DMGetStratumIS(plex, "dim", depth - 1, faceIS));
5722   if (!*faceIS) PetscCall(DMGetStratumIS(plex, "depth", depth - 1, faceIS));
5723   PetscFunctionReturn(PETSC_SUCCESS);
5724 }
5725 
5726 /*
5727  Returns number of components and tensor degree for the field.  For interpolated meshes, line should be a point
5728  representing a line in the section.
5729 */
5730 static PetscErrorCode PetscSectionFieldGetTensorDegree_Private(DM dm, PetscSection section, PetscInt field, PetscInt line, PetscInt *Nc, PetscInt *k, PetscBool *continuous, PetscBool *tensor)
5731 {
5732   PetscObject  obj;
5733   PetscClassId id;
5734   PetscFE      fe = NULL;
5735 
5736   PetscFunctionBeginHot;
5737   PetscCall(PetscSectionGetFieldComponents(section, field, Nc));
5738   PetscCall(DMGetField(dm, field, NULL, &obj));
5739   PetscCall(PetscObjectGetClassId(obj, &id));
5740   if (id == PETSCFE_CLASSID) fe = (PetscFE)obj;
5741 
5742   if (!fe) {
5743     /* Assume the full interpolated mesh is in the chart; lines in particular */
5744     /* An order k SEM disc has k-1 dofs on an edge */
5745     PetscCall(PetscSectionGetFieldDof(section, line, field, k));
5746     *k = *k / *Nc + 1;
5747   } else {
5748     PetscInt       dual_space_size, dim;
5749     PetscDualSpace dsp;
5750 
5751     PetscCall(DMGetDimension(dm, &dim));
5752     PetscCall(PetscFEGetDualSpace(fe, &dsp));
5753     PetscCall(PetscDualSpaceGetDimension(dsp, &dual_space_size));
5754     *k = (PetscInt)PetscCeilReal(PetscPowReal(dual_space_size / *Nc, 1.0 / dim)) - 1;
5755     PetscCall(PetscDualSpaceLagrangeGetContinuity(dsp, continuous));
5756     PetscCall(PetscDualSpaceLagrangeGetTensor(dsp, tensor));
5757   }
5758   PetscFunctionReturn(PETSC_SUCCESS);
5759 }
5760 
5761 static PetscErrorCode GetFieldSize_Private(PetscInt dim, PetscInt k, PetscBool tensor, PetscInt *dof)
5762 {
5763   PetscFunctionBeginHot;
5764   if (tensor) {
5765     *dof = PetscPowInt(k + 1, dim);
5766   } else {
5767     switch (dim) {
5768     case 1:
5769       *dof = k + 1;
5770       break;
5771     case 2:
5772       *dof = ((k + 1) * (k + 2)) / 2;
5773       break;
5774     case 3:
5775       *dof = ((k + 1) * (k + 2) * (k + 3)) / 6;
5776       break;
5777     default:
5778       *dof = 0;
5779     }
5780   }
5781   PetscFunctionReturn(PETSC_SUCCESS);
5782 }
5783 
5784 /*@
5785   DMPlexSetClosurePermutationTensor - Create a permutation from the default (BFS) point ordering in the closure, to a
5786   lexicographic ordering over the tensor product cell (i.e., line, quad, hex, etc.), and set this permutation in the
5787   section provided (or the section of the `DM`).
5788 
5789   Input Parameters:
5790 + dm      - The `DM`
5791 . point   - Either a cell (highest dim point) or an edge (dim 1 point), or `PETSC_DETERMINE`
5792 - section - The `PetscSection` to reorder, or `NULL` for the default section
5793 
5794   Example:
5795   A typical interpolated single-quad mesh might order points as
5796 .vb
5797   [c0, v1, v2, v3, v4, e5, e6, e7, e8]
5798 
5799   v4 -- e6 -- v3
5800   |           |
5801   e7    c0    e8
5802   |           |
5803   v1 -- e5 -- v2
5804 .ve
5805 
5806   (There is no significance to the ordering described here.)  The default section for a Q3 quad might typically assign
5807   dofs in the order of points, e.g.,
5808 .vb
5809     c0 -> [0,1,2,3]
5810     v1 -> [4]
5811     ...
5812     e5 -> [8, 9]
5813 .ve
5814 
5815   which corresponds to the dofs
5816 .vb
5817     6   10  11  7
5818     13  2   3   15
5819     12  0   1   14
5820     4   8   9   5
5821 .ve
5822 
5823   The closure in BFS ordering works through height strata (cells, edges, vertices) to produce the ordering
5824 .vb
5825   0 1 2 3 8 9 14 15 11 10 13 12 4 5 7 6
5826 .ve
5827 
5828   After calling DMPlexSetClosurePermutationTensor(), the closure will be ordered lexicographically,
5829 .vb
5830    4 8 9 5 12 0 1 14 13 2 3 15 6 10 11 7
5831 .ve
5832 
5833   Level: developer
5834 
5835   Notes:
5836   The point is used to determine the number of dofs/field on an edge. For SEM, this is related to the polynomial
5837   degree of the basis.
5838 
5839   This is required to run with libCEED.
5840 
5841 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMGetLocalSection()`, `PetscSectionSetClosurePermutation()`, `DMSetGlobalSection()`
5842 @*/
5843 PetscErrorCode DMPlexSetClosurePermutationTensor(DM dm, PetscInt point, PetscSection section)
5844 {
5845   DMLabel   label;
5846   PetscInt  dim, depth = -1, eStart = -1, Nf;
5847   PetscBool continuous = PETSC_TRUE, tensor = PETSC_TRUE;
5848 
5849   PetscFunctionBegin;
5850   PetscCall(DMGetDimension(dm, &dim));
5851   if (dim < 1) PetscFunctionReturn(PETSC_SUCCESS);
5852   if (point < 0) {
5853     PetscInt sStart, sEnd;
5854 
5855     PetscCall(DMPlexGetDepthStratum(dm, 1, &sStart, &sEnd));
5856     point = sEnd - sStart ? sStart : point;
5857   }
5858   PetscCall(DMPlexGetDepthLabel(dm, &label));
5859   if (point >= 0) PetscCall(DMLabelGetValue(label, point, &depth));
5860   if (!section) PetscCall(DMGetLocalSection(dm, &section));
5861   if (depth == 1) {
5862     eStart = point;
5863   } else if (depth == dim) {
5864     const PetscInt *cone;
5865 
5866     PetscCall(DMPlexGetCone(dm, point, &cone));
5867     if (dim == 2) eStart = cone[0];
5868     else if (dim == 3) {
5869       const PetscInt *cone2;
5870       PetscCall(DMPlexGetCone(dm, cone[0], &cone2));
5871       eStart = cone2[0];
5872     } 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);
5873   } 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);
5874 
5875   PetscCall(PetscSectionGetNumFields(section, &Nf));
5876   for (PetscInt d = 1; d <= dim; d++) {
5877     PetscInt  k, f, Nc, c, i, j, size = 0, offset = 0, foffset = 0;
5878     PetscInt *perm;
5879 
5880     for (f = 0; f < Nf; ++f) {
5881       PetscInt dof;
5882 
5883       PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
5884       PetscCheck(dim == 1 || tensor || !continuous, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Continuous field %" PetscInt_FMT " must have a tensor product discretization", f);
5885       if (!continuous && d < dim) continue;
5886       PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
5887       size += dof * Nc;
5888     }
5889     PetscCall(PetscMalloc1(size, &perm));
5890     for (f = 0; f < Nf; ++f) {
5891       switch (d) {
5892       case 1:
5893         PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
5894         if (!continuous && d < dim) continue;
5895         /*
5896          Original ordering is [ edge of length k-1; vtx0; vtx1 ]
5897          We want              [ vtx0; edge of length k-1; vtx1 ]
5898          */
5899         if (continuous) {
5900           for (c = 0; c < Nc; c++, offset++) perm[offset] = (k - 1) * Nc + c + foffset;
5901           for (i = 0; i < k - 1; i++)
5902             for (c = 0; c < Nc; c++, offset++) perm[offset] = i * Nc + c + foffset;
5903           for (c = 0; c < Nc; c++, offset++) perm[offset] = k * Nc + c + foffset;
5904           foffset = offset;
5905         } else {
5906           PetscInt dof;
5907 
5908           PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
5909           for (i = 0; i < dof * Nc; ++i, ++offset) perm[offset] = i + foffset;
5910           foffset = offset;
5911         }
5912         break;
5913       case 2:
5914         /* The original quad closure is oriented clockwise, {f, e_b, e_r, e_t, e_l, v_lb, v_rb, v_tr, v_tl} */
5915         PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
5916         if (!continuous && d < dim) continue;
5917         /* The SEM order is
5918 
5919          v_lb, {e_b}, v_rb,
5920          e^{(k-1)-i}_l, {f^{i*(k-1)}}, e^i_r,
5921          v_lt, reverse {e_t}, v_rt
5922          */
5923         if (continuous) {
5924           const PetscInt of   = 0;
5925           const PetscInt oeb  = of + PetscSqr(k - 1);
5926           const PetscInt oer  = oeb + (k - 1);
5927           const PetscInt oet  = oer + (k - 1);
5928           const PetscInt oel  = oet + (k - 1);
5929           const PetscInt ovlb = oel + (k - 1);
5930           const PetscInt ovrb = ovlb + 1;
5931           const PetscInt ovrt = ovrb + 1;
5932           const PetscInt ovlt = ovrt + 1;
5933           PetscInt       o;
5934 
5935           /* bottom */
5936           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlb * Nc + c + foffset;
5937           for (o = oeb; o < oer; ++o)
5938             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5939           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrb * Nc + c + foffset;
5940           /* middle */
5941           for (i = 0; i < k - 1; ++i) {
5942             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oel + (k - 2) - i) * Nc + c + foffset;
5943             for (o = of + (k - 1) * i; o < of + (k - 1) * (i + 1); ++o)
5944               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5945             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oer + i) * Nc + c + foffset;
5946           }
5947           /* top */
5948           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlt * Nc + c + foffset;
5949           for (o = oel - 1; o >= oet; --o)
5950             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5951           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrt * Nc + c + foffset;
5952           foffset = offset;
5953         } else {
5954           PetscInt dof;
5955 
5956           PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
5957           for (i = 0; i < dof * Nc; ++i, ++offset) perm[offset] = i + foffset;
5958           foffset = offset;
5959         }
5960         break;
5961       case 3:
5962         /* The original hex closure is
5963 
5964          {c,
5965          f_b, f_t, f_f, f_b, f_r, f_l,
5966          e_bl, e_bb, e_br, e_bf,  e_tf, e_tr, e_tb, e_tl,  e_rf, e_lf, e_lb, e_rb,
5967          v_blf, v_blb, v_brb, v_brf, v_tlf, v_trf, v_trb, v_tlb}
5968          */
5969         PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
5970         if (!continuous && d < dim) continue;
5971         /* The SEM order is
5972          Bottom Slice
5973          v_blf, {e^{(k-1)-n}_bf}, v_brf,
5974          e^{i}_bl, f^{n*(k-1)+(k-1)-i}_b, e^{(k-1)-i}_br,
5975          v_blb, {e_bb}, v_brb,
5976 
5977          Middle Slice (j)
5978          {e^{(k-1)-j}_lf}, {f^{j*(k-1)+n}_f}, e^j_rf,
5979          f^{i*(k-1)+j}_l, {c^{(j*(k-1) + i)*(k-1)+n}_t}, f^{j*(k-1)+i}_r,
5980          e^j_lb, {f^{j*(k-1)+(k-1)-n}_b}, e^{(k-1)-j}_rb,
5981 
5982          Top Slice
5983          v_tlf, {e_tf}, v_trf,
5984          e^{(k-1)-i}_tl, {f^{i*(k-1)}_t}, e^{i}_tr,
5985          v_tlb, {e^{(k-1)-n}_tb}, v_trb,
5986          */
5987         if (continuous) {
5988           const PetscInt oc    = 0;
5989           const PetscInt ofb   = oc + PetscSqr(k - 1) * (k - 1);
5990           const PetscInt oft   = ofb + PetscSqr(k - 1);
5991           const PetscInt off   = oft + PetscSqr(k - 1);
5992           const PetscInt ofk   = off + PetscSqr(k - 1);
5993           const PetscInt ofr   = ofk + PetscSqr(k - 1);
5994           const PetscInt ofl   = ofr + PetscSqr(k - 1);
5995           const PetscInt oebl  = ofl + PetscSqr(k - 1);
5996           const PetscInt oebb  = oebl + (k - 1);
5997           const PetscInt oebr  = oebb + (k - 1);
5998           const PetscInt oebf  = oebr + (k - 1);
5999           const PetscInt oetf  = oebf + (k - 1);
6000           const PetscInt oetr  = oetf + (k - 1);
6001           const PetscInt oetb  = oetr + (k - 1);
6002           const PetscInt oetl  = oetb + (k - 1);
6003           const PetscInt oerf  = oetl + (k - 1);
6004           const PetscInt oelf  = oerf + (k - 1);
6005           const PetscInt oelb  = oelf + (k - 1);
6006           const PetscInt oerb  = oelb + (k - 1);
6007           const PetscInt ovblf = oerb + (k - 1);
6008           const PetscInt ovblb = ovblf + 1;
6009           const PetscInt ovbrb = ovblb + 1;
6010           const PetscInt ovbrf = ovbrb + 1;
6011           const PetscInt ovtlf = ovbrf + 1;
6012           const PetscInt ovtrf = ovtlf + 1;
6013           const PetscInt ovtrb = ovtrf + 1;
6014           const PetscInt ovtlb = ovtrb + 1;
6015           PetscInt       o, n;
6016 
6017           /* Bottom Slice */
6018           /*   bottom */
6019           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblf * Nc + c + foffset;
6020           for (o = oetf - 1; o >= oebf; --o)
6021             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6022           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrf * Nc + c + foffset;
6023           /*   middle */
6024           for (i = 0; i < k - 1; ++i) {
6025             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebl + i) * Nc + c + foffset;
6026             for (n = 0; n < k - 1; ++n) {
6027               o = ofb + n * (k - 1) + i;
6028               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6029             }
6030             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebr + (k - 2) - i) * Nc + c + foffset;
6031           }
6032           /*   top */
6033           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblb * Nc + c + foffset;
6034           for (o = oebb; o < oebr; ++o)
6035             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6036           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrb * Nc + c + foffset;
6037 
6038           /* Middle Slice */
6039           for (j = 0; j < k - 1; ++j) {
6040             /*   bottom */
6041             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelf + (k - 2) - j) * Nc + c + foffset;
6042             for (o = off + j * (k - 1); o < off + (j + 1) * (k - 1); ++o)
6043               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6044             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerf + j) * Nc + c + foffset;
6045             /*   middle */
6046             for (i = 0; i < k - 1; ++i) {
6047               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofl + i * (k - 1) + j) * Nc + c + foffset;
6048               for (n = 0; n < k - 1; ++n)
6049                 for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oc + (j * (k - 1) + i) * (k - 1) + n) * Nc + c + foffset;
6050               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofr + j * (k - 1) + i) * Nc + c + foffset;
6051             }
6052             /*   top */
6053             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelb + j) * Nc + c + foffset;
6054             for (o = ofk + j * (k - 1) + (k - 2); o >= ofk + j * (k - 1); --o)
6055               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6056             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerb + (k - 2) - j) * Nc + c + foffset;
6057           }
6058 
6059           /* Top Slice */
6060           /*   bottom */
6061           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlf * Nc + c + foffset;
6062           for (o = oetf; o < oetr; ++o)
6063             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6064           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrf * Nc + c + foffset;
6065           /*   middle */
6066           for (i = 0; i < k - 1; ++i) {
6067             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetl + (k - 2) - i) * Nc + c + foffset;
6068             for (n = 0; n < k - 1; ++n)
6069               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oft + i * (k - 1) + n) * Nc + c + foffset;
6070             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetr + i) * Nc + c + foffset;
6071           }
6072           /*   top */
6073           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlb * Nc + c + foffset;
6074           for (o = oetl - 1; o >= oetb; --o)
6075             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6076           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrb * Nc + c + foffset;
6077 
6078           foffset = offset;
6079         } else {
6080           PetscInt dof;
6081 
6082           PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
6083           for (i = 0; i < dof * Nc; ++i, ++offset) perm[offset] = i + foffset;
6084           foffset = offset;
6085         }
6086         break;
6087       default:
6088         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "No spectral ordering for dimension %" PetscInt_FMT, d);
6089       }
6090     }
6091     PetscCheck(offset == size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Number of permutation entries %" PetscInt_FMT " != %" PetscInt_FMT, offset, size);
6092     /* Check permutation */
6093     {
6094       PetscInt *check;
6095 
6096       PetscCall(PetscMalloc1(size, &check));
6097       for (i = 0; i < size; ++i) {
6098         check[i] = -1;
6099         PetscCheck(perm[i] >= 0 && perm[i] < size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid permutation index p[%" PetscInt_FMT "] = %" PetscInt_FMT, i, perm[i]);
6100       }
6101       for (i = 0; i < size; ++i) check[perm[i]] = i;
6102       for (i = 0; i < size; ++i) PetscCheck(check[i] >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Missing permutation index %" PetscInt_FMT, i);
6103       PetscCall(PetscFree(check));
6104     }
6105     PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size, PETSC_OWN_POINTER, perm));
6106     if (d == dim) { // Add permutation for localized (in case this is a coordinate DM)
6107       PetscInt *loc_perm;
6108       PetscCall(PetscMalloc1(size * 2, &loc_perm));
6109       for (PetscInt i = 0; i < size; i++) {
6110         loc_perm[i]        = perm[i];
6111         loc_perm[size + i] = size + perm[i];
6112       }
6113       PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size * 2, PETSC_OWN_POINTER, loc_perm));
6114     }
6115   }
6116   PetscFunctionReturn(PETSC_SUCCESS);
6117 }
6118 
6119 PetscErrorCode DMPlexGetPointDualSpaceFEM(DM dm, PetscInt point, PetscInt field, PetscDualSpace *dspace)
6120 {
6121   PetscDS  prob;
6122   PetscInt depth, Nf, h;
6123   DMLabel  label;
6124 
6125   PetscFunctionBeginHot;
6126   PetscCall(DMGetDS(dm, &prob));
6127   Nf      = prob->Nf;
6128   label   = dm->depthLabel;
6129   *dspace = NULL;
6130   if (field < Nf) {
6131     PetscObject disc = prob->disc[field];
6132 
6133     if (disc->classid == PETSCFE_CLASSID) {
6134       PetscDualSpace dsp;
6135 
6136       PetscCall(PetscFEGetDualSpace((PetscFE)disc, &dsp));
6137       PetscCall(DMLabelGetNumValues(label, &depth));
6138       PetscCall(DMLabelGetValue(label, point, &h));
6139       h = depth - 1 - h;
6140       if (h) {
6141         PetscCall(PetscDualSpaceGetHeightSubspace(dsp, h, dspace));
6142       } else {
6143         *dspace = dsp;
6144       }
6145     }
6146   }
6147   PetscFunctionReturn(PETSC_SUCCESS);
6148 }
6149 
6150 static inline PetscErrorCode DMPlexVecGetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6151 {
6152   PetscScalar       *array;
6153   const PetscScalar *vArray;
6154   const PetscInt    *cone, *coneO;
6155   PetscInt           pStart, pEnd, p, numPoints, size = 0, offset = 0;
6156 
6157   PetscFunctionBeginHot;
6158   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6159   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
6160   PetscCall(DMPlexGetCone(dm, point, &cone));
6161   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
6162   if (!values || !*values) {
6163     if ((point >= pStart) && (point < pEnd)) {
6164       PetscInt dof;
6165 
6166       PetscCall(PetscSectionGetDof(section, point, &dof));
6167       size += dof;
6168     }
6169     for (p = 0; p < numPoints; ++p) {
6170       const PetscInt cp = cone[p];
6171       PetscInt       dof;
6172 
6173       if ((cp < pStart) || (cp >= pEnd)) continue;
6174       PetscCall(PetscSectionGetDof(section, cp, &dof));
6175       size += dof;
6176     }
6177     if (!values) {
6178       if (csize) *csize = size;
6179       PetscFunctionReturn(PETSC_SUCCESS);
6180     }
6181     PetscCall(DMGetWorkArray(dm, size, MPIU_SCALAR, &array));
6182   } else {
6183     array = *values;
6184   }
6185   size = 0;
6186   PetscCall(VecGetArrayRead(v, &vArray));
6187   if ((point >= pStart) && (point < pEnd)) {
6188     PetscInt           dof, off, d;
6189     const PetscScalar *varr;
6190 
6191     PetscCall(PetscSectionGetDof(section, point, &dof));
6192     PetscCall(PetscSectionGetOffset(section, point, &off));
6193     varr = PetscSafePointerPlusOffset(vArray, off);
6194     for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
6195     size += dof;
6196   }
6197   for (p = 0; p < numPoints; ++p) {
6198     const PetscInt     cp = cone[p];
6199     PetscInt           o  = coneO[p];
6200     PetscInt           dof, off, d;
6201     const PetscScalar *varr;
6202 
6203     if ((cp < pStart) || (cp >= pEnd)) continue;
6204     PetscCall(PetscSectionGetDof(section, cp, &dof));
6205     PetscCall(PetscSectionGetOffset(section, cp, &off));
6206     varr = PetscSafePointerPlusOffset(vArray, off);
6207     if (o >= 0) {
6208       for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
6209     } else {
6210       for (d = dof - 1; d >= 0; --d, ++offset) array[offset] = varr[d];
6211     }
6212     size += dof;
6213   }
6214   PetscCall(VecRestoreArrayRead(v, &vArray));
6215   if (!*values) {
6216     if (csize) *csize = size;
6217     *values = array;
6218   } else {
6219     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
6220     *csize = size;
6221   }
6222   PetscFunctionReturn(PETSC_SUCCESS);
6223 }
6224 
6225 /* Compress out points not in the section */
6226 static inline PetscErrorCode CompressPoints_Private(PetscSection section, PetscInt *numPoints, PetscInt points[])
6227 {
6228   const PetscInt np = *numPoints;
6229   PetscInt       pStart, pEnd, p, q;
6230 
6231   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6232   for (p = 0, q = 0; p < np; ++p) {
6233     const PetscInt r = points[p * 2];
6234     if ((r >= pStart) && (r < pEnd)) {
6235       points[q * 2]     = r;
6236       points[q * 2 + 1] = points[p * 2 + 1];
6237       ++q;
6238     }
6239   }
6240   *numPoints = q;
6241   return PETSC_SUCCESS;
6242 }
6243 
6244 /* Compressed closure does not apply closure permutation */
6245 PetscErrorCode DMPlexGetCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt ornt, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
6246 {
6247   const PetscInt *cla = NULL;
6248   PetscInt        np, *pts = NULL;
6249 
6250   PetscFunctionBeginHot;
6251   PetscCall(PetscSectionGetClosureIndex(section, (PetscObject)dm, clSec, clPoints));
6252   if (!ornt && *clPoints) {
6253     PetscInt dof, off;
6254 
6255     PetscCall(PetscSectionGetDof(*clSec, point, &dof));
6256     PetscCall(PetscSectionGetOffset(*clSec, point, &off));
6257     PetscCall(ISGetIndices(*clPoints, &cla));
6258     np  = dof / 2;
6259     pts = PetscSafePointerPlusOffset((PetscInt *)cla, off);
6260   } else {
6261     PetscCall(DMPlexGetTransitiveClosure_Internal(dm, point, ornt, PETSC_TRUE, &np, &pts));
6262     PetscCall(CompressPoints_Private(section, &np, pts));
6263   }
6264   *numPoints = np;
6265   *points    = pts;
6266   *clp       = cla;
6267   PetscFunctionReturn(PETSC_SUCCESS);
6268 }
6269 
6270 PetscErrorCode DMPlexRestoreCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
6271 {
6272   PetscFunctionBeginHot;
6273   if (!*clPoints) {
6274     PetscCall(DMPlexRestoreTransitiveClosure(dm, point, PETSC_TRUE, numPoints, points));
6275   } else {
6276     PetscCall(ISRestoreIndices(*clPoints, clp));
6277   }
6278   *numPoints = 0;
6279   *points    = NULL;
6280   *clSec     = NULL;
6281   *clPoints  = NULL;
6282   *clp       = NULL;
6283   PetscFunctionReturn(PETSC_SUCCESS);
6284 }
6285 
6286 static inline PetscErrorCode DMPlexVecGetClosure_Static(DM dm, PetscSection section, PetscInt numPoints, const PetscInt points[], const PetscInt clperm[], const PetscScalar vArray[], PetscInt *size, PetscScalar array[])
6287 {
6288   PetscInt            offset = 0, p;
6289   const PetscInt    **perms  = NULL;
6290   const PetscScalar **flips  = NULL;
6291 
6292   PetscFunctionBeginHot;
6293   *size = 0;
6294   PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
6295   for (p = 0; p < numPoints; p++) {
6296     const PetscInt     point = points[2 * p];
6297     const PetscInt    *perm  = perms ? perms[p] : NULL;
6298     const PetscScalar *flip  = flips ? flips[p] : NULL;
6299     PetscInt           dof, off, d;
6300     const PetscScalar *varr;
6301 
6302     PetscCall(PetscSectionGetDof(section, point, &dof));
6303     PetscCall(PetscSectionGetOffset(section, point, &off));
6304     varr = PetscSafePointerPlusOffset(vArray, off);
6305     if (clperm) {
6306       if (perm) {
6307         for (d = 0; d < dof; d++) array[clperm[offset + perm[d]]] = varr[d];
6308       } else {
6309         for (d = 0; d < dof; d++) array[clperm[offset + d]] = varr[d];
6310       }
6311       if (flip) {
6312         for (d = 0; d < dof; d++) array[clperm[offset + d]] *= flip[d];
6313       }
6314     } else {
6315       if (perm) {
6316         for (d = 0; d < dof; d++) array[offset + perm[d]] = varr[d];
6317       } else {
6318         for (d = 0; d < dof; d++) array[offset + d] = varr[d];
6319       }
6320       if (flip) {
6321         for (d = 0; d < dof; d++) array[offset + d] *= flip[d];
6322       }
6323     }
6324     offset += dof;
6325   }
6326   PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
6327   *size = offset;
6328   PetscFunctionReturn(PETSC_SUCCESS);
6329 }
6330 
6331 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[])
6332 {
6333   PetscInt offset = 0, f;
6334 
6335   PetscFunctionBeginHot;
6336   *size = 0;
6337   for (f = 0; f < numFields; ++f) {
6338     PetscInt            p;
6339     const PetscInt    **perms = NULL;
6340     const PetscScalar **flips = NULL;
6341 
6342     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6343     for (p = 0; p < numPoints; p++) {
6344       const PetscInt     point = points[2 * p];
6345       PetscInt           fdof, foff, b;
6346       const PetscScalar *varr;
6347       const PetscInt    *perm = perms ? perms[p] : NULL;
6348       const PetscScalar *flip = flips ? flips[p] : NULL;
6349 
6350       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6351       PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6352       varr = &vArray[foff];
6353       if (clperm) {
6354         if (perm) {
6355           for (b = 0; b < fdof; b++) array[clperm[offset + perm[b]]] = varr[b];
6356         } else {
6357           for (b = 0; b < fdof; b++) array[clperm[offset + b]] = varr[b];
6358         }
6359         if (flip) {
6360           for (b = 0; b < fdof; b++) array[clperm[offset + b]] *= flip[b];
6361         }
6362       } else {
6363         if (perm) {
6364           for (b = 0; b < fdof; b++) array[offset + perm[b]] = varr[b];
6365         } else {
6366           for (b = 0; b < fdof; b++) array[offset + b] = varr[b];
6367         }
6368         if (flip) {
6369           for (b = 0; b < fdof; b++) array[offset + b] *= flip[b];
6370         }
6371       }
6372       offset += fdof;
6373     }
6374     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6375   }
6376   *size = offset;
6377   PetscFunctionReturn(PETSC_SUCCESS);
6378 }
6379 
6380 PetscErrorCode DMPlexVecGetOrientedClosure_Internal(DM dm, PetscSection section, PetscBool useClPerm, Vec v, PetscInt point, PetscInt ornt, PetscInt *csize, PetscScalar *values[])
6381 {
6382   PetscSection    clSection;
6383   IS              clPoints;
6384   PetscInt       *points = NULL;
6385   const PetscInt *clp, *perm = NULL;
6386   PetscInt        depth, numFields, numPoints, asize;
6387 
6388   PetscFunctionBeginHot;
6389   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6390   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6391   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6392   PetscValidHeaderSpecific(v, VEC_CLASSID, 4);
6393   PetscCall(DMPlexGetDepth(dm, &depth));
6394   PetscCall(PetscSectionGetNumFields(section, &numFields));
6395   if (depth == 1 && numFields < 2) {
6396     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6397     PetscFunctionReturn(PETSC_SUCCESS);
6398   }
6399   /* Get points */
6400   PetscCall(DMPlexGetCompressedClosure(dm, section, point, ornt, &numPoints, &points, &clSection, &clPoints, &clp));
6401   /* Get sizes */
6402   asize = 0;
6403   for (PetscInt p = 0; p < numPoints * 2; p += 2) {
6404     PetscInt dof;
6405     PetscCall(PetscSectionGetDof(section, points[p], &dof));
6406     asize += dof;
6407   }
6408   if (values) {
6409     const PetscScalar *vArray;
6410     PetscInt           size;
6411 
6412     if (*values) {
6413       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);
6414     } else PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, values));
6415     if (useClPerm) PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, asize, &perm));
6416     PetscCall(VecGetArrayRead(v, &vArray));
6417     /* Get values */
6418     if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, numPoints, points, numFields, perm, vArray, &size, *values));
6419     else PetscCall(DMPlexVecGetClosure_Static(dm, section, numPoints, points, perm, vArray, &size, *values));
6420     PetscCheck(asize == size, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Section size %" PetscInt_FMT " does not match Vec closure size %" PetscInt_FMT, asize, size);
6421     /* Cleanup array */
6422     PetscCall(VecRestoreArrayRead(v, &vArray));
6423   }
6424   if (csize) *csize = asize;
6425   /* Cleanup points */
6426   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6427   PetscFunctionReturn(PETSC_SUCCESS);
6428 }
6429 
6430 /*@C
6431   DMPlexVecGetClosure - Get an array of the values on the closure of 'point'
6432 
6433   Not collective
6434 
6435   Input Parameters:
6436 + dm      - The `DM`
6437 . section - The section describing the layout in `v`, or `NULL` to use the default section
6438 . v       - The local vector
6439 - point   - The point in the `DM`
6440 
6441   Input/Output Parameters:
6442 + csize  - The size of the input values array, or `NULL`; on output the number of values in the closure
6443 - values - An array to use for the values, or *values = `NULL` to have it allocated automatically;
6444            if the user provided `NULL`, it is a borrowed array and should not be freed, use  `DMPlexVecRestoreClosure()` to return it
6445 
6446   Level: intermediate
6447 
6448   Notes:
6449   `DMPlexVecGetClosure()`/`DMPlexVecRestoreClosure()` only allocates the values array if it set to `NULL` in the
6450   calling function. This is because `DMPlexVecGetClosure()` is typically called in the inner loop of a `Vec` or `Mat`
6451   assembly function, and a user may already have allocated storage for this operation.
6452 
6453   A typical use could be
6454 .vb
6455    values = NULL;
6456    PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
6457    for (cl = 0; cl < clSize; ++cl) {
6458      <Compute on closure>
6459    }
6460    PetscCall(DMPlexVecRestoreClosure(dm, NULL, v, p, &clSize, &values));
6461 .ve
6462   or
6463 .vb
6464    PetscMalloc1(clMaxSize, &values);
6465    for (p = pStart; p < pEnd; ++p) {
6466      clSize = clMaxSize;
6467      PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
6468      for (cl = 0; cl < clSize; ++cl) {
6469        <Compute on closure>
6470      }
6471    }
6472    PetscFree(values);
6473 .ve
6474 
6475   Fortran Notes:
6476   The `csize` argument is not present in the Fortran binding.
6477 
6478   `values` must be declared with
6479 .vb
6480   PetscScalar,dimension(:),pointer   :: values
6481 .ve
6482   and it will be allocated internally by PETSc to hold the values returned
6483 
6484 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecRestoreClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6485 @*/
6486 PetscErrorCode DMPlexVecGetClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6487 {
6488   PetscFunctionBeginHot;
6489   PetscCall(DMPlexVecGetOrientedClosure_Internal(dm, section, PETSC_TRUE, v, point, 0, csize, values));
6490   PetscFunctionReturn(PETSC_SUCCESS);
6491 }
6492 
6493 PetscErrorCode DMPlexVecGetClosureAtDepth_Internal(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt depth, PetscInt *csize, PetscScalar *values[])
6494 {
6495   DMLabel            depthLabel;
6496   PetscSection       clSection;
6497   IS                 clPoints;
6498   PetscScalar       *array;
6499   const PetscScalar *vArray;
6500   PetscInt          *points = NULL;
6501   const PetscInt    *clp, *perm = NULL;
6502   PetscInt           mdepth, numFields, numPoints, Np = 0, p, clsize, size;
6503 
6504   PetscFunctionBeginHot;
6505   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6506   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6507   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6508   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6509   PetscCall(DMPlexGetDepth(dm, &mdepth));
6510   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
6511   PetscCall(PetscSectionGetNumFields(section, &numFields));
6512   if (mdepth == 1 && numFields < 2) {
6513     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6514     PetscFunctionReturn(PETSC_SUCCESS);
6515   }
6516   /* Get points */
6517   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
6518   for (clsize = 0, p = 0; p < Np; p++) {
6519     PetscInt dof;
6520     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
6521     clsize += dof;
6522   }
6523   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &perm));
6524   /* Filter points */
6525   for (p = 0; p < numPoints * 2; p += 2) {
6526     PetscInt dep;
6527 
6528     PetscCall(DMLabelGetValue(depthLabel, points[p], &dep));
6529     if (dep != depth) continue;
6530     points[Np * 2 + 0] = points[p];
6531     points[Np * 2 + 1] = points[p + 1];
6532     ++Np;
6533   }
6534   /* Get array */
6535   if (!values || !*values) {
6536     PetscInt asize = 0, dof;
6537 
6538     for (p = 0; p < Np * 2; p += 2) {
6539       PetscCall(PetscSectionGetDof(section, points[p], &dof));
6540       asize += dof;
6541     }
6542     if (!values) {
6543       PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6544       if (csize) *csize = asize;
6545       PetscFunctionReturn(PETSC_SUCCESS);
6546     }
6547     PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, &array));
6548   } else {
6549     array = *values;
6550   }
6551   PetscCall(VecGetArrayRead(v, &vArray));
6552   /* Get values */
6553   if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, Np, points, numFields, perm, vArray, &size, array));
6554   else PetscCall(DMPlexVecGetClosure_Static(dm, section, Np, points, perm, vArray, &size, array));
6555   /* Cleanup points */
6556   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6557   /* Cleanup array */
6558   PetscCall(VecRestoreArrayRead(v, &vArray));
6559   if (!*values) {
6560     if (csize) *csize = size;
6561     *values = array;
6562   } else {
6563     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
6564     *csize = size;
6565   }
6566   PetscFunctionReturn(PETSC_SUCCESS);
6567 }
6568 
6569 /*@C
6570   DMPlexVecRestoreClosure - Restore the array of the values on the closure of 'point' obtained with `DMPlexVecGetClosure()`
6571 
6572   Not collective
6573 
6574   Input Parameters:
6575 + dm      - The `DM`
6576 . section - The section describing the layout in `v`, or `NULL` to use the default section
6577 . v       - The local vector
6578 . point   - The point in the `DM`
6579 . csize   - The number of values in the closure, or `NULL`
6580 - values  - The array of values
6581 
6582   Level: intermediate
6583 
6584   Note:
6585   The array values are discarded and not copied back into `v`. In order to copy values back to `v`, use `DMPlexVecSetClosure()`
6586 
6587   Fortran Note:
6588   The `csize` argument is not present in the Fortran binding since it is internal to the array.
6589 
6590 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6591 @*/
6592 PetscErrorCode DMPlexVecRestoreClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6593 {
6594   PetscInt size = 0;
6595 
6596   PetscFunctionBegin;
6597   /* Should work without recalculating size */
6598   PetscCall(DMRestoreWorkArray(dm, size, MPIU_SCALAR, (void *)values));
6599   *values = NULL;
6600   PetscFunctionReturn(PETSC_SUCCESS);
6601 }
6602 
6603 static inline void add(PetscScalar *x, PetscScalar y)
6604 {
6605   *x += y;
6606 }
6607 static inline void insert(PetscScalar *x, PetscScalar y)
6608 {
6609   *x = y;
6610 }
6611 
6612 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[])
6613 {
6614   PetscInt        cdof;  /* The number of constraints on this point */
6615   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6616   PetscScalar    *a;
6617   PetscInt        off, cind = 0, k;
6618 
6619   PetscFunctionBegin;
6620   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6621   PetscCall(PetscSectionGetOffset(section, point, &off));
6622   a = &array[off];
6623   if (!cdof || setBC) {
6624     if (clperm) {
6625       if (perm) {
6626         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6627       } else {
6628         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6629       }
6630     } else {
6631       if (perm) {
6632         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6633       } else {
6634         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6635       }
6636     }
6637   } else {
6638     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6639     if (clperm) {
6640       if (perm) {
6641         for (k = 0; k < dof; ++k) {
6642           if ((cind < cdof) && (k == cdofs[cind])) {
6643             ++cind;
6644             continue;
6645           }
6646           fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6647         }
6648       } else {
6649         for (k = 0; k < dof; ++k) {
6650           if ((cind < cdof) && (k == cdofs[cind])) {
6651             ++cind;
6652             continue;
6653           }
6654           fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6655         }
6656       }
6657     } else {
6658       if (perm) {
6659         for (k = 0; k < dof; ++k) {
6660           if ((cind < cdof) && (k == cdofs[cind])) {
6661             ++cind;
6662             continue;
6663           }
6664           fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6665         }
6666       } else {
6667         for (k = 0; k < dof; ++k) {
6668           if ((cind < cdof) && (k == cdofs[cind])) {
6669             ++cind;
6670             continue;
6671           }
6672           fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6673         }
6674       }
6675     }
6676   }
6677   PetscFunctionReturn(PETSC_SUCCESS);
6678 }
6679 
6680 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[])
6681 {
6682   PetscInt        cdof;  /* The number of constraints on this point */
6683   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6684   PetscScalar    *a;
6685   PetscInt        off, cind = 0, k;
6686 
6687   PetscFunctionBegin;
6688   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6689   PetscCall(PetscSectionGetOffset(section, point, &off));
6690   a = &array[off];
6691   if (cdof) {
6692     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6693     if (clperm) {
6694       if (perm) {
6695         for (k = 0; k < dof; ++k) {
6696           if ((cind < cdof) && (k == cdofs[cind])) {
6697             fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6698             cind++;
6699           }
6700         }
6701       } else {
6702         for (k = 0; k < dof; ++k) {
6703           if ((cind < cdof) && (k == cdofs[cind])) {
6704             fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6705             cind++;
6706           }
6707         }
6708       }
6709     } else {
6710       if (perm) {
6711         for (k = 0; k < dof; ++k) {
6712           if ((cind < cdof) && (k == cdofs[cind])) {
6713             fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6714             cind++;
6715           }
6716         }
6717       } else {
6718         for (k = 0; k < dof; ++k) {
6719           if ((cind < cdof) && (k == cdofs[cind])) {
6720             fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6721             cind++;
6722           }
6723         }
6724       }
6725     }
6726   }
6727   PetscFunctionReturn(PETSC_SUCCESS);
6728 }
6729 
6730 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[])
6731 {
6732   PetscScalar    *a;
6733   PetscInt        fdof, foff, fcdof, foffset = *offset;
6734   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6735   PetscInt        cind = 0, b;
6736 
6737   PetscFunctionBegin;
6738   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6739   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6740   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6741   a = &array[foff];
6742   if (!fcdof || setBC) {
6743     if (clperm) {
6744       if (perm) {
6745         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6746       } else {
6747         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6748       }
6749     } else {
6750       if (perm) {
6751         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6752       } else {
6753         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6754       }
6755     }
6756   } else {
6757     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6758     if (clperm) {
6759       if (perm) {
6760         for (b = 0; b < fdof; b++) {
6761           if ((cind < fcdof) && (b == fcdofs[cind])) {
6762             ++cind;
6763             continue;
6764           }
6765           fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6766         }
6767       } else {
6768         for (b = 0; b < fdof; b++) {
6769           if ((cind < fcdof) && (b == fcdofs[cind])) {
6770             ++cind;
6771             continue;
6772           }
6773           fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6774         }
6775       }
6776     } else {
6777       if (perm) {
6778         for (b = 0; b < fdof; b++) {
6779           if ((cind < fcdof) && (b == fcdofs[cind])) {
6780             ++cind;
6781             continue;
6782           }
6783           fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6784         }
6785       } else {
6786         for (b = 0; b < fdof; b++) {
6787           if ((cind < fcdof) && (b == fcdofs[cind])) {
6788             ++cind;
6789             continue;
6790           }
6791           fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6792         }
6793       }
6794     }
6795   }
6796   *offset += fdof;
6797   PetscFunctionReturn(PETSC_SUCCESS);
6798 }
6799 
6800 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[])
6801 {
6802   PetscScalar    *a;
6803   PetscInt        fdof, foff, fcdof, foffset = *offset;
6804   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6805   PetscInt        Nc, cind = 0, ncind = 0, b;
6806   PetscBool       ncSet, fcSet;
6807 
6808   PetscFunctionBegin;
6809   PetscCall(PetscSectionGetFieldComponents(section, f, &Nc));
6810   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6811   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6812   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6813   a = &array[foff];
6814   if (fcdof) {
6815     /* We just override fcdof and fcdofs with Ncc and comps */
6816     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6817     if (clperm) {
6818       if (perm) {
6819         if (comps) {
6820           for (b = 0; b < fdof; b++) {
6821             ncSet = fcSet = PETSC_FALSE;
6822             if (b % Nc == comps[ncind]) {
6823               ncind = (ncind + 1) % Ncc;
6824               ncSet = PETSC_TRUE;
6825             }
6826             if ((cind < fcdof) && (b == fcdofs[cind])) {
6827               ++cind;
6828               fcSet = PETSC_TRUE;
6829             }
6830             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6831           }
6832         } else {
6833           for (b = 0; b < fdof; b++) {
6834             if ((cind < fcdof) && (b == fcdofs[cind])) {
6835               fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6836               ++cind;
6837             }
6838           }
6839         }
6840       } else {
6841         if (comps) {
6842           for (b = 0; b < fdof; b++) {
6843             ncSet = fcSet = PETSC_FALSE;
6844             if (b % Nc == comps[ncind]) {
6845               ncind = (ncind + 1) % Ncc;
6846               ncSet = PETSC_TRUE;
6847             }
6848             if ((cind < fcdof) && (b == fcdofs[cind])) {
6849               ++cind;
6850               fcSet = PETSC_TRUE;
6851             }
6852             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6853           }
6854         } else {
6855           for (b = 0; b < fdof; b++) {
6856             if ((cind < fcdof) && (b == fcdofs[cind])) {
6857               fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6858               ++cind;
6859             }
6860           }
6861         }
6862       }
6863     } else {
6864       if (perm) {
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[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6877           }
6878         } else {
6879           for (b = 0; b < fdof; b++) {
6880             if ((cind < fcdof) && (b == fcdofs[cind])) {
6881               fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6882               ++cind;
6883             }
6884           }
6885         }
6886       } else {
6887         if (comps) {
6888           for (b = 0; b < fdof; b++) {
6889             ncSet = fcSet = PETSC_FALSE;
6890             if (b % Nc == comps[ncind]) {
6891               ncind = (ncind + 1) % Ncc;
6892               ncSet = PETSC_TRUE;
6893             }
6894             if ((cind < fcdof) && (b == fcdofs[cind])) {
6895               ++cind;
6896               fcSet = PETSC_TRUE;
6897             }
6898             if (ncSet && fcSet) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6899           }
6900         } else {
6901           for (b = 0; b < fdof; b++) {
6902             if ((cind < fcdof) && (b == fcdofs[cind])) {
6903               fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6904               ++cind;
6905             }
6906           }
6907         }
6908       }
6909     }
6910   }
6911   *offset += fdof;
6912   PetscFunctionReturn(PETSC_SUCCESS);
6913 }
6914 
6915 static inline PetscErrorCode DMPlexVecSetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6916 {
6917   PetscScalar    *array;
6918   const PetscInt *cone, *coneO;
6919   PetscInt        pStart, pEnd, p, numPoints, off, dof;
6920 
6921   PetscFunctionBeginHot;
6922   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6923   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
6924   PetscCall(DMPlexGetCone(dm, point, &cone));
6925   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
6926   PetscCall(VecGetArray(v, &array));
6927   for (p = 0, off = 0; p <= numPoints; ++p, off += dof) {
6928     const PetscInt cp = !p ? point : cone[p - 1];
6929     const PetscInt o  = !p ? 0 : coneO[p - 1];
6930 
6931     if ((cp < pStart) || (cp >= pEnd)) {
6932       dof = 0;
6933       continue;
6934     }
6935     PetscCall(PetscSectionGetDof(section, cp, &dof));
6936     /* ADD_VALUES */
6937     {
6938       const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6939       PetscScalar    *a;
6940       PetscInt        cdof, coff, cind = 0, k;
6941 
6942       PetscCall(PetscSectionGetConstraintDof(section, cp, &cdof));
6943       PetscCall(PetscSectionGetOffset(section, cp, &coff));
6944       a = &array[coff];
6945       if (!cdof) {
6946         if (o >= 0) {
6947           for (k = 0; k < dof; ++k) a[k] += values[off + k];
6948         } else {
6949           for (k = 0; k < dof; ++k) a[k] += values[off + dof - k - 1];
6950         }
6951       } else {
6952         PetscCall(PetscSectionGetConstraintIndices(section, cp, &cdofs));
6953         if (o >= 0) {
6954           for (k = 0; k < dof; ++k) {
6955             if ((cind < cdof) && (k == cdofs[cind])) {
6956               ++cind;
6957               continue;
6958             }
6959             a[k] += values[off + k];
6960           }
6961         } else {
6962           for (k = 0; k < dof; ++k) {
6963             if ((cind < cdof) && (k == cdofs[cind])) {
6964               ++cind;
6965               continue;
6966             }
6967             a[k] += values[off + dof - k - 1];
6968           }
6969         }
6970       }
6971     }
6972   }
6973   PetscCall(VecRestoreArray(v, &array));
6974   PetscFunctionReturn(PETSC_SUCCESS);
6975 }
6976 
6977 /*@C
6978   DMPlexVecSetClosure - Set an array of the values on the closure of `point`
6979 
6980   Not collective
6981 
6982   Input Parameters:
6983 + dm      - The `DM`
6984 . section - The section describing the layout in `v`, or `NULL` to use the default section
6985 . v       - The local vector
6986 . point   - The point in the `DM`
6987 . values  - The array of values
6988 - mode    - The insert mode. One of `INSERT_ALL_VALUES`, `ADD_ALL_VALUES`, `INSERT_VALUES`, `ADD_VALUES`, `INSERT_BC_VALUES`, and `ADD_BC_VALUES`,
6989             where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions.
6990 
6991   Level: intermediate
6992 
6993   Note:
6994   Usually the input arrays were obtained with `DMPlexVecGetClosure()`
6995 
6996   Fortran Note:
6997   `values` must be declared with
6998 .vb
6999   PetscScalar,dimension(:),pointer   :: values
7000 .ve
7001 
7002 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`
7003 @*/
7004 PetscErrorCode DMPlexVecSetClosure(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
7005 {
7006   PetscSection    clSection;
7007   IS              clPoints;
7008   PetscScalar    *array;
7009   PetscInt       *points = NULL;
7010   const PetscInt *clp, *clperm = NULL;
7011   PetscInt        depth, numFields, numPoints, p, clsize;
7012 
7013   PetscFunctionBeginHot;
7014   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7015   if (!section) PetscCall(DMGetLocalSection(dm, &section));
7016   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7017   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
7018   PetscCall(DMPlexGetDepth(dm, &depth));
7019   PetscCall(PetscSectionGetNumFields(section, &numFields));
7020   if (depth == 1 && numFields < 2 && mode == ADD_VALUES) {
7021     PetscCall(DMPlexVecSetClosure_Depth1_Static(dm, section, v, point, values, mode));
7022     PetscFunctionReturn(PETSC_SUCCESS);
7023   }
7024   /* Get points */
7025   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
7026   for (clsize = 0, p = 0; p < numPoints; p++) {
7027     PetscInt dof;
7028     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
7029     clsize += dof;
7030   }
7031   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
7032   /* Get array */
7033   PetscCall(VecGetArray(v, &array));
7034   /* Get values */
7035   if (numFields > 0) {
7036     PetscInt offset = 0, f;
7037     for (f = 0; f < numFields; ++f) {
7038       const PetscInt    **perms = NULL;
7039       const PetscScalar **flips = NULL;
7040 
7041       PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7042       switch (mode) {
7043       case INSERT_VALUES:
7044         for (p = 0; p < numPoints; p++) {
7045           const PetscInt     point = points[2 * p];
7046           const PetscInt    *perm  = perms ? perms[p] : NULL;
7047           const PetscScalar *flip  = flips ? flips[p] : NULL;
7048           PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, clperm, values, &offset, array));
7049         }
7050         break;
7051       case INSERT_ALL_VALUES:
7052         for (p = 0; p < numPoints; p++) {
7053           const PetscInt     point = points[2 * p];
7054           const PetscInt    *perm  = perms ? perms[p] : NULL;
7055           const PetscScalar *flip  = flips ? flips[p] : NULL;
7056           PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, clperm, values, &offset, array));
7057         }
7058         break;
7059       case INSERT_BC_VALUES:
7060         for (p = 0; p < numPoints; p++) {
7061           const PetscInt     point = points[2 * p];
7062           const PetscInt    *perm  = perms ? perms[p] : NULL;
7063           const PetscScalar *flip  = flips ? flips[p] : NULL;
7064           PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, insert, clperm, values, &offset, array));
7065         }
7066         break;
7067       case ADD_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, add, PETSC_FALSE, clperm, values, &offset, array));
7073         }
7074         break;
7075       case ADD_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, add, PETSC_TRUE, clperm, values, &offset, array));
7081         }
7082         break;
7083       case ADD_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, add, clperm, values, &offset, array));
7089         }
7090         break;
7091       default:
7092         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
7093       }
7094       PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7095     }
7096   } else {
7097     PetscInt            dof, off;
7098     const PetscInt    **perms = NULL;
7099     const PetscScalar **flips = NULL;
7100 
7101     PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
7102     switch (mode) {
7103     case INSERT_VALUES:
7104       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7105         const PetscInt     point = points[2 * p];
7106         const PetscInt    *perm  = perms ? perms[p] : NULL;
7107         const PetscScalar *flip  = flips ? flips[p] : NULL;
7108         PetscCall(PetscSectionGetDof(section, point, &dof));
7109         PetscCall(updatePoint_private(section, point, dof, insert, PETSC_FALSE, perm, flip, clperm, values, off, array));
7110       }
7111       break;
7112     case INSERT_ALL_VALUES:
7113       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7114         const PetscInt     point = points[2 * p];
7115         const PetscInt    *perm  = perms ? perms[p] : NULL;
7116         const PetscScalar *flip  = flips ? flips[p] : NULL;
7117         PetscCall(PetscSectionGetDof(section, point, &dof));
7118         PetscCall(updatePoint_private(section, point, dof, insert, PETSC_TRUE, perm, flip, clperm, values, off, array));
7119       }
7120       break;
7121     case INSERT_BC_VALUES:
7122       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7123         const PetscInt     point = points[2 * p];
7124         const PetscInt    *perm  = perms ? perms[p] : NULL;
7125         const PetscScalar *flip  = flips ? flips[p] : NULL;
7126         PetscCall(PetscSectionGetDof(section, point, &dof));
7127         PetscCall(updatePointBC_private(section, point, dof, insert, perm, flip, clperm, values, off, array));
7128       }
7129       break;
7130     case ADD_VALUES:
7131       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7132         const PetscInt     point = points[2 * p];
7133         const PetscInt    *perm  = perms ? perms[p] : NULL;
7134         const PetscScalar *flip  = flips ? flips[p] : NULL;
7135         PetscCall(PetscSectionGetDof(section, point, &dof));
7136         PetscCall(updatePoint_private(section, point, dof, add, PETSC_FALSE, perm, flip, clperm, values, off, array));
7137       }
7138       break;
7139     case ADD_ALL_VALUES:
7140       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7141         const PetscInt     point = points[2 * p];
7142         const PetscInt    *perm  = perms ? perms[p] : NULL;
7143         const PetscScalar *flip  = flips ? flips[p] : NULL;
7144         PetscCall(PetscSectionGetDof(section, point, &dof));
7145         PetscCall(updatePoint_private(section, point, dof, add, PETSC_TRUE, perm, flip, clperm, values, off, array));
7146       }
7147       break;
7148     case ADD_BC_VALUES:
7149       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7150         const PetscInt     point = points[2 * p];
7151         const PetscInt    *perm  = perms ? perms[p] : NULL;
7152         const PetscScalar *flip  = flips ? flips[p] : NULL;
7153         PetscCall(PetscSectionGetDof(section, point, &dof));
7154         PetscCall(updatePointBC_private(section, point, dof, add, perm, flip, clperm, values, off, array));
7155       }
7156       break;
7157     default:
7158       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
7159     }
7160     PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
7161   }
7162   /* Cleanup points */
7163   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
7164   /* Cleanup array */
7165   PetscCall(VecRestoreArray(v, &array));
7166   PetscFunctionReturn(PETSC_SUCCESS);
7167 }
7168 
7169 /* Check whether the given point is in the label. If not, update the offset to skip this point */
7170 static inline PetscErrorCode CheckPoint_Private(DMLabel label, PetscInt labelId, PetscSection section, PetscInt point, PetscInt f, PetscInt *offset, PetscBool *contains)
7171 {
7172   PetscFunctionBegin;
7173   *contains = PETSC_TRUE;
7174   if (label) {
7175     PetscInt fdof;
7176 
7177     PetscCall(DMLabelStratumHasPoint(label, labelId, point, contains));
7178     if (!*contains) {
7179       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7180       *offset += fdof;
7181       PetscFunctionReturn(PETSC_SUCCESS);
7182     }
7183   }
7184   PetscFunctionReturn(PETSC_SUCCESS);
7185 }
7186 
7187 /* Unlike DMPlexVecSetClosure(), this uses plex-native closure permutation, not a user-specified permutation such as DMPlexSetClosurePermutationTensor(). */
7188 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)
7189 {
7190   PetscSection    clSection;
7191   IS              clPoints;
7192   PetscScalar    *array;
7193   PetscInt       *points = NULL;
7194   const PetscInt *clp;
7195   PetscInt        numFields, numPoints, p;
7196   PetscInt        offset = 0, f;
7197 
7198   PetscFunctionBeginHot;
7199   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7200   if (!section) PetscCall(DMGetLocalSection(dm, &section));
7201   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7202   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
7203   PetscCall(PetscSectionGetNumFields(section, &numFields));
7204   /* Get points */
7205   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
7206   /* Get array */
7207   PetscCall(VecGetArray(v, &array));
7208   /* Get values */
7209   for (f = 0; f < numFields; ++f) {
7210     const PetscInt    **perms = NULL;
7211     const PetscScalar **flips = NULL;
7212     PetscBool           contains;
7213 
7214     if (!fieldActive[f]) {
7215       for (p = 0; p < numPoints * 2; p += 2) {
7216         PetscInt fdof;
7217         PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
7218         offset += fdof;
7219       }
7220       continue;
7221     }
7222     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7223     switch (mode) {
7224     case INSERT_VALUES:
7225       for (p = 0; p < numPoints; p++) {
7226         const PetscInt     point = points[2 * p];
7227         const PetscInt    *perm  = perms ? perms[p] : NULL;
7228         const PetscScalar *flip  = flips ? flips[p] : NULL;
7229         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7230         if (!contains) continue;
7231         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, NULL, values, &offset, array));
7232       }
7233       break;
7234     case INSERT_ALL_VALUES:
7235       for (p = 0; p < numPoints; p++) {
7236         const PetscInt     point = points[2 * p];
7237         const PetscInt    *perm  = perms ? perms[p] : NULL;
7238         const PetscScalar *flip  = flips ? flips[p] : NULL;
7239         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7240         if (!contains) continue;
7241         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, NULL, values, &offset, array));
7242       }
7243       break;
7244     case INSERT_BC_VALUES:
7245       for (p = 0; p < numPoints; p++) {
7246         const PetscInt     point = points[2 * p];
7247         const PetscInt    *perm  = perms ? perms[p] : NULL;
7248         const PetscScalar *flip  = flips ? flips[p] : NULL;
7249         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7250         if (!contains) continue;
7251         PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, Ncc, comps, insert, NULL, values, &offset, array));
7252       }
7253       break;
7254     case ADD_VALUES:
7255       for (p = 0; p < numPoints; p++) {
7256         const PetscInt     point = points[2 * p];
7257         const PetscInt    *perm  = perms ? perms[p] : NULL;
7258         const PetscScalar *flip  = flips ? flips[p] : NULL;
7259         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7260         if (!contains) continue;
7261         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, NULL, values, &offset, array));
7262       }
7263       break;
7264     case ADD_ALL_VALUES:
7265       for (p = 0; p < numPoints; p++) {
7266         const PetscInt     point = points[2 * p];
7267         const PetscInt    *perm  = perms ? perms[p] : NULL;
7268         const PetscScalar *flip  = flips ? flips[p] : NULL;
7269         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7270         if (!contains) continue;
7271         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, NULL, values, &offset, array));
7272       }
7273       break;
7274     default:
7275       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
7276     }
7277     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7278   }
7279   /* Cleanup points */
7280   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
7281   /* Cleanup array */
7282   PetscCall(VecRestoreArray(v, &array));
7283   PetscFunctionReturn(PETSC_SUCCESS);
7284 }
7285 
7286 static PetscErrorCode DMPlexPrintMatSetValues(PetscViewer viewer, Mat A, PetscInt point, PetscInt numRIndices, const PetscInt rindices[], PetscInt numCIndices, const PetscInt cindices[], const PetscScalar values[])
7287 {
7288   PetscMPIInt rank;
7289   PetscInt    i, j;
7290 
7291   PetscFunctionBegin;
7292   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7293   PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat for point %" PetscInt_FMT "\n", rank, point));
7294   for (i = 0; i < numRIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat row indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, rindices[i]));
7295   for (i = 0; i < numCIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat col indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, cindices[i]));
7296   numCIndices = numCIndices ? numCIndices : numRIndices;
7297   if (!values) PetscFunctionReturn(PETSC_SUCCESS);
7298   for (i = 0; i < numRIndices; i++) {
7299     PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]", rank));
7300     for (j = 0; j < numCIndices; j++) {
7301 #if defined(PETSC_USE_COMPLEX)
7302       PetscCall(PetscViewerASCIIPrintf(viewer, " (%g,%g)", (double)PetscRealPart(values[i * numCIndices + j]), (double)PetscImaginaryPart(values[i * numCIndices + j])));
7303 #else
7304       PetscCall(PetscViewerASCIIPrintf(viewer, " %g", (double)values[i * numCIndices + j]));
7305 #endif
7306     }
7307     PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
7308   }
7309   PetscFunctionReturn(PETSC_SUCCESS);
7310 }
7311 
7312 /*
7313   DMPlexGetIndicesPoint_Internal - Add the indices for dofs on a point to an index array
7314 
7315   Input Parameters:
7316 + section - The section for this data layout
7317 . islocal - Is the section (and thus indices being requested) local or global?
7318 . point   - The point contributing dofs with these indices
7319 . off     - The global offset of this point
7320 . loff    - The local offset of each field
7321 . setBC   - The flag determining whether to include indices of boundary values
7322 . perm    - A permutation of the dofs on this point, or NULL
7323 - indperm - A permutation of the entire indices array, or NULL
7324 
7325   Output Parameter:
7326 . indices - Indices for dofs on this point
7327 
7328   Level: developer
7329 
7330   Note: The indices could be local or global, depending on the value of 'off'.
7331 */
7332 PetscErrorCode DMPlexGetIndicesPoint_Internal(PetscSection section, PetscBool islocal, PetscInt point, PetscInt off, PetscInt *loff, PetscBool setBC, const PetscInt perm[], const PetscInt indperm[], PetscInt indices[])
7333 {
7334   PetscInt        dof;   /* The number of unknowns on this point */
7335   PetscInt        cdof;  /* The number of constraints on this point */
7336   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
7337   PetscInt        cind = 0, k;
7338 
7339   PetscFunctionBegin;
7340   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
7341   PetscCall(PetscSectionGetDof(section, point, &dof));
7342   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
7343   if (!cdof || setBC) {
7344     for (k = 0; k < dof; ++k) {
7345       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
7346       const PetscInt ind    = indperm ? indperm[preind] : preind;
7347 
7348       indices[ind] = off + k;
7349     }
7350   } else {
7351     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
7352     for (k = 0; k < dof; ++k) {
7353       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
7354       const PetscInt ind    = indperm ? indperm[preind] : preind;
7355 
7356       if ((cind < cdof) && (k == cdofs[cind])) {
7357         /* Insert check for returning constrained indices */
7358         indices[ind] = -(off + k + 1);
7359         ++cind;
7360       } else {
7361         indices[ind] = off + k - (islocal ? 0 : cind);
7362       }
7363     }
7364   }
7365   *loff += dof;
7366   PetscFunctionReturn(PETSC_SUCCESS);
7367 }
7368 
7369 /*
7370  DMPlexGetIndicesPointFields_Internal - gets section indices for a point in its canonical ordering.
7371 
7372  Input Parameters:
7373 + section - a section (global or local)
7374 - islocal - `PETSC_TRUE` if requesting local indices (i.e., section is local); `PETSC_FALSE` for global
7375 . point - point within section
7376 . off - The offset of this point in the (local or global) indexed space - should match islocal and (usually) the section
7377 . foffs - array of length numFields containing the offset in canonical point ordering (the location in indices) of each field
7378 . setBC - identify constrained (boundary condition) points via involution.
7379 . perms - perms[f][permsoff][:] is a permutation of dofs within each field
7380 . permsoff - offset
7381 - indperm - index permutation
7382 
7383  Output Parameter:
7384 . foffs - each entry is incremented by the number of (unconstrained if setBC=FALSE) dofs in that field
7385 . indices - array to hold indices (as defined by section) of each dof associated with point
7386 
7387  Notes:
7388  If section is local and setBC=true, there is no distinction between constrained and unconstrained dofs.
7389  If section is local and setBC=false, the indices for constrained points are the involution -(i+1) of their position
7390  in the local vector.
7391 
7392  If section is global and setBC=false, the indices for constrained points are negative (and their value is not
7393  significant).  It is invalid to call with a global section and setBC=true.
7394 
7395  Developer Note:
7396  The section is only used for field layout, so islocal is technically a statement about the offset (off).  At some point
7397  in the future, global sections may have fields set, in which case we could pass the global section and obtain the
7398  offset could be obtained from the section instead of passing it explicitly as we do now.
7399 
7400  Example:
7401  Suppose a point contains one field with three components, and for which the unconstrained indices are {10, 11, 12}.
7402  When the middle component is constrained, we get the array {10, -12, 12} for (islocal=TRUE, setBC=FALSE).
7403  Note that -12 is the involution of 11, so the user can involute negative indices to recover local indices.
7404  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.
7405 
7406  Level: developer
7407 */
7408 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[])
7409 {
7410   PetscInt numFields, foff, f;
7411 
7412   PetscFunctionBegin;
7413   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
7414   PetscCall(PetscSectionGetNumFields(section, &numFields));
7415   for (f = 0, foff = 0; f < numFields; ++f) {
7416     PetscInt        fdof, cfdof;
7417     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7418     PetscInt        cind = 0, b;
7419     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7420 
7421     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7422     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7423     if (!cfdof || setBC) {
7424       for (b = 0; b < fdof; ++b) {
7425         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7426         const PetscInt ind    = indperm ? indperm[preind] : preind;
7427 
7428         indices[ind] = off + foff + b;
7429       }
7430     } else {
7431       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7432       for (b = 0; b < fdof; ++b) {
7433         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7434         const PetscInt ind    = indperm ? indperm[preind] : preind;
7435 
7436         if ((cind < cfdof) && (b == fcdofs[cind])) {
7437           indices[ind] = -(off + foff + b + 1);
7438           ++cind;
7439         } else {
7440           indices[ind] = off + foff + b - (islocal ? 0 : cind);
7441         }
7442       }
7443     }
7444     foff += (setBC || islocal ? fdof : (fdof - cfdof));
7445     foffs[f] += fdof;
7446   }
7447   PetscFunctionReturn(PETSC_SUCCESS);
7448 }
7449 
7450 /*
7451   This version believes the globalSection offsets for each field, rather than just the point offset
7452 
7453  . foffs - The offset into 'indices' for each field, since it is segregated by field
7454 
7455  Notes:
7456  The semantics of this function relate to that of setBC=FALSE in DMPlexGetIndicesPointFields_Internal.
7457  Since this function uses global indices, setBC=TRUE would be invalid, so no such argument exists.
7458 */
7459 static PetscErrorCode DMPlexGetIndicesPointFieldsSplit_Internal(PetscSection section, PetscSection globalSection, PetscInt point, PetscInt foffs[], const PetscInt ***perms, PetscInt permsoff, const PetscInt indperm[], PetscInt indices[])
7460 {
7461   PetscInt numFields, foff, f;
7462 
7463   PetscFunctionBegin;
7464   PetscCall(PetscSectionGetNumFields(section, &numFields));
7465   for (f = 0; f < numFields; ++f) {
7466     PetscInt        fdof, cfdof;
7467     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7468     PetscInt        cind = 0, b;
7469     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7470 
7471     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7472     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7473     PetscCall(PetscSectionGetFieldOffset(globalSection, point, f, &foff));
7474     if (!cfdof) {
7475       for (b = 0; b < fdof; ++b) {
7476         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7477         const PetscInt ind    = indperm ? indperm[preind] : preind;
7478 
7479         indices[ind] = foff + b;
7480       }
7481     } else {
7482       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7483       for (b = 0; b < fdof; ++b) {
7484         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7485         const PetscInt ind    = indperm ? indperm[preind] : preind;
7486 
7487         if ((cind < cfdof) && (b == fcdofs[cind])) {
7488           indices[ind] = -(foff + b + 1);
7489           ++cind;
7490         } else {
7491           indices[ind] = foff + b - cind;
7492         }
7493       }
7494     }
7495     foffs[f] += fdof;
7496   }
7497   PetscFunctionReturn(PETSC_SUCCESS);
7498 }
7499 
7500 static PetscErrorCode DMPlexAnchorsGetSubMatIndices(PetscInt nPoints, const PetscInt pnts[], PetscSection section, PetscSection cSec, PetscInt tmpIndices[], PetscInt fieldOffsets[], PetscInt indices[], const PetscInt ***perms)
7501 {
7502   PetscInt numFields, sStart, sEnd, cStart, cEnd;
7503 
7504   PetscFunctionBegin;
7505   PetscCall(PetscSectionGetNumFields(section, &numFields));
7506   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
7507   PetscCall(PetscSectionGetChart(cSec, &cStart, &cEnd));
7508   for (PetscInt p = 0; p < nPoints; p++) {
7509     PetscInt     b       = pnts[2 * p];
7510     PetscInt     bSecDof = 0, bOff;
7511     PetscInt     cSecDof = 0;
7512     PetscSection indices_section;
7513 
7514     if (b >= sStart && b < sEnd) PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7515     if (!bSecDof) continue;
7516     if (b >= cStart && b < cEnd) PetscCall(PetscSectionGetDof(cSec, b, &cSecDof));
7517     indices_section = cSecDof > 0 ? cSec : section;
7518     if (numFields) {
7519       PetscInt fStart[32], fEnd[32];
7520 
7521       fStart[0] = 0;
7522       fEnd[0]   = 0;
7523       for (PetscInt f = 0; f < numFields; f++) {
7524         PetscInt fDof = 0;
7525 
7526         PetscCall(PetscSectionGetFieldDof(indices_section, b, f, &fDof));
7527         fStart[f + 1] = fStart[f] + fDof;
7528         fEnd[f + 1]   = fStart[f + 1];
7529       }
7530       PetscCall(PetscSectionGetOffset(indices_section, b, &bOff));
7531       // only apply permutations on one side
7532       PetscCall(DMPlexGetIndicesPointFields_Internal(indices_section, PETSC_TRUE, b, bOff, fEnd, PETSC_TRUE, perms, perms ? p : -1, NULL, tmpIndices));
7533       for (PetscInt f = 0; f < numFields; f++) {
7534         for (PetscInt i = fStart[f]; i < fEnd[f]; i++) { indices[fieldOffsets[f]++] = (cSecDof > 0) ? tmpIndices[i] : -(tmpIndices[i] + 1); }
7535       }
7536     } else {
7537       PetscInt bEnd = 0;
7538 
7539       PetscCall(PetscSectionGetOffset(indices_section, b, &bOff));
7540       PetscCall(DMPlexGetIndicesPoint_Internal(indices_section, PETSC_TRUE, b, bOff, &bEnd, PETSC_TRUE, (perms && perms[0]) ? perms[0][p] : NULL, NULL, tmpIndices));
7541 
7542       for (PetscInt i = 0; i < bEnd; i++) indices[fieldOffsets[0]++] = (cSecDof > 0) ? tmpIndices[i] : -(tmpIndices[i] + 1);
7543     }
7544   }
7545   PetscFunctionReturn(PETSC_SUCCESS);
7546 }
7547 
7548 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[])
7549 {
7550   Mat             cMat;
7551   PetscSection    aSec, cSec;
7552   IS              aIS;
7553   PetscInt        aStart = -1, aEnd = -1;
7554   PetscInt        sStart = -1, sEnd = -1;
7555   PetscInt        cStart = -1, cEnd = -1;
7556   const PetscInt *anchors;
7557   PetscInt        numFields, p;
7558   PetscInt        newNumPoints = 0, newNumIndices = 0;
7559   PetscInt       *newPoints, *indices, *newIndices, *tmpIndices, *tmpNewIndices;
7560   PetscInt        oldOffsets[32];
7561   PetscInt        newOffsets[32];
7562   PetscInt        oldOffsetsCopy[32];
7563   PetscInt        newOffsetsCopy[32];
7564   PetscScalar    *modMat         = NULL;
7565   PetscBool       anyConstrained = PETSC_FALSE;
7566 
7567   PetscFunctionBegin;
7568   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7569   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7570   PetscCall(PetscSectionGetNumFields(section, &numFields));
7571 
7572   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
7573   /* if there are point-to-point constraints */
7574   if (aSec) {
7575     PetscCall(PetscArrayzero(newOffsets, 32));
7576     PetscCall(PetscArrayzero(oldOffsets, 32));
7577     PetscCall(ISGetIndices(aIS, &anchors));
7578     PetscCall(PetscSectionGetChart(aSec, &aStart, &aEnd));
7579     PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
7580     /* figure out how many points are going to be in the new element matrix
7581      * (we allow double counting, because it's all just going to be summed
7582      * into the global matrix anyway) */
7583     for (p = 0; p < 2 * numPoints; p += 2) {
7584       PetscInt b    = points[p];
7585       PetscInt bDof = 0, bSecDof = 0;
7586 
7587       if (b >= sStart && b < sEnd) PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7588       if (!bSecDof) continue;
7589 
7590       for (PetscInt f = 0; f < numFields; f++) {
7591         PetscInt fDof = 0;
7592 
7593         PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7594         oldOffsets[f + 1] += fDof;
7595       }
7596       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7597       if (bDof) {
7598         /* this point is constrained */
7599         /* it is going to be replaced by its anchors */
7600         PetscInt bOff, q;
7601 
7602         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7603         for (q = 0; q < bDof; q++) {
7604           PetscInt a    = anchors[bOff + q];
7605           PetscInt aDof = 0;
7606 
7607           if (a >= sStart && a < sEnd) PetscCall(PetscSectionGetDof(section, a, &aDof));
7608           if (aDof) {
7609             anyConstrained = PETSC_TRUE;
7610             newNumPoints += 1;
7611           }
7612           newNumIndices += aDof;
7613           for (PetscInt f = 0; f < numFields; ++f) {
7614             PetscInt fDof = 0;
7615 
7616             if (a >= sStart && a < sEnd) PetscCall(PetscSectionGetFieldDof(section, a, f, &fDof));
7617             newOffsets[f + 1] += fDof;
7618           }
7619         }
7620       } else {
7621         /* this point is not constrained */
7622         newNumPoints++;
7623         newNumIndices += bSecDof;
7624         for (PetscInt f = 0; f < numFields; ++f) {
7625           PetscInt fDof;
7626 
7627           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7628           newOffsets[f + 1] += fDof;
7629         }
7630       }
7631     }
7632   }
7633   if (!anyConstrained) {
7634     if (outNumPoints) *outNumPoints = 0;
7635     if (outNumIndices) *outNumIndices = 0;
7636     if (outPoints) *outPoints = NULL;
7637     if (outMat) *outMat = NULL;
7638     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7639     PetscFunctionReturn(PETSC_SUCCESS);
7640   }
7641 
7642   if (outNumPoints) *outNumPoints = newNumPoints;
7643   if (outNumIndices) *outNumIndices = newNumIndices;
7644 
7645   for (PetscInt f = 0; f < numFields; ++f) newOffsets[f + 1] += newOffsets[f];
7646   for (PetscInt f = 0; f < numFields; ++f) oldOffsets[f + 1] += oldOffsets[f];
7647 
7648   if (!outPoints && !outMat) {
7649     if (offsets) {
7650       for (PetscInt f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7651     }
7652     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7653     PetscFunctionReturn(PETSC_SUCCESS);
7654   }
7655 
7656   PetscCheck(!numFields || newOffsets[numFields] == newNumIndices, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, newOffsets[numFields], newNumIndices);
7657   PetscCheck(!numFields || oldOffsets[numFields] == numIndices, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, oldOffsets[numFields], numIndices);
7658 
7659   PetscCall(DMGetDefaultConstraints(dm, &cSec, &cMat, NULL));
7660   PetscCall(PetscSectionGetChart(cSec, &cStart, &cEnd));
7661 
7662   /* output arrays */
7663   PetscCall(DMGetWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7664   PetscCall(PetscArrayzero(newPoints, 2 * newNumPoints));
7665 
7666   // get the new Points
7667   for (PetscInt p = 0, newP = 0; p < numPoints; p++) {
7668     PetscInt b    = points[2 * p];
7669     PetscInt bDof = 0, bSecDof = 0, bOff;
7670 
7671     if (b >= sStart && b < sEnd) PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7672     if (!bSecDof) continue;
7673     if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7674     if (bDof) {
7675       PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7676       for (PetscInt q = 0; q < bDof; q++) {
7677         PetscInt a = anchors[bOff + q], aDof = 0;
7678 
7679         if (a >= sStart && a < sEnd) PetscCall(PetscSectionGetDof(section, a, &aDof));
7680         if (aDof) {
7681           newPoints[2 * newP]     = a;
7682           newPoints[2 * newP + 1] = 0; // orientations are accounted for in constructing the matrix, newly added points are in default orientation
7683           newP++;
7684         }
7685       }
7686     } else {
7687       newPoints[2 * newP]     = b;
7688       newPoints[2 * newP + 1] = points[2 * p + 1];
7689       newP++;
7690     }
7691   }
7692 
7693   if (outMat) {
7694     PetscScalar *tmpMat;
7695     PetscCall(PetscArraycpy(oldOffsetsCopy, oldOffsets, 32));
7696     PetscCall(PetscArraycpy(newOffsetsCopy, newOffsets, 32));
7697 
7698     PetscCall(DMGetWorkArray(dm, numIndices, MPIU_INT, &indices));
7699     PetscCall(DMGetWorkArray(dm, numIndices, MPIU_INT, &tmpIndices));
7700     PetscCall(DMGetWorkArray(dm, newNumIndices, MPIU_INT, &newIndices));
7701     PetscCall(DMGetWorkArray(dm, newNumIndices, MPIU_INT, &tmpNewIndices));
7702 
7703     for (PetscInt i = 0; i < numIndices; i++) indices[i] = -1;
7704     for (PetscInt i = 0; i < newNumIndices; i++) newIndices[i] = -1;
7705 
7706     PetscCall(DMPlexAnchorsGetSubMatIndices(numPoints, points, section, cSec, tmpIndices, oldOffsetsCopy, indices, perms));
7707     PetscCall(DMPlexAnchorsGetSubMatIndices(newNumPoints, newPoints, section, section, tmpNewIndices, newOffsetsCopy, newIndices, NULL));
7708 
7709     PetscCall(DMGetWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &modMat));
7710     PetscCall(DMGetWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &tmpMat));
7711     PetscCall(PetscArrayzero(modMat, newNumIndices * numIndices));
7712     // for each field, insert the anchor modification into modMat
7713     for (PetscInt f = 0; f < PetscMax(1, numFields); f++) {
7714       PetscInt fStart    = oldOffsets[f];
7715       PetscInt fNewStart = newOffsets[f];
7716       for (PetscInt p = 0, newP = 0, o = fStart, oNew = fNewStart; p < numPoints; p++) {
7717         PetscInt b    = points[2 * p];
7718         PetscInt bDof = 0, bSecDof = 0, bOff;
7719 
7720         if (b >= sStart && b < sEnd) {
7721           if (numFields) {
7722             PetscCall(PetscSectionGetFieldDof(section, b, f, &bSecDof));
7723           } else {
7724             PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7725           }
7726         }
7727         if (!bSecDof) continue;
7728         if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7729         if (bDof) {
7730           PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7731           for (PetscInt q = 0; q < bDof; q++, newP++) {
7732             PetscInt a = anchors[bOff + q], aDof = 0;
7733 
7734             if (a >= sStart && a < sEnd) {
7735               if (numFields) {
7736                 PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
7737               } else {
7738                 PetscCall(PetscSectionGetDof(section, a, &aDof));
7739               }
7740             }
7741             if (aDof) {
7742               PetscCall(MatGetValues(cMat, bSecDof, &indices[o], aDof, &newIndices[oNew], tmpMat));
7743               for (PetscInt d = 0; d < bSecDof; d++) {
7744                 for (PetscInt e = 0; e < aDof; e++) modMat[(o + d) * newNumIndices + oNew + e] = tmpMat[d * aDof + e];
7745               }
7746             }
7747             oNew += aDof;
7748           }
7749         } else {
7750           // Insert the identity matrix in this block
7751           for (PetscInt d = 0; d < bSecDof; d++) modMat[(o + d) * newNumIndices + oNew + d] = 1;
7752           oNew += bSecDof;
7753           newP++;
7754         }
7755         o += bSecDof;
7756       }
7757     }
7758 
7759     *outMat = modMat;
7760 
7761     PetscCall(DMRestoreWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &tmpMat));
7762     PetscCall(DMRestoreWorkArray(dm, newNumIndices, MPIU_INT, &tmpNewIndices));
7763     PetscCall(DMRestoreWorkArray(dm, newNumIndices, MPIU_INT, &newIndices));
7764     PetscCall(DMRestoreWorkArray(dm, numIndices, MPIU_INT, &tmpIndices));
7765     PetscCall(DMRestoreWorkArray(dm, numIndices, MPIU_INT, &indices));
7766   }
7767   PetscCall(ISRestoreIndices(aIS, &anchors));
7768 
7769   /* output */
7770   if (outPoints) {
7771     *outPoints = newPoints;
7772   } else {
7773     PetscCall(DMRestoreWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7774   }
7775   for (PetscInt f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7776   PetscFunctionReturn(PETSC_SUCCESS);
7777 }
7778 
7779 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)
7780 {
7781   PetscScalar *modMat        = NULL;
7782   PetscInt     newNumIndices = -1;
7783 
7784   PetscFunctionBegin;
7785   /* 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.
7786      modMat is that matrix C */
7787   PetscCall(DMPlexAnchorsGetSubMatModification(dm, section, numPoints, numIndices, points, perms, outNumPoints, &newNumIndices, outPoints, offsets, outValues ? &modMat : NULL));
7788   if (outNumIndices) *outNumIndices = newNumIndices;
7789   if (modMat) {
7790     const PetscScalar *newValues = values;
7791 
7792     if (multiplyRight) {
7793       PetscScalar *newNewValues = NULL;
7794       PetscBLASInt M, N, K;
7795       PetscScalar  a = 1.0, b = 0.0;
7796 
7797       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);
7798 
7799       PetscCall(PetscBLASIntCast(newNumIndices, &M));
7800       PetscCall(PetscBLASIntCast(numRows, &N));
7801       PetscCall(PetscBLASIntCast(numIndices, &K));
7802       PetscCall(DMGetWorkArray(dm, numRows * newNumIndices, MPIU_SCALAR, &newNewValues));
7803       // row-major to column-major conversion, right multiplication becomes left multiplication
7804       PetscCallBLAS("BLASgemm", BLASgemm_("N", "N", &M, &N, &K, &a, modMat, &M, newValues, &K, &b, newNewValues, &M));
7805       numCols   = newNumIndices;
7806       newValues = newNewValues;
7807     }
7808 
7809     if (multiplyLeft) {
7810       PetscScalar *newNewValues = NULL;
7811       PetscBLASInt M, N, K;
7812       PetscScalar  a = 1.0, b = 0.0;
7813 
7814       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);
7815 
7816       PetscCall(PetscBLASIntCast(numCols, &M));
7817       PetscCall(PetscBLASIntCast(newNumIndices, &N));
7818       PetscCall(PetscBLASIntCast(numIndices, &K));
7819       PetscCall(DMGetWorkArray(dm, newNumIndices * numCols, MPIU_SCALAR, &newNewValues));
7820       // row-major to column-major conversion, left multiplication becomes right multiplication
7821       PetscCallBLAS("BLASgemm", BLASgemm_("N", "T", &M, &N, &K, &a, newValues, &M, modMat, &N, &b, newNewValues, &M));
7822       if (newValues != values) PetscCall(DMRestoreWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &newValues));
7823       newValues = newNewValues;
7824     }
7825     *outValues = (PetscScalar *)newValues;
7826     PetscCall(DMRestoreWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &modMat));
7827   }
7828   PetscFunctionReturn(PETSC_SUCCESS);
7829 }
7830 
7831 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)
7832 {
7833   PetscFunctionBegin;
7834   PetscCall(DMPlexAnchorsModifyMat_Internal(dm, section, numPoints, numIndices, points, perms, numIndices, numIndices, values, outNumPoints, outNumIndices, outPoints, outValues, offsets, PETSC_TRUE, multiplyLeft));
7835   PetscFunctionReturn(PETSC_SUCCESS);
7836 }
7837 
7838 static PetscErrorCode DMPlexGetClosureIndicesSize_Internal(DM dm, PetscSection section, PetscInt point, PetscInt *closureSize)
7839 {
7840   /* Closure ordering */
7841   PetscSection    clSection;
7842   IS              clPoints;
7843   const PetscInt *clp;
7844   PetscInt       *points;
7845   PetscInt        Ncl, Ni = 0;
7846 
7847   PetscFunctionBeginHot;
7848   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &Ncl, &points, &clSection, &clPoints, &clp));
7849   for (PetscInt p = 0; p < Ncl * 2; p += 2) {
7850     PetscInt dof;
7851 
7852     PetscCall(PetscSectionGetDof(section, points[p], &dof));
7853     Ni += dof;
7854   }
7855   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7856   *closureSize = Ni;
7857   PetscFunctionReturn(PETSC_SUCCESS);
7858 }
7859 
7860 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)
7861 {
7862   /* Closure ordering */
7863   PetscSection    clSection;
7864   IS              clPoints;
7865   const PetscInt *clp;
7866   PetscInt       *points;
7867   const PetscInt *clperm = NULL;
7868   /* Dof permutation and sign flips */
7869   const PetscInt    **perms[32] = {NULL};
7870   const PetscScalar **flips[32] = {NULL};
7871   PetscScalar        *valCopy   = NULL;
7872   /* Hanging node constraints */
7873   PetscInt    *pointsC = NULL;
7874   PetscScalar *valuesC = NULL;
7875   PetscInt     NclC, NiC;
7876 
7877   PetscInt *idx;
7878   PetscInt  Nf, Ncl, Ni = 0, offsets[32], p, f;
7879   PetscBool isLocal = (section == idxSection) ? PETSC_TRUE : PETSC_FALSE;
7880   PetscInt  idxStart, idxEnd;
7881   PetscInt  nRows, nCols;
7882 
7883   PetscFunctionBeginHot;
7884   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7885   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7886   PetscValidHeaderSpecific(idxSection, PETSC_SECTION_CLASSID, 3);
7887   PetscAssertPointer(numRows, 6);
7888   PetscAssertPointer(numCols, 7);
7889   if (indices) PetscAssertPointer(indices, 8);
7890   if (outOffsets) PetscAssertPointer(outOffsets, 9);
7891   if (values) PetscAssertPointer(values, 10);
7892   PetscCall(PetscSectionGetNumFields(section, &Nf));
7893   PetscCheck(Nf <= 31, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", Nf);
7894   PetscCall(PetscArrayzero(offsets, 32));
7895   /* 1) Get points in closure */
7896   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &Ncl, &points, &clSection, &clPoints, &clp));
7897   if (useClPerm) {
7898     PetscInt depth, clsize;
7899     PetscCall(DMPlexGetPointDepth(dm, point, &depth));
7900     for (clsize = 0, p = 0; p < Ncl; p++) {
7901       PetscInt dof;
7902       PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
7903       clsize += dof;
7904     }
7905     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
7906   }
7907   /* 2) Get number of indices on these points and field offsets from section */
7908   for (p = 0; p < Ncl * 2; p += 2) {
7909     PetscInt dof, fdof;
7910 
7911     PetscCall(PetscSectionGetDof(section, points[p], &dof));
7912     for (f = 0; f < Nf; ++f) {
7913       PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
7914       offsets[f + 1] += fdof;
7915     }
7916     Ni += dof;
7917   }
7918   if (*numRows == -1) *numRows = Ni;
7919   if (*numCols == -1) *numCols = Ni;
7920   nRows = *numRows;
7921   nCols = *numCols;
7922   for (f = 1; f < Nf; ++f) offsets[f + 1] += offsets[f];
7923   PetscCheck(!Nf || offsets[Nf] == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, offsets[Nf], Ni);
7924   /* 3) Get symmetries and sign flips. Apply sign flips to values if passed in (only works for square values matrix) */
7925   if (multiplyRight) PetscCheck(nCols == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Expected %" PetscInt_FMT " columns, got %" PetscInt_FMT, Ni, nCols);
7926   if (multiplyLeft) PetscCheck(nRows == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Expected %" PetscInt_FMT " rows, got %" PetscInt_FMT, Ni, nRows);
7927   for (f = 0; f < PetscMax(1, Nf); ++f) {
7928     if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7929     else PetscCall(PetscSectionGetPointSyms(section, Ncl, points, &perms[f], &flips[f]));
7930     /* may need to apply sign changes to the element matrix */
7931     if (values && flips[f]) {
7932       PetscInt foffset = offsets[f];
7933 
7934       for (p = 0; p < Ncl; ++p) {
7935         PetscInt           pnt  = points[2 * p], fdof;
7936         const PetscScalar *flip = flips[f] ? flips[f][p] : NULL;
7937 
7938         if (!Nf) PetscCall(PetscSectionGetDof(section, pnt, &fdof));
7939         else PetscCall(PetscSectionGetFieldDof(section, pnt, f, &fdof));
7940         if (flip) {
7941           PetscInt i, j, k;
7942 
7943           if (!valCopy) {
7944             PetscCall(DMGetWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
7945             for (j = 0; j < Ni * Ni; ++j) valCopy[j] = (*values)[j];
7946             *values = valCopy;
7947           }
7948           for (i = 0; i < fdof; ++i) {
7949             PetscScalar fval = flip[i];
7950 
7951             if (multiplyRight) {
7952               for (k = 0; k < nRows; ++k) { valCopy[Ni * k + (foffset + i)] *= fval; }
7953             }
7954             if (multiplyLeft) {
7955               for (k = 0; k < nCols; ++k) { valCopy[nCols * (foffset + i) + k] *= fval; }
7956             }
7957           }
7958         }
7959         foffset += fdof;
7960       }
7961     }
7962   }
7963   /* 4) Apply hanging node constraints. Get new symmetries and replace all storage with constrained storage */
7964   PetscCall(DMPlexAnchorsModifyMat_Internal(dm, section, Ncl, Ni, points, perms, nRows, nCols, values ? *values : NULL, &NclC, &NiC, &pointsC, values ? &valuesC : NULL, offsets, multiplyRight, multiplyLeft));
7965   if (NclC) {
7966     if (multiplyRight) *numCols = NiC;
7967     if (multiplyLeft) *numRows = NiC;
7968     if (valCopy) PetscCall(DMRestoreWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
7969     for (f = 0; f < PetscMax(1, Nf); ++f) {
7970       if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7971       else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
7972     }
7973     for (f = 0; f < PetscMax(1, Nf); ++f) {
7974       if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, NclC, pointsC, &perms[f], &flips[f]));
7975       else PetscCall(PetscSectionGetPointSyms(section, NclC, pointsC, &perms[f], &flips[f]));
7976     }
7977     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7978     Ncl    = NclC;
7979     Ni     = NiC;
7980     points = pointsC;
7981     if (values) *values = valuesC;
7982   }
7983   /* 5) Calculate indices */
7984   PetscCall(DMGetWorkArray(dm, Ni, MPIU_INT, &idx));
7985   PetscCall(PetscSectionGetChart(idxSection, &idxStart, &idxEnd));
7986   if (Nf) {
7987     PetscInt  idxOff;
7988     PetscBool useFieldOffsets;
7989 
7990     if (outOffsets) {
7991       for (f = 0; f <= Nf; f++) outOffsets[f] = offsets[f];
7992     }
7993     PetscCall(PetscSectionGetUseFieldOffsets(idxSection, &useFieldOffsets));
7994     if (useFieldOffsets) {
7995       for (p = 0; p < Ncl; ++p) {
7996         const PetscInt pnt = points[p * 2];
7997 
7998         PetscCall(DMPlexGetIndicesPointFieldsSplit_Internal(section, idxSection, pnt, offsets, perms, p, clperm, idx));
7999       }
8000     } else {
8001       for (p = 0; p < Ncl; ++p) {
8002         const PetscInt pnt = points[p * 2];
8003 
8004         if (pnt < idxStart || pnt >= idxEnd) continue;
8005         PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
8006         /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
8007          * not (at the time of this writing) have fields set. They probably should, in which case we would pass the
8008          * global section. */
8009         PetscCall(DMPlexGetIndicesPointFields_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, offsets, PETSC_FALSE, perms, p, clperm, idx));
8010       }
8011     }
8012   } else {
8013     PetscInt off = 0, idxOff;
8014 
8015     for (p = 0; p < Ncl; ++p) {
8016       const PetscInt  pnt  = points[p * 2];
8017       const PetscInt *perm = perms[0] ? perms[0][p] : NULL;
8018 
8019       if (pnt < idxStart || pnt >= idxEnd) continue;
8020       PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
8021       /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
8022        * not (at the time of this writing) have fields set. They probably should, in which case we would pass the global section. */
8023       PetscCall(DMPlexGetIndicesPoint_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, &off, PETSC_FALSE, perm, clperm, idx));
8024     }
8025   }
8026   /* 6) Cleanup */
8027   for (f = 0; f < PetscMax(1, Nf); ++f) {
8028     if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
8029     else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
8030   }
8031   if (NclC) {
8032     PetscCall(DMRestoreWorkArray(dm, NclC * 2, MPIU_INT, &pointsC));
8033   } else {
8034     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
8035   }
8036 
8037   if (indices) *indices = idx;
8038   PetscFunctionReturn(PETSC_SUCCESS);
8039 }
8040 
8041 /*@C
8042   DMPlexGetClosureIndices - Gets the global dof indices associated with the closure of the given point within the provided sections.
8043 
8044   Not collective
8045 
8046   Input Parameters:
8047 + dm         - The `DM`
8048 . section    - The `PetscSection` describing the points (a local section)
8049 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
8050 . point      - The point defining the closure
8051 - useClPerm  - Use the closure point permutation if available
8052 
8053   Output Parameters:
8054 + numIndices - The number of dof indices in the closure of point with the input sections
8055 . indices    - The dof indices
8056 . outOffsets - Array to write the field offsets into, or `NULL`
8057 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or `NULL`
8058 
8059   Level: advanced
8060 
8061   Notes:
8062   Call `DMPlexRestoreClosureIndices()` to free allocated memory
8063 
8064   If `idxSection` is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
8065   of those indices is not significant.  If `idxSection` is local, the constrained dofs will yield the involution -(idx+1)
8066   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
8067   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when `idxSection` == section, otherwise global
8068   indices (with the above semantics) are implied.
8069 
8070 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`,
8071           `PetscSection`, `DMGetGlobalSection()`
8072 @*/
8073 PetscErrorCode DMPlexGetClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
8074 {
8075   PetscInt numRows = -1, numCols = -1;
8076 
8077   PetscFunctionBeginHot;
8078   PetscCall(DMPlexGetClosureIndices_Internal(dm, section, idxSection, point, useClPerm, &numRows, &numCols, indices, outOffsets, values, PETSC_TRUE, PETSC_TRUE));
8079   PetscCheck(numRows == numCols, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Symmetric matrix transformation produces rectangular dimensions (%" PetscInt_FMT ", %" PetscInt_FMT ")", numRows, numCols);
8080   *numIndices = numRows;
8081   PetscFunctionReturn(PETSC_SUCCESS);
8082 }
8083 
8084 /*@C
8085   DMPlexRestoreClosureIndices - Restores the global dof indices associated with the closure of the given point within the provided sections.
8086 
8087   Not collective
8088 
8089   Input Parameters:
8090 + dm         - The `DM`
8091 . section    - The `PetscSection` describing the points (a local section)
8092 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
8093 . point      - The point defining the closure
8094 - useClPerm  - Use the closure point permutation if available
8095 
8096   Output Parameters:
8097 + numIndices - The number of dof indices in the closure of point with the input sections
8098 . indices    - The dof indices
8099 . outOffsets - Array to write the field offsets into, or `NULL`
8100 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or `NULL`
8101 
8102   Level: advanced
8103 
8104   Notes:
8105   If values were modified, the user is responsible for calling `DMRestoreWorkArray`(dm, 0, `MPIU_SCALAR`, &values).
8106 
8107   If idxSection is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
8108   of those indices is not significant.  If idxSection is local, the constrained dofs will yield the involution -(idx+1)
8109   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
8110   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when idxSection == section, otherwise global
8111   indices (with the above semantics) are implied.
8112 
8113 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`, `DMGetGlobalSection()`
8114 @*/
8115 PetscErrorCode DMPlexRestoreClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
8116 {
8117   PetscFunctionBegin;
8118   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8119   PetscAssertPointer(indices, 7);
8120   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, indices));
8121   PetscFunctionReturn(PETSC_SUCCESS);
8122 }
8123 
8124 PetscErrorCode DMPlexMatSetClosure_Internal(DM dm, PetscSection section, PetscSection globalSection, PetscBool useClPerm, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8125 {
8126   DM_Plex           *mesh = (DM_Plex *)dm->data;
8127   PetscInt          *indices;
8128   PetscInt           numIndices;
8129   const PetscScalar *valuesOrig = values;
8130   PetscErrorCode     ierr;
8131 
8132   PetscFunctionBegin;
8133   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8134   if (!section) PetscCall(DMGetLocalSection(dm, &section));
8135   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
8136   if (!globalSection) PetscCall(DMGetGlobalSection(dm, &globalSection));
8137   PetscValidHeaderSpecific(globalSection, PETSC_SECTION_CLASSID, 3);
8138   PetscValidHeaderSpecific(A, MAT_CLASSID, 5);
8139 
8140   PetscCall(DMPlexGetClosureIndices(dm, section, globalSection, point, useClPerm, &numIndices, &indices, NULL, (PetscScalar **)&values));
8141 
8142   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndices, indices, 0, NULL, values));
8143   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8144   ierr = MatSetValues(A, numIndices, indices, numIndices, indices, values, mode);
8145   if (ierr) {
8146     PetscMPIInt rank;
8147 
8148     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8149     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8150     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndices, indices, 0, NULL, values));
8151     PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
8152     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
8153     SETERRQ(PetscObjectComm((PetscObject)dm), ierr, "Not possible to set matrix values");
8154   }
8155   if (mesh->printFEM > 1) {
8156     PetscInt i;
8157     PetscCall(PetscPrintf(PETSC_COMM_SELF, "  Indices:"));
8158     for (i = 0; i < numIndices; ++i) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, indices[i]));
8159     PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8160   }
8161 
8162   PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
8163   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
8164   PetscFunctionReturn(PETSC_SUCCESS);
8165 }
8166 
8167 /*@C
8168   DMPlexMatSetClosure - Set an array of the values on the closure of 'point'
8169 
8170   Not collective
8171 
8172   Input Parameters:
8173 + dm            - The `DM`
8174 . section       - The section describing the layout in `v`, or `NULL` to use the default section
8175 . globalSection - The section describing the layout in `v`, or `NULL` to use the default global section
8176 . A             - The matrix
8177 . point         - The point in the `DM`
8178 . values        - The array of values
8179 - mode          - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
8180 
8181   Level: intermediate
8182 
8183 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosureGeneral()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
8184 @*/
8185 PetscErrorCode DMPlexMatSetClosure(DM dm, PetscSection section, PetscSection globalSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8186 {
8187   PetscFunctionBegin;
8188   PetscCall(DMPlexMatSetClosure_Internal(dm, section, globalSection, PETSC_TRUE, A, point, values, mode));
8189   PetscFunctionReturn(PETSC_SUCCESS);
8190 }
8191 
8192 /*@C
8193   DMPlexMatSetClosureGeneral - Set an array of the values on the closure of 'point' using a different row and column section
8194 
8195   Not collective
8196 
8197   Input Parameters:
8198 + dmRow            - The `DM` for the row fields
8199 . sectionRow       - The section describing the layout, or `NULL` to use the default section in `dmRow`
8200 . useRowPerm       - The flag to use the closure permutation of the `dmRow` if available
8201 . globalSectionRow - The section describing the layout, or `NULL` to use the default global section in `dmRow`
8202 . dmCol            - The `DM` for the column fields
8203 . sectionCol       - The section describing the layout, or `NULL` to use the default section in `dmCol`
8204 . useColPerm       - The flag to use the closure permutation of the `dmCol` if available
8205 . globalSectionCol - The section describing the layout, or `NULL` to use the default global section in `dmCol`
8206 . A                - The matrix
8207 . point            - The point in the `DM`
8208 . values           - The array of values
8209 - mode             - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
8210 
8211   Level: intermediate
8212 
8213 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosure()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
8214 @*/
8215 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)
8216 {
8217   DM_Plex           *mesh = (DM_Plex *)dmRow->data;
8218   PetscInt          *indicesRow, *indicesCol;
8219   PetscInt           numIndicesRow = -1, numIndicesCol = -1;
8220   const PetscScalar *valuesV0 = values, *valuesV1, *valuesV2;
8221 
8222   PetscErrorCode ierr;
8223 
8224   PetscFunctionBegin;
8225   PetscValidHeaderSpecific(dmRow, DM_CLASSID, 1);
8226   if (!sectionRow) PetscCall(DMGetLocalSection(dmRow, &sectionRow));
8227   PetscValidHeaderSpecific(sectionRow, PETSC_SECTION_CLASSID, 2);
8228   if (!globalSectionRow) PetscCall(DMGetGlobalSection(dmRow, &globalSectionRow));
8229   PetscValidHeaderSpecific(globalSectionRow, PETSC_SECTION_CLASSID, 3);
8230   PetscValidHeaderSpecific(dmCol, DM_CLASSID, 5);
8231   if (!sectionCol) PetscCall(DMGetLocalSection(dmCol, &sectionCol));
8232   PetscValidHeaderSpecific(sectionCol, PETSC_SECTION_CLASSID, 6);
8233   if (!globalSectionCol) PetscCall(DMGetGlobalSection(dmCol, &globalSectionCol));
8234   PetscValidHeaderSpecific(globalSectionCol, PETSC_SECTION_CLASSID, 7);
8235   PetscValidHeaderSpecific(A, MAT_CLASSID, 9);
8236 
8237   PetscCall(DMPlexGetClosureIndicesSize_Internal(dmRow, sectionRow, point, &numIndicesRow));
8238   PetscCall(DMPlexGetClosureIndicesSize_Internal(dmCol, sectionCol, point, &numIndicesCol));
8239   valuesV1 = valuesV0;
8240   PetscCall(DMPlexGetClosureIndices_Internal(dmRow, sectionRow, globalSectionRow, point, useRowPerm, &numIndicesRow, &numIndicesCol, &indicesRow, NULL, (PetscScalar **)&valuesV1, PETSC_FALSE, PETSC_TRUE));
8241   valuesV2 = valuesV1;
8242   PetscCall(DMPlexGetClosureIndices_Internal(dmCol, sectionCol, globalSectionCol, point, useColPerm, &numIndicesRow, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&valuesV2, PETSC_TRUE, PETSC_FALSE));
8243 
8244   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, valuesV2));
8245   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8246   ierr = MatSetValues(A, numIndicesRow, indicesRow, numIndicesCol, indicesCol, valuesV2, mode);
8247   if (ierr) {
8248     PetscMPIInt rank;
8249 
8250     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8251     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8252     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
8253     PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesRow, NULL, (PetscScalar **)&valuesV2));
8254     PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&valuesV1));
8255     if (valuesV2 != valuesV1) PetscCall(DMRestoreWorkArray(dmCol, 0, MPIU_SCALAR, &valuesV2));
8256     if (valuesV1 != valuesV0) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &valuesV1));
8257   }
8258 
8259   PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, useColPerm, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&valuesV2));
8260   PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, useRowPerm, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&valuesV1));
8261   if (valuesV2 != valuesV1) PetscCall(DMRestoreWorkArray(dmCol, 0, MPIU_SCALAR, &valuesV2));
8262   if (valuesV1 != valuesV0) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &valuesV1));
8263   PetscFunctionReturn(PETSC_SUCCESS);
8264 }
8265 
8266 PetscErrorCode DMPlexMatSetClosureRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8267 {
8268   DM_Plex        *mesh    = (DM_Plex *)dmf->data;
8269   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
8270   PetscInt       *cpoints = NULL;
8271   PetscInt       *findices, *cindices;
8272   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
8273   PetscInt        foffsets[32], coffsets[32];
8274   DMPolytopeType  ct;
8275   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
8276   PetscErrorCode  ierr;
8277 
8278   PetscFunctionBegin;
8279   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
8280   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
8281   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
8282   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
8283   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
8284   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
8285   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
8286   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
8287   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
8288   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
8289   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
8290   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
8291   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
8292   PetscCall(PetscArrayzero(foffsets, 32));
8293   PetscCall(PetscArrayzero(coffsets, 32));
8294   /* Column indices */
8295   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8296   maxFPoints = numCPoints;
8297   /* Compress out points not in the section */
8298   /*   TODO: Squeeze out points with 0 dof as well */
8299   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
8300   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
8301     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
8302       cpoints[q * 2]     = cpoints[p];
8303       cpoints[q * 2 + 1] = cpoints[p + 1];
8304       ++q;
8305     }
8306   }
8307   numCPoints = q;
8308   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
8309     PetscInt fdof;
8310 
8311     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
8312     if (!dof) continue;
8313     for (f = 0; f < numFields; ++f) {
8314       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
8315       coffsets[f + 1] += fdof;
8316     }
8317     numCIndices += dof;
8318   }
8319   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
8320   /* Row indices */
8321   PetscCall(DMPlexGetCellType(dmc, point, &ct));
8322   {
8323     DMPlexTransform tr;
8324     DMPolytopeType *rct;
8325     PetscInt       *rsize, *rcone, *rornt, Nt;
8326 
8327     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8328     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8329     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8330     numSubcells = rsize[Nt - 1];
8331     PetscCall(DMPlexTransformDestroy(&tr));
8332   }
8333   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8334   for (r = 0, q = 0; r < numSubcells; ++r) {
8335     /* TODO Map from coarse to fine cells */
8336     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8337     /* Compress out points not in the section */
8338     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8339     for (p = 0; p < numFPoints * 2; p += 2) {
8340       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8341         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8342         if (!dof) continue;
8343         for (s = 0; s < q; ++s)
8344           if (fpoints[p] == ftotpoints[s * 2]) break;
8345         if (s < q) continue;
8346         ftotpoints[q * 2]     = fpoints[p];
8347         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8348         ++q;
8349       }
8350     }
8351     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8352   }
8353   numFPoints = q;
8354   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8355     PetscInt fdof;
8356 
8357     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8358     if (!dof) continue;
8359     for (f = 0; f < numFields; ++f) {
8360       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8361       foffsets[f + 1] += fdof;
8362     }
8363     numFIndices += dof;
8364   }
8365   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8366 
8367   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8368   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8369   PetscCall(DMGetWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8370   PetscCall(DMGetWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8371   if (numFields) {
8372     const PetscInt **permsF[32] = {NULL};
8373     const PetscInt **permsC[32] = {NULL};
8374 
8375     for (f = 0; f < numFields; f++) {
8376       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8377       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8378     }
8379     for (p = 0; p < numFPoints; p++) {
8380       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8381       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8382     }
8383     for (p = 0; p < numCPoints; p++) {
8384       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8385       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8386     }
8387     for (f = 0; f < numFields; f++) {
8388       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8389       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8390     }
8391   } else {
8392     const PetscInt **permsF = NULL;
8393     const PetscInt **permsC = NULL;
8394 
8395     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8396     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8397     for (p = 0, off = 0; p < numFPoints; p++) {
8398       const PetscInt *perm = permsF ? permsF[p] : NULL;
8399 
8400       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8401       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8402     }
8403     for (p = 0, off = 0; p < numCPoints; p++) {
8404       const PetscInt *perm = permsC ? permsC[p] : NULL;
8405 
8406       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8407       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8408     }
8409     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8410     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8411   }
8412   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8413   /* TODO: flips */
8414   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8415   ierr = MatSetValues(A, numFIndices, findices, numCIndices, cindices, values, mode);
8416   if (ierr) {
8417     PetscMPIInt rank;
8418 
8419     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8420     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8421     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8422     PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8423     PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8424   }
8425   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8426   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8427   PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8428   PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8429   PetscFunctionReturn(PETSC_SUCCESS);
8430 }
8431 
8432 PetscErrorCode DMPlexMatGetClosureIndicesRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, PetscInt point, PetscInt cindices[], PetscInt findices[])
8433 {
8434   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
8435   PetscInt       *cpoints      = NULL;
8436   PetscInt        foffsets[32] = {0}, coffsets[32] = {0};
8437   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
8438   DMPolytopeType  ct;
8439   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
8440 
8441   PetscFunctionBegin;
8442   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
8443   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
8444   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
8445   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
8446   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
8447   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
8448   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
8449   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
8450   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
8451   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
8452   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
8453   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
8454   /* Column indices */
8455   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8456   maxFPoints = numCPoints;
8457   /* Compress out points not in the section */
8458   /*   TODO: Squeeze out points with 0 dof as well */
8459   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
8460   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
8461     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
8462       cpoints[q * 2]     = cpoints[p];
8463       cpoints[q * 2 + 1] = cpoints[p + 1];
8464       ++q;
8465     }
8466   }
8467   numCPoints = q;
8468   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
8469     PetscInt fdof;
8470 
8471     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
8472     if (!dof) continue;
8473     for (f = 0; f < numFields; ++f) {
8474       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
8475       coffsets[f + 1] += fdof;
8476     }
8477     numCIndices += dof;
8478   }
8479   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
8480   /* Row indices */
8481   PetscCall(DMPlexGetCellType(dmc, point, &ct));
8482   {
8483     DMPlexTransform tr;
8484     DMPolytopeType *rct;
8485     PetscInt       *rsize, *rcone, *rornt, Nt;
8486 
8487     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8488     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8489     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8490     numSubcells = rsize[Nt - 1];
8491     PetscCall(DMPlexTransformDestroy(&tr));
8492   }
8493   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8494   for (r = 0, q = 0; r < numSubcells; ++r) {
8495     /* TODO Map from coarse to fine cells */
8496     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8497     /* Compress out points not in the section */
8498     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8499     for (p = 0; p < numFPoints * 2; p += 2) {
8500       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8501         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8502         if (!dof) continue;
8503         for (s = 0; s < q; ++s)
8504           if (fpoints[p] == ftotpoints[s * 2]) break;
8505         if (s < q) continue;
8506         ftotpoints[q * 2]     = fpoints[p];
8507         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8508         ++q;
8509       }
8510     }
8511     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8512   }
8513   numFPoints = q;
8514   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8515     PetscInt fdof;
8516 
8517     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8518     if (!dof) continue;
8519     for (f = 0; f < numFields; ++f) {
8520       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8521       foffsets[f + 1] += fdof;
8522     }
8523     numFIndices += dof;
8524   }
8525   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8526 
8527   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8528   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8529   if (numFields) {
8530     const PetscInt **permsF[32] = {NULL};
8531     const PetscInt **permsC[32] = {NULL};
8532 
8533     for (f = 0; f < numFields; f++) {
8534       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8535       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8536     }
8537     for (p = 0; p < numFPoints; p++) {
8538       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8539       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8540     }
8541     for (p = 0; p < numCPoints; p++) {
8542       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8543       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8544     }
8545     for (f = 0; f < numFields; f++) {
8546       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8547       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8548     }
8549   } else {
8550     const PetscInt **permsF = NULL;
8551     const PetscInt **permsC = NULL;
8552 
8553     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8554     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8555     for (p = 0, off = 0; p < numFPoints; p++) {
8556       const PetscInt *perm = permsF ? permsF[p] : NULL;
8557 
8558       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8559       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8560     }
8561     for (p = 0, off = 0; p < numCPoints; p++) {
8562       const PetscInt *perm = permsC ? permsC[p] : NULL;
8563 
8564       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8565       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8566     }
8567     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8568     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8569   }
8570   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8571   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8572   PetscFunctionReturn(PETSC_SUCCESS);
8573 }
8574 
8575 /*@
8576   DMPlexGetVTKCellHeight - Returns the height in the DAG used to determine which points are cells (normally 0)
8577 
8578   Input Parameter:
8579 . dm - The `DMPLEX` object
8580 
8581   Output Parameter:
8582 . cellHeight - The height of a cell
8583 
8584   Level: developer
8585 
8586 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetVTKCellHeight()`
8587 @*/
8588 PetscErrorCode DMPlexGetVTKCellHeight(DM dm, PetscInt *cellHeight)
8589 {
8590   DM_Plex *mesh = (DM_Plex *)dm->data;
8591 
8592   PetscFunctionBegin;
8593   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8594   PetscAssertPointer(cellHeight, 2);
8595   *cellHeight = mesh->vtkCellHeight;
8596   PetscFunctionReturn(PETSC_SUCCESS);
8597 }
8598 
8599 /*@
8600   DMPlexSetVTKCellHeight - Sets the height in the DAG used to determine which points are cells (normally 0)
8601 
8602   Input Parameters:
8603 + dm         - The `DMPLEX` object
8604 - cellHeight - The height of a cell
8605 
8606   Level: developer
8607 
8608 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetVTKCellHeight()`
8609 @*/
8610 PetscErrorCode DMPlexSetVTKCellHeight(DM dm, PetscInt cellHeight)
8611 {
8612   DM_Plex *mesh = (DM_Plex *)dm->data;
8613 
8614   PetscFunctionBegin;
8615   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8616   mesh->vtkCellHeight = cellHeight;
8617   PetscFunctionReturn(PETSC_SUCCESS);
8618 }
8619 
8620 /*@
8621   DMPlexGetCellTypeStratum - Get the range of cells of a given celltype
8622 
8623   Input Parameters:
8624 + dm - The `DMPLEX` object
8625 - ct - The `DMPolytopeType` of the cell
8626 
8627   Output Parameters:
8628 + start - The first cell of this type, or `NULL`
8629 - end   - The upper bound on this celltype, or `NULL`
8630 
8631   Level: advanced
8632 
8633 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexConstructGhostCells()`, `DMPlexGetDepthStratum()`, `DMPlexGetHeightStratum()`
8634 @*/
8635 PetscErrorCode DMPlexGetCellTypeStratum(DM dm, DMPolytopeType ct, PetscInt *start, PetscInt *end)
8636 {
8637   DM_Plex *mesh = (DM_Plex *)dm->data;
8638   DMLabel  label;
8639   PetscInt pStart, pEnd;
8640 
8641   PetscFunctionBegin;
8642   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8643   if (start) {
8644     PetscAssertPointer(start, 3);
8645     *start = 0;
8646   }
8647   if (end) {
8648     PetscAssertPointer(end, 4);
8649     *end = 0;
8650   }
8651   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8652   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
8653   if (mesh->tr) {
8654     PetscCall(DMPlexTransformGetCellTypeStratum(mesh->tr, ct, start, end));
8655   } else {
8656     PetscCall(DMPlexGetCellTypeLabel(dm, &label));
8657     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named celltype was found");
8658     PetscCall(DMLabelGetStratumBounds(label, ct, start, end));
8659   }
8660   PetscFunctionReturn(PETSC_SUCCESS);
8661 }
8662 
8663 PetscErrorCode DMPlexCreateNumbering_Plex(DM dm, PetscInt pStart, PetscInt pEnd, PetscInt shift, PetscInt *globalSize, PetscSF sf, IS *numbering)
8664 {
8665   PetscSection section, globalSection;
8666   PetscInt    *numbers, p;
8667 
8668   PetscFunctionBegin;
8669   if (PetscDefined(USE_DEBUG)) PetscCall(DMPlexCheckPointSF(dm, sf, PETSC_TRUE));
8670   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
8671   PetscCall(PetscSectionSetChart(section, pStart, pEnd));
8672   for (p = pStart; p < pEnd; ++p) PetscCall(PetscSectionSetDof(section, p, 1));
8673   PetscCall(PetscSectionSetUp(section));
8674   PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &globalSection));
8675   PetscCall(PetscMalloc1(pEnd - pStart, &numbers));
8676   for (p = pStart; p < pEnd; ++p) {
8677     PetscCall(PetscSectionGetOffset(globalSection, p, &numbers[p - pStart]));
8678     if (numbers[p - pStart] < 0) numbers[p - pStart] -= shift;
8679     else numbers[p - pStart] += shift;
8680   }
8681   PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), pEnd - pStart, numbers, PETSC_OWN_POINTER, numbering));
8682   if (globalSize) {
8683     PetscLayout layout;
8684     PetscCall(PetscSectionGetPointLayout(PetscObjectComm((PetscObject)dm), globalSection, &layout));
8685     PetscCall(PetscLayoutGetSize(layout, globalSize));
8686     PetscCall(PetscLayoutDestroy(&layout));
8687   }
8688   PetscCall(PetscSectionDestroy(&section));
8689   PetscCall(PetscSectionDestroy(&globalSection));
8690   PetscFunctionReturn(PETSC_SUCCESS);
8691 }
8692 
8693 /*@
8694   DMPlexCreateCellNumbering - Get a global cell numbering for all cells on this process
8695 
8696   Input Parameters:
8697 + dm         - The `DMPLEX` object
8698 - includeAll - Whether to include all cells, or just the simplex and box cells
8699 
8700   Output Parameter:
8701 . globalCellNumbers - Global cell numbers for all cells on this process
8702 
8703   Level: developer
8704 
8705 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`, `DMPlexGetVertexNumbering()`
8706 @*/
8707 PetscErrorCode DMPlexCreateCellNumbering(DM dm, PetscBool includeAll, IS *globalCellNumbers)
8708 {
8709   PetscInt cellHeight, cStart, cEnd;
8710 
8711   PetscFunctionBegin;
8712   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8713   if (includeAll) PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8714   else PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd));
8715   PetscCall(DMPlexCreateNumbering_Plex(dm, cStart, cEnd, 0, NULL, dm->sf, globalCellNumbers));
8716   PetscFunctionReturn(PETSC_SUCCESS);
8717 }
8718 
8719 /*@
8720   DMPlexGetCellNumbering - Get a global cell numbering for all cells on this process
8721 
8722   Input Parameter:
8723 . dm - The `DMPLEX` object
8724 
8725   Output Parameter:
8726 . globalCellNumbers - Global cell numbers for all cells on this process
8727 
8728   Level: developer
8729 
8730 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreateCellNumbering()`, `DMPlexGetVertexNumbering()`
8731 @*/
8732 PetscErrorCode DMPlexGetCellNumbering(DM dm, IS *globalCellNumbers)
8733 {
8734   DM_Plex *mesh = (DM_Plex *)dm->data;
8735 
8736   PetscFunctionBegin;
8737   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8738   if (!mesh->globalCellNumbers) PetscCall(DMPlexCreateCellNumbering(dm, PETSC_FALSE, &mesh->globalCellNumbers));
8739   *globalCellNumbers = mesh->globalCellNumbers;
8740   PetscFunctionReturn(PETSC_SUCCESS);
8741 }
8742 
8743 PetscErrorCode DMPlexCreateVertexNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalVertexNumbers)
8744 {
8745   PetscInt vStart, vEnd;
8746 
8747   PetscFunctionBegin;
8748   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8749   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8750   PetscCall(DMPlexCreateNumbering_Plex(dm, vStart, vEnd, 0, NULL, dm->sf, globalVertexNumbers));
8751   PetscFunctionReturn(PETSC_SUCCESS);
8752 }
8753 
8754 /*@
8755   DMPlexGetVertexNumbering - Get a global vertex numbering for all vertices on this process
8756 
8757   Input Parameter:
8758 . dm - The `DMPLEX` object
8759 
8760   Output Parameter:
8761 . globalVertexNumbers - Global vertex numbers for all vertices on this process
8762 
8763   Level: developer
8764 
8765 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
8766 @*/
8767 PetscErrorCode DMPlexGetVertexNumbering(DM dm, IS *globalVertexNumbers)
8768 {
8769   DM_Plex *mesh = (DM_Plex *)dm->data;
8770 
8771   PetscFunctionBegin;
8772   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8773   if (!mesh->globalVertexNumbers) PetscCall(DMPlexCreateVertexNumbering_Internal(dm, PETSC_FALSE, &mesh->globalVertexNumbers));
8774   *globalVertexNumbers = mesh->globalVertexNumbers;
8775   PetscFunctionReturn(PETSC_SUCCESS);
8776 }
8777 
8778 /*@
8779   DMPlexCreatePointNumbering - Create a global numbering for all points.
8780 
8781   Collective
8782 
8783   Input Parameter:
8784 . dm - The `DMPLEX` object
8785 
8786   Output Parameter:
8787 . globalPointNumbers - Global numbers for all points on this process
8788 
8789   Level: developer
8790 
8791   Notes:
8792   The point numbering `IS` is parallel, with local portion indexed by local points (see `DMGetLocalSection()`). The global
8793   points are taken as stratified, with each MPI rank owning a contiguous subset of each stratum. In the IS, owned points
8794   will have their non-negative value while points owned by different ranks will be involuted -(idx+1). As an example,
8795   consider a parallel mesh in which the first two elements and first two vertices are owned by rank 0.
8796 
8797   The partitioned mesh is
8798   ```
8799   (2)--0--(3)--1--(4)    (1)--0--(2)
8800   ```
8801   and its global numbering is
8802   ```
8803   (3)--0--(4)--1--(5)--2--(6)
8804   ```
8805   Then the global numbering is provided as
8806   ```
8807   [0] Number of indices in set 5
8808   [0] 0 0
8809   [0] 1 1
8810   [0] 2 3
8811   [0] 3 4
8812   [0] 4 -6
8813   [1] Number of indices in set 3
8814   [1] 0 2
8815   [1] 1 5
8816   [1] 2 6
8817   ```
8818 
8819 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
8820 @*/
8821 PetscErrorCode DMPlexCreatePointNumbering(DM dm, IS *globalPointNumbers)
8822 {
8823   IS        nums[4];
8824   PetscInt  depths[4], gdepths[4], starts[4];
8825   PetscInt  depth, d, shift = 0;
8826   PetscBool empty = PETSC_FALSE;
8827 
8828   PetscFunctionBegin;
8829   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8830   PetscCall(DMPlexGetDepth(dm, &depth));
8831   // For unstratified meshes use dim instead of depth
8832   if (depth < 0) PetscCall(DMGetDimension(dm, &depth));
8833   // If any stratum is empty, we must mark all empty
8834   for (d = 0; d <= depth; ++d) {
8835     PetscInt end;
8836 
8837     depths[d] = depth - d;
8838     PetscCall(DMPlexGetDepthStratum(dm, depths[d], &starts[d], &end));
8839     if (!(starts[d] - end)) empty = PETSC_TRUE;
8840   }
8841   if (empty)
8842     for (d = 0; d <= depth; ++d) {
8843       depths[d] = -1;
8844       starts[d] = -1;
8845     }
8846   else PetscCall(PetscSortIntWithArray(depth + 1, starts, depths));
8847   PetscCallMPI(MPIU_Allreduce(depths, gdepths, (PetscMPIInt)(depth + 1), MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
8848   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]);
8849   // Note here that 'shift' is collective, so that the numbering is stratified by depth
8850   for (d = 0; d <= depth; ++d) {
8851     PetscInt pStart, pEnd, gsize;
8852 
8853     PetscCall(DMPlexGetDepthStratum(dm, gdepths[d], &pStart, &pEnd));
8854     PetscCall(DMPlexCreateNumbering_Plex(dm, pStart, pEnd, shift, &gsize, dm->sf, &nums[d]));
8855     shift += gsize;
8856   }
8857   PetscCall(ISConcatenate(PETSC_COMM_SELF, depth + 1, nums, globalPointNumbers));
8858   for (d = 0; d <= depth; ++d) PetscCall(ISDestroy(&nums[d]));
8859   PetscFunctionReturn(PETSC_SUCCESS);
8860 }
8861 
8862 /*@
8863   DMPlexCreateEdgeNumbering - Create a global numbering for edges.
8864 
8865   Collective
8866 
8867   Input Parameter:
8868 . dm - The `DMPLEX` object
8869 
8870   Output Parameter:
8871 . globalEdgeNumbers - Global numbers for all edges on this process
8872 
8873   Level: developer
8874 
8875   Notes:
8876   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).
8877 
8878 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`, `DMPlexGetVertexNumbering()`, `DMPlexCreatePointNumbering()`
8879 @*/
8880 PetscErrorCode DMPlexCreateEdgeNumbering(DM dm, IS *globalEdgeNumbers)
8881 {
8882   PetscSF  sf;
8883   PetscInt eStart, eEnd;
8884 
8885   PetscFunctionBegin;
8886   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8887   PetscCall(DMGetPointSF(dm, &sf));
8888   PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
8889   PetscCall(DMPlexCreateNumbering_Plex(dm, eStart, eEnd, 0, NULL, sf, globalEdgeNumbers));
8890   PetscFunctionReturn(PETSC_SUCCESS);
8891 }
8892 
8893 /*@
8894   DMPlexCreateRankField - Create a cell field whose value is the rank of the owner
8895 
8896   Input Parameter:
8897 . dm - The `DMPLEX` object
8898 
8899   Output Parameter:
8900 . ranks - The rank field
8901 
8902   Options Database Key:
8903 . -dm_partition_view - Adds the rank field into the `DM` output from `-dm_view` using the same viewer
8904 
8905   Level: intermediate
8906 
8907 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`
8908 @*/
8909 PetscErrorCode DMPlexCreateRankField(DM dm, Vec *ranks)
8910 {
8911   DM             rdm;
8912   PetscFE        fe;
8913   PetscScalar   *r;
8914   PetscMPIInt    rank;
8915   DMPolytopeType ct;
8916   PetscInt       dim, cStart, cEnd, c;
8917   PetscBool      simplex;
8918 
8919   PetscFunctionBeginUser;
8920   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8921   PetscAssertPointer(ranks, 2);
8922   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
8923   PetscCall(DMClone(dm, &rdm));
8924   PetscCall(DMGetDimension(rdm, &dim));
8925   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
8926   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
8927   simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct) + 1 ? PETSC_TRUE : PETSC_FALSE;
8928   PetscCall(PetscFECreateDefault(PETSC_COMM_SELF, dim, 1, simplex, "PETSc___rank_", -1, &fe));
8929   PetscCall(PetscObjectSetName((PetscObject)fe, "rank"));
8930   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
8931   PetscCall(PetscFEDestroy(&fe));
8932   PetscCall(DMCreateDS(rdm));
8933   PetscCall(DMCreateGlobalVector(rdm, ranks));
8934   PetscCall(PetscObjectSetName((PetscObject)*ranks, "partition"));
8935   PetscCall(VecGetArray(*ranks, &r));
8936   for (c = cStart; c < cEnd; ++c) {
8937     PetscScalar *lr;
8938 
8939     PetscCall(DMPlexPointGlobalRef(rdm, c, r, &lr));
8940     if (lr) *lr = rank;
8941   }
8942   PetscCall(VecRestoreArray(*ranks, &r));
8943   PetscCall(DMDestroy(&rdm));
8944   PetscFunctionReturn(PETSC_SUCCESS);
8945 }
8946 
8947 /*@
8948   DMPlexCreateLabelField - Create a field whose value is the label value for that point
8949 
8950   Input Parameters:
8951 + dm    - The `DMPLEX`
8952 - label - The `DMLabel`
8953 
8954   Output Parameter:
8955 . val - The label value field
8956 
8957   Options Database Key:
8958 . -dm_label_view - Adds the label value field into the `DM` output from `-dm_view` using the same viewer
8959 
8960   Level: intermediate
8961 
8962 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`
8963 @*/
8964 PetscErrorCode DMPlexCreateLabelField(DM dm, DMLabel label, Vec *val)
8965 {
8966   DM             rdm, plex;
8967   Vec            lval;
8968   PetscSection   section;
8969   PetscFE        fe;
8970   PetscScalar   *v;
8971   PetscInt       dim, pStart, pEnd, p, cStart;
8972   DMPolytopeType ct;
8973   char           name[PETSC_MAX_PATH_LEN];
8974   const char    *lname, *prefix;
8975 
8976   PetscFunctionBeginUser;
8977   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8978   PetscAssertPointer(label, 2);
8979   PetscAssertPointer(val, 3);
8980   PetscCall(DMClone(dm, &rdm));
8981   PetscCall(DMConvert(rdm, DMPLEX, &plex));
8982   PetscCall(DMPlexGetHeightStratum(plex, 0, &cStart, NULL));
8983   PetscCall(DMPlexGetCellType(plex, cStart, &ct));
8984   PetscCall(DMDestroy(&plex));
8985   PetscCall(DMGetDimension(rdm, &dim));
8986   PetscCall(DMGetOptionsPrefix(dm, &prefix));
8987   PetscCall(PetscObjectGetName((PetscObject)label, &lname));
8988   PetscCall(PetscSNPrintf(name, sizeof(name), "%s%s_", prefix ? prefix : "", lname));
8989   PetscCall(PetscFECreateByCell(PETSC_COMM_SELF, dim, 1, ct, name, -1, &fe));
8990   PetscCall(PetscObjectSetName((PetscObject)fe, ""));
8991   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
8992   PetscCall(PetscFEDestroy(&fe));
8993   PetscCall(DMCreateDS(rdm));
8994   PetscCall(DMCreateGlobalVector(rdm, val));
8995   PetscCall(DMCreateLocalVector(rdm, &lval));
8996   PetscCall(PetscObjectSetName((PetscObject)*val, lname));
8997   PetscCall(DMGetLocalSection(rdm, &section));
8998   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
8999   PetscCall(VecGetArray(lval, &v));
9000   for (p = pStart; p < pEnd; ++p) {
9001     PetscInt cval, dof, off;
9002 
9003     PetscCall(PetscSectionGetDof(section, p, &dof));
9004     if (!dof) continue;
9005     PetscCall(DMLabelGetValue(label, p, &cval));
9006     PetscCall(PetscSectionGetOffset(section, p, &off));
9007     for (PetscInt d = 0; d < dof; d++) v[off + d] = cval;
9008   }
9009   PetscCall(VecRestoreArray(lval, &v));
9010   PetscCall(DMLocalToGlobal(rdm, lval, INSERT_VALUES, *val));
9011   PetscCall(VecDestroy(&lval));
9012   PetscCall(DMDestroy(&rdm));
9013   PetscFunctionReturn(PETSC_SUCCESS);
9014 }
9015 
9016 /*@
9017   DMPlexCheckSymmetry - Check that the adjacency information in the mesh is symmetric.
9018 
9019   Input Parameter:
9020 . dm - The `DMPLEX` object
9021 
9022   Level: developer
9023 
9024   Notes:
9025   This is a useful diagnostic when creating meshes programmatically.
9026 
9027   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9028 
9029 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9030 @*/
9031 PetscErrorCode DMPlexCheckSymmetry(DM dm)
9032 {
9033   PetscSection    coneSection, supportSection;
9034   const PetscInt *cone, *support;
9035   PetscInt        coneSize, c, supportSize, s;
9036   PetscInt        pStart, pEnd, p, pp, csize, ssize;
9037   PetscBool       storagecheck = PETSC_TRUE;
9038 
9039   PetscFunctionBegin;
9040   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9041   PetscCall(DMViewFromOptions(dm, NULL, "-sym_dm_view"));
9042   PetscCall(DMPlexGetConeSection(dm, &coneSection));
9043   PetscCall(DMPlexGetSupportSection(dm, &supportSection));
9044   /* Check that point p is found in the support of its cone points, and vice versa */
9045   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
9046   for (p = pStart; p < pEnd; ++p) {
9047     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
9048     PetscCall(DMPlexGetCone(dm, p, &cone));
9049     for (c = 0; c < coneSize; ++c) {
9050       PetscBool dup = PETSC_FALSE;
9051       PetscInt  d;
9052       for (d = c - 1; d >= 0; --d) {
9053         if (cone[c] == cone[d]) {
9054           dup = PETSC_TRUE;
9055           break;
9056         }
9057       }
9058       PetscCall(DMPlexGetSupportSize(dm, cone[c], &supportSize));
9059       PetscCall(DMPlexGetSupport(dm, cone[c], &support));
9060       for (s = 0; s < supportSize; ++s) {
9061         if (support[s] == p) break;
9062       }
9063       if ((s >= supportSize) || (dup && (support[s + 1] != p))) {
9064         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", p));
9065         for (s = 0; s < coneSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[s]));
9066         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9067         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", cone[c]));
9068         for (s = 0; s < supportSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[s]));
9069         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9070         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]);
9071         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in support of cone point %" PetscInt_FMT, p, cone[c]);
9072       }
9073     }
9074     PetscCall(DMPlexGetTreeParent(dm, p, &pp, NULL));
9075     if (p != pp) {
9076       storagecheck = PETSC_FALSE;
9077       continue;
9078     }
9079     PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
9080     PetscCall(DMPlexGetSupport(dm, p, &support));
9081     for (s = 0; s < supportSize; ++s) {
9082       PetscCall(DMPlexGetConeSize(dm, support[s], &coneSize));
9083       PetscCall(DMPlexGetCone(dm, support[s], &cone));
9084       for (c = 0; c < coneSize; ++c) {
9085         PetscCall(DMPlexGetTreeParent(dm, cone[c], &pp, NULL));
9086         if (cone[c] != pp) {
9087           c = 0;
9088           break;
9089         }
9090         if (cone[c] == p) break;
9091       }
9092       if (c >= coneSize) {
9093         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", p));
9094         for (c = 0; c < supportSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[c]));
9095         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9096         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", support[s]));
9097         for (c = 0; c < coneSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[c]));
9098         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9099         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in cone of support point %" PetscInt_FMT, p, support[s]);
9100       }
9101     }
9102   }
9103   if (storagecheck) {
9104     PetscCall(PetscSectionGetStorageSize(coneSection, &csize));
9105     PetscCall(PetscSectionGetStorageSize(supportSection, &ssize));
9106     PetscCheck(csize == ssize, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Total cone size %" PetscInt_FMT " != Total support size %" PetscInt_FMT, csize, ssize);
9107   }
9108   PetscFunctionReturn(PETSC_SUCCESS);
9109 }
9110 
9111 /*
9112   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.
9113 */
9114 static PetscErrorCode DMPlexCellUnsplitVertices_Private(DM dm, PetscInt c, DMPolytopeType ct, PetscInt *unsplit)
9115 {
9116   DMPolytopeType  cct;
9117   PetscInt        ptpoints[4];
9118   const PetscInt *cone, *ccone, *ptcone;
9119   PetscInt        coneSize, cp, cconeSize, ccp, npt = 0, pt;
9120 
9121   PetscFunctionBegin;
9122   *unsplit = 0;
9123   switch (ct) {
9124   case DM_POLYTOPE_POINT_PRISM_TENSOR:
9125     ptpoints[npt++] = c;
9126     break;
9127   case DM_POLYTOPE_SEG_PRISM_TENSOR:
9128     PetscCall(DMPlexGetCone(dm, c, &cone));
9129     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9130     for (cp = 0; cp < coneSize; ++cp) {
9131       PetscCall(DMPlexGetCellType(dm, cone[cp], &cct));
9132       if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) ptpoints[npt++] = cone[cp];
9133     }
9134     break;
9135   case DM_POLYTOPE_TRI_PRISM_TENSOR:
9136   case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9137     PetscCall(DMPlexGetCone(dm, c, &cone));
9138     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9139     for (cp = 0; cp < coneSize; ++cp) {
9140       PetscCall(DMPlexGetCone(dm, cone[cp], &ccone));
9141       PetscCall(DMPlexGetConeSize(dm, cone[cp], &cconeSize));
9142       for (ccp = 0; ccp < cconeSize; ++ccp) {
9143         PetscCall(DMPlexGetCellType(dm, ccone[ccp], &cct));
9144         if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) {
9145           PetscInt p;
9146           for (p = 0; p < npt; ++p)
9147             if (ptpoints[p] == ccone[ccp]) break;
9148           if (p == npt) ptpoints[npt++] = ccone[ccp];
9149         }
9150       }
9151     }
9152     break;
9153   default:
9154     break;
9155   }
9156   for (pt = 0; pt < npt; ++pt) {
9157     PetscCall(DMPlexGetCone(dm, ptpoints[pt], &ptcone));
9158     if (ptcone[0] == ptcone[1]) ++(*unsplit);
9159   }
9160   PetscFunctionReturn(PETSC_SUCCESS);
9161 }
9162 
9163 /*@
9164   DMPlexCheckSkeleton - Check that each cell has the correct number of vertices
9165 
9166   Input Parameters:
9167 + dm         - The `DMPLEX` object
9168 - cellHeight - Normally 0
9169 
9170   Level: developer
9171 
9172   Notes:
9173   This is a useful diagnostic when creating meshes programmatically.
9174   Currently applicable only to homogeneous simplex or tensor meshes.
9175 
9176   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9177 
9178 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9179 @*/
9180 PetscErrorCode DMPlexCheckSkeleton(DM dm, PetscInt cellHeight)
9181 {
9182   DMPlexInterpolatedFlag interp;
9183   DMPolytopeType         ct;
9184   PetscInt               vStart, vEnd, cStart, cEnd, c;
9185 
9186   PetscFunctionBegin;
9187   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9188   PetscCall(DMPlexIsInterpolated(dm, &interp));
9189   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9190   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
9191   for (c = cStart; c < cEnd; ++c) {
9192     PetscInt *closure = NULL;
9193     PetscInt  coneSize, closureSize, cl, Nv = 0;
9194 
9195     PetscCall(DMPlexGetCellType(dm, c, &ct));
9196     if (ct == DM_POLYTOPE_UNKNOWN) continue;
9197     if (interp == DMPLEX_INTERPOLATED_FULL) {
9198       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9199       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));
9200     }
9201     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9202     for (cl = 0; cl < closureSize * 2; cl += 2) {
9203       const PetscInt p = closure[cl];
9204       if ((p >= vStart) && (p < vEnd)) ++Nv;
9205     }
9206     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9207     /* Special Case: Tensor faces with identified vertices */
9208     if (Nv < DMPolytopeTypeGetNumVertices(ct)) {
9209       PetscInt unsplit;
9210 
9211       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9212       if (Nv + unsplit == DMPolytopeTypeGetNumVertices(ct)) continue;
9213     }
9214     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));
9215   }
9216   PetscFunctionReturn(PETSC_SUCCESS);
9217 }
9218 
9219 /*@
9220   DMPlexCheckFaces - Check that the faces of each cell give a vertex order this is consistent with what we expect from the cell type
9221 
9222   Collective
9223 
9224   Input Parameters:
9225 + dm         - The `DMPLEX` object
9226 - cellHeight - Normally 0
9227 
9228   Level: developer
9229 
9230   Notes:
9231   This is a useful diagnostic when creating meshes programmatically.
9232   This routine is only relevant for meshes that are fully interpolated across all ranks.
9233   It will error out if a partially interpolated mesh is given on some rank.
9234   It will do nothing for locally uninterpolated mesh (as there is nothing to check).
9235 
9236   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9237 
9238 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMPlexGetVTKCellHeight()`, `DMSetFromOptions()`
9239 @*/
9240 PetscErrorCode DMPlexCheckFaces(DM dm, PetscInt cellHeight)
9241 {
9242   PetscInt               dim, depth, vStart, vEnd, cStart, cEnd, c, h;
9243   DMPlexInterpolatedFlag interpEnum;
9244 
9245   PetscFunctionBegin;
9246   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9247   PetscCall(DMPlexIsInterpolatedCollective(dm, &interpEnum));
9248   if (interpEnum == DMPLEX_INTERPOLATED_NONE) PetscFunctionReturn(PETSC_SUCCESS);
9249   if (interpEnum != DMPLEX_INTERPOLATED_FULL) {
9250     PetscCall(PetscPrintf(PetscObjectComm((PetscObject)dm), "DMPlexCheckFaces() warning: Mesh is only partially interpolated, this is currently not supported"));
9251     PetscFunctionReturn(PETSC_SUCCESS);
9252   }
9253 
9254   PetscCall(DMGetDimension(dm, &dim));
9255   PetscCall(DMPlexGetDepth(dm, &depth));
9256   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
9257   for (h = cellHeight; h < PetscMin(depth, dim); ++h) {
9258     PetscCall(DMPlexGetHeightStratum(dm, h, &cStart, &cEnd));
9259     for (c = cStart; c < cEnd; ++c) {
9260       const PetscInt       *cone, *ornt, *faceSizes, *faces;
9261       const DMPolytopeType *faceTypes;
9262       DMPolytopeType        ct;
9263       PetscInt              numFaces, coneSize, f;
9264       PetscInt             *closure = NULL, closureSize, cl, numCorners = 0, fOff = 0, unsplit;
9265 
9266       PetscCall(DMPlexGetCellType(dm, c, &ct));
9267       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9268       if (unsplit) continue;
9269       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9270       PetscCall(DMPlexGetCone(dm, c, &cone));
9271       PetscCall(DMPlexGetConeOrientation(dm, c, &ornt));
9272       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9273       for (cl = 0; cl < closureSize * 2; cl += 2) {
9274         const PetscInt p = closure[cl];
9275         if ((p >= vStart) && (p < vEnd)) closure[numCorners++] = p;
9276       }
9277       PetscCall(DMPlexGetRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
9278       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);
9279       for (f = 0; f < numFaces; ++f) {
9280         DMPolytopeType fct;
9281         PetscInt      *fclosure = NULL, fclosureSize, cl, fnumCorners = 0, v;
9282 
9283         PetscCall(DMPlexGetCellType(dm, cone[f], &fct));
9284         PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[f], ornt[f], PETSC_TRUE, &fclosureSize, &fclosure));
9285         for (cl = 0; cl < fclosureSize * 2; cl += 2) {
9286           const PetscInt p = fclosure[cl];
9287           if ((p >= vStart) && (p < vEnd)) fclosure[fnumCorners++] = p;
9288         }
9289         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]);
9290         for (v = 0; v < fnumCorners; ++v) {
9291           if (fclosure[v] != faces[fOff + v]) {
9292             PetscInt v1;
9293 
9294             PetscCall(PetscPrintf(PETSC_COMM_SELF, "face closure:"));
9295             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, fclosure[v1]));
9296             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\ncell face:"));
9297             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, faces[fOff + v1]));
9298             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9299             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]);
9300           }
9301         }
9302         PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[f], PETSC_TRUE, &fclosureSize, &fclosure));
9303         fOff += faceSizes[f];
9304       }
9305       PetscCall(DMPlexRestoreRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
9306       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9307     }
9308   }
9309   PetscFunctionReturn(PETSC_SUCCESS);
9310 }
9311 
9312 /*@
9313   DMPlexCheckGeometry - Check the geometry of mesh cells
9314 
9315   Input Parameter:
9316 . dm - The `DMPLEX` object
9317 
9318   Level: developer
9319 
9320   Notes:
9321   This is a useful diagnostic when creating meshes programmatically.
9322 
9323   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9324 
9325 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9326 @*/
9327 PetscErrorCode DMPlexCheckGeometry(DM dm)
9328 {
9329   Vec       coordinates;
9330   PetscReal detJ, J[9], refVol = 1.0;
9331   PetscReal vol;
9332   PetscInt  dim, depth, dE, d, cStart, cEnd, c;
9333 
9334   PetscFunctionBegin;
9335   PetscCall(DMGetDimension(dm, &dim));
9336   PetscCall(DMGetCoordinateDim(dm, &dE));
9337   if (dim != dE) PetscFunctionReturn(PETSC_SUCCESS);
9338   PetscCall(DMPlexGetDepth(dm, &depth));
9339   for (d = 0; d < dim; ++d) refVol *= 2.0;
9340   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
9341   /* Make sure local coordinates are created, because that step is collective */
9342   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
9343   if (!coordinates) PetscFunctionReturn(PETSC_SUCCESS);
9344   for (c = cStart; c < cEnd; ++c) {
9345     DMPolytopeType ct;
9346     PetscInt       unsplit;
9347     PetscBool      ignoreZeroVol = PETSC_FALSE;
9348 
9349     PetscCall(DMPlexGetCellType(dm, c, &ct));
9350     switch (ct) {
9351     case DM_POLYTOPE_SEG_PRISM_TENSOR:
9352     case DM_POLYTOPE_TRI_PRISM_TENSOR:
9353     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9354       ignoreZeroVol = PETSC_TRUE;
9355       break;
9356     default:
9357       break;
9358     }
9359     switch (ct) {
9360     case DM_POLYTOPE_TRI_PRISM:
9361     case DM_POLYTOPE_TRI_PRISM_TENSOR:
9362     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9363     case DM_POLYTOPE_PYRAMID:
9364       continue;
9365     default:
9366       break;
9367     }
9368     PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9369     if (unsplit) continue;
9370     PetscCall(DMPlexComputeCellGeometryFEM(dm, c, NULL, NULL, J, NULL, &detJ));
9371     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);
9372     PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FEM Volume %g\n", c, (double)(detJ * refVol)));
9373     /* This should work with periodicity since DG coordinates should be used */
9374     if (depth > 1) {
9375       PetscCall(DMPlexComputeCellGeometryFVM(dm, c, &vol, NULL, NULL));
9376       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);
9377       PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FVM Volume %g\n", c, (double)vol));
9378     }
9379   }
9380   PetscFunctionReturn(PETSC_SUCCESS);
9381 }
9382 
9383 /*@
9384   DMPlexCheckPointSF - Check that several necessary conditions are met for the point `PetscSF` of this plex.
9385 
9386   Collective
9387 
9388   Input Parameters:
9389 + dm              - The `DMPLEX` object
9390 . pointSF         - The `PetscSF`, or `NULL` for `PointSF` attached to `DM`
9391 - allowExtraRoots - Flag to allow extra points not present in the `DM`
9392 
9393   Level: developer
9394 
9395   Notes:
9396   This is mainly intended for debugging/testing purposes.
9397 
9398   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9399 
9400   Extra roots can come from periodic cuts, where additional points appear on the boundary
9401 
9402 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMGetPointSF()`, `DMSetFromOptions()`
9403 @*/
9404 PetscErrorCode DMPlexCheckPointSF(DM dm, PetscSF pointSF, PetscBool allowExtraRoots)
9405 {
9406   PetscInt           l, nleaves, nroots, overlap;
9407   const PetscInt    *locals;
9408   const PetscSFNode *remotes;
9409   PetscBool          distributed;
9410   MPI_Comm           comm;
9411   PetscMPIInt        rank;
9412 
9413   PetscFunctionBegin;
9414   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9415   if (pointSF) PetscValidHeaderSpecific(pointSF, PETSCSF_CLASSID, 2);
9416   else pointSF = dm->sf;
9417   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9418   PetscCheck(pointSF, comm, PETSC_ERR_ARG_WRONGSTATE, "DMPlex must have Point SF attached");
9419   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9420   {
9421     PetscMPIInt mpiFlag;
9422 
9423     PetscCallMPI(MPI_Comm_compare(comm, PetscObjectComm((PetscObject)pointSF), &mpiFlag));
9424     PetscCheck(mpiFlag == MPI_CONGRUENT || mpiFlag == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "DM and Point SF have different communicators (flag %d)", mpiFlag);
9425   }
9426   PetscCall(PetscSFGetGraph(pointSF, &nroots, &nleaves, &locals, &remotes));
9427   PetscCall(DMPlexIsDistributed(dm, &distributed));
9428   if (!distributed) {
9429     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);
9430     PetscFunctionReturn(PETSC_SUCCESS);
9431   }
9432   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);
9433   PetscCall(DMPlexGetOverlap(dm, &overlap));
9434 
9435   /* Check SF graph is compatible with DMPlex chart */
9436   {
9437     PetscInt pStart, pEnd, maxLeaf;
9438 
9439     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
9440     PetscCall(PetscSFGetLeafRange(pointSF, NULL, &maxLeaf));
9441     PetscCheck(allowExtraRoots || pEnd - pStart == nroots, PETSC_COMM_SELF, PETSC_ERR_PLIB, "pEnd - pStart = %" PetscInt_FMT " != nroots = %" PetscInt_FMT, pEnd - pStart, nroots);
9442     PetscCheck(maxLeaf < pEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "maxLeaf = %" PetscInt_FMT " >= pEnd = %" PetscInt_FMT, maxLeaf, pEnd);
9443   }
9444 
9445   /* Check Point SF has no local points referenced */
9446   for (l = 0; l < nleaves; l++) {
9447     PetscAssert(remotes[l].rank != (PetscInt)rank, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains local point %" PetscInt_FMT " <- (%d,%" PetscInt_FMT ")", locals ? locals[l] : l, (PetscMPIInt)remotes[l].rank, remotes[l].index);
9448   }
9449 
9450   /* Check there are no cells in interface */
9451   if (!overlap) {
9452     PetscInt cellHeight, cStart, cEnd;
9453 
9454     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9455     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9456     for (l = 0; l < nleaves; ++l) {
9457       const PetscInt point = locals ? locals[l] : l;
9458 
9459       PetscCheck(point < cStart || point >= cEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " which is a cell", point);
9460     }
9461   }
9462 
9463   /* If some point is in interface, then all its cone points must be also in interface (either as leaves or roots) */
9464   {
9465     const PetscInt *rootdegree;
9466 
9467     PetscCall(PetscSFComputeDegreeBegin(pointSF, &rootdegree));
9468     PetscCall(PetscSFComputeDegreeEnd(pointSF, &rootdegree));
9469     for (l = 0; l < nleaves; ++l) {
9470       const PetscInt  point = locals ? locals[l] : l;
9471       const PetscInt *cone;
9472       PetscInt        coneSize, c, idx;
9473 
9474       PetscCall(DMPlexGetConeSize(dm, point, &coneSize));
9475       PetscCall(DMPlexGetCone(dm, point, &cone));
9476       for (c = 0; c < coneSize; ++c) {
9477         if (!rootdegree[cone[c]]) {
9478           if (locals) {
9479             PetscCall(PetscFindInt(cone[c], nleaves, locals, &idx));
9480           } else {
9481             idx = (cone[c] < nleaves) ? cone[c] : -1;
9482           }
9483           PetscCheck(idx >= 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " but not %" PetscInt_FMT " from its cone", point, cone[c]);
9484         }
9485       }
9486     }
9487   }
9488   PetscFunctionReturn(PETSC_SUCCESS);
9489 }
9490 
9491 /*@
9492   DMPlexCheckOrphanVertices - Check that no vertices are disconnected from the mesh, unless the mesh only consists of disconnected vertices.
9493 
9494   Collective
9495 
9496   Input Parameter:
9497 . dm - The `DMPLEX` object
9498 
9499   Level: developer
9500 
9501   Notes:
9502   This is mainly intended for debugging/testing purposes.
9503 
9504   Other cell types which are disconnected would be caught by the symmetry and face checks.
9505 
9506   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9507 
9508 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCheck()`, `DMSetFromOptions()`
9509 @*/
9510 PetscErrorCode DMPlexCheckOrphanVertices(DM dm)
9511 {
9512   PetscInt pStart, pEnd, vStart, vEnd;
9513 
9514   PetscFunctionBegin;
9515   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
9516   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
9517   if (pStart == vStart && pEnd == vEnd) PetscFunctionReturn(PETSC_SUCCESS);
9518   for (PetscInt v = vStart; v < vEnd; ++v) {
9519     PetscInt suppSize;
9520 
9521     PetscCall(DMPlexGetSupportSize(dm, v, &suppSize));
9522     PetscCheck(suppSize, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Vertex %" PetscInt_FMT " is disconnected from the mesh", v);
9523   }
9524   PetscFunctionReturn(PETSC_SUCCESS);
9525 }
9526 
9527 /*@
9528   DMPlexCheck - Perform various checks of `DMPLEX` sanity
9529 
9530   Input Parameter:
9531 . dm - The `DMPLEX` object
9532 
9533   Level: developer
9534 
9535   Notes:
9536   This is a useful diagnostic when creating meshes programmatically.
9537 
9538   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9539 
9540   Currently does not include `DMPlexCheckCellShape()`.
9541 
9542 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9543 @*/
9544 PetscErrorCode DMPlexCheck(DM dm)
9545 {
9546   PetscInt cellHeight;
9547 
9548   PetscFunctionBegin;
9549   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9550   PetscCall(DMPlexCheckSymmetry(dm));
9551   PetscCall(DMPlexCheckSkeleton(dm, cellHeight));
9552   PetscCall(DMPlexCheckFaces(dm, cellHeight));
9553   PetscCall(DMPlexCheckGeometry(dm));
9554   PetscCall(DMPlexCheckPointSF(dm, NULL, PETSC_FALSE));
9555   PetscCall(DMPlexCheckInterfaceCones(dm));
9556   PetscCall(DMPlexCheckOrphanVertices(dm));
9557   PetscFunctionReturn(PETSC_SUCCESS);
9558 }
9559 
9560 typedef struct cell_stats {
9561   PetscReal min, max, sum, squaresum;
9562   PetscInt  count;
9563 } cell_stats_t;
9564 
9565 static void MPIAPI cell_stats_reduce(void *a, void *b, int *len, MPI_Datatype *datatype)
9566 {
9567   PetscInt i, N = *len;
9568 
9569   for (i = 0; i < N; i++) {
9570     cell_stats_t *A = (cell_stats_t *)a;
9571     cell_stats_t *B = (cell_stats_t *)b;
9572 
9573     B->min = PetscMin(A->min, B->min);
9574     B->max = PetscMax(A->max, B->max);
9575     B->sum += A->sum;
9576     B->squaresum += A->squaresum;
9577     B->count += A->count;
9578   }
9579 }
9580 
9581 /*@
9582   DMPlexCheckCellShape - Checks the Jacobian of the mapping from reference to real cells and computes some minimal statistics.
9583 
9584   Collective
9585 
9586   Input Parameters:
9587 + dm        - The `DMPLEX` object
9588 . output    - If true, statistics will be displayed on `stdout`
9589 - condLimit - Display all cells above this condition number, or `PETSC_DETERMINE` for no cell output
9590 
9591   Level: developer
9592 
9593   Notes:
9594   This is mainly intended for debugging/testing purposes.
9595 
9596   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9597 
9598 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexComputeOrthogonalQuality()`
9599 @*/
9600 PetscErrorCode DMPlexCheckCellShape(DM dm, PetscBool output, PetscReal condLimit)
9601 {
9602   DM           dmCoarse;
9603   cell_stats_t stats, globalStats;
9604   MPI_Comm     comm = PetscObjectComm((PetscObject)dm);
9605   PetscReal   *J, *invJ, min = 0, max = 0, mean = 0, stdev = 0;
9606   PetscReal    limit = condLimit > 0 ? condLimit : PETSC_MAX_REAL;
9607   PetscInt     cdim, cStart, cEnd, c, eStart, eEnd, count = 0;
9608   PetscMPIInt  rank, size;
9609 
9610   PetscFunctionBegin;
9611   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9612   stats.min = PETSC_MAX_REAL;
9613   stats.max = PETSC_MIN_REAL;
9614   stats.sum = stats.squaresum = 0.;
9615   stats.count                 = 0;
9616 
9617   PetscCallMPI(MPI_Comm_size(comm, &size));
9618   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9619   PetscCall(DMGetCoordinateDim(dm, &cdim));
9620   PetscCall(PetscMalloc2(PetscSqr(cdim), &J, PetscSqr(cdim), &invJ));
9621   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
9622   PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
9623   for (c = cStart; c < cEnd; c++) {
9624     PetscInt  i;
9625     PetscReal frobJ = 0., frobInvJ = 0., cond2, cond, detJ;
9626 
9627     PetscCall(DMPlexComputeCellGeometryAffineFEM(dm, c, NULL, J, invJ, &detJ));
9628     PetscCheck(detJ >= 0.0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Mesh cell %" PetscInt_FMT " is inverted", c);
9629     for (i = 0; i < PetscSqr(cdim); ++i) {
9630       frobJ += J[i] * J[i];
9631       frobInvJ += invJ[i] * invJ[i];
9632     }
9633     cond2 = frobJ * frobInvJ;
9634     cond  = PetscSqrtReal(cond2);
9635 
9636     stats.min = PetscMin(stats.min, cond);
9637     stats.max = PetscMax(stats.max, cond);
9638     stats.sum += cond;
9639     stats.squaresum += cond2;
9640     stats.count++;
9641     if (output && cond > limit) {
9642       PetscSection coordSection;
9643       Vec          coordsLocal;
9644       PetscScalar *coords = NULL;
9645       PetscInt     Nv, d, clSize, cl, *closure = NULL;
9646 
9647       PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
9648       PetscCall(DMGetCoordinateSection(dm, &coordSection));
9649       PetscCall(DMPlexVecGetClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9650       PetscCall(PetscSynchronizedPrintf(comm, "[%d] Cell %" PetscInt_FMT " cond %g\n", rank, c, (double)cond));
9651       for (i = 0; i < Nv / cdim; ++i) {
9652         PetscCall(PetscSynchronizedPrintf(comm, "  Vertex %" PetscInt_FMT ": (", i));
9653         for (d = 0; d < cdim; ++d) {
9654           if (d > 0) PetscCall(PetscSynchronizedPrintf(comm, ", "));
9655           PetscCall(PetscSynchronizedPrintf(comm, "%g", (double)PetscRealPart(coords[i * cdim + d])));
9656         }
9657         PetscCall(PetscSynchronizedPrintf(comm, ")\n"));
9658       }
9659       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9660       for (cl = 0; cl < clSize * 2; cl += 2) {
9661         const PetscInt edge = closure[cl];
9662 
9663         if ((edge >= eStart) && (edge < eEnd)) {
9664           PetscReal len;
9665 
9666           PetscCall(DMPlexComputeCellGeometryFVM(dm, edge, &len, NULL, NULL));
9667           PetscCall(PetscSynchronizedPrintf(comm, "  Edge %" PetscInt_FMT ": length %g\n", edge, (double)len));
9668         }
9669       }
9670       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9671       PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9672     }
9673   }
9674   if (output) PetscCall(PetscSynchronizedFlush(comm, NULL));
9675 
9676   if (size > 1) {
9677     PetscMPIInt  blockLengths[2] = {4, 1};
9678     MPI_Aint     blockOffsets[2] = {offsetof(cell_stats_t, min), offsetof(cell_stats_t, count)};
9679     MPI_Datatype blockTypes[2]   = {MPIU_REAL, MPIU_INT}, statType;
9680     MPI_Op       statReduce;
9681 
9682     PetscCallMPI(MPI_Type_create_struct(2, blockLengths, blockOffsets, blockTypes, &statType));
9683     PetscCallMPI(MPI_Type_commit(&statType));
9684     PetscCallMPI(MPI_Op_create(cell_stats_reduce, PETSC_TRUE, &statReduce));
9685     PetscCallMPI(MPI_Reduce(&stats, &globalStats, 1, statType, statReduce, 0, comm));
9686     PetscCallMPI(MPI_Op_free(&statReduce));
9687     PetscCallMPI(MPI_Type_free(&statType));
9688   } else {
9689     PetscCall(PetscArraycpy(&globalStats, &stats, 1));
9690   }
9691   if (rank == 0) {
9692     count = globalStats.count;
9693     min   = globalStats.min;
9694     max   = globalStats.max;
9695     mean  = globalStats.sum / globalStats.count;
9696     stdev = globalStats.count > 1 ? PetscSqrtReal(PetscMax((globalStats.squaresum - globalStats.count * mean * mean) / (globalStats.count - 1), 0)) : 0.0;
9697   }
9698 
9699   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));
9700   PetscCall(PetscFree2(J, invJ));
9701 
9702   PetscCall(DMGetCoarseDM(dm, &dmCoarse));
9703   if (dmCoarse) {
9704     PetscBool isplex;
9705 
9706     PetscCall(PetscObjectTypeCompare((PetscObject)dmCoarse, DMPLEX, &isplex));
9707     if (isplex) PetscCall(DMPlexCheckCellShape(dmCoarse, output, condLimit));
9708   }
9709   PetscFunctionReturn(PETSC_SUCCESS);
9710 }
9711 
9712 /*@
9713   DMPlexComputeOrthogonalQuality - Compute cell-wise orthogonal quality mesh statistic. Optionally tags all cells with
9714   orthogonal quality below given tolerance.
9715 
9716   Collective
9717 
9718   Input Parameters:
9719 + dm   - The `DMPLEX` object
9720 . fv   - Optional `PetscFV` object for pre-computed cell/face centroid information
9721 - atol - [0, 1] Absolute tolerance for tagging cells.
9722 
9723   Output Parameters:
9724 + OrthQual      - `Vec` containing orthogonal quality per cell
9725 - OrthQualLabel - `DMLabel` tagging cells below atol with `DM_ADAPT_REFINE`
9726 
9727   Options Database Keys:
9728 + -dm_plex_orthogonal_quality_label_view - view OrthQualLabel if label is requested. Currently only `PETSCVIEWERASCII` is supported.
9729 - -dm_plex_orthogonal_quality_vec_view   - view OrthQual vector.
9730 
9731   Level: intermediate
9732 
9733   Notes:
9734   Orthogonal quality is given by the following formula\:
9735 
9736   $ \min \left[ \frac{A_i \cdot f_i}{\|A_i\| \|f_i\|} , \frac{A_i \cdot c_i}{\|A_i\| \|c_i\|} \right]$
9737 
9738   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
9739   is the vector from the current cells centroid to the centroid of its i'th neighbor (which shares a face with the
9740   current cell). This computes the vector similarity between each cell face and its corresponding neighbor centroid by
9741   calculating the cosine of the angle between these vectors.
9742 
9743   Orthogonal quality ranges from 1 (best) to 0 (worst).
9744 
9745   This routine is mainly useful for FVM, however is not restricted to only FVM. The `PetscFV` object is optionally used to check for
9746   pre-computed FVM cell data, but if it is not passed in then this data will be computed.
9747 
9748   Cells are tagged if they have an orthogonal quality less than or equal to the absolute tolerance.
9749 
9750 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCheckCellShape()`, `DMCreateLabel()`, `PetscFV`, `DMLabel`, `Vec`
9751 @*/
9752 PetscErrorCode DMPlexComputeOrthogonalQuality(DM dm, PetscFV fv, PetscReal atol, Vec *OrthQual, DMLabel *OrthQualLabel)
9753 {
9754   PetscInt               nc, cellHeight, cStart, cEnd, cell, cellIter = 0;
9755   PetscInt              *idx;
9756   PetscScalar           *oqVals;
9757   const PetscScalar     *cellGeomArr, *faceGeomArr;
9758   PetscReal             *ci, *fi, *Ai;
9759   MPI_Comm               comm;
9760   Vec                    cellgeom, facegeom;
9761   DM                     dmFace, dmCell;
9762   IS                     glob;
9763   ISLocalToGlobalMapping ltog;
9764   PetscViewer            vwr;
9765 
9766   PetscFunctionBegin;
9767   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9768   if (fv) PetscValidHeaderSpecific(fv, PETSCFV_CLASSID, 2);
9769   PetscAssertPointer(OrthQual, 4);
9770   PetscCheck(atol >= 0.0 && atol <= 1.0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Absolute tolerance %g not in [0,1]", (double)atol);
9771   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9772   PetscCall(DMGetDimension(dm, &nc));
9773   PetscCheck(nc >= 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must have dimension >= 2 (current %" PetscInt_FMT ")", nc);
9774   {
9775     DMPlexInterpolatedFlag interpFlag;
9776 
9777     PetscCall(DMPlexIsInterpolated(dm, &interpFlag));
9778     if (interpFlag != DMPLEX_INTERPOLATED_FULL) {
9779       PetscMPIInt rank;
9780 
9781       PetscCallMPI(MPI_Comm_rank(comm, &rank));
9782       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must be fully interpolated, DM on rank %d is not fully interpolated", rank);
9783     }
9784   }
9785   if (OrthQualLabel) {
9786     PetscAssertPointer(OrthQualLabel, 5);
9787     PetscCall(DMCreateLabel(dm, "Orthogonal_Quality"));
9788     PetscCall(DMGetLabel(dm, "Orthogonal_Quality", OrthQualLabel));
9789   } else {
9790     *OrthQualLabel = NULL;
9791   }
9792   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9793   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9794   PetscCall(DMPlexCreateCellNumbering(dm, PETSC_TRUE, &glob));
9795   PetscCall(ISLocalToGlobalMappingCreateIS(glob, &ltog));
9796   PetscCall(ISLocalToGlobalMappingSetType(ltog, ISLOCALTOGLOBALMAPPINGHASH));
9797   PetscCall(VecCreate(comm, OrthQual));
9798   PetscCall(VecSetType(*OrthQual, VECSTANDARD));
9799   PetscCall(VecSetSizes(*OrthQual, cEnd - cStart, PETSC_DETERMINE));
9800   PetscCall(VecSetLocalToGlobalMapping(*OrthQual, ltog));
9801   PetscCall(VecSetUp(*OrthQual));
9802   PetscCall(ISDestroy(&glob));
9803   PetscCall(ISLocalToGlobalMappingDestroy(&ltog));
9804   PetscCall(DMPlexGetDataFVM(dm, fv, &cellgeom, &facegeom, NULL));
9805   PetscCall(VecGetArrayRead(cellgeom, &cellGeomArr));
9806   PetscCall(VecGetArrayRead(facegeom, &faceGeomArr));
9807   PetscCall(VecGetDM(cellgeom, &dmCell));
9808   PetscCall(VecGetDM(facegeom, &dmFace));
9809   PetscCall(PetscMalloc5(cEnd - cStart, &idx, cEnd - cStart, &oqVals, nc, &ci, nc, &fi, nc, &Ai));
9810   for (cell = cStart; cell < cEnd; cellIter++, cell++) {
9811     PetscInt         cellneigh, cellneighiter = 0, adjSize = PETSC_DETERMINE;
9812     PetscInt         cellarr[2], *adj = NULL;
9813     PetscScalar     *cArr, *fArr;
9814     PetscReal        minvalc = 1.0, minvalf = 1.0;
9815     PetscFVCellGeom *cg;
9816 
9817     idx[cellIter] = cell - cStart;
9818     cellarr[0]    = cell;
9819     /* Make indexing into cellGeom easier */
9820     PetscCall(DMPlexPointLocalRead(dmCell, cell, cellGeomArr, &cg));
9821     PetscCall(DMPlexGetAdjacency_Internal(dm, cell, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &adjSize, &adj));
9822     /* Technically 1 too big, but easier than fiddling with empty adjacency array */
9823     PetscCall(PetscCalloc2(adjSize, &cArr, adjSize, &fArr));
9824     for (cellneigh = 0; cellneigh < adjSize; cellneighiter++, cellneigh++) {
9825       PetscInt         i;
9826       const PetscInt   neigh  = adj[cellneigh];
9827       PetscReal        normci = 0, normfi = 0, normai = 0;
9828       PetscFVCellGeom *cgneigh;
9829       PetscFVFaceGeom *fg;
9830 
9831       /* Don't count ourselves in the neighbor list */
9832       if (neigh == cell) continue;
9833       PetscCall(DMPlexPointLocalRead(dmCell, neigh, cellGeomArr, &cgneigh));
9834       cellarr[1] = neigh;
9835       {
9836         PetscInt        numcovpts;
9837         const PetscInt *covpts;
9838 
9839         PetscCall(DMPlexGetMeet(dm, 2, cellarr, &numcovpts, &covpts));
9840         PetscCall(DMPlexPointLocalRead(dmFace, covpts[0], faceGeomArr, &fg));
9841         PetscCall(DMPlexRestoreMeet(dm, 2, cellarr, &numcovpts, &covpts));
9842       }
9843 
9844       /* Compute c_i, f_i and their norms */
9845       for (i = 0; i < nc; i++) {
9846         ci[i] = cgneigh->centroid[i] - cg->centroid[i];
9847         fi[i] = fg->centroid[i] - cg->centroid[i];
9848         Ai[i] = fg->normal[i];
9849         normci += PetscPowReal(ci[i], 2);
9850         normfi += PetscPowReal(fi[i], 2);
9851         normai += PetscPowReal(Ai[i], 2);
9852       }
9853       normci = PetscSqrtReal(normci);
9854       normfi = PetscSqrtReal(normfi);
9855       normai = PetscSqrtReal(normai);
9856 
9857       /* Normalize and compute for each face-cell-normal pair */
9858       for (i = 0; i < nc; i++) {
9859         ci[i] = ci[i] / normci;
9860         fi[i] = fi[i] / normfi;
9861         Ai[i] = Ai[i] / normai;
9862         /* PetscAbs because I don't know if normals are guaranteed to point out */
9863         cArr[cellneighiter] += PetscAbs(Ai[i] * ci[i]);
9864         fArr[cellneighiter] += PetscAbs(Ai[i] * fi[i]);
9865       }
9866       if (PetscRealPart(cArr[cellneighiter]) < minvalc) minvalc = PetscRealPart(cArr[cellneighiter]);
9867       if (PetscRealPart(fArr[cellneighiter]) < minvalf) minvalf = PetscRealPart(fArr[cellneighiter]);
9868     }
9869     PetscCall(PetscFree(adj));
9870     PetscCall(PetscFree2(cArr, fArr));
9871     /* Defer to cell if they're equal */
9872     oqVals[cellIter] = PetscMin(minvalf, minvalc);
9873     if (OrthQualLabel) {
9874       if (PetscRealPart(oqVals[cellIter]) <= atol) PetscCall(DMLabelSetValue(*OrthQualLabel, cell, DM_ADAPT_REFINE));
9875     }
9876   }
9877   PetscCall(VecSetValuesLocal(*OrthQual, cEnd - cStart, idx, oqVals, INSERT_VALUES));
9878   PetscCall(VecAssemblyBegin(*OrthQual));
9879   PetscCall(VecAssemblyEnd(*OrthQual));
9880   PetscCall(VecRestoreArrayRead(cellgeom, &cellGeomArr));
9881   PetscCall(VecRestoreArrayRead(facegeom, &faceGeomArr));
9882   PetscCall(PetscOptionsCreateViewer(comm, NULL, NULL, "-dm_plex_orthogonal_quality_label_view", &vwr, NULL, NULL));
9883   if (OrthQualLabel) {
9884     if (vwr) PetscCall(DMLabelView(*OrthQualLabel, vwr));
9885   }
9886   PetscCall(PetscFree5(idx, oqVals, ci, fi, Ai));
9887   PetscCall(PetscViewerDestroy(&vwr));
9888   PetscCall(VecViewFromOptions(*OrthQual, NULL, "-dm_plex_orthogonal_quality_vec_view"));
9889   PetscFunctionReturn(PETSC_SUCCESS);
9890 }
9891 
9892 /* this is here instead of DMGetOutputDM because output DM still has constraints in the local indices that affect
9893  * interpolator construction */
9894 static PetscErrorCode DMGetFullDM(DM dm, DM *odm)
9895 {
9896   PetscSection section, newSection, gsection;
9897   PetscSF      sf;
9898   PetscBool    hasConstraints, ghasConstraints;
9899 
9900   PetscFunctionBegin;
9901   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9902   PetscAssertPointer(odm, 2);
9903   PetscCall(DMGetLocalSection(dm, &section));
9904   PetscCall(PetscSectionHasConstraints(section, &hasConstraints));
9905   PetscCallMPI(MPIU_Allreduce(&hasConstraints, &ghasConstraints, 1, MPIU_BOOL, MPI_LOR, PetscObjectComm((PetscObject)dm)));
9906   if (!ghasConstraints) {
9907     PetscCall(PetscObjectReference((PetscObject)dm));
9908     *odm = dm;
9909     PetscFunctionReturn(PETSC_SUCCESS);
9910   }
9911   PetscCall(DMClone(dm, odm));
9912   PetscCall(DMCopyFields(dm, PETSC_DETERMINE, PETSC_DETERMINE, *odm));
9913   PetscCall(DMGetLocalSection(*odm, &newSection));
9914   PetscCall(DMGetPointSF(*odm, &sf));
9915   PetscCall(PetscSectionCreateGlobalSection(newSection, sf, PETSC_TRUE, PETSC_TRUE, PETSC_FALSE, &gsection));
9916   PetscCall(DMSetGlobalSection(*odm, gsection));
9917   PetscCall(PetscSectionDestroy(&gsection));
9918   PetscFunctionReturn(PETSC_SUCCESS);
9919 }
9920 
9921 static PetscErrorCode DMCreateAffineInterpolationCorrection_Plex(DM dmc, DM dmf, Vec *shift)
9922 {
9923   DM        dmco, dmfo;
9924   Mat       interpo;
9925   Vec       rscale;
9926   Vec       cglobalo, clocal;
9927   Vec       fglobal, fglobalo, flocal;
9928   PetscBool regular;
9929 
9930   PetscFunctionBegin;
9931   PetscCall(DMGetFullDM(dmc, &dmco));
9932   PetscCall(DMGetFullDM(dmf, &dmfo));
9933   PetscCall(DMSetCoarseDM(dmfo, dmco));
9934   PetscCall(DMPlexGetRegularRefinement(dmf, &regular));
9935   PetscCall(DMPlexSetRegularRefinement(dmfo, regular));
9936   PetscCall(DMCreateInterpolation(dmco, dmfo, &interpo, &rscale));
9937   PetscCall(DMCreateGlobalVector(dmco, &cglobalo));
9938   PetscCall(DMCreateLocalVector(dmc, &clocal));
9939   PetscCall(VecSet(cglobalo, 0.));
9940   PetscCall(VecSet(clocal, 0.));
9941   PetscCall(DMCreateGlobalVector(dmf, &fglobal));
9942   PetscCall(DMCreateGlobalVector(dmfo, &fglobalo));
9943   PetscCall(DMCreateLocalVector(dmf, &flocal));
9944   PetscCall(VecSet(fglobal, 0.));
9945   PetscCall(VecSet(fglobalo, 0.));
9946   PetscCall(VecSet(flocal, 0.));
9947   PetscCall(DMPlexInsertBoundaryValues(dmc, PETSC_TRUE, clocal, 0., NULL, NULL, NULL));
9948   PetscCall(DMLocalToGlobalBegin(dmco, clocal, INSERT_VALUES, cglobalo));
9949   PetscCall(DMLocalToGlobalEnd(dmco, clocal, INSERT_VALUES, cglobalo));
9950   PetscCall(MatMult(interpo, cglobalo, fglobalo));
9951   PetscCall(DMGlobalToLocalBegin(dmfo, fglobalo, INSERT_VALUES, flocal));
9952   PetscCall(DMGlobalToLocalEnd(dmfo, fglobalo, INSERT_VALUES, flocal));
9953   PetscCall(DMLocalToGlobalBegin(dmf, flocal, INSERT_VALUES, fglobal));
9954   PetscCall(DMLocalToGlobalEnd(dmf, flocal, INSERT_VALUES, fglobal));
9955   *shift = fglobal;
9956   PetscCall(VecDestroy(&flocal));
9957   PetscCall(VecDestroy(&fglobalo));
9958   PetscCall(VecDestroy(&clocal));
9959   PetscCall(VecDestroy(&cglobalo));
9960   PetscCall(VecDestroy(&rscale));
9961   PetscCall(MatDestroy(&interpo));
9962   PetscCall(DMDestroy(&dmfo));
9963   PetscCall(DMDestroy(&dmco));
9964   PetscFunctionReturn(PETSC_SUCCESS);
9965 }
9966 
9967 PETSC_INTERN PetscErrorCode DMInterpolateSolution_Plex(DM coarse, DM fine, Mat interp, Vec coarseSol, Vec fineSol)
9968 {
9969   PetscObject shifto;
9970   Vec         shift;
9971 
9972   PetscFunctionBegin;
9973   if (!interp) {
9974     Vec rscale;
9975 
9976     PetscCall(DMCreateInterpolation(coarse, fine, &interp, &rscale));
9977     PetscCall(VecDestroy(&rscale));
9978   } else {
9979     PetscCall(PetscObjectReference((PetscObject)interp));
9980   }
9981   PetscCall(PetscObjectQuery((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", &shifto));
9982   if (!shifto) {
9983     PetscCall(DMCreateAffineInterpolationCorrection_Plex(coarse, fine, &shift));
9984     PetscCall(PetscObjectCompose((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", (PetscObject)shift));
9985     shifto = (PetscObject)shift;
9986     PetscCall(VecDestroy(&shift));
9987   }
9988   shift = (Vec)shifto;
9989   PetscCall(MatInterpolate(interp, coarseSol, fineSol));
9990   PetscCall(VecAXPY(fineSol, 1.0, shift));
9991   PetscCall(MatDestroy(&interp));
9992   PetscFunctionReturn(PETSC_SUCCESS);
9993 }
9994 
9995 /* Pointwise interpolation
9996      Just code FEM for now
9997      u^f = I u^c
9998      sum_k u^f_k phi^f_k = I sum_j u^c_j phi^c_j
9999      u^f_i = sum_j psi^f_i I phi^c_j u^c_j
10000      I_{ij} = psi^f_i phi^c_j
10001 */
10002 PetscErrorCode DMCreateInterpolation_Plex(DM dmCoarse, DM dmFine, Mat *interpolation, Vec *scaling)
10003 {
10004   PetscSection gsc, gsf;
10005   PetscInt     m, n;
10006   void        *ctx;
10007   DM           cdm;
10008   PetscBool    regular, ismatis, isRefined = dmCoarse->data == dmFine->data ? PETSC_FALSE : PETSC_TRUE;
10009 
10010   PetscFunctionBegin;
10011   PetscCall(DMGetGlobalSection(dmFine, &gsf));
10012   PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
10013   PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
10014   PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
10015 
10016   PetscCall(PetscStrcmp(dmCoarse->mattype, MATIS, &ismatis));
10017   PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), interpolation));
10018   PetscCall(MatSetSizes(*interpolation, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
10019   PetscCall(MatSetType(*interpolation, ismatis ? MATAIJ : dmCoarse->mattype));
10020   PetscCall(DMGetApplicationContext(dmFine, &ctx));
10021 
10022   PetscCall(DMGetCoarseDM(dmFine, &cdm));
10023   PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
10024   if (!isRefined || (regular && cdm == dmCoarse)) PetscCall(DMPlexComputeInterpolatorNested(dmCoarse, dmFine, isRefined, *interpolation, ctx));
10025   else PetscCall(DMPlexComputeInterpolatorGeneral(dmCoarse, dmFine, *interpolation, ctx));
10026   PetscCall(MatViewFromOptions(*interpolation, NULL, "-interp_mat_view"));
10027   if (scaling) {
10028     /* Use naive scaling */
10029     PetscCall(DMCreateInterpolationScale(dmCoarse, dmFine, *interpolation, scaling));
10030   }
10031   PetscFunctionReturn(PETSC_SUCCESS);
10032 }
10033 
10034 PetscErrorCode DMCreateInjection_Plex(DM dmCoarse, DM dmFine, Mat *mat)
10035 {
10036   VecScatter ctx;
10037 
10038   PetscFunctionBegin;
10039   PetscCall(DMPlexComputeInjectorFEM(dmCoarse, dmFine, &ctx, NULL));
10040   PetscCall(MatCreateScatter(PetscObjectComm((PetscObject)ctx), ctx, mat));
10041   PetscCall(VecScatterDestroy(&ctx));
10042   PetscFunctionReturn(PETSC_SUCCESS);
10043 }
10044 
10045 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[])
10046 {
10047   const PetscInt f  = (PetscInt)PetscRealPart(constants[numConstants]);
10048   const PetscInt Nc = uOff[f + 1] - uOff[f];
10049   for (PetscInt c = 0; c < Nc; ++c) g0[c * Nc + c] = 1.0;
10050 }
10051 
10052 PetscErrorCode DMCreateMassMatrixLumped_Plex(DM dm, Vec *lmass, Vec *mass)
10053 {
10054   DM           dmc;
10055   PetscDS      ds;
10056   Vec          ones, locmass;
10057   IS           cellIS;
10058   PetscFormKey key;
10059   PetscInt     depth;
10060 
10061   PetscFunctionBegin;
10062   PetscCall(DMClone(dm, &dmc));
10063   PetscCall(DMCopyDisc(dm, dmc));
10064   PetscCall(DMGetDS(dmc, &ds));
10065   for (PetscInt f = 0; f < dmc->Nf; ++f) PetscCall(PetscDSSetJacobian(ds, f, f, g0_identity_private, NULL, NULL, NULL));
10066   if (mass) PetscCall(DMCreateGlobalVector(dm, mass));
10067   if (lmass) PetscCall(DMCreateLocalVector(dm, &locmass));
10068   else PetscCall(DMGetLocalVector(dm, &locmass));
10069   PetscCall(DMGetLocalVector(dm, &ones));
10070   PetscCall(DMPlexGetDepth(dm, &depth));
10071   PetscCall(DMGetStratumIS(dm, "depth", depth, &cellIS));
10072   PetscCall(VecSet(locmass, 0.0));
10073   PetscCall(VecSet(ones, 1.0));
10074   key.label = NULL;
10075   key.value = 0;
10076   key.field = 0;
10077   key.part  = 0;
10078   PetscCall(DMPlexComputeJacobian_Action_Internal(dmc, key, cellIS, 0.0, 0.0, ones, NULL, ones, locmass, NULL));
10079   PetscCall(ISDestroy(&cellIS));
10080   if (mass) {
10081     PetscCall(DMLocalToGlobalBegin(dm, locmass, ADD_VALUES, *mass));
10082     PetscCall(DMLocalToGlobalEnd(dm, locmass, ADD_VALUES, *mass));
10083   }
10084   PetscCall(DMRestoreLocalVector(dm, &ones));
10085   if (lmass) *lmass = locmass;
10086   else PetscCall(DMRestoreLocalVector(dm, &locmass));
10087   PetscCall(DMDestroy(&dmc));
10088   PetscFunctionReturn(PETSC_SUCCESS);
10089 }
10090 
10091 PetscErrorCode DMCreateMassMatrix_Plex(DM dmCoarse, DM dmFine, Mat *mass)
10092 {
10093   PetscSection gsc, gsf;
10094   PetscInt     m, n;
10095   void        *ctx;
10096   DM           cdm;
10097   PetscBool    regular;
10098 
10099   PetscFunctionBegin;
10100   if (dmFine == dmCoarse) {
10101     DM            dmc;
10102     PetscDS       ds;
10103     PetscWeakForm wf;
10104     Vec           u;
10105     IS            cellIS;
10106     PetscFormKey  key;
10107     PetscInt      depth;
10108 
10109     PetscCall(DMClone(dmFine, &dmc));
10110     PetscCall(DMCopyDisc(dmFine, dmc));
10111     PetscCall(DMGetDS(dmc, &ds));
10112     PetscCall(PetscDSGetWeakForm(ds, &wf));
10113     PetscCall(PetscWeakFormClear(wf));
10114     for (PetscInt f = 0; f < dmc->Nf; ++f) PetscCall(PetscDSSetJacobian(ds, f, f, g0_identity_private, NULL, NULL, NULL));
10115     PetscCall(DMCreateMatrix(dmc, mass));
10116     PetscCall(DMGetLocalVector(dmc, &u));
10117     PetscCall(DMPlexGetDepth(dmc, &depth));
10118     PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
10119     PetscCall(MatZeroEntries(*mass));
10120     key.label = NULL;
10121     key.value = 0;
10122     key.field = 0;
10123     key.part  = 0;
10124     PetscCall(DMPlexComputeJacobian_Internal(dmc, key, cellIS, 0.0, 0.0, u, NULL, *mass, *mass, NULL));
10125     PetscCall(ISDestroy(&cellIS));
10126     PetscCall(DMRestoreLocalVector(dmc, &u));
10127     PetscCall(DMDestroy(&dmc));
10128   } else {
10129     PetscCall(DMGetGlobalSection(dmFine, &gsf));
10130     PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
10131     PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
10132     PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
10133 
10134     PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), mass));
10135     PetscCall(MatSetSizes(*mass, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
10136     PetscCall(MatSetType(*mass, dmCoarse->mattype));
10137     PetscCall(DMGetApplicationContext(dmFine, &ctx));
10138 
10139     PetscCall(DMGetCoarseDM(dmFine, &cdm));
10140     PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
10141     if (regular && cdm == dmCoarse) PetscCall(DMPlexComputeMassMatrixNested(dmCoarse, dmFine, *mass, ctx));
10142     else PetscCall(DMPlexComputeMassMatrixGeneral(dmCoarse, dmFine, *mass, ctx));
10143   }
10144   PetscCall(MatViewFromOptions(*mass, NULL, "-mass_mat_view"));
10145   PetscFunctionReturn(PETSC_SUCCESS);
10146 }
10147 
10148 /*@
10149   DMPlexGetRegularRefinement - Get the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
10150 
10151   Input Parameter:
10152 . dm - The `DMPLEX` object
10153 
10154   Output Parameter:
10155 . regular - The flag
10156 
10157   Level: intermediate
10158 
10159 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetRegularRefinement()`
10160 @*/
10161 PetscErrorCode DMPlexGetRegularRefinement(DM dm, PetscBool *regular)
10162 {
10163   PetscFunctionBegin;
10164   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10165   PetscAssertPointer(regular, 2);
10166   *regular = ((DM_Plex *)dm->data)->regularRefinement;
10167   PetscFunctionReturn(PETSC_SUCCESS);
10168 }
10169 
10170 /*@
10171   DMPlexSetRegularRefinement - Set the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
10172 
10173   Input Parameters:
10174 + dm      - The `DMPLEX` object
10175 - regular - The flag
10176 
10177   Level: intermediate
10178 
10179 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetRegularRefinement()`
10180 @*/
10181 PetscErrorCode DMPlexSetRegularRefinement(DM dm, PetscBool regular)
10182 {
10183   PetscFunctionBegin;
10184   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10185   ((DM_Plex *)dm->data)->regularRefinement = regular;
10186   PetscFunctionReturn(PETSC_SUCCESS);
10187 }
10188 
10189 /*@
10190   DMPlexGetAnchors - Get the layout of the anchor (point-to-point) constraints.  Typically, the user will not have to
10191   call DMPlexGetAnchors() directly: if there are anchors, then `DMPlexGetAnchors()` is called during `DMGetDefaultConstraints()`.
10192 
10193   Not Collective
10194 
10195   Input Parameter:
10196 . dm - The `DMPLEX` object
10197 
10198   Output Parameters:
10199 + anchorSection - If not `NULL`, set to the section describing which points anchor the constrained points.
10200 - anchorIS      - If not `NULL`, set to the list of anchors indexed by `anchorSection`
10201 
10202   Level: intermediate
10203 
10204 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`, `IS`, `PetscSection`
10205 @*/
10206 PetscErrorCode DMPlexGetAnchors(DM dm, PetscSection *anchorSection, IS *anchorIS)
10207 {
10208   DM_Plex *plex = (DM_Plex *)dm->data;
10209 
10210   PetscFunctionBegin;
10211   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10212   if (!plex->anchorSection && !plex->anchorIS && plex->createanchors) PetscCall((*plex->createanchors)(dm));
10213   if (anchorSection) *anchorSection = plex->anchorSection;
10214   if (anchorIS) *anchorIS = plex->anchorIS;
10215   PetscFunctionReturn(PETSC_SUCCESS);
10216 }
10217 
10218 /*@
10219   DMPlexSetAnchors - Set the layout of the local anchor (point-to-point) constraints.
10220 
10221   Collective
10222 
10223   Input Parameters:
10224 + dm            - The `DMPLEX` object
10225 . anchorSection - The section that describes the mapping from constrained points to the anchor points listed in anchorIS.
10226                   Must have a local communicator (`PETSC_COMM_SELF` or derivative).
10227 - anchorIS      - The list of all anchor points.  Must have a local communicator (`PETSC_COMM_SELF` or derivative).
10228 
10229   Level: intermediate
10230 
10231   Notes:
10232   Unlike boundary conditions, when a point's degrees of freedom in a section are constrained to
10233   an outside value, the anchor constraints set a point's degrees of freedom to be a linear
10234   combination of other points' degrees of freedom.
10235 
10236   After specifying the layout of constraints with `DMPlexSetAnchors()`, one specifies the constraints by calling
10237   `DMGetDefaultConstraints()` and filling in the entries in the constraint matrix.
10238 
10239   The reference counts of `anchorSection` and `anchorIS` are incremented.
10240 
10241 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`
10242 @*/
10243 PetscErrorCode DMPlexSetAnchors(DM dm, PetscSection anchorSection, IS anchorIS)
10244 {
10245   DM_Plex    *plex = (DM_Plex *)dm->data;
10246   PetscMPIInt result;
10247 
10248   PetscFunctionBegin;
10249   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10250   if (anchorSection) {
10251     PetscValidHeaderSpecific(anchorSection, PETSC_SECTION_CLASSID, 2);
10252     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorSection), &result));
10253     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor section must have local communicator");
10254   }
10255   if (anchorIS) {
10256     PetscValidHeaderSpecific(anchorIS, IS_CLASSID, 3);
10257     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorIS), &result));
10258     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor IS must have local communicator");
10259   }
10260 
10261   PetscCall(PetscObjectReference((PetscObject)anchorSection));
10262   PetscCall(PetscSectionDestroy(&plex->anchorSection));
10263   plex->anchorSection = anchorSection;
10264 
10265   PetscCall(PetscObjectReference((PetscObject)anchorIS));
10266   PetscCall(ISDestroy(&plex->anchorIS));
10267   plex->anchorIS = anchorIS;
10268 
10269   if (PetscUnlikelyDebug(anchorIS && anchorSection)) {
10270     PetscInt        size, a, pStart, pEnd;
10271     const PetscInt *anchors;
10272 
10273     PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
10274     PetscCall(ISGetLocalSize(anchorIS, &size));
10275     PetscCall(ISGetIndices(anchorIS, &anchors));
10276     for (a = 0; a < size; a++) {
10277       PetscInt p;
10278 
10279       p = anchors[a];
10280       if (p >= pStart && p < pEnd) {
10281         PetscInt dof;
10282 
10283         PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
10284         if (dof) {
10285           PetscCall(ISRestoreIndices(anchorIS, &anchors));
10286           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Point %" PetscInt_FMT " cannot be constrained and an anchor", p);
10287         }
10288       }
10289     }
10290     PetscCall(ISRestoreIndices(anchorIS, &anchors));
10291   }
10292   /* reset the generic constraints */
10293   PetscCall(DMSetDefaultConstraints(dm, NULL, NULL, NULL));
10294   PetscFunctionReturn(PETSC_SUCCESS);
10295 }
10296 
10297 static PetscErrorCode DMPlexCreateConstraintSection_Anchors(DM dm, PetscSection section, PetscSection *cSec)
10298 {
10299   PetscSection anchorSection;
10300   PetscInt     pStart, pEnd, sStart, sEnd, p, dof, numFields, f;
10301 
10302   PetscFunctionBegin;
10303   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10304   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
10305   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, cSec));
10306   PetscCall(PetscSectionGetNumFields(section, &numFields));
10307   if (numFields) {
10308     PetscInt f;
10309     PetscCall(PetscSectionSetNumFields(*cSec, numFields));
10310 
10311     for (f = 0; f < numFields; f++) {
10312       PetscInt numComp;
10313 
10314       PetscCall(PetscSectionGetFieldComponents(section, f, &numComp));
10315       PetscCall(PetscSectionSetFieldComponents(*cSec, f, numComp));
10316     }
10317   }
10318   PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
10319   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
10320   pStart = PetscMax(pStart, sStart);
10321   pEnd   = PetscMin(pEnd, sEnd);
10322   pEnd   = PetscMax(pStart, pEnd);
10323   PetscCall(PetscSectionSetChart(*cSec, pStart, pEnd));
10324   for (p = pStart; p < pEnd; p++) {
10325     PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
10326     if (dof) {
10327       PetscCall(PetscSectionGetDof(section, p, &dof));
10328       PetscCall(PetscSectionSetDof(*cSec, p, dof));
10329       for (f = 0; f < numFields; f++) {
10330         PetscCall(PetscSectionGetFieldDof(section, p, f, &dof));
10331         PetscCall(PetscSectionSetFieldDof(*cSec, p, f, dof));
10332       }
10333     }
10334   }
10335   PetscCall(PetscSectionSetUp(*cSec));
10336   PetscCall(PetscObjectSetName((PetscObject)*cSec, "Constraint Section"));
10337   PetscFunctionReturn(PETSC_SUCCESS);
10338 }
10339 
10340 static PetscErrorCode DMPlexCreateConstraintMatrix_Anchors(DM dm, PetscSection section, PetscSection cSec, Mat *cMat)
10341 {
10342   PetscSection    aSec;
10343   PetscInt        pStart, pEnd, p, sStart, sEnd, dof, aDof, aOff, off, nnz, annz, m, n, q, a, offset, *i, *j;
10344   const PetscInt *anchors;
10345   PetscInt        numFields, f;
10346   IS              aIS;
10347   MatType         mtype;
10348   PetscBool       iscuda, iskokkos;
10349 
10350   PetscFunctionBegin;
10351   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10352   PetscCall(PetscSectionGetStorageSize(cSec, &m));
10353   PetscCall(PetscSectionGetStorageSize(section, &n));
10354   PetscCall(MatCreate(PETSC_COMM_SELF, cMat));
10355   PetscCall(MatSetSizes(*cMat, m, n, m, n));
10356   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJCUSPARSE, &iscuda));
10357   if (!iscuda) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJCUSPARSE, &iscuda));
10358   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJKOKKOS, &iskokkos));
10359   if (!iskokkos) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJKOKKOS, &iskokkos));
10360   if (iscuda) mtype = MATSEQAIJCUSPARSE;
10361   else if (iskokkos) mtype = MATSEQAIJKOKKOS;
10362   else mtype = MATSEQAIJ;
10363   PetscCall(MatSetType(*cMat, mtype));
10364   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
10365   PetscCall(ISGetIndices(aIS, &anchors));
10366   /* cSec will be a subset of aSec and section */
10367   PetscCall(PetscSectionGetChart(cSec, &pStart, &pEnd));
10368   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
10369   PetscCall(PetscMalloc1(m + 1, &i));
10370   i[0] = 0;
10371   PetscCall(PetscSectionGetNumFields(section, &numFields));
10372   for (p = pStart; p < pEnd; p++) {
10373     PetscInt rDof, rOff, r;
10374 
10375     PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10376     if (!rDof) continue;
10377     PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10378     if (numFields) {
10379       for (f = 0; f < numFields; f++) {
10380         annz = 0;
10381         for (r = 0; r < rDof; r++) {
10382           a = anchors[rOff + r];
10383           if (a < sStart || a >= sEnd) continue;
10384           PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
10385           annz += aDof;
10386         }
10387         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
10388         PetscCall(PetscSectionGetFieldOffset(cSec, p, f, &off));
10389         for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
10390       }
10391     } else {
10392       annz = 0;
10393       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10394       for (q = 0; q < dof; q++) {
10395         a = anchors[rOff + q];
10396         if (a < sStart || a >= sEnd) continue;
10397         PetscCall(PetscSectionGetDof(section, a, &aDof));
10398         annz += aDof;
10399       }
10400       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10401       PetscCall(PetscSectionGetOffset(cSec, p, &off));
10402       for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
10403     }
10404   }
10405   nnz = i[m];
10406   PetscCall(PetscMalloc1(nnz, &j));
10407   offset = 0;
10408   for (p = pStart; p < pEnd; p++) {
10409     if (numFields) {
10410       for (f = 0; f < numFields; f++) {
10411         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
10412         for (q = 0; q < dof; q++) {
10413           PetscInt rDof, rOff, r;
10414           PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10415           PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10416           for (r = 0; r < rDof; r++) {
10417             PetscInt s;
10418 
10419             a = anchors[rOff + r];
10420             if (a < sStart || a >= sEnd) continue;
10421             PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
10422             PetscCall(PetscSectionGetFieldOffset(section, a, f, &aOff));
10423             for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
10424           }
10425         }
10426       }
10427     } else {
10428       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10429       for (q = 0; q < dof; q++) {
10430         PetscInt rDof, rOff, r;
10431         PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10432         PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10433         for (r = 0; r < rDof; r++) {
10434           PetscInt s;
10435 
10436           a = anchors[rOff + r];
10437           if (a < sStart || a >= sEnd) continue;
10438           PetscCall(PetscSectionGetDof(section, a, &aDof));
10439           PetscCall(PetscSectionGetOffset(section, a, &aOff));
10440           for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
10441         }
10442       }
10443     }
10444   }
10445   PetscCall(MatSeqAIJSetPreallocationCSR(*cMat, i, j, NULL));
10446   PetscCall(PetscFree(i));
10447   PetscCall(PetscFree(j));
10448   PetscCall(ISRestoreIndices(aIS, &anchors));
10449   PetscFunctionReturn(PETSC_SUCCESS);
10450 }
10451 
10452 PetscErrorCode DMCreateDefaultConstraints_Plex(DM dm)
10453 {
10454   DM_Plex     *plex = (DM_Plex *)dm->data;
10455   PetscSection anchorSection, section, cSec;
10456   Mat          cMat;
10457 
10458   PetscFunctionBegin;
10459   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10460   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
10461   if (anchorSection) {
10462     PetscInt Nf;
10463 
10464     PetscCall(DMGetLocalSection(dm, &section));
10465     PetscCall(DMPlexCreateConstraintSection_Anchors(dm, section, &cSec));
10466     PetscCall(DMPlexCreateConstraintMatrix_Anchors(dm, section, cSec, &cMat));
10467     PetscCall(DMGetNumFields(dm, &Nf));
10468     if (Nf && plex->computeanchormatrix) PetscCall((*plex->computeanchormatrix)(dm, section, cSec, cMat));
10469     PetscCall(DMSetDefaultConstraints(dm, cSec, cMat, NULL));
10470     PetscCall(PetscSectionDestroy(&cSec));
10471     PetscCall(MatDestroy(&cMat));
10472   }
10473   PetscFunctionReturn(PETSC_SUCCESS);
10474 }
10475 
10476 PetscErrorCode DMCreateSubDomainDM_Plex(DM dm, DMLabel label, PetscInt value, IS *is, DM *subdm)
10477 {
10478   IS           subis;
10479   PetscSection section, subsection;
10480 
10481   PetscFunctionBegin;
10482   PetscCall(DMGetLocalSection(dm, &section));
10483   PetscCheck(section, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set default section for DM before splitting subdomain");
10484   PetscCheck(subdm, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set output subDM for splitting subdomain");
10485   /* Create subdomain */
10486   PetscCall(DMPlexFilter(dm, label, value, PETSC_FALSE, PETSC_FALSE, NULL, subdm));
10487   /* Create submodel */
10488   PetscCall(DMPlexGetSubpointIS(*subdm, &subis));
10489   PetscCall(PetscSectionCreateSubmeshSection(section, subis, &subsection));
10490   PetscCall(DMSetLocalSection(*subdm, subsection));
10491   PetscCall(PetscSectionDestroy(&subsection));
10492   PetscCall(DMCopyDisc(dm, *subdm));
10493   /* Create map from submodel to global model */
10494   if (is) {
10495     PetscSection    sectionGlobal, subsectionGlobal;
10496     IS              spIS;
10497     const PetscInt *spmap;
10498     PetscInt       *subIndices;
10499     PetscInt        subSize = 0, subOff = 0, pStart, pEnd, p;
10500     PetscInt        Nf, f, bs = -1, bsLocal[2], bsMinMax[2];
10501 
10502     PetscCall(DMPlexGetSubpointIS(*subdm, &spIS));
10503     PetscCall(ISGetIndices(spIS, &spmap));
10504     PetscCall(PetscSectionGetNumFields(section, &Nf));
10505     PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
10506     PetscCall(DMGetGlobalSection(*subdm, &subsectionGlobal));
10507     PetscCall(PetscSectionGetChart(subsection, &pStart, &pEnd));
10508     for (p = pStart; p < pEnd; ++p) {
10509       PetscInt gdof, pSubSize = 0;
10510 
10511       PetscCall(PetscSectionGetDof(sectionGlobal, p, &gdof));
10512       if (gdof > 0) {
10513         for (f = 0; f < Nf; ++f) {
10514           PetscInt fdof, fcdof;
10515 
10516           PetscCall(PetscSectionGetFieldDof(subsection, p, f, &fdof));
10517           PetscCall(PetscSectionGetFieldConstraintDof(subsection, p, f, &fcdof));
10518           pSubSize += fdof - fcdof;
10519         }
10520         subSize += pSubSize;
10521         if (pSubSize) {
10522           if (bs < 0) {
10523             bs = pSubSize;
10524           } else if (bs != pSubSize) {
10525             /* Layout does not admit a pointwise block size */
10526             bs = 1;
10527           }
10528         }
10529       }
10530     }
10531     /* Must have same blocksize on all procs (some might have no points) */
10532     bsLocal[0] = bs < 0 ? PETSC_INT_MAX : bs;
10533     bsLocal[1] = bs;
10534     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
10535     if (bsMinMax[0] != bsMinMax[1]) {
10536       bs = 1;
10537     } else {
10538       bs = bsMinMax[0];
10539     }
10540     PetscCall(PetscMalloc1(subSize, &subIndices));
10541     for (p = pStart; p < pEnd; ++p) {
10542       PetscInt gdof, goff;
10543 
10544       PetscCall(PetscSectionGetDof(subsectionGlobal, p, &gdof));
10545       if (gdof > 0) {
10546         const PetscInt point = spmap[p];
10547 
10548         PetscCall(PetscSectionGetOffset(sectionGlobal, point, &goff));
10549         for (f = 0; f < Nf; ++f) {
10550           PetscInt fdof, fcdof, fc, f2, poff = 0;
10551 
10552           /* Can get rid of this loop by storing field information in the global section */
10553           for (f2 = 0; f2 < f; ++f2) {
10554             PetscCall(PetscSectionGetFieldDof(section, p, f2, &fdof));
10555             PetscCall(PetscSectionGetFieldConstraintDof(section, p, f2, &fcdof));
10556             poff += fdof - fcdof;
10557           }
10558           PetscCall(PetscSectionGetFieldDof(section, p, f, &fdof));
10559           PetscCall(PetscSectionGetFieldConstraintDof(section, p, f, &fcdof));
10560           for (fc = 0; fc < fdof - fcdof; ++fc, ++subOff) subIndices[subOff] = goff + poff + fc;
10561         }
10562       }
10563     }
10564     PetscCall(ISRestoreIndices(spIS, &spmap));
10565     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), subSize, subIndices, PETSC_OWN_POINTER, is));
10566     if (bs > 1) {
10567       /* We need to check that the block size does not come from non-contiguous fields */
10568       PetscInt i, j, set = 1;
10569       for (i = 0; i < subSize; i += bs) {
10570         for (j = 0; j < bs; ++j) {
10571           if (subIndices[i + j] != subIndices[i] + j) {
10572             set = 0;
10573             break;
10574           }
10575         }
10576       }
10577       if (set) PetscCall(ISSetBlockSize(*is, bs));
10578     }
10579     /* Attach nullspace */
10580     for (f = 0; f < Nf; ++f) {
10581       (*subdm)->nullspaceConstructors[f] = dm->nullspaceConstructors[f];
10582       if ((*subdm)->nullspaceConstructors[f]) break;
10583     }
10584     if (f < Nf) {
10585       MatNullSpace nullSpace;
10586       PetscCall((*(*subdm)->nullspaceConstructors[f])(*subdm, f, f, &nullSpace));
10587 
10588       PetscCall(PetscObjectCompose((PetscObject)*is, "nullspace", (PetscObject)nullSpace));
10589       PetscCall(MatNullSpaceDestroy(&nullSpace));
10590     }
10591   }
10592   PetscFunctionReturn(PETSC_SUCCESS);
10593 }
10594 
10595 /*@
10596   DMPlexMonitorThroughput - Report the cell throughput of FE integration
10597 
10598   Input Parameters:
10599 + dm    - The `DM`
10600 - dummy - unused argument
10601 
10602   Options Database Key:
10603 . -dm_plex_monitor_throughput - Activate the monitor
10604 
10605   Level: developer
10606 
10607 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexCreate()`
10608 @*/
10609 PetscErrorCode DMPlexMonitorThroughput(DM dm, void *dummy)
10610 {
10611   PetscLogHandler default_handler;
10612 
10613   PetscFunctionBegin;
10614   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10615   PetscCall(PetscLogGetDefaultHandler(&default_handler));
10616   if (default_handler) {
10617     PetscLogEvent      event;
10618     PetscEventPerfInfo eventInfo;
10619     PetscReal          cellRate, flopRate;
10620     PetscInt           cStart, cEnd, Nf, N;
10621     const char        *name;
10622 
10623     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
10624     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
10625     PetscCall(DMGetNumFields(dm, &Nf));
10626     PetscCall(PetscLogEventGetId("DMPlexResidualFE", &event));
10627     PetscCall(PetscLogEventGetPerfInfo(PETSC_DEFAULT, event, &eventInfo));
10628     N        = (cEnd - cStart) * Nf * eventInfo.count;
10629     flopRate = eventInfo.flops / eventInfo.time;
10630     cellRate = N / eventInfo.time;
10631     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)));
10632   } else {
10633     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.");
10634   }
10635   PetscFunctionReturn(PETSC_SUCCESS);
10636 }
10637