xref: /petsc/src/dm/impls/plex/plex.c (revision 8a7d4057d9226490dba4e1a062f54f84e7d90861)
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_MAX_INT, 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 DMPlexGetFieldTypes_Internal(DM dm, PetscSection section, PetscInt field, PetscInt *types, PetscInt **ssStart, PetscInt **ssEnd, PetscViewerVTKFieldType **sft)
125 {
126   PetscInt                 cdim, pStart, pEnd, vStart, vEnd, cStart, cEnd, c, depth, cellHeight, t;
127   PetscInt                *sStart, *sEnd;
128   PetscViewerVTKFieldType *ft;
129   PetscInt                 vcdof[DM_NUM_POLYTOPES + 1], globalvcdof[DM_NUM_POLYTOPES + 1];
130   DMLabel                  depthLabel, ctLabel;
131 
132   PetscFunctionBegin;
133   /* the vcdof and globalvcdof are sized to allow every polytope type and simple vertex at DM_NUM_POLYTOPES */
134   PetscCall(PetscArrayzero(vcdof, DM_NUM_POLYTOPES + 1));
135   PetscCall(DMGetCoordinateDim(dm, &cdim));
136   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
137   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
138   if (field >= 0) {
139     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, vStart, field, &vcdof[DM_NUM_POLYTOPES]));
140   } else {
141     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetDof(section, vStart, &vcdof[DM_NUM_POLYTOPES]));
142   }
143 
144   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
145   PetscCall(DMPlexGetDepth(dm, &depth));
146   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
147   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
148   for (c = 0; c < DM_NUM_POLYTOPES; ++c) {
149     const DMPolytopeType ict = (DMPolytopeType)c;
150     PetscInt             dep;
151 
152     if (ict == DM_POLYTOPE_FV_GHOST) continue;
153     PetscCall(DMLabelGetStratumBounds(ctLabel, ict, &cStart, &cEnd));
154     if (pStart >= 0) {
155       PetscCall(DMLabelGetValue(depthLabel, cStart, &dep));
156       if (dep != depth - cellHeight) continue;
157     }
158     if (field >= 0) {
159       if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, cStart, field, &vcdof[c]));
160     } else {
161       if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetDof(section, cStart, &vcdof[c]));
162     }
163   }
164 
165   PetscCall(MPIU_Allreduce(vcdof, globalvcdof, DM_NUM_POLYTOPES + 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
166   *types = 0;
167 
168   for (c = 0; c < DM_NUM_POLYTOPES + 1; ++c) {
169     if (globalvcdof[c]) ++(*types);
170   }
171 
172   PetscCall(PetscMalloc3(*types, &sStart, *types, &sEnd, *types, &ft));
173   t = 0;
174   if (globalvcdof[DM_NUM_POLYTOPES]) {
175     sStart[t] = vStart;
176     sEnd[t]   = vEnd;
177     ft[t]     = (globalvcdof[t] == cdim) ? PETSC_VTK_POINT_VECTOR_FIELD : PETSC_VTK_POINT_FIELD;
178     ++t;
179   }
180 
181   for (c = 0; c < DM_NUM_POLYTOPES; ++c) {
182     if (globalvcdof[c]) {
183       const DMPolytopeType ict = (DMPolytopeType)c;
184 
185       PetscCall(DMLabelGetStratumBounds(ctLabel, ict, &cStart, &cEnd));
186       sStart[t] = cStart;
187       sEnd[t]   = cEnd;
188       ft[t]     = (globalvcdof[c] == cdim) ? PETSC_VTK_CELL_VECTOR_FIELD : PETSC_VTK_CELL_FIELD;
189       ++t;
190     }
191   }
192 
193   if (!*types) {
194     if (field >= 0) {
195       const char *fieldname;
196 
197       PetscCall(PetscSectionGetFieldName(section, field, &fieldname));
198       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section field %" PetscInt_FMT " \"%s\"\n", field, fieldname));
199     } else {
200       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section\n"));
201     }
202   }
203 
204   *ssStart = sStart;
205   *ssEnd   = sEnd;
206   *sft     = ft;
207   PetscFunctionReturn(PETSC_SUCCESS);
208 }
209 
210 PetscErrorCode DMPlexRestoreFieldTypes_Internal(DM dm, PetscSection section, PetscInt field, PetscInt *types, PetscInt **sStart, PetscInt **sEnd, PetscViewerVTKFieldType **ft)
211 {
212   PetscFunctionBegin;
213   PetscCall(PetscFree3(*sStart, *sEnd, *ft));
214   PetscFunctionReturn(PETSC_SUCCESS);
215 }
216 
217 PetscErrorCode DMPlexGetFieldType_Internal(DM dm, PetscSection section, PetscInt field, PetscInt *sStart, PetscInt *sEnd, PetscViewerVTKFieldType *ft)
218 {
219   PetscInt cdim, pStart, pEnd, vStart, vEnd, cStart, cEnd;
220   PetscInt vcdof[2] = {0, 0}, globalvcdof[2];
221 
222   PetscFunctionBegin;
223   *ft = PETSC_VTK_INVALID;
224   PetscCall(DMGetCoordinateDim(dm, &cdim));
225   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
226   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
227   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
228   if (field >= 0) {
229     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, vStart, field, &vcdof[0]));
230     if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, cStart, field, &vcdof[1]));
231   } else {
232     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetDof(section, vStart, &vcdof[0]));
233     if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetDof(section, cStart, &vcdof[1]));
234   }
235   PetscCall(MPIU_Allreduce(vcdof, globalvcdof, 2, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
236   if (globalvcdof[0]) {
237     *sStart = vStart;
238     *sEnd   = vEnd;
239     if (globalvcdof[0] == cdim) *ft = PETSC_VTK_POINT_VECTOR_FIELD;
240     else *ft = PETSC_VTK_POINT_FIELD;
241   } else if (globalvcdof[1]) {
242     *sStart = cStart;
243     *sEnd   = cEnd;
244     if (globalvcdof[1] == cdim) *ft = PETSC_VTK_CELL_VECTOR_FIELD;
245     else *ft = PETSC_VTK_CELL_FIELD;
246   } else {
247     if (field >= 0) {
248       const char *fieldname;
249 
250       PetscCall(PetscSectionGetFieldName(section, field, &fieldname));
251       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section field %" PetscInt_FMT " \"%s\"\n", field, fieldname));
252     } else {
253       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section\n"));
254     }
255   }
256   PetscFunctionReturn(PETSC_SUCCESS);
257 }
258 
259 /*@
260   DMPlexVecView1D - Plot many 1D solutions on the same line graph
261 
262   Collective
263 
264   Input Parameters:
265 + dm     - The `DMPLEX` object
266 . n      - The number of vectors
267 . u      - The array of local vectors
268 - viewer - The `PetscViewer`
269 
270   Level: advanced
271 
272 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `VecViewFromOptions()`, `VecView()`
273 @*/
274 PetscErrorCode DMPlexVecView1D(DM dm, PetscInt n, Vec u[], PetscViewer viewer)
275 {
276   PetscDS            ds;
277   PetscDraw          draw = NULL;
278   PetscDrawLG        lg;
279   Vec                coordinates;
280   const PetscScalar *coords, **sol;
281   PetscReal         *vals;
282   PetscInt          *Nc;
283   PetscInt           Nf, f, c, Nl, l, i, vStart, vEnd, v;
284   char             **names;
285 
286   PetscFunctionBegin;
287   PetscCall(DMGetDS(dm, &ds));
288   PetscCall(PetscDSGetNumFields(ds, &Nf));
289   PetscCall(PetscDSGetTotalComponents(ds, &Nl));
290   PetscCall(PetscDSGetComponents(ds, &Nc));
291 
292   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
293   if (!draw) PetscFunctionReturn(PETSC_SUCCESS);
294   PetscCall(PetscDrawLGCreate(draw, n * Nl, &lg));
295 
296   PetscCall(PetscMalloc3(n, &sol, n * Nl, &names, n * Nl, &vals));
297   for (i = 0, l = 0; i < n; ++i) {
298     const char *vname;
299 
300     PetscCall(PetscObjectGetName((PetscObject)u[i], &vname));
301     for (f = 0; f < Nf; ++f) {
302       PetscObject disc;
303       const char *fname;
304       char        tmpname[PETSC_MAX_PATH_LEN];
305 
306       PetscCall(PetscDSGetDiscretization(ds, f, &disc));
307       /* TODO Create names for components */
308       for (c = 0; c < Nc[f]; ++c, ++l) {
309         PetscCall(PetscObjectGetName(disc, &fname));
310         PetscCall(PetscStrncpy(tmpname, vname, sizeof(tmpname)));
311         PetscCall(PetscStrlcat(tmpname, ":", sizeof(tmpname)));
312         PetscCall(PetscStrlcat(tmpname, fname, sizeof(tmpname)));
313         PetscCall(PetscStrallocpy(tmpname, &names[l]));
314       }
315     }
316   }
317   PetscCall(PetscDrawLGSetLegend(lg, (const char *const *)names));
318   /* Just add P_1 support for now */
319   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
320   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
321   PetscCall(VecGetArrayRead(coordinates, &coords));
322   for (i = 0; i < n; ++i) PetscCall(VecGetArrayRead(u[i], &sol[i]));
323   for (v = vStart; v < vEnd; ++v) {
324     PetscScalar *x, *svals;
325 
326     PetscCall(DMPlexPointLocalRead(dm, v, coords, &x));
327     for (i = 0; i < n; ++i) {
328       PetscCall(DMPlexPointLocalRead(dm, v, sol[i], &svals));
329       for (l = 0; l < Nl; ++l) vals[i * Nl + l] = PetscRealPart(svals[l]);
330     }
331     PetscCall(PetscDrawLGAddCommonPoint(lg, PetscRealPart(x[0]), vals));
332   }
333   PetscCall(VecRestoreArrayRead(coordinates, &coords));
334   for (i = 0; i < n; ++i) PetscCall(VecRestoreArrayRead(u[i], &sol[i]));
335   for (l = 0; l < n * Nl; ++l) PetscCall(PetscFree(names[l]));
336   PetscCall(PetscFree3(sol, names, vals));
337 
338   PetscCall(PetscDrawLGDraw(lg));
339   PetscCall(PetscDrawLGDestroy(&lg));
340   PetscFunctionReturn(PETSC_SUCCESS);
341 }
342 
343 static PetscErrorCode VecView_Plex_Local_Draw_1D(Vec u, PetscViewer viewer)
344 {
345   DM dm;
346 
347   PetscFunctionBegin;
348   PetscCall(VecGetDM(u, &dm));
349   PetscCall(DMPlexVecView1D(dm, 1, &u, viewer));
350   PetscFunctionReturn(PETSC_SUCCESS);
351 }
352 
353 static PetscErrorCode VecView_Plex_Local_Draw_2D(Vec v, PetscViewer viewer)
354 {
355   DM                 dm;
356   PetscSection       s;
357   PetscDraw          draw, popup;
358   DM                 cdm;
359   PetscSection       coordSection;
360   Vec                coordinates;
361   const PetscScalar *array;
362   PetscReal          lbound[3], ubound[3];
363   PetscReal          vbound[2], time;
364   PetscBool          flg;
365   PetscInt           dim, Nf, f, Nc, comp, vStart, vEnd, cStart, cEnd, c, N, level, step, w = 0;
366   const char        *name;
367   char               title[PETSC_MAX_PATH_LEN];
368 
369   PetscFunctionBegin;
370   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
371   PetscCall(VecGetDM(v, &dm));
372   PetscCall(DMGetCoordinateDim(dm, &dim));
373   PetscCall(DMGetLocalSection(dm, &s));
374   PetscCall(PetscSectionGetNumFields(s, &Nf));
375   PetscCall(DMGetCoarsenLevel(dm, &level));
376   PetscCall(DMGetCoordinateDM(dm, &cdm));
377   PetscCall(DMGetLocalSection(cdm, &coordSection));
378   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
379   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
380   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
381 
382   PetscCall(PetscObjectGetName((PetscObject)v, &name));
383   PetscCall(DMGetOutputSequenceNumber(dm, &step, &time));
384 
385   PetscCall(VecGetLocalSize(coordinates, &N));
386   PetscCall(DMGetBoundingBox(dm, lbound, ubound));
387   PetscCall(PetscDrawClear(draw));
388 
389   /* Could implement something like DMDASelectFields() */
390   for (f = 0; f < Nf; ++f) {
391     DM          fdm = dm;
392     Vec         fv  = v;
393     IS          fis;
394     char        prefix[PETSC_MAX_PATH_LEN];
395     const char *fname;
396 
397     PetscCall(PetscSectionGetFieldComponents(s, f, &Nc));
398     PetscCall(PetscSectionGetFieldName(s, f, &fname));
399 
400     if (v->hdr.prefix) PetscCall(PetscStrncpy(prefix, v->hdr.prefix, sizeof(prefix)));
401     else prefix[0] = '\0';
402     if (Nf > 1) {
403       PetscCall(DMCreateSubDM(dm, 1, &f, &fis, &fdm));
404       PetscCall(VecGetSubVector(v, fis, &fv));
405       PetscCall(PetscStrlcat(prefix, fname, sizeof(prefix)));
406       PetscCall(PetscStrlcat(prefix, "_", sizeof(prefix)));
407     }
408     for (comp = 0; comp < Nc; ++comp, ++w) {
409       PetscInt nmax = 2;
410 
411       PetscCall(PetscViewerDrawGetDraw(viewer, w, &draw));
412       if (Nc > 1) PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s_%" PetscInt_FMT " Step: %" PetscInt_FMT " Time: %.4g", name, fname, comp, step, (double)time));
413       else PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s Step: %" PetscInt_FMT " Time: %.4g", name, fname, step, (double)time));
414       PetscCall(PetscDrawSetTitle(draw, title));
415 
416       /* TODO Get max and min only for this component */
417       PetscCall(PetscOptionsGetRealArray(NULL, prefix, "-vec_view_bounds", vbound, &nmax, &flg));
418       if (!flg) {
419         PetscCall(VecMin(fv, NULL, &vbound[0]));
420         PetscCall(VecMax(fv, NULL, &vbound[1]));
421         if (vbound[1] <= vbound[0]) vbound[1] = vbound[0] + 1.0;
422       }
423 
424       PetscCall(PetscDrawGetPopup(draw, &popup));
425       PetscCall(PetscDrawScalePopup(popup, vbound[0], vbound[1]));
426       PetscCall(PetscDrawSetCoordinates(draw, lbound[0], lbound[1], ubound[0], ubound[1]));
427       PetscCall(VecGetArrayRead(fv, &array));
428       for (c = cStart; c < cEnd; ++c) {
429         PetscScalar       *coords = NULL, *a = NULL;
430         const PetscScalar *coords_arr;
431         PetscBool          isDG;
432         PetscInt           numCoords, color[4] = {-1, -1, -1, -1};
433 
434         PetscCall(DMPlexPointLocalRead(fdm, c, array, &a));
435         if (a) {
436           color[0] = PetscDrawRealToColor(PetscRealPart(a[comp]), vbound[0], vbound[1]);
437           color[1] = color[2] = color[3] = color[0];
438         } else {
439           PetscScalar *vals = NULL;
440           PetscInt     numVals, va;
441 
442           PetscCall(DMPlexVecGetClosure(fdm, NULL, fv, c, &numVals, &vals));
443           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);
444           switch (numVals / Nc) {
445           case 3: /* P1 Triangle */
446           case 4: /* P1 Quadrangle */
447             for (va = 0; va < numVals / Nc; ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp]), vbound[0], vbound[1]);
448             break;
449           case 6: /* P2 Triangle */
450           case 8: /* P2 Quadrangle */
451             for (va = 0; va < numVals / (Nc * 2); ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp + numVals / (Nc * 2)]), vbound[0], vbound[1]);
452             break;
453           default:
454             SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of values for cell closure %" PetscInt_FMT " cannot be handled", numVals / Nc);
455           }
456           PetscCall(DMPlexVecRestoreClosure(fdm, NULL, fv, c, &numVals, &vals));
457         }
458         PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
459         switch (numCoords) {
460         case 6:
461         case 12: /* Localized triangle */
462           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]));
463           break;
464         case 8:
465         case 16: /* Localized quadrilateral */
466           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]));
467           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]));
468           break;
469         default:
470           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells with %" PetscInt_FMT " coordinates", numCoords);
471         }
472         PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
473       }
474       PetscCall(VecRestoreArrayRead(fv, &array));
475       PetscCall(PetscDrawFlush(draw));
476       PetscCall(PetscDrawPause(draw));
477       PetscCall(PetscDrawSave(draw));
478     }
479     if (Nf > 1) {
480       PetscCall(VecRestoreSubVector(v, fis, &fv));
481       PetscCall(ISDestroy(&fis));
482       PetscCall(DMDestroy(&fdm));
483     }
484   }
485   PetscFunctionReturn(PETSC_SUCCESS);
486 }
487 
488 static PetscErrorCode VecView_Plex_Local_Draw(Vec v, PetscViewer viewer)
489 {
490   DM        dm;
491   PetscDraw draw;
492   PetscInt  dim;
493   PetscBool isnull;
494 
495   PetscFunctionBegin;
496   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
497   PetscCall(PetscDrawIsNull(draw, &isnull));
498   if (isnull) PetscFunctionReturn(PETSC_SUCCESS);
499 
500   PetscCall(VecGetDM(v, &dm));
501   PetscCall(DMGetCoordinateDim(dm, &dim));
502   switch (dim) {
503   case 1:
504     PetscCall(VecView_Plex_Local_Draw_1D(v, viewer));
505     break;
506   case 2:
507     PetscCall(VecView_Plex_Local_Draw_2D(v, viewer));
508     break;
509   default:
510     SETERRQ(PetscObjectComm((PetscObject)v), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT ". Try PETSCVIEWERGLVIS", dim);
511   }
512   PetscFunctionReturn(PETSC_SUCCESS);
513 }
514 
515 static PetscErrorCode VecView_Plex_Local_VTK(Vec v, PetscViewer viewer)
516 {
517   DM                      dm;
518   Vec                     locv;
519   const char             *name;
520   PetscSection            section;
521   PetscInt                pStart, pEnd;
522   PetscInt                numFields;
523   PetscViewerVTKFieldType ft;
524 
525   PetscFunctionBegin;
526   PetscCall(VecGetDM(v, &dm));
527   PetscCall(DMCreateLocalVector(dm, &locv)); /* VTK viewer requires exclusive ownership of the vector */
528   PetscCall(PetscObjectGetName((PetscObject)v, &name));
529   PetscCall(PetscObjectSetName((PetscObject)locv, name));
530   PetscCall(VecCopy(v, locv));
531   PetscCall(DMGetLocalSection(dm, &section));
532   PetscCall(PetscSectionGetNumFields(section, &numFields));
533   if (!numFields) {
534     PetscCall(DMPlexGetFieldType_Internal(dm, section, PETSC_DETERMINE, &pStart, &pEnd, &ft));
535     PetscCall(PetscViewerVTKAddField(viewer, (PetscObject)dm, DMPlexVTKWriteAll, PETSC_DEFAULT, ft, PETSC_TRUE, (PetscObject)locv));
536   } else {
537     PetscInt f;
538 
539     for (f = 0; f < numFields; f++) {
540       PetscCall(DMPlexGetFieldType_Internal(dm, section, f, &pStart, &pEnd, &ft));
541       if (ft == PETSC_VTK_INVALID) continue;
542       PetscCall(PetscObjectReference((PetscObject)locv));
543       PetscCall(PetscViewerVTKAddField(viewer, (PetscObject)dm, DMPlexVTKWriteAll, f, ft, PETSC_TRUE, (PetscObject)locv));
544     }
545     PetscCall(VecDestroy(&locv));
546   }
547   PetscFunctionReturn(PETSC_SUCCESS);
548 }
549 
550 PetscErrorCode VecView_Plex_Local(Vec v, PetscViewer viewer)
551 {
552   DM        dm;
553   PetscBool isvtk, ishdf5, isdraw, isglvis, iscgns;
554 
555   PetscFunctionBegin;
556   PetscCall(VecGetDM(v, &dm));
557   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
558   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
559   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
560   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
561   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
562   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
563   if (isvtk || ishdf5 || isdraw || isglvis || iscgns) {
564     PetscInt    i, numFields;
565     PetscObject fe;
566     PetscBool   fem  = PETSC_FALSE;
567     Vec         locv = v;
568     const char *name;
569     PetscInt    step;
570     PetscReal   time;
571 
572     PetscCall(DMGetNumFields(dm, &numFields));
573     for (i = 0; i < numFields; i++) {
574       PetscCall(DMGetField(dm, i, NULL, &fe));
575       if (fe->classid == PETSCFE_CLASSID) {
576         fem = PETSC_TRUE;
577         break;
578       }
579     }
580     if (fem) {
581       PetscObject isZero;
582 
583       PetscCall(DMGetLocalVector(dm, &locv));
584       PetscCall(PetscObjectGetName((PetscObject)v, &name));
585       PetscCall(PetscObjectSetName((PetscObject)locv, name));
586       PetscCall(PetscObjectQuery((PetscObject)v, "__Vec_bc_zero__", &isZero));
587       PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", isZero));
588       PetscCall(VecCopy(v, locv));
589       PetscCall(DMGetOutputSequenceNumber(dm, NULL, &time));
590       PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_TRUE, locv, time, NULL, NULL, NULL));
591     }
592     if (isvtk) {
593       PetscCall(VecView_Plex_Local_VTK(locv, viewer));
594     } else if (ishdf5) {
595 #if defined(PETSC_HAVE_HDF5)
596       PetscCall(VecView_Plex_Local_HDF5_Internal(locv, viewer));
597 #else
598       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
599 #endif
600     } else if (isdraw) {
601       PetscCall(VecView_Plex_Local_Draw(locv, viewer));
602     } else if (isglvis) {
603       PetscCall(DMGetOutputSequenceNumber(dm, &step, NULL));
604       PetscCall(PetscViewerGLVisSetSnapId(viewer, step));
605       PetscCall(VecView_GLVis(locv, viewer));
606     } else if (iscgns) {
607 #if defined(PETSC_HAVE_CGNS)
608       PetscCall(VecView_Plex_Local_CGNS(locv, viewer));
609 #else
610       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "CGNS not supported in this build.\nPlease reconfigure using --download-cgns");
611 #endif
612     }
613     if (fem) {
614       PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", NULL));
615       PetscCall(DMRestoreLocalVector(dm, &locv));
616     }
617   } else {
618     PetscBool isseq;
619 
620     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
621     if (isseq) PetscCall(VecView_Seq(v, viewer));
622     else PetscCall(VecView_MPI(v, viewer));
623   }
624   PetscFunctionReturn(PETSC_SUCCESS);
625 }
626 
627 PetscErrorCode VecView_Plex(Vec v, PetscViewer viewer)
628 {
629   DM        dm;
630   PetscBool isvtk, ishdf5, isdraw, isglvis, isexodusii, iscgns;
631 
632   PetscFunctionBegin;
633   PetscCall(VecGetDM(v, &dm));
634   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
635   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
636   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
637   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
638   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
639   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
640   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodusii));
641   if (isvtk || isdraw || isglvis || iscgns) {
642     Vec         locv;
643     PetscObject isZero;
644     const char *name;
645 
646     PetscCall(DMGetLocalVector(dm, &locv));
647     PetscCall(PetscObjectGetName((PetscObject)v, &name));
648     PetscCall(PetscObjectSetName((PetscObject)locv, name));
649     PetscCall(DMGlobalToLocalBegin(dm, v, INSERT_VALUES, locv));
650     PetscCall(DMGlobalToLocalEnd(dm, v, INSERT_VALUES, locv));
651     PetscCall(PetscObjectQuery((PetscObject)v, "__Vec_bc_zero__", &isZero));
652     PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", isZero));
653     PetscCall(VecView_Plex_Local(locv, viewer));
654     PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", NULL));
655     PetscCall(DMRestoreLocalVector(dm, &locv));
656   } else if (ishdf5) {
657 #if defined(PETSC_HAVE_HDF5)
658     PetscCall(VecView_Plex_HDF5_Internal(v, viewer));
659 #else
660     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
661 #endif
662   } else if (isexodusii) {
663 #if defined(PETSC_HAVE_EXODUSII)
664     PetscCall(VecView_PlexExodusII_Internal(v, viewer));
665 #else
666     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
667 #endif
668   } else {
669     PetscBool isseq;
670 
671     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
672     if (isseq) PetscCall(VecView_Seq(v, viewer));
673     else PetscCall(VecView_MPI(v, viewer));
674   }
675   PetscFunctionReturn(PETSC_SUCCESS);
676 }
677 
678 PetscErrorCode VecView_Plex_Native(Vec originalv, PetscViewer viewer)
679 {
680   DM                dm;
681   MPI_Comm          comm;
682   PetscViewerFormat format;
683   Vec               v;
684   PetscBool         isvtk, ishdf5;
685 
686   PetscFunctionBegin;
687   PetscCall(VecGetDM(originalv, &dm));
688   PetscCall(PetscObjectGetComm((PetscObject)originalv, &comm));
689   PetscCheck(dm, comm, PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
690   PetscCall(PetscViewerGetFormat(viewer, &format));
691   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
692   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
693   if (format == PETSC_VIEWER_NATIVE) {
694     /* Natural ordering is the common case for DMDA, NATIVE means plain vector, for PLEX is the opposite */
695     /* this need a better fix */
696     if (dm->useNatural) {
697       if (dm->sfNatural) {
698         const char *vecname;
699         PetscInt    n, nroots;
700 
701         PetscCall(VecGetLocalSize(originalv, &n));
702         PetscCall(PetscSFGetGraph(dm->sfNatural, &nroots, NULL, NULL, NULL));
703         if (n == nroots) {
704           PetscCall(DMPlexCreateNaturalVector(dm, &v));
705           PetscCall(DMPlexGlobalToNaturalBegin(dm, originalv, v));
706           PetscCall(DMPlexGlobalToNaturalEnd(dm, originalv, v));
707           PetscCall(PetscObjectGetName((PetscObject)originalv, &vecname));
708           PetscCall(PetscObjectSetName((PetscObject)v, vecname));
709         } else SETERRQ(comm, PETSC_ERR_ARG_WRONG, "DM global to natural SF only handles global vectors");
710       } else SETERRQ(comm, PETSC_ERR_ARG_WRONGSTATE, "DM global to natural SF was not created");
711     } else v = originalv;
712   } else v = originalv;
713 
714   if (ishdf5) {
715 #if defined(PETSC_HAVE_HDF5)
716     PetscCall(VecView_Plex_HDF5_Native_Internal(v, viewer));
717 #else
718     SETERRQ(comm, PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
719 #endif
720   } else if (isvtk) {
721     SETERRQ(comm, PETSC_ERR_SUP, "VTK format does not support viewing in natural order. Please switch to HDF5.");
722   } else {
723     PetscBool isseq;
724 
725     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
726     if (isseq) PetscCall(VecView_Seq(v, viewer));
727     else PetscCall(VecView_MPI(v, viewer));
728   }
729   if (v != originalv) PetscCall(VecDestroy(&v));
730   PetscFunctionReturn(PETSC_SUCCESS);
731 }
732 
733 PetscErrorCode VecLoad_Plex_Local(Vec v, PetscViewer viewer)
734 {
735   DM        dm;
736   PetscBool ishdf5;
737 
738   PetscFunctionBegin;
739   PetscCall(VecGetDM(v, &dm));
740   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
741   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
742   if (ishdf5) {
743     DM          dmBC;
744     Vec         gv;
745     const char *name;
746 
747     PetscCall(DMGetOutputDM(dm, &dmBC));
748     PetscCall(DMGetGlobalVector(dmBC, &gv));
749     PetscCall(PetscObjectGetName((PetscObject)v, &name));
750     PetscCall(PetscObjectSetName((PetscObject)gv, name));
751     PetscCall(VecLoad_Default(gv, viewer));
752     PetscCall(DMGlobalToLocalBegin(dmBC, gv, INSERT_VALUES, v));
753     PetscCall(DMGlobalToLocalEnd(dmBC, gv, INSERT_VALUES, v));
754     PetscCall(DMRestoreGlobalVector(dmBC, &gv));
755   } else PetscCall(VecLoad_Default(v, viewer));
756   PetscFunctionReturn(PETSC_SUCCESS);
757 }
758 
759 PetscErrorCode VecLoad_Plex(Vec v, PetscViewer viewer)
760 {
761   DM        dm;
762   PetscBool ishdf5, isexodusii;
763 
764   PetscFunctionBegin;
765   PetscCall(VecGetDM(v, &dm));
766   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
767   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
768   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodusii));
769   if (ishdf5) {
770 #if defined(PETSC_HAVE_HDF5)
771     PetscCall(VecLoad_Plex_HDF5_Internal(v, viewer));
772 #else
773     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
774 #endif
775   } else if (isexodusii) {
776 #if defined(PETSC_HAVE_EXODUSII)
777     PetscCall(VecLoad_PlexExodusII_Internal(v, viewer));
778 #else
779     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
780 #endif
781   } else PetscCall(VecLoad_Default(v, viewer));
782   PetscFunctionReturn(PETSC_SUCCESS);
783 }
784 
785 PetscErrorCode VecLoad_Plex_Native(Vec originalv, PetscViewer viewer)
786 {
787   DM                dm;
788   PetscViewerFormat format;
789   PetscBool         ishdf5;
790 
791   PetscFunctionBegin;
792   PetscCall(VecGetDM(originalv, &dm));
793   PetscCheck(dm, PetscObjectComm((PetscObject)originalv), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
794   PetscCall(PetscViewerGetFormat(viewer, &format));
795   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
796   if (format == PETSC_VIEWER_NATIVE) {
797     if (dm->useNatural) {
798       if (dm->sfNatural) {
799         if (ishdf5) {
800 #if defined(PETSC_HAVE_HDF5)
801           Vec         v;
802           const char *vecname;
803 
804           PetscCall(DMPlexCreateNaturalVector(dm, &v));
805           PetscCall(PetscObjectGetName((PetscObject)originalv, &vecname));
806           PetscCall(PetscObjectSetName((PetscObject)v, vecname));
807           PetscCall(VecLoad_Plex_HDF5_Native_Internal(v, viewer));
808           PetscCall(DMPlexNaturalToGlobalBegin(dm, v, originalv));
809           PetscCall(DMPlexNaturalToGlobalEnd(dm, v, originalv));
810           PetscCall(VecDestroy(&v));
811 #else
812           SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
813 #endif
814         } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Reading in natural order is not supported for anything but HDF5.");
815       }
816     } else PetscCall(VecLoad_Default(originalv, viewer));
817   }
818   PetscFunctionReturn(PETSC_SUCCESS);
819 }
820 
821 PETSC_UNUSED static PetscErrorCode DMPlexView_Ascii_Geometry(DM dm, PetscViewer viewer)
822 {
823   PetscSection       coordSection;
824   Vec                coordinates;
825   DMLabel            depthLabel, celltypeLabel;
826   const char        *name[4];
827   const PetscScalar *a;
828   PetscInt           dim, pStart, pEnd, cStart, cEnd, c;
829 
830   PetscFunctionBegin;
831   PetscCall(DMGetDimension(dm, &dim));
832   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
833   PetscCall(DMGetCoordinateSection(dm, &coordSection));
834   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
835   PetscCall(DMPlexGetCellTypeLabel(dm, &celltypeLabel));
836   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
837   PetscCall(PetscSectionGetChart(coordSection, &pStart, &pEnd));
838   PetscCall(VecGetArrayRead(coordinates, &a));
839   name[0]       = "vertex";
840   name[1]       = "edge";
841   name[dim - 1] = "face";
842   name[dim]     = "cell";
843   for (c = cStart; c < cEnd; ++c) {
844     PetscInt *closure = NULL;
845     PetscInt  closureSize, cl, ct;
846 
847     PetscCall(DMLabelGetValue(celltypeLabel, c, &ct));
848     PetscCall(PetscViewerASCIIPrintf(viewer, "Geometry for cell %" PetscInt_FMT " polytope type %s:\n", c, DMPolytopeTypes[ct]));
849     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
850     PetscCall(PetscViewerASCIIPushTab(viewer));
851     for (cl = 0; cl < closureSize * 2; cl += 2) {
852       PetscInt point = closure[cl], depth, dof, off, d, p;
853 
854       if ((point < pStart) || (point >= pEnd)) continue;
855       PetscCall(PetscSectionGetDof(coordSection, point, &dof));
856       if (!dof) continue;
857       PetscCall(DMLabelGetValue(depthLabel, point, &depth));
858       PetscCall(PetscSectionGetOffset(coordSection, point, &off));
859       PetscCall(PetscViewerASCIIPrintf(viewer, "%s %" PetscInt_FMT " coords:", name[depth], point));
860       for (p = 0; p < dof / dim; ++p) {
861         PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
862         for (d = 0; d < dim; ++d) {
863           if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
864           PetscCall(PetscViewerASCIIPrintf(viewer, "%g", (double)PetscRealPart(a[off + p * dim + d])));
865         }
866         PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
867       }
868       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
869     }
870     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
871     PetscCall(PetscViewerASCIIPopTab(viewer));
872   }
873   PetscCall(VecRestoreArrayRead(coordinates, &a));
874   PetscFunctionReturn(PETSC_SUCCESS);
875 }
876 
877 typedef enum {
878   CS_CARTESIAN,
879   CS_POLAR,
880   CS_CYLINDRICAL,
881   CS_SPHERICAL
882 } CoordSystem;
883 const char *CoordSystems[] = {"cartesian", "polar", "cylindrical", "spherical", "CoordSystem", "CS_", NULL};
884 
885 static PetscErrorCode DMPlexView_Ascii_Coordinates(PetscViewer viewer, CoordSystem cs, PetscInt dim, const PetscScalar x[])
886 {
887   PetscInt i;
888 
889   PetscFunctionBegin;
890   if (dim > 3) {
891     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double)PetscRealPart(x[i])));
892   } else {
893     PetscReal coords[3], trcoords[3] = {0., 0., 0.};
894 
895     for (i = 0; i < dim; ++i) coords[i] = PetscRealPart(x[i]);
896     switch (cs) {
897     case CS_CARTESIAN:
898       for (i = 0; i < dim; ++i) trcoords[i] = coords[i];
899       break;
900     case CS_POLAR:
901       PetscCheck(dim == 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Polar coordinates are for 2 dimension, not %" PetscInt_FMT, dim);
902       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
903       trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
904       break;
905     case CS_CYLINDRICAL:
906       PetscCheck(dim == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cylindrical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
907       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
908       trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
909       trcoords[2] = coords[2];
910       break;
911     case CS_SPHERICAL:
912       PetscCheck(dim == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Spherical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
913       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]) + PetscSqr(coords[2]));
914       trcoords[1] = PetscAtan2Real(PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1])), coords[2]);
915       trcoords[2] = PetscAtan2Real(coords[1], coords[0]);
916       break;
917     }
918     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double)trcoords[i]));
919   }
920   PetscFunctionReturn(PETSC_SUCCESS);
921 }
922 
923 static PetscErrorCode DMPlexView_Ascii(DM dm, PetscViewer viewer)
924 {
925   DM_Plex          *mesh = (DM_Plex *)dm->data;
926   DM                cdm, cdmCell;
927   PetscSection      coordSection, coordSectionCell;
928   Vec               coordinates, coordinatesCell;
929   PetscViewerFormat format;
930 
931   PetscFunctionBegin;
932   PetscCall(PetscViewerGetFormat(viewer, &format));
933   if (format == PETSC_VIEWER_ASCII_INFO_DETAIL) {
934     const char *name;
935     PetscInt    dim, cellHeight, maxConeSize, maxSupportSize;
936     PetscInt    pStart, pEnd, p, numLabels, l;
937     PetscMPIInt rank, size;
938 
939     PetscCall(DMGetCoordinateDM(dm, &cdm));
940     PetscCall(DMGetCoordinateSection(dm, &coordSection));
941     PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
942     PetscCall(DMGetCellCoordinateDM(dm, &cdmCell));
943     PetscCall(DMGetCellCoordinateSection(dm, &coordSectionCell));
944     PetscCall(DMGetCellCoordinatesLocal(dm, &coordinatesCell));
945     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
946     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
947     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
948     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
949     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
950     PetscCall(DMGetDimension(dm, &dim));
951     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
952     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
953     else PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
954     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
955     PetscCall(PetscViewerASCIIPrintf(viewer, "Supports:\n"));
956     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
957     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max support size: %" PetscInt_FMT "\n", rank, maxSupportSize));
958     for (p = pStart; p < pEnd; ++p) {
959       PetscInt dof, off, s;
960 
961       PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
962       PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
963       for (s = off; s < off + dof; ++s) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d]: %" PetscInt_FMT " ----> %" PetscInt_FMT "\n", rank, p, mesh->supports[s]));
964     }
965     PetscCall(PetscViewerFlush(viewer));
966     PetscCall(PetscViewerASCIIPrintf(viewer, "Cones:\n"));
967     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max cone size: %" PetscInt_FMT "\n", rank, maxConeSize));
968     for (p = pStart; p < pEnd; ++p) {
969       PetscInt dof, off, c;
970 
971       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
972       PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
973       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]));
974     }
975     PetscCall(PetscViewerFlush(viewer));
976     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
977     if (coordSection && coordinates) {
978       CoordSystem        cs = CS_CARTESIAN;
979       const PetscScalar *array, *arrayCell = NULL;
980       PetscInt           Nf, Nc, pvStart, pvEnd, pcStart = PETSC_MAX_INT, pcEnd = PETSC_MIN_INT, pStart, pEnd, p;
981       PetscMPIInt        rank;
982       const char        *name;
983 
984       PetscCall(PetscOptionsGetEnum(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_coord_system", CoordSystems, (PetscEnum *)&cs, NULL));
985       PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)viewer), &rank));
986       PetscCall(PetscSectionGetNumFields(coordSection, &Nf));
987       PetscCheck(Nf == 1, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Coordinate section should have 1 field, not %" PetscInt_FMT, Nf);
988       PetscCall(PetscSectionGetFieldComponents(coordSection, 0, &Nc));
989       PetscCall(PetscSectionGetChart(coordSection, &pvStart, &pvEnd));
990       if (coordSectionCell) PetscCall(PetscSectionGetChart(coordSectionCell, &pcStart, &pcEnd));
991       pStart = PetscMin(pvStart, pcStart);
992       pEnd   = PetscMax(pvEnd, pcEnd);
993       PetscCall(PetscObjectGetName((PetscObject)coordinates, &name));
994       PetscCall(PetscViewerASCIIPrintf(viewer, "%s with %" PetscInt_FMT " fields\n", name, Nf));
995       PetscCall(PetscViewerASCIIPrintf(viewer, "  field 0 with %" PetscInt_FMT " components\n", Nc));
996       if (cs != CS_CARTESIAN) PetscCall(PetscViewerASCIIPrintf(viewer, "  output coordinate system: %s\n", CoordSystems[cs]));
997 
998       PetscCall(VecGetArrayRead(coordinates, &array));
999       if (coordinatesCell) PetscCall(VecGetArrayRead(coordinatesCell, &arrayCell));
1000       PetscCall(PetscViewerASCIIPushSynchronized(viewer));
1001       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "Process %d:\n", rank));
1002       for (p = pStart; p < pEnd; ++p) {
1003         PetscInt dof, off;
1004 
1005         if (p >= pvStart && p < pvEnd) {
1006           PetscCall(PetscSectionGetDof(coordSection, p, &dof));
1007           PetscCall(PetscSectionGetOffset(coordSection, p, &off));
1008           if (dof) {
1009             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dim %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
1010             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &array[off]));
1011             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
1012           }
1013         }
1014         if (cdmCell && p >= pcStart && p < pcEnd) {
1015           PetscCall(PetscSectionGetDof(coordSectionCell, p, &dof));
1016           PetscCall(PetscSectionGetOffset(coordSectionCell, p, &off));
1017           if (dof) {
1018             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dim %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
1019             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &arrayCell[off]));
1020             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
1021           }
1022         }
1023       }
1024       PetscCall(PetscViewerFlush(viewer));
1025       PetscCall(PetscViewerASCIIPopSynchronized(viewer));
1026       PetscCall(VecRestoreArrayRead(coordinates, &array));
1027       if (coordinatesCell) PetscCall(VecRestoreArrayRead(coordinatesCell, &arrayCell));
1028     }
1029     PetscCall(DMGetNumLabels(dm, &numLabels));
1030     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
1031     for (l = 0; l < numLabels; ++l) {
1032       DMLabel     label;
1033       PetscBool   isdepth;
1034       const char *name;
1035 
1036       PetscCall(DMGetLabelName(dm, l, &name));
1037       PetscCall(PetscStrcmp(name, "depth", &isdepth));
1038       if (isdepth) continue;
1039       PetscCall(DMGetLabel(dm, name, &label));
1040       PetscCall(DMLabelView(label, viewer));
1041     }
1042     if (size > 1) {
1043       PetscSF sf;
1044 
1045       PetscCall(DMGetPointSF(dm, &sf));
1046       PetscCall(PetscSFView(sf, viewer));
1047     }
1048     if (mesh->periodic.face_sfs)
1049       for (PetscInt i = 0; i < mesh->periodic.num_face_sfs; i++) PetscCall(PetscSFView(mesh->periodic.face_sfs[i], viewer));
1050     PetscCall(PetscViewerFlush(viewer));
1051   } else if (format == PETSC_VIEWER_ASCII_LATEX) {
1052     const char  *name, *color;
1053     const char  *defcolors[3]  = {"gray", "orange", "green"};
1054     const char  *deflcolors[4] = {"blue", "cyan", "red", "magenta"};
1055     char         lname[PETSC_MAX_PATH_LEN];
1056     PetscReal    scale      = 2.0;
1057     PetscReal    tikzscale  = 1.0;
1058     PetscBool    useNumbers = PETSC_TRUE, drawNumbers[4], drawColors[4], useLabels, useColors, plotEdges, drawHasse = PETSC_FALSE;
1059     double       tcoords[3];
1060     PetscScalar *coords;
1061     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;
1062     PetscMPIInt  rank, size;
1063     char       **names, **colors, **lcolors;
1064     PetscBool    flg, lflg;
1065     PetscBT      wp = NULL;
1066     PetscInt     pEnd, pStart;
1067 
1068     PetscCall(DMGetCoordinateDM(dm, &cdm));
1069     PetscCall(DMGetCoordinateSection(dm, &coordSection));
1070     PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1071     PetscCall(DMGetCellCoordinateDM(dm, &cdmCell));
1072     PetscCall(DMGetCellCoordinateSection(dm, &coordSectionCell));
1073     PetscCall(DMGetCellCoordinatesLocal(dm, &coordinatesCell));
1074     PetscCall(DMGetDimension(dm, &dim));
1075     PetscCall(DMPlexGetDepth(dm, &depth));
1076     PetscCall(DMGetNumLabels(dm, &numLabels));
1077     numLabels  = PetscMax(numLabels, 10);
1078     numColors  = 10;
1079     numLColors = 10;
1080     PetscCall(PetscCalloc3(numLabels, &names, numColors, &colors, numLColors, &lcolors));
1081     PetscCall(PetscOptionsGetReal(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_scale", &scale, NULL));
1082     PetscCall(PetscOptionsGetReal(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_tikzscale", &tikzscale, NULL));
1083     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_numbers", &useNumbers, NULL));
1084     for (d = 0; d < 4; ++d) drawNumbers[d] = useNumbers;
1085     for (d = 0; d < 4; ++d) drawColors[d] = PETSC_TRUE;
1086     n = 4;
1087     PetscCall(PetscOptionsGetBoolArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_numbers_depth", drawNumbers, &n, &flg));
1088     PetscCheck(!flg || n == dim + 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim + 1);
1089     n = 4;
1090     PetscCall(PetscOptionsGetBoolArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_colors_depth", drawColors, &n, &flg));
1091     PetscCheck(!flg || n == dim + 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim + 1);
1092     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_labels", names, &numLabels, &useLabels));
1093     if (!useLabels) numLabels = 0;
1094     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_colors", colors, &numColors, &useColors));
1095     if (!useColors) {
1096       numColors = 3;
1097       for (c = 0; c < numColors; ++c) PetscCall(PetscStrallocpy(defcolors[c], &colors[c]));
1098     }
1099     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_lcolors", lcolors, &numLColors, &useColors));
1100     if (!useColors) {
1101       numLColors = 4;
1102       for (c = 0; c < numLColors; ++c) PetscCall(PetscStrallocpy(deflcolors[c], &lcolors[c]));
1103     }
1104     PetscCall(PetscOptionsGetString(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_label_filter", lname, sizeof(lname), &lflg));
1105     plotEdges = (PetscBool)(depth > 1 && drawNumbers[1] && dim < 3);
1106     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_edges", &plotEdges, &flg));
1107     PetscCheck(!flg || !plotEdges || depth >= dim, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Mesh must be interpolated");
1108     if (depth < dim) plotEdges = PETSC_FALSE;
1109     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_hasse", &drawHasse, NULL));
1110 
1111     /* filter points with labelvalue != labeldefaultvalue */
1112     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
1113     PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1114     PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
1115     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1116     PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd));
1117     if (lflg) {
1118       DMLabel lbl;
1119 
1120       PetscCall(DMGetLabel(dm, lname, &lbl));
1121       if (lbl) {
1122         PetscInt val, defval;
1123 
1124         PetscCall(DMLabelGetDefaultValue(lbl, &defval));
1125         PetscCall(PetscBTCreate(pEnd - pStart, &wp));
1126         for (c = pStart; c < pEnd; c++) {
1127           PetscInt *closure = NULL;
1128           PetscInt  closureSize;
1129 
1130           PetscCall(DMLabelGetValue(lbl, c, &val));
1131           if (val == defval) continue;
1132 
1133           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1134           for (p = 0; p < closureSize * 2; p += 2) PetscCall(PetscBTSet(wp, closure[p] - pStart));
1135           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1136         }
1137       }
1138     }
1139 
1140     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1141     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
1142     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1143     PetscCall(PetscViewerASCIIPrintf(viewer, "\
1144 \\documentclass[tikz]{standalone}\n\n\
1145 \\usepackage{pgflibraryshapes}\n\
1146 \\usetikzlibrary{backgrounds}\n\
1147 \\usetikzlibrary{arrows}\n\
1148 \\begin{document}\n"));
1149     if (size > 1) {
1150       PetscCall(PetscViewerASCIIPrintf(viewer, "%s for process ", name));
1151       for (p = 0; p < size; ++p) {
1152         if (p) PetscCall(PetscViewerASCIIPrintf(viewer, (p == size - 1) ? ", and " : ", "));
1153         PetscCall(PetscViewerASCIIPrintf(viewer, "{\\textcolor{%s}%" PetscInt_FMT "}", colors[p % numColors], p));
1154       }
1155       PetscCall(PetscViewerASCIIPrintf(viewer, ".\n\n\n"));
1156     }
1157     if (drawHasse) {
1158       PetscInt maxStratum = PetscMax(vEnd - vStart, PetscMax(eEnd - eStart, PetscMax(fEnd - fStart, cEnd - cStart)));
1159 
1160       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vStart}{%" PetscInt_FMT "}\n", vStart));
1161       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vEnd}{%" PetscInt_FMT "}\n", vEnd - 1));
1162       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numVertices}{%" PetscInt_FMT "}\n", vEnd - vStart));
1163       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vShift}{%.2f}\n", 3 + (maxStratum - (vEnd - vStart)) / 2.));
1164       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eStart}{%" PetscInt_FMT "}\n", eStart));
1165       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eEnd}{%" PetscInt_FMT "}\n", eEnd - 1));
1166       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eShift}{%.2f}\n", 3 + (maxStratum - (eEnd - eStart)) / 2.));
1167       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numEdges}{%" PetscInt_FMT "}\n", eEnd - eStart));
1168       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\fStart}{%" PetscInt_FMT "}\n", fStart));
1169       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\fEnd}{%" PetscInt_FMT "}\n", fEnd - 1));
1170       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\fShift}{%.2f}\n", 3 + (maxStratum - (fEnd - fStart)) / 2.));
1171       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numFaces}{%" PetscInt_FMT "}\n", fEnd - fStart));
1172       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cStart}{%" PetscInt_FMT "}\n", cStart));
1173       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cEnd}{%" PetscInt_FMT "}\n", cEnd - 1));
1174       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numCells}{%" PetscInt_FMT "}\n", cEnd - cStart));
1175       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cShift}{%.2f}\n", 3 + (maxStratum - (cEnd - cStart)) / 2.));
1176     }
1177     PetscCall(PetscViewerASCIIPrintf(viewer, "\\begin{tikzpicture}[scale = %g,font=\\fontsize{8}{8}\\selectfont]\n", (double)tikzscale));
1178 
1179     /* Plot vertices */
1180     PetscCall(VecGetArray(coordinates, &coords));
1181     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
1182     for (v = vStart; v < vEnd; ++v) {
1183       PetscInt  off, dof, d;
1184       PetscBool isLabeled = PETSC_FALSE;
1185 
1186       if (wp && !PetscBTLookup(wp, v - pStart)) continue;
1187       PetscCall(PetscSectionGetDof(coordSection, v, &dof));
1188       PetscCall(PetscSectionGetOffset(coordSection, v, &off));
1189       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1190       PetscCheck(dof <= 3, PETSC_COMM_SELF, PETSC_ERR_PLIB, "coordSection vertex %" PetscInt_FMT " has dof %" PetscInt_FMT " > 3", v, dof);
1191       for (d = 0; d < dof; ++d) {
1192         tcoords[d] = (double)(scale * PetscRealPart(coords[off + d]));
1193         tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1194       }
1195       /* Rotate coordinates since PGF makes z point out of the page instead of up */
1196       if (dim == 3) {
1197         PetscReal tmp = tcoords[1];
1198         tcoords[1]    = tcoords[2];
1199         tcoords[2]    = -tmp;
1200       }
1201       for (d = 0; d < dof; ++d) {
1202         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1203         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)tcoords[d]));
1204       }
1205       if (drawHasse) color = colors[0 % numColors];
1206       else color = colors[rank % numColors];
1207       for (l = 0; l < numLabels; ++l) {
1208         PetscInt val;
1209         PetscCall(DMGetLabelValue(dm, names[l], v, &val));
1210         if (val >= 0) {
1211           color     = lcolors[l % numLColors];
1212           isLabeled = PETSC_TRUE;
1213           break;
1214         }
1215       }
1216       if (drawNumbers[0]) {
1217         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", v, rank, color, v));
1218       } else if (drawColors[0]) {
1219         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", v, rank, !isLabeled ? 1 : 2, color));
1220       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", v, rank));
1221     }
1222     PetscCall(VecRestoreArray(coordinates, &coords));
1223     PetscCall(PetscViewerFlush(viewer));
1224     /* Plot edges */
1225     if (plotEdges) {
1226       PetscCall(VecGetArray(coordinates, &coords));
1227       PetscCall(PetscViewerASCIIPrintf(viewer, "\\path\n"));
1228       for (e = eStart; e < eEnd; ++e) {
1229         const PetscInt *cone;
1230         PetscInt        coneSize, offA, offB, dof, d;
1231 
1232         if (wp && !PetscBTLookup(wp, e - pStart)) continue;
1233         PetscCall(DMPlexGetConeSize(dm, e, &coneSize));
1234         PetscCheck(coneSize == 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Edge %" PetscInt_FMT " cone should have two vertices, not %" PetscInt_FMT, e, coneSize);
1235         PetscCall(DMPlexGetCone(dm, e, &cone));
1236         PetscCall(PetscSectionGetDof(coordSection, cone[0], &dof));
1237         PetscCall(PetscSectionGetOffset(coordSection, cone[0], &offA));
1238         PetscCall(PetscSectionGetOffset(coordSection, cone[1], &offB));
1239         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "("));
1240         for (d = 0; d < dof; ++d) {
1241           tcoords[d] = (double)(0.5 * scale * PetscRealPart(coords[offA + d] + coords[offB + d]));
1242           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1243         }
1244         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1245         if (dim == 3) {
1246           PetscReal tmp = tcoords[1];
1247           tcoords[1]    = tcoords[2];
1248           tcoords[2]    = -tmp;
1249         }
1250         for (d = 0; d < dof; ++d) {
1251           if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1252           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)tcoords[d]));
1253         }
1254         if (drawHasse) color = colors[1 % numColors];
1255         else color = colors[rank % numColors];
1256         for (l = 0; l < numLabels; ++l) {
1257           PetscInt val;
1258           PetscCall(DMGetLabelValue(dm, names[l], e, &val));
1259           if (val >= 0) {
1260             color = lcolors[l % numLColors];
1261             break;
1262           }
1263         }
1264         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "} --\n", e, rank, color, e));
1265       }
1266       PetscCall(VecRestoreArray(coordinates, &coords));
1267       PetscCall(PetscViewerFlush(viewer));
1268       PetscCall(PetscViewerASCIIPrintf(viewer, "(0,0);\n"));
1269     }
1270     /* Plot cells */
1271     if (dim == 3 || !drawNumbers[1]) {
1272       for (e = eStart; e < eEnd; ++e) {
1273         const PetscInt *cone;
1274 
1275         if (wp && !PetscBTLookup(wp, e - pStart)) continue;
1276         color = colors[rank % numColors];
1277         for (l = 0; l < numLabels; ++l) {
1278           PetscInt val;
1279           PetscCall(DMGetLabelValue(dm, names[l], e, &val));
1280           if (val >= 0) {
1281             color = lcolors[l % numLColors];
1282             break;
1283           }
1284         }
1285         PetscCall(DMPlexGetCone(dm, e, &cone));
1286         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", color, cone[0], rank, cone[1], rank));
1287       }
1288     } else {
1289       DMPolytopeType ct;
1290 
1291       /* Drawing a 2D polygon */
1292       for (c = cStart; c < cEnd; ++c) {
1293         if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1294         PetscCall(DMPlexGetCellType(dm, c, &ct));
1295         if (DMPolytopeTypeIsHybrid(ct)) {
1296           const PetscInt *cone;
1297           PetscInt        coneSize, e;
1298 
1299           PetscCall(DMPlexGetCone(dm, c, &cone));
1300           PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
1301           for (e = 0; e < coneSize; ++e) {
1302             const PetscInt *econe;
1303 
1304             PetscCall(DMPlexGetCone(dm, cone[e], &econe));
1305             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));
1306           }
1307         } else {
1308           PetscInt *closure = NULL;
1309           PetscInt  closureSize, Nv = 0, v;
1310 
1311           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1312           for (p = 0; p < closureSize * 2; p += 2) {
1313             const PetscInt point = closure[p];
1314 
1315             if ((point >= vStart) && (point < vEnd)) closure[Nv++] = point;
1316           }
1317           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] ", colors[rank % numColors]));
1318           for (v = 0; v <= Nv; ++v) {
1319             const PetscInt vertex = closure[v % Nv];
1320 
1321             if (v > 0) {
1322               if (plotEdges) {
1323                 const PetscInt *edge;
1324                 PetscInt        endpoints[2], ne;
1325 
1326                 endpoints[0] = closure[v - 1];
1327                 endpoints[1] = vertex;
1328                 PetscCall(DMPlexGetJoin(dm, 2, endpoints, &ne, &edge));
1329                 PetscCheck(ne == 1, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Could not find edge for vertices %" PetscInt_FMT ", %" PetscInt_FMT, endpoints[0], endpoints[1]);
1330                 PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- (%" PetscInt_FMT "_%d) -- ", edge[0], rank));
1331                 PetscCall(DMPlexRestoreJoin(dm, 2, endpoints, &ne, &edge));
1332               } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- "));
1333             }
1334             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "(%" PetscInt_FMT "_%d)", vertex, rank));
1335           }
1336           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ";\n"));
1337           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1338         }
1339       }
1340     }
1341     for (c = cStart; c < cEnd; ++c) {
1342       double             ccoords[3] = {0.0, 0.0, 0.0};
1343       PetscBool          isLabeled  = PETSC_FALSE;
1344       PetscScalar       *cellCoords = NULL;
1345       const PetscScalar *array;
1346       PetscInt           numCoords, cdim, d;
1347       PetscBool          isDG;
1348 
1349       if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1350       PetscCall(DMGetCoordinateDim(dm, &cdim));
1351       PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1352       PetscCheck(!(numCoords % cdim), PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "coordinate dim %" PetscInt_FMT " does not divide numCoords %" PetscInt_FMT, cdim, numCoords);
1353       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1354       for (p = 0; p < numCoords / cdim; ++p) {
1355         for (d = 0; d < cdim; ++d) {
1356           tcoords[d] = (double)(scale * PetscRealPart(cellCoords[p * cdim + d]));
1357           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1358         }
1359         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1360         if (cdim == 3) {
1361           PetscReal tmp = tcoords[1];
1362           tcoords[1]    = tcoords[2];
1363           tcoords[2]    = -tmp;
1364         }
1365         for (d = 0; d < dim; ++d) ccoords[d] += tcoords[d];
1366       }
1367       for (d = 0; d < cdim; ++d) ccoords[d] /= (numCoords / cdim);
1368       PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1369       for (d = 0; d < cdim; ++d) {
1370         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1371         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)ccoords[d]));
1372       }
1373       if (drawHasse) color = colors[depth % numColors];
1374       else color = colors[rank % numColors];
1375       for (l = 0; l < numLabels; ++l) {
1376         PetscInt val;
1377         PetscCall(DMGetLabelValue(dm, names[l], c, &val));
1378         if (val >= 0) {
1379           color     = lcolors[l % numLColors];
1380           isLabeled = PETSC_TRUE;
1381           break;
1382         }
1383       }
1384       if (drawNumbers[dim]) {
1385         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", c, rank, color, c));
1386       } else if (drawColors[dim]) {
1387         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", c, rank, !isLabeled ? 1 : 2, color));
1388       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", c, rank));
1389     }
1390     if (drawHasse) {
1391       int height = 0;
1392 
1393       color = colors[depth % numColors];
1394       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Cells\n"));
1395       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\c in {\\cStart,...,\\cEnd}\n"));
1396       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1397       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\c_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\cShift+\\c-\\cStart,%d) {\\c};\n", rank, color, height++));
1398       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1399 
1400       if (depth > 2) {
1401         color = colors[1 % numColors];
1402         PetscCall(PetscViewerASCIIPrintf(viewer, "%% Faces\n"));
1403         PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\f in {\\fStart,...,\\fEnd}\n"));
1404         PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1405         PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\f_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\fShift+\\f-\\fStart,%d) {\\f};\n", rank, color, height++));
1406         PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1407       }
1408 
1409       color = colors[1 % numColors];
1410       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Edges\n"));
1411       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\e in {\\eStart,...,\\eEnd}\n"));
1412       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1413       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\e_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\eShift+\\e-\\eStart,%d) {\\e};\n", rank, color, height++));
1414       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1415 
1416       color = colors[0 % numColors];
1417       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Vertices\n"));
1418       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\v in {\\vStart,...,\\vEnd}\n"));
1419       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1420       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\v_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\vShift+\\v-\\vStart,%d) {\\v};\n", rank, color, height++));
1421       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1422 
1423       for (p = pStart; p < pEnd; ++p) {
1424         const PetscInt *cone;
1425         PetscInt        coneSize, cp;
1426 
1427         PetscCall(DMPlexGetCone(dm, p, &cone));
1428         PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
1429         for (cp = 0; cp < coneSize; ++cp) PetscCall(PetscViewerASCIIPrintf(viewer, "\\draw[->, shorten >=1pt] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", cone[cp], rank, p, rank));
1430       }
1431     }
1432     PetscCall(PetscViewerFlush(viewer));
1433     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
1434     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{tikzpicture}\n"));
1435     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{document}\n"));
1436     for (l = 0; l < numLabels; ++l) PetscCall(PetscFree(names[l]));
1437     for (c = 0; c < numColors; ++c) PetscCall(PetscFree(colors[c]));
1438     for (c = 0; c < numLColors; ++c) PetscCall(PetscFree(lcolors[c]));
1439     PetscCall(PetscFree3(names, colors, lcolors));
1440     PetscCall(PetscBTDestroy(&wp));
1441   } else if (format == PETSC_VIEWER_LOAD_BALANCE) {
1442     Vec                    cown, acown;
1443     VecScatter             sct;
1444     ISLocalToGlobalMapping g2l;
1445     IS                     gid, acis;
1446     MPI_Comm               comm, ncomm = MPI_COMM_NULL;
1447     MPI_Group              ggroup, ngroup;
1448     PetscScalar           *array, nid;
1449     const PetscInt        *idxs;
1450     PetscInt              *idxs2, *start, *adjacency, *work;
1451     PetscInt64             lm[3], gm[3];
1452     PetscInt               i, c, cStart, cEnd, cum, numVertices, ect, ectn, cellHeight;
1453     PetscMPIInt            d1, d2, rank;
1454 
1455     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1456     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1457 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1458     PetscCallMPI(MPI_Comm_split_type(comm, MPI_COMM_TYPE_SHARED, rank, MPI_INFO_NULL, &ncomm));
1459 #endif
1460     if (ncomm != MPI_COMM_NULL) {
1461       PetscCallMPI(MPI_Comm_group(comm, &ggroup));
1462       PetscCallMPI(MPI_Comm_group(ncomm, &ngroup));
1463       d1 = 0;
1464       PetscCallMPI(MPI_Group_translate_ranks(ngroup, 1, &d1, ggroup, &d2));
1465       nid = d2;
1466       PetscCallMPI(MPI_Group_free(&ggroup));
1467       PetscCallMPI(MPI_Group_free(&ngroup));
1468       PetscCallMPI(MPI_Comm_free(&ncomm));
1469     } else nid = 0.0;
1470 
1471     /* Get connectivity */
1472     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1473     PetscCall(DMPlexCreatePartitionerGraph(dm, cellHeight, &numVertices, &start, &adjacency, &gid));
1474 
1475     /* filter overlapped local cells */
1476     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
1477     PetscCall(ISGetIndices(gid, &idxs));
1478     PetscCall(ISGetLocalSize(gid, &cum));
1479     PetscCall(PetscMalloc1(cum, &idxs2));
1480     for (c = cStart, cum = 0; c < cEnd; c++) {
1481       if (idxs[c - cStart] < 0) continue;
1482       idxs2[cum++] = idxs[c - cStart];
1483     }
1484     PetscCall(ISRestoreIndices(gid, &idxs));
1485     PetscCheck(numVertices == cum, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Unexpected %" PetscInt_FMT " != %" PetscInt_FMT, numVertices, cum);
1486     PetscCall(ISDestroy(&gid));
1487     PetscCall(ISCreateGeneral(comm, numVertices, idxs2, PETSC_OWN_POINTER, &gid));
1488 
1489     /* support for node-aware cell locality */
1490     PetscCall(ISCreateGeneral(comm, start[numVertices], adjacency, PETSC_USE_POINTER, &acis));
1491     PetscCall(VecCreateSeq(PETSC_COMM_SELF, start[numVertices], &acown));
1492     PetscCall(VecCreateMPI(comm, numVertices, PETSC_DECIDE, &cown));
1493     PetscCall(VecGetArray(cown, &array));
1494     for (c = 0; c < numVertices; c++) array[c] = nid;
1495     PetscCall(VecRestoreArray(cown, &array));
1496     PetscCall(VecScatterCreate(cown, acis, acown, NULL, &sct));
1497     PetscCall(VecScatterBegin(sct, cown, acown, INSERT_VALUES, SCATTER_FORWARD));
1498     PetscCall(VecScatterEnd(sct, cown, acown, INSERT_VALUES, SCATTER_FORWARD));
1499     PetscCall(ISDestroy(&acis));
1500     PetscCall(VecScatterDestroy(&sct));
1501     PetscCall(VecDestroy(&cown));
1502 
1503     /* compute edgeCut */
1504     for (c = 0, cum = 0; c < numVertices; c++) cum = PetscMax(cum, start[c + 1] - start[c]);
1505     PetscCall(PetscMalloc1(cum, &work));
1506     PetscCall(ISLocalToGlobalMappingCreateIS(gid, &g2l));
1507     PetscCall(ISLocalToGlobalMappingSetType(g2l, ISLOCALTOGLOBALMAPPINGHASH));
1508     PetscCall(ISDestroy(&gid));
1509     PetscCall(VecGetArray(acown, &array));
1510     for (c = 0, ect = 0, ectn = 0; c < numVertices; c++) {
1511       PetscInt totl;
1512 
1513       totl = start[c + 1] - start[c];
1514       PetscCall(ISGlobalToLocalMappingApply(g2l, IS_GTOLM_MASK, totl, adjacency + start[c], NULL, work));
1515       for (i = 0; i < totl; i++) {
1516         if (work[i] < 0) {
1517           ect += 1;
1518           ectn += (array[i + start[c]] != nid) ? 0 : 1;
1519         }
1520       }
1521     }
1522     PetscCall(PetscFree(work));
1523     PetscCall(VecRestoreArray(acown, &array));
1524     lm[0] = numVertices > 0 ? numVertices : PETSC_MAX_INT;
1525     lm[1] = -numVertices;
1526     PetscCall(MPIU_Allreduce(lm, gm, 2, MPIU_INT64, MPI_MIN, comm));
1527     PetscCall(PetscViewerASCIIPrintf(viewer, "  Cell balance: %.2f (max %" PetscInt_FMT ", min %" PetscInt_FMT, -((double)gm[1]) / ((double)gm[0]), -(PetscInt)gm[1], (PetscInt)gm[0]));
1528     lm[0] = ect;                     /* edgeCut */
1529     lm[1] = ectn;                    /* node-aware edgeCut */
1530     lm[2] = numVertices > 0 ? 0 : 1; /* empty processes */
1531     PetscCall(MPIU_Allreduce(lm, gm, 3, MPIU_INT64, MPI_SUM, comm));
1532     PetscCall(PetscViewerASCIIPrintf(viewer, ", empty %" PetscInt_FMT ")\n", (PetscInt)gm[2]));
1533 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1534     PetscCall(PetscViewerASCIIPrintf(viewer, "  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n", (PetscInt)(gm[0] / 2), gm[0] ? ((double)gm[1]) / ((double)gm[0]) : 1.));
1535 #else
1536     PetscCall(PetscViewerASCIIPrintf(viewer, "  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n", (PetscInt)(gm[0] / 2), 0.0));
1537 #endif
1538     PetscCall(ISLocalToGlobalMappingDestroy(&g2l));
1539     PetscCall(PetscFree(start));
1540     PetscCall(PetscFree(adjacency));
1541     PetscCall(VecDestroy(&acown));
1542   } else {
1543     const char    *name;
1544     PetscInt      *sizes, *hybsizes, *ghostsizes;
1545     PetscInt       locDepth, depth, cellHeight, dim, d;
1546     PetscInt       pStart, pEnd, p, gcStart, gcEnd, gcNum;
1547     PetscInt       numLabels, l, maxSize = 17;
1548     DMPolytopeType ct0 = DM_POLYTOPE_UNKNOWN;
1549     MPI_Comm       comm;
1550     PetscMPIInt    size, rank;
1551 
1552     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1553     PetscCallMPI(MPI_Comm_size(comm, &size));
1554     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1555     PetscCall(DMGetDimension(dm, &dim));
1556     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1557     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1558     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
1559     else PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
1560     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
1561     PetscCall(DMPlexGetDepth(dm, &locDepth));
1562     PetscCall(MPIU_Allreduce(&locDepth, &depth, 1, MPIU_INT, MPI_MAX, comm));
1563     PetscCall(DMPlexGetCellTypeStratum(dm, DM_POLYTOPE_FV_GHOST, &gcStart, &gcEnd));
1564     gcNum = gcEnd - gcStart;
1565     if (size < maxSize) PetscCall(PetscCalloc3(size, &sizes, size, &hybsizes, size, &ghostsizes));
1566     else PetscCall(PetscCalloc3(3, &sizes, 3, &hybsizes, 3, &ghostsizes));
1567     for (d = 0; d <= depth; d++) {
1568       PetscInt Nc[2] = {0, 0}, ict;
1569 
1570       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
1571       if (pStart < pEnd) PetscCall(DMPlexGetCellType(dm, pStart, &ct0));
1572       ict = ct0;
1573       PetscCallMPI(MPI_Bcast(&ict, 1, MPIU_INT, 0, comm));
1574       ct0 = (DMPolytopeType)ict;
1575       for (p = pStart; p < pEnd; ++p) {
1576         DMPolytopeType ct;
1577 
1578         PetscCall(DMPlexGetCellType(dm, p, &ct));
1579         if (ct == ct0) ++Nc[0];
1580         else ++Nc[1];
1581       }
1582       if (size < maxSize) {
1583         PetscCallMPI(MPI_Gather(&Nc[0], 1, MPIU_INT, sizes, 1, MPIU_INT, 0, comm));
1584         PetscCallMPI(MPI_Gather(&Nc[1], 1, MPIU_INT, hybsizes, 1, MPIU_INT, 0, comm));
1585         if (d == depth) PetscCallMPI(MPI_Gather(&gcNum, 1, MPIU_INT, ghostsizes, 1, MPIU_INT, 0, comm));
1586         PetscCall(PetscViewerASCIIPrintf(viewer, "  Number of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1587         for (p = 0; p < size; ++p) {
1588           if (rank == 0) {
1589             PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT, sizes[p] + hybsizes[p]));
1590             if (hybsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT ")", hybsizes[p]));
1591             if (ghostsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "]", ghostsizes[p]));
1592           }
1593         }
1594       } else {
1595         PetscInt locMinMax[2];
1596 
1597         locMinMax[0] = Nc[0] + Nc[1];
1598         locMinMax[1] = Nc[0] + Nc[1];
1599         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, sizes));
1600         locMinMax[0] = Nc[1];
1601         locMinMax[1] = Nc[1];
1602         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, hybsizes));
1603         if (d == depth) {
1604           locMinMax[0] = gcNum;
1605           locMinMax[1] = gcNum;
1606           PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, ghostsizes));
1607         }
1608         PetscCall(PetscViewerASCIIPrintf(viewer, "  Min/Max of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1609         PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT "/%" PetscInt_FMT, sizes[0], sizes[1]));
1610         if (hybsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT "/%" PetscInt_FMT ")", hybsizes[0], hybsizes[1]));
1611         if (ghostsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "/%" PetscInt_FMT "]", ghostsizes[0], ghostsizes[1]));
1612       }
1613       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
1614     }
1615     PetscCall(PetscFree3(sizes, hybsizes, ghostsizes));
1616     {
1617       const PetscReal *maxCell;
1618       const PetscReal *L;
1619       PetscBool        localized;
1620 
1621       PetscCall(DMGetPeriodicity(dm, &maxCell, NULL, &L));
1622       PetscCall(DMGetCoordinatesLocalized(dm, &localized));
1623       if (L || localized) {
1624         PetscCall(PetscViewerASCIIPrintf(viewer, "Periodic mesh"));
1625         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1626         if (L) {
1627           PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
1628           for (d = 0; d < dim; ++d) {
1629             if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1630             PetscCall(PetscViewerASCIIPrintf(viewer, "%s", L[d] > 0.0 ? "PERIODIC" : "NONE"));
1631           }
1632           PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
1633         }
1634         PetscCall(PetscViewerASCIIPrintf(viewer, " coordinates %s\n", localized ? "localized" : "not localized"));
1635         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1636       }
1637     }
1638     PetscCall(DMGetNumLabels(dm, &numLabels));
1639     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
1640     for (l = 0; l < numLabels; ++l) {
1641       DMLabel         label;
1642       const char     *name;
1643       IS              valueIS;
1644       const PetscInt *values;
1645       PetscInt        numValues, v;
1646 
1647       PetscCall(DMGetLabelName(dm, l, &name));
1648       PetscCall(DMGetLabel(dm, name, &label));
1649       PetscCall(DMLabelGetNumValues(label, &numValues));
1650       PetscCall(PetscViewerASCIIPrintf(viewer, "  %s: %" PetscInt_FMT " strata with value/size (", name, numValues));
1651       PetscCall(DMLabelGetValueIS(label, &valueIS));
1652       PetscCall(ISGetIndices(valueIS, &values));
1653       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1654       for (v = 0; v < numValues; ++v) {
1655         PetscInt size;
1656 
1657         PetscCall(DMLabelGetStratumSize(label, values[v], &size));
1658         if (v > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1659         PetscCall(PetscViewerASCIIPrintf(viewer, "%" PetscInt_FMT " (%" PetscInt_FMT ")", values[v], size));
1660       }
1661       PetscCall(PetscViewerASCIIPrintf(viewer, ")\n"));
1662       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1663       PetscCall(ISRestoreIndices(valueIS, &values));
1664       PetscCall(ISDestroy(&valueIS));
1665     }
1666     {
1667       char    **labelNames;
1668       PetscInt  Nl = numLabels;
1669       PetscBool flg;
1670 
1671       PetscCall(PetscMalloc1(Nl, &labelNames));
1672       PetscCall(PetscOptionsGetStringArray(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_plex_view_labels", labelNames, &Nl, &flg));
1673       for (l = 0; l < Nl; ++l) {
1674         DMLabel label;
1675 
1676         PetscCall(DMHasLabel(dm, labelNames[l], &flg));
1677         if (flg) {
1678           PetscCall(DMGetLabel(dm, labelNames[l], &label));
1679           PetscCall(DMLabelView(label, viewer));
1680         }
1681         PetscCall(PetscFree(labelNames[l]));
1682       }
1683       PetscCall(PetscFree(labelNames));
1684     }
1685     /* If no fields are specified, people do not want to see adjacency */
1686     if (dm->Nf) {
1687       PetscInt f;
1688 
1689       for (f = 0; f < dm->Nf; ++f) {
1690         const char *name;
1691 
1692         PetscCall(PetscObjectGetName(dm->fields[f].disc, &name));
1693         if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Field %s:\n", name));
1694         PetscCall(PetscViewerASCIIPushTab(viewer));
1695         if (dm->fields[f].label) PetscCall(DMLabelView(dm->fields[f].label, viewer));
1696         if (dm->fields[f].adjacency[0]) {
1697           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM++\n"));
1698           else PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM\n"));
1699         } else {
1700           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FEM\n"));
1701           else PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FUNKY\n"));
1702         }
1703         PetscCall(PetscViewerASCIIPopTab(viewer));
1704       }
1705     }
1706     PetscCall(DMGetCoarseDM(dm, &cdm));
1707     if (cdm) {
1708       PetscCall(PetscViewerASCIIPushTab(viewer));
1709       PetscCall(PetscViewerASCIIPrintf(viewer, "Defined by transform from:\n"));
1710       PetscCall(DMPlexView_Ascii(cdm, viewer));
1711       PetscCall(PetscViewerASCIIPopTab(viewer));
1712     }
1713   }
1714   PetscFunctionReturn(PETSC_SUCCESS);
1715 }
1716 
1717 static PetscErrorCode DMPlexDrawCell(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[])
1718 {
1719   DMPolytopeType ct;
1720   PetscMPIInt    rank;
1721   PetscInt       cdim;
1722 
1723   PetscFunctionBegin;
1724   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1725   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1726   PetscCall(DMGetCoordinateDim(dm, &cdim));
1727   switch (ct) {
1728   case DM_POLYTOPE_SEGMENT:
1729   case DM_POLYTOPE_POINT_PRISM_TENSOR:
1730     switch (cdim) {
1731     case 1: {
1732       const PetscReal y  = 0.5;  /* TODO Put it in the middle of the viewport */
1733       const PetscReal dy = 0.05; /* TODO Make it a fraction of the total length */
1734 
1735       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y, PetscRealPart(coords[1]), y, PETSC_DRAW_BLACK));
1736       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y + dy, PetscRealPart(coords[0]), y - dy, PETSC_DRAW_BLACK));
1737       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[1]), y + dy, PetscRealPart(coords[1]), y - dy, PETSC_DRAW_BLACK));
1738     } break;
1739     case 2: {
1740       const PetscReal dx = (PetscRealPart(coords[3]) - PetscRealPart(coords[1]));
1741       const PetscReal dy = (PetscRealPart(coords[2]) - PetscRealPart(coords[0]));
1742       const PetscReal l  = 0.1 / PetscSqrtReal(dx * dx + dy * dy);
1743 
1744       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1745       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));
1746       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));
1747     } break;
1748     default:
1749       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of dimension %" PetscInt_FMT, cdim);
1750     }
1751     break;
1752   case DM_POLYTOPE_TRIANGLE:
1753     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));
1754     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1755     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1756     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1757     break;
1758   case DM_POLYTOPE_QUADRILATERAL:
1759     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));
1760     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));
1761     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1762     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1763     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_BLACK));
1764     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1765     break;
1766   case DM_POLYTOPE_SEG_PRISM_TENSOR:
1767     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));
1768     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));
1769     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1770     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_BLACK));
1771     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1772     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1773     break;
1774   case DM_POLYTOPE_FV_GHOST:
1775     break;
1776   default:
1777     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1778   }
1779   PetscFunctionReturn(PETSC_SUCCESS);
1780 }
1781 
1782 static PetscErrorCode DrawPolygon_Private(DM dm, PetscDraw draw, PetscInt cell, PetscInt Nv, const PetscReal refVertices[], const PetscScalar coords[], PetscInt edgeDiv, PetscReal refCoords[], PetscReal edgeCoords[])
1783 {
1784   PetscReal   centroid[2] = {0., 0.};
1785   PetscMPIInt rank;
1786   PetscInt    fillColor;
1787 
1788   PetscFunctionBegin;
1789   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1790   fillColor = PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2;
1791   for (PetscInt v = 0; v < Nv; ++v) {
1792     centroid[0] += PetscRealPart(coords[v * 2 + 0]) / Nv;
1793     centroid[1] += PetscRealPart(coords[v * 2 + 1]) / Nv;
1794   }
1795   for (PetscInt e = 0; e < Nv; ++e) {
1796     refCoords[0] = refVertices[e * 2 + 0];
1797     refCoords[1] = refVertices[e * 2 + 1];
1798     for (PetscInt d = 1; d <= edgeDiv; ++d) {
1799       refCoords[d * 2 + 0] = refCoords[0] + (refVertices[(e + 1) % Nv * 2 + 0] - refCoords[0]) * d / edgeDiv;
1800       refCoords[d * 2 + 1] = refCoords[1] + (refVertices[(e + 1) % Nv * 2 + 1] - refCoords[1]) * d / edgeDiv;
1801     }
1802     PetscCall(DMPlexReferenceToCoordinates(dm, cell, edgeDiv + 1, refCoords, edgeCoords));
1803     for (PetscInt d = 0; d < edgeDiv; ++d) {
1804       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));
1805       PetscCall(PetscDrawLine(draw, edgeCoords[d * 2 + 0], edgeCoords[d * 2 + 1], edgeCoords[(d + 1) * 2 + 0], edgeCoords[(d + 1) * 2 + 1], PETSC_DRAW_BLACK));
1806     }
1807   }
1808   PetscFunctionReturn(PETSC_SUCCESS);
1809 }
1810 
1811 static PetscErrorCode DMPlexDrawCellHighOrder(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[], PetscInt edgeDiv, PetscReal refCoords[], PetscReal edgeCoords[])
1812 {
1813   DMPolytopeType ct;
1814 
1815   PetscFunctionBegin;
1816   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1817   switch (ct) {
1818   case DM_POLYTOPE_TRIANGLE: {
1819     PetscReal refVertices[6] = {-1., -1., 1., -1., -1., 1.};
1820 
1821     PetscCall(DrawPolygon_Private(dm, draw, cell, 3, refVertices, coords, edgeDiv, refCoords, edgeCoords));
1822   } break;
1823   case DM_POLYTOPE_QUADRILATERAL: {
1824     PetscReal refVertices[8] = {-1., -1., 1., -1., 1., 1., -1., 1.};
1825 
1826     PetscCall(DrawPolygon_Private(dm, draw, cell, 4, refVertices, coords, edgeDiv, refCoords, edgeCoords));
1827   } break;
1828   default:
1829     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1830   }
1831   PetscFunctionReturn(PETSC_SUCCESS);
1832 }
1833 
1834 static PetscErrorCode DMPlexView_Draw(DM dm, PetscViewer viewer)
1835 {
1836   PetscDraw    draw;
1837   DM           cdm;
1838   PetscSection coordSection;
1839   Vec          coordinates;
1840   PetscReal    xyl[3], xyr[3];
1841   PetscReal   *refCoords, *edgeCoords;
1842   PetscBool    isnull, drawAffine;
1843   PetscInt     dim, vStart, vEnd, cStart, cEnd, c, cDegree, edgeDiv;
1844 
1845   PetscFunctionBegin;
1846   PetscCall(DMGetCoordinateDim(dm, &dim));
1847   PetscCheck(dim <= 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT, dim);
1848   PetscCall(DMGetCoordinateDegree_Internal(dm, &cDegree));
1849   drawAffine = cDegree > 1 ? PETSC_FALSE : PETSC_TRUE;
1850   edgeDiv    = cDegree + 1;
1851   PetscCall(PetscOptionsGetBool(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_view_draw_affine", &drawAffine, NULL));
1852   if (!drawAffine) PetscCall(PetscMalloc2((edgeDiv + 1) * dim, &refCoords, (edgeDiv + 1) * dim, &edgeCoords));
1853   PetscCall(DMGetCoordinateDM(dm, &cdm));
1854   PetscCall(DMGetLocalSection(cdm, &coordSection));
1855   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1856   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1857   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1858 
1859   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
1860   PetscCall(PetscDrawIsNull(draw, &isnull));
1861   if (isnull) PetscFunctionReturn(PETSC_SUCCESS);
1862   PetscCall(PetscDrawSetTitle(draw, "Mesh"));
1863 
1864   PetscCall(DMGetBoundingBox(dm, xyl, xyr));
1865   PetscCall(PetscDrawSetCoordinates(draw, xyl[0], xyl[1], xyr[0], xyr[1]));
1866   PetscCall(PetscDrawClear(draw));
1867 
1868   for (c = cStart; c < cEnd; ++c) {
1869     PetscScalar       *coords = NULL;
1870     const PetscScalar *coords_arr;
1871     PetscInt           numCoords;
1872     PetscBool          isDG;
1873 
1874     PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
1875     if (drawAffine) PetscCall(DMPlexDrawCell(dm, draw, c, coords));
1876     else PetscCall(DMPlexDrawCellHighOrder(dm, draw, c, coords, edgeDiv, refCoords, edgeCoords));
1877     PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
1878   }
1879   if (!drawAffine) PetscCall(PetscFree2(refCoords, edgeCoords));
1880   PetscCall(PetscDrawFlush(draw));
1881   PetscCall(PetscDrawPause(draw));
1882   PetscCall(PetscDrawSave(draw));
1883   PetscFunctionReturn(PETSC_SUCCESS);
1884 }
1885 
1886 static PetscErrorCode DMPlexCreateHighOrderSurrogate_Internal(DM dm, DM *hdm)
1887 {
1888   DM           odm = dm, rdm = dm, cdm;
1889   PetscFE      fe;
1890   PetscSpace   sp;
1891   PetscClassId id;
1892   PetscInt     degree;
1893   PetscBool    hoView = PETSC_TRUE;
1894 
1895   PetscFunctionBegin;
1896   PetscObjectOptionsBegin((PetscObject)dm);
1897   PetscCall(PetscOptionsBool("-dm_plex_high_order_view", "Subsample to view meshes with high order coordinates", "DMPlexCreateHighOrderSurrogate_Internal", hoView, &hoView, NULL));
1898   PetscOptionsEnd();
1899   PetscCall(PetscObjectReference((PetscObject)dm));
1900   *hdm = dm;
1901   if (!hoView) PetscFunctionReturn(PETSC_SUCCESS);
1902   PetscCall(DMGetCoordinateDM(dm, &cdm));
1903   PetscCall(DMGetField(cdm, 0, NULL, (PetscObject *)&fe));
1904   PetscCall(PetscObjectGetClassId((PetscObject)fe, &id));
1905   if (id != PETSCFE_CLASSID) PetscFunctionReturn(PETSC_SUCCESS);
1906   PetscCall(PetscFEGetBasisSpace(fe, &sp));
1907   PetscCall(PetscSpaceGetDegree(sp, &degree, NULL));
1908   for (PetscInt r = 0, rd = PetscCeilReal(((PetscReal)degree) / 2.); r < (PetscInt)PetscCeilReal(PetscLog2Real(degree)); ++r, rd = PetscCeilReal(((PetscReal)rd) / 2.)) {
1909     DM  cdm, rcdm;
1910     Mat In;
1911     Vec cl, rcl;
1912 
1913     PetscCall(DMRefine(odm, PetscObjectComm((PetscObject)odm), &rdm));
1914     PetscCall(DMPlexCreateCoordinateSpace(rdm, rd, PETSC_FALSE, NULL));
1915     PetscCall(PetscObjectSetName((PetscObject)rdm, "Refined Mesh with Linear Coordinates"));
1916     PetscCall(DMGetCoordinateDM(odm, &cdm));
1917     PetscCall(DMGetCoordinateDM(rdm, &rcdm));
1918     PetscCall(DMGetCoordinatesLocal(odm, &cl));
1919     PetscCall(DMGetCoordinatesLocal(rdm, &rcl));
1920     PetscCall(DMSetCoarseDM(rcdm, cdm));
1921     PetscCall(DMCreateInterpolation(cdm, rcdm, &In, NULL));
1922     PetscCall(MatMult(In, cl, rcl));
1923     PetscCall(MatDestroy(&In));
1924     PetscCall(DMSetCoordinatesLocal(rdm, rcl));
1925     PetscCall(DMDestroy(&odm));
1926     odm = rdm;
1927   }
1928   *hdm = rdm;
1929   PetscFunctionReturn(PETSC_SUCCESS);
1930 }
1931 
1932 #if defined(PETSC_HAVE_EXODUSII)
1933   #include <exodusII.h>
1934   #include <petscviewerexodusii.h>
1935 #endif
1936 
1937 PetscErrorCode DMView_Plex(DM dm, PetscViewer viewer)
1938 {
1939   PetscBool iascii, ishdf5, isvtk, isdraw, flg, isglvis, isexodus, iscgns;
1940   char      name[PETSC_MAX_PATH_LEN];
1941 
1942   PetscFunctionBegin;
1943   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1944   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1945   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &iascii));
1946   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
1947   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1948   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
1949   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
1950   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodus));
1951   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
1952   if (iascii) {
1953     PetscViewerFormat format;
1954     PetscCall(PetscViewerGetFormat(viewer, &format));
1955     if (format == PETSC_VIEWER_ASCII_GLVIS) PetscCall(DMPlexView_GLVis(dm, viewer));
1956     else PetscCall(DMPlexView_Ascii(dm, viewer));
1957   } else if (ishdf5) {
1958 #if defined(PETSC_HAVE_HDF5)
1959     PetscCall(DMPlexView_HDF5_Internal(dm, viewer));
1960 #else
1961     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1962 #endif
1963   } else if (isvtk) {
1964     PetscCall(DMPlexVTKWriteAll((PetscObject)dm, viewer));
1965   } else if (isdraw) {
1966     DM hdm;
1967 
1968     PetscCall(DMPlexCreateHighOrderSurrogate_Internal(dm, &hdm));
1969     PetscCall(DMPlexView_Draw(hdm, viewer));
1970     PetscCall(DMDestroy(&hdm));
1971   } else if (isglvis) {
1972     PetscCall(DMPlexView_GLVis(dm, viewer));
1973 #if defined(PETSC_HAVE_EXODUSII)
1974   } else if (isexodus) {
1975     /*
1976       exodusII requires that all sets be part of exactly one cell set.
1977       If the dm does not have a "Cell Sets" label defined, we create one
1978       with ID 1, containing all cells.
1979       Note that if the Cell Sets label is defined but does not cover all cells,
1980       we may still have a problem. This should probably be checked here or in the viewer;
1981     */
1982     PetscInt numCS;
1983     PetscCall(DMGetLabelSize(dm, "Cell Sets", &numCS));
1984     if (!numCS) {
1985       PetscInt cStart, cEnd, c;
1986       PetscCall(DMCreateLabel(dm, "Cell Sets"));
1987       PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1988       for (c = cStart; c < cEnd; ++c) PetscCall(DMSetLabelValue(dm, "Cell Sets", c, 1));
1989     }
1990     PetscCall(DMView_PlexExodusII(dm, viewer));
1991 #endif
1992 #if defined(PETSC_HAVE_CGNS)
1993   } else if (iscgns) {
1994     PetscCall(DMView_PlexCGNS(dm, viewer));
1995 #endif
1996   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex writing", ((PetscObject)viewer)->type_name);
1997   /* Optionally view the partition */
1998   PetscCall(PetscOptionsHasName(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_partition_view", &flg));
1999   if (flg) {
2000     Vec ranks;
2001     PetscCall(DMPlexCreateRankField(dm, &ranks));
2002     PetscCall(VecView(ranks, viewer));
2003     PetscCall(VecDestroy(&ranks));
2004   }
2005   /* Optionally view a label */
2006   PetscCall(PetscOptionsGetString(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_label_view", name, sizeof(name), &flg));
2007   if (flg) {
2008     DMLabel label;
2009     Vec     val;
2010 
2011     PetscCall(DMGetLabel(dm, name, &label));
2012     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Label %s provided to -dm_label_view does not exist in this DM", name);
2013     PetscCall(DMPlexCreateLabelField(dm, label, &val));
2014     PetscCall(VecView(val, viewer));
2015     PetscCall(VecDestroy(&val));
2016   }
2017   PetscFunctionReturn(PETSC_SUCCESS);
2018 }
2019 
2020 /*@
2021   DMPlexTopologyView - Saves a `DMPLEX` topology into a file
2022 
2023   Collective
2024 
2025   Input Parameters:
2026 + dm     - The `DM` whose topology is to be saved
2027 - viewer - The `PetscViewer` to save it in
2028 
2029   Level: advanced
2030 
2031 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexTopologyLoad()`, `PetscViewer`
2032 @*/
2033 PetscErrorCode DMPlexTopologyView(DM dm, PetscViewer viewer)
2034 {
2035   PetscBool ishdf5;
2036 
2037   PetscFunctionBegin;
2038   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2039   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2040   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2041   PetscCall(PetscLogEventBegin(DMPLEX_TopologyView, viewer, 0, 0, 0));
2042   if (ishdf5) {
2043 #if defined(PETSC_HAVE_HDF5)
2044     PetscViewerFormat format;
2045     PetscCall(PetscViewerGetFormat(viewer, &format));
2046     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2047       IS globalPointNumbering;
2048 
2049       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
2050       PetscCall(DMPlexTopologyView_HDF5_Internal(dm, globalPointNumbering, viewer));
2051       PetscCall(ISDestroy(&globalPointNumbering));
2052     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
2053 #else
2054     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2055 #endif
2056   }
2057   PetscCall(PetscLogEventEnd(DMPLEX_TopologyView, viewer, 0, 0, 0));
2058   PetscFunctionReturn(PETSC_SUCCESS);
2059 }
2060 
2061 /*@
2062   DMPlexCoordinatesView - Saves `DMPLEX` coordinates into a file
2063 
2064   Collective
2065 
2066   Input Parameters:
2067 + dm     - The `DM` whose coordinates are to be saved
2068 - viewer - The `PetscViewer` for saving
2069 
2070   Level: advanced
2071 
2072 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexLabelsView()`, `DMPlexCoordinatesLoad()`, `PetscViewer`
2073 @*/
2074 PetscErrorCode DMPlexCoordinatesView(DM dm, PetscViewer viewer)
2075 {
2076   PetscBool ishdf5;
2077 
2078   PetscFunctionBegin;
2079   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2080   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2081   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2082   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
2083   if (ishdf5) {
2084 #if defined(PETSC_HAVE_HDF5)
2085     PetscViewerFormat format;
2086     PetscCall(PetscViewerGetFormat(viewer, &format));
2087     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2088       PetscCall(DMPlexCoordinatesView_HDF5_Internal(dm, viewer));
2089     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
2090 #else
2091     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2092 #endif
2093   }
2094   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
2095   PetscFunctionReturn(PETSC_SUCCESS);
2096 }
2097 
2098 /*@
2099   DMPlexLabelsView - Saves `DMPLEX` labels into a file
2100 
2101   Collective
2102 
2103   Input Parameters:
2104 + dm     - The `DM` whose labels are to be saved
2105 - viewer - The `PetscViewer` for saving
2106 
2107   Level: advanced
2108 
2109 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsLoad()`, `PetscViewer`
2110 @*/
2111 PetscErrorCode DMPlexLabelsView(DM dm, PetscViewer viewer)
2112 {
2113   PetscBool ishdf5;
2114 
2115   PetscFunctionBegin;
2116   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2117   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2118   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2119   PetscCall(PetscLogEventBegin(DMPLEX_LabelsView, viewer, 0, 0, 0));
2120   if (ishdf5) {
2121 #if defined(PETSC_HAVE_HDF5)
2122     IS                globalPointNumbering;
2123     PetscViewerFormat format;
2124 
2125     PetscCall(PetscViewerGetFormat(viewer, &format));
2126     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2127       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
2128       PetscCall(DMPlexLabelsView_HDF5_Internal(dm, globalPointNumbering, viewer));
2129       PetscCall(ISDestroy(&globalPointNumbering));
2130     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2131 #else
2132     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2133 #endif
2134   }
2135   PetscCall(PetscLogEventEnd(DMPLEX_LabelsView, viewer, 0, 0, 0));
2136   PetscFunctionReturn(PETSC_SUCCESS);
2137 }
2138 
2139 /*@
2140   DMPlexSectionView - Saves a section associated with a `DMPLEX`
2141 
2142   Collective
2143 
2144   Input Parameters:
2145 + dm        - The `DM` that contains the topology on which the section to be saved is defined
2146 . viewer    - The `PetscViewer` for saving
2147 - sectiondm - The `DM` that contains the section to be saved, can be `NULL`
2148 
2149   Level: advanced
2150 
2151   Notes:
2152   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.
2153 
2154   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.
2155 
2156 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`, `PetscSectionView()`, `DMPlexSectionLoad()`, `PetscViewer`
2157 @*/
2158 PetscErrorCode DMPlexSectionView(DM dm, PetscViewer viewer, DM sectiondm)
2159 {
2160   PetscBool ishdf5;
2161 
2162   PetscFunctionBegin;
2163   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2164   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2165   if (!sectiondm) sectiondm = dm;
2166   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2167   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2168   PetscCall(PetscLogEventBegin(DMPLEX_SectionView, viewer, 0, 0, 0));
2169   if (ishdf5) {
2170 #if defined(PETSC_HAVE_HDF5)
2171     PetscCall(DMPlexSectionView_HDF5_Internal(dm, viewer, sectiondm));
2172 #else
2173     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2174 #endif
2175   }
2176   PetscCall(PetscLogEventEnd(DMPLEX_SectionView, viewer, 0, 0, 0));
2177   PetscFunctionReturn(PETSC_SUCCESS);
2178 }
2179 
2180 /*@
2181   DMPlexGlobalVectorView - Saves a global vector
2182 
2183   Collective
2184 
2185   Input Parameters:
2186 + dm        - The `DM` that represents the topology
2187 . viewer    - The `PetscViewer` to save data with
2188 . sectiondm - The `DM` that contains the global section on which vec is defined, can be `NULL`
2189 - vec       - The global vector to be saved
2190 
2191   Level: advanced
2192 
2193   Notes:
2194   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.
2195 
2196   Calling sequence:
2197 .vb
2198        DMCreate(PETSC_COMM_WORLD, &dm);
2199        DMSetType(dm, DMPLEX);
2200        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2201        DMClone(dm, &sectiondm);
2202        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2203        PetscSectionCreate(PETSC_COMM_WORLD, &section);
2204        DMPlexGetChart(sectiondm, &pStart, &pEnd);
2205        PetscSectionSetChart(section, pStart, pEnd);
2206        PetscSectionSetUp(section);
2207        DMSetLocalSection(sectiondm, section);
2208        PetscSectionDestroy(&section);
2209        DMGetGlobalVector(sectiondm, &vec);
2210        PetscObjectSetName((PetscObject)vec, "vec_name");
2211        DMPlexTopologyView(dm, viewer);
2212        DMPlexSectionView(dm, viewer, sectiondm);
2213        DMPlexGlobalVectorView(dm, viewer, sectiondm, vec);
2214        DMRestoreGlobalVector(sectiondm, &vec);
2215        DMDestroy(&sectiondm);
2216        DMDestroy(&dm);
2217 .ve
2218 
2219 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexLocalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2220 @*/
2221 PetscErrorCode DMPlexGlobalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2222 {
2223   PetscBool ishdf5;
2224 
2225   PetscFunctionBegin;
2226   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2227   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2228   if (!sectiondm) sectiondm = dm;
2229   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2230   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2231   /* Check consistency */
2232   {
2233     PetscSection section;
2234     PetscBool    includesConstraints;
2235     PetscInt     m, m1;
2236 
2237     PetscCall(VecGetLocalSize(vec, &m1));
2238     PetscCall(DMGetGlobalSection(sectiondm, &section));
2239     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2240     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2241     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2242     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2243   }
2244   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2245   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorView, viewer, 0, 0, 0));
2246   if (ishdf5) {
2247 #if defined(PETSC_HAVE_HDF5)
2248     PetscCall(DMPlexGlobalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2249 #else
2250     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2251 #endif
2252   }
2253   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorView, viewer, 0, 0, 0));
2254   PetscFunctionReturn(PETSC_SUCCESS);
2255 }
2256 
2257 /*@
2258   DMPlexLocalVectorView - Saves a local vector
2259 
2260   Collective
2261 
2262   Input Parameters:
2263 + dm        - The `DM` that represents the topology
2264 . viewer    - The `PetscViewer` to save data with
2265 . sectiondm - The `DM` that contains the local section on which `vec` is defined, can be `NULL`
2266 - vec       - The local vector to be saved
2267 
2268   Level: advanced
2269 
2270   Note:
2271   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.
2272 
2273   Calling sequence:
2274 .vb
2275        DMCreate(PETSC_COMM_WORLD, &dm);
2276        DMSetType(dm, DMPLEX);
2277        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2278        DMClone(dm, &sectiondm);
2279        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2280        PetscSectionCreate(PETSC_COMM_WORLD, &section);
2281        DMPlexGetChart(sectiondm, &pStart, &pEnd);
2282        PetscSectionSetChart(section, pStart, pEnd);
2283        PetscSectionSetUp(section);
2284        DMSetLocalSection(sectiondm, section);
2285        DMGetLocalVector(sectiondm, &vec);
2286        PetscObjectSetName((PetscObject)vec, "vec_name");
2287        DMPlexTopologyView(dm, viewer);
2288        DMPlexSectionView(dm, viewer, sectiondm);
2289        DMPlexLocalVectorView(dm, viewer, sectiondm, vec);
2290        DMRestoreLocalVector(sectiondm, &vec);
2291        DMDestroy(&sectiondm);
2292        DMDestroy(&dm);
2293 .ve
2294 
2295 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexGlobalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2296 @*/
2297 PetscErrorCode DMPlexLocalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2298 {
2299   PetscBool ishdf5;
2300 
2301   PetscFunctionBegin;
2302   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2303   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2304   if (!sectiondm) sectiondm = dm;
2305   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2306   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2307   /* Check consistency */
2308   {
2309     PetscSection section;
2310     PetscBool    includesConstraints;
2311     PetscInt     m, m1;
2312 
2313     PetscCall(VecGetLocalSize(vec, &m1));
2314     PetscCall(DMGetLocalSection(sectiondm, &section));
2315     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2316     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2317     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2318     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2319   }
2320   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2321   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorView, viewer, 0, 0, 0));
2322   if (ishdf5) {
2323 #if defined(PETSC_HAVE_HDF5)
2324     PetscCall(DMPlexLocalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2325 #else
2326     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2327 #endif
2328   }
2329   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorView, viewer, 0, 0, 0));
2330   PetscFunctionReturn(PETSC_SUCCESS);
2331 }
2332 
2333 PetscErrorCode DMLoad_Plex(DM dm, PetscViewer viewer)
2334 {
2335   PetscBool ishdf5;
2336 
2337   PetscFunctionBegin;
2338   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2339   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2340   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2341   if (ishdf5) {
2342 #if defined(PETSC_HAVE_HDF5)
2343     PetscViewerFormat format;
2344     PetscCall(PetscViewerGetFormat(viewer, &format));
2345     if (format == PETSC_VIEWER_HDF5_XDMF || format == PETSC_VIEWER_HDF5_VIZ) {
2346       PetscCall(DMPlexLoad_HDF5_Xdmf_Internal(dm, viewer));
2347     } else if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2348       PetscCall(DMPlexLoad_HDF5_Internal(dm, viewer));
2349     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2350     PetscFunctionReturn(PETSC_SUCCESS);
2351 #else
2352     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2353 #endif
2354   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex loading", ((PetscObject)viewer)->type_name);
2355 }
2356 
2357 /*@
2358   DMPlexTopologyLoad - Loads a topology into a `DMPLEX`
2359 
2360   Collective
2361 
2362   Input Parameters:
2363 + dm     - The `DM` into which the topology is loaded
2364 - viewer - The `PetscViewer` for the saved topology
2365 
2366   Output Parameter:
2367 . 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;
2368   `NULL` if unneeded
2369 
2370   Level: advanced
2371 
2372 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2373           `PetscViewer`, `PetscSF`
2374 @*/
2375 PetscErrorCode DMPlexTopologyLoad(DM dm, PetscViewer viewer, PetscSF *globalToLocalPointSF)
2376 {
2377   PetscBool ishdf5;
2378 
2379   PetscFunctionBegin;
2380   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2381   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2382   if (globalToLocalPointSF) PetscAssertPointer(globalToLocalPointSF, 3);
2383   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2384   PetscCall(PetscLogEventBegin(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2385   if (ishdf5) {
2386 #if defined(PETSC_HAVE_HDF5)
2387     PetscViewerFormat format;
2388     PetscCall(PetscViewerGetFormat(viewer, &format));
2389     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2390       PetscCall(DMPlexTopologyLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2391     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2392 #else
2393     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2394 #endif
2395   }
2396   PetscCall(PetscLogEventEnd(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2397   PetscFunctionReturn(PETSC_SUCCESS);
2398 }
2399 
2400 /*@
2401   DMPlexCoordinatesLoad - Loads coordinates into a `DMPLEX`
2402 
2403   Collective
2404 
2405   Input Parameters:
2406 + dm                   - The `DM` into which the coordinates are loaded
2407 . viewer               - The `PetscViewer` for the saved coordinates
2408 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad()` when loading dm from viewer
2409 
2410   Level: advanced
2411 
2412 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2413           `PetscSF`, `PetscViewer`
2414 @*/
2415 PetscErrorCode DMPlexCoordinatesLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2416 {
2417   PetscBool ishdf5;
2418 
2419   PetscFunctionBegin;
2420   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2421   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2422   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2423   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2424   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesLoad, viewer, 0, 0, 0));
2425   if (ishdf5) {
2426 #if defined(PETSC_HAVE_HDF5)
2427     PetscViewerFormat format;
2428     PetscCall(PetscViewerGetFormat(viewer, &format));
2429     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2430       PetscCall(DMPlexCoordinatesLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2431     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2432 #else
2433     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2434 #endif
2435   }
2436   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesLoad, viewer, 0, 0, 0));
2437   PetscFunctionReturn(PETSC_SUCCESS);
2438 }
2439 
2440 /*@
2441   DMPlexLabelsLoad - Loads labels into a `DMPLEX`
2442 
2443   Collective
2444 
2445   Input Parameters:
2446 + dm                   - The `DM` into which the labels are loaded
2447 . viewer               - The `PetscViewer` for the saved labels
2448 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad()` when loading `dm` from viewer
2449 
2450   Level: advanced
2451 
2452   Note:
2453   The `PetscSF` argument must not be `NULL` if the `DM` is distributed, otherwise an error occurs.
2454 
2455 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2456           `PetscSF`, `PetscViewer`
2457 @*/
2458 PetscErrorCode DMPlexLabelsLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2459 {
2460   PetscBool ishdf5;
2461 
2462   PetscFunctionBegin;
2463   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2464   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2465   if (globalToLocalPointSF) PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2466   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2467   PetscCall(PetscLogEventBegin(DMPLEX_LabelsLoad, viewer, 0, 0, 0));
2468   if (ishdf5) {
2469 #if defined(PETSC_HAVE_HDF5)
2470     PetscViewerFormat format;
2471 
2472     PetscCall(PetscViewerGetFormat(viewer, &format));
2473     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2474       PetscCall(DMPlexLabelsLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2475     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2476 #else
2477     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2478 #endif
2479   }
2480   PetscCall(PetscLogEventEnd(DMPLEX_LabelsLoad, viewer, 0, 0, 0));
2481   PetscFunctionReturn(PETSC_SUCCESS);
2482 }
2483 
2484 /*@
2485   DMPlexSectionLoad - Loads section into a `DMPLEX`
2486 
2487   Collective
2488 
2489   Input Parameters:
2490 + dm                   - The `DM` that represents the topology
2491 . viewer               - The `PetscViewer` that represents the on-disk section (sectionA)
2492 . sectiondm            - The `DM` into which the on-disk section (sectionA) is migrated, can be `NULL`
2493 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad(`) when loading dm from viewer
2494 
2495   Output Parameters:
2496 + 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)
2497 - 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)
2498 
2499   Level: advanced
2500 
2501   Notes:
2502   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.
2503 
2504   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.
2505 
2506   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.
2507 
2508   Example using 2 processes:
2509 .vb
2510   NX (number of points on dm): 4
2511   sectionA                   : the on-disk section
2512   vecA                       : a vector associated with sectionA
2513   sectionB                   : sectiondm's local section constructed in this function
2514   vecB (local)               : a vector associated with sectiondm's local section
2515   vecB (global)              : a vector associated with sectiondm's global section
2516 
2517                                      rank 0    rank 1
2518   vecA (global)                  : [.0 .4 .1 | .2 .3]        <- to be loaded in DMPlexGlobalVectorLoad() or DMPlexLocalVectorLoad()
2519   sectionA->atlasOff             :       0 2 | 1             <- loaded in PetscSectionLoad()
2520   sectionA->atlasDof             :       1 3 | 1             <- loaded in PetscSectionLoad()
2521   sectionA's global point numbers:       0 2 | 3             <- loaded in DMPlexSectionLoad()
2522   [0, NX)                        :       0 1 | 2 3           <- conceptual partition used in globalToLocalPointSF
2523   sectionB's global point numbers:     0 1 3 | 3 2           <- associated with [0, NX) by globalToLocalPointSF
2524   sectionB->atlasDof             :     1 0 1 | 1 3
2525   sectionB->atlasOff (no perm)   :     0 1 1 | 0 1
2526   vecB (local)                   :   [.0 .4] | [.4 .1 .2 .3] <- to be constructed by calling DMPlexLocalVectorLoad() with localDofSF
2527   vecB (global)                  :    [.0 .4 | .1 .2 .3]     <- to be constructed by calling DMPlexGlobalVectorLoad() with globalDofSF
2528 .ve
2529   where "|" represents a partition of loaded data, and global point 3 is assumed to be owned by rank 0.
2530 
2531 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`, `PetscSectionLoad()`, `DMPlexSectionView()`, `PetscSF`, `PetscViewer`
2532 @*/
2533 PetscErrorCode DMPlexSectionLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF globalToLocalPointSF, PetscSF *globalDofSF, PetscSF *localDofSF)
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(globalToLocalPointSF, PETSCSF_CLASSID, 4);
2543   if (globalDofSF) PetscAssertPointer(globalDofSF, 5);
2544   if (localDofSF) PetscAssertPointer(localDofSF, 6);
2545   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2546   PetscCall(PetscLogEventBegin(DMPLEX_SectionLoad, viewer, 0, 0, 0));
2547   if (ishdf5) {
2548 #if defined(PETSC_HAVE_HDF5)
2549     PetscCall(DMPlexSectionLoad_HDF5_Internal(dm, viewer, sectiondm, globalToLocalPointSF, globalDofSF, localDofSF));
2550 #else
2551     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2552 #endif
2553   }
2554   PetscCall(PetscLogEventEnd(DMPLEX_SectionLoad, viewer, 0, 0, 0));
2555   PetscFunctionReturn(PETSC_SUCCESS);
2556 }
2557 
2558 /*@
2559   DMPlexGlobalVectorLoad - Loads on-disk vector data into a global vector
2560 
2561   Collective
2562 
2563   Input Parameters:
2564 + dm        - The `DM` that represents the topology
2565 . viewer    - The `PetscViewer` that represents the on-disk vector data
2566 . sectiondm - The `DM` that contains the global section on which vec is defined, can be `NULL`
2567 . sf        - The `PetscSF` that migrates the on-disk vector data into vec
2568 - vec       - The global vector to set values of
2569 
2570   Level: advanced
2571 
2572   Notes:
2573   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.
2574 
2575   Calling sequence:
2576 .vb
2577        DMCreate(PETSC_COMM_WORLD, &dm);
2578        DMSetType(dm, DMPLEX);
2579        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2580        DMPlexTopologyLoad(dm, viewer, &sfX);
2581        DMClone(dm, &sectiondm);
2582        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2583        DMPlexSectionLoad(dm, viewer, sectiondm, sfX, &gsf, NULL);
2584        DMGetGlobalVector(sectiondm, &vec);
2585        PetscObjectSetName((PetscObject)vec, "vec_name");
2586        DMPlexGlobalVectorLoad(dm, viewer, sectiondm, gsf, vec);
2587        DMRestoreGlobalVector(sectiondm, &vec);
2588        PetscSFDestroy(&gsf);
2589        PetscSFDestroy(&sfX);
2590        DMDestroy(&sectiondm);
2591        DMDestroy(&dm);
2592 .ve
2593 
2594 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexLocalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`,
2595           `PetscSF`, `PetscViewer`
2596 @*/
2597 PetscErrorCode DMPlexGlobalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2598 {
2599   PetscBool ishdf5;
2600 
2601   PetscFunctionBegin;
2602   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2603   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2604   if (!sectiondm) sectiondm = dm;
2605   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2606   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2607   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2608   /* Check consistency */
2609   {
2610     PetscSection section;
2611     PetscBool    includesConstraints;
2612     PetscInt     m, m1;
2613 
2614     PetscCall(VecGetLocalSize(vec, &m1));
2615     PetscCall(DMGetGlobalSection(sectiondm, &section));
2616     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2617     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2618     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2619     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2620   }
2621   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2622   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorLoad, viewer, 0, 0, 0));
2623   if (ishdf5) {
2624 #if defined(PETSC_HAVE_HDF5)
2625     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2626 #else
2627     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2628 #endif
2629   }
2630   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorLoad, viewer, 0, 0, 0));
2631   PetscFunctionReturn(PETSC_SUCCESS);
2632 }
2633 
2634 /*@
2635   DMPlexLocalVectorLoad - Loads on-disk vector data into a local vector
2636 
2637   Collective
2638 
2639   Input Parameters:
2640 + dm        - The `DM` that represents the topology
2641 . viewer    - The `PetscViewer` that represents the on-disk vector data
2642 . sectiondm - The `DM` that contains the local section on which vec is defined, can be `NULL`
2643 . sf        - The `PetscSF` that migrates the on-disk vector data into vec
2644 - vec       - The local vector to set values of
2645 
2646   Level: advanced
2647 
2648   Notes:
2649   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.
2650 
2651   Calling sequence:
2652 .vb
2653        DMCreate(PETSC_COMM_WORLD, &dm);
2654        DMSetType(dm, DMPLEX);
2655        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2656        DMPlexTopologyLoad(dm, viewer, &sfX);
2657        DMClone(dm, &sectiondm);
2658        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2659        DMPlexSectionLoad(dm, viewer, sectiondm, sfX, NULL, &lsf);
2660        DMGetLocalVector(sectiondm, &vec);
2661        PetscObjectSetName((PetscObject)vec, "vec_name");
2662        DMPlexLocalVectorLoad(dm, viewer, sectiondm, lsf, vec);
2663        DMRestoreLocalVector(sectiondm, &vec);
2664        PetscSFDestroy(&lsf);
2665        PetscSFDestroy(&sfX);
2666        DMDestroy(&sectiondm);
2667        DMDestroy(&dm);
2668 .ve
2669 
2670 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`,
2671           `PetscSF`, `PetscViewer`
2672 @*/
2673 PetscErrorCode DMPlexLocalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2674 {
2675   PetscBool ishdf5;
2676 
2677   PetscFunctionBegin;
2678   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2679   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2680   if (!sectiondm) sectiondm = dm;
2681   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2682   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2683   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2684   /* Check consistency */
2685   {
2686     PetscSection section;
2687     PetscBool    includesConstraints;
2688     PetscInt     m, m1;
2689 
2690     PetscCall(VecGetLocalSize(vec, &m1));
2691     PetscCall(DMGetLocalSection(sectiondm, &section));
2692     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2693     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2694     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2695     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2696   }
2697   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2698   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorLoad, viewer, 0, 0, 0));
2699   if (ishdf5) {
2700 #if defined(PETSC_HAVE_HDF5)
2701     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2702 #else
2703     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2704 #endif
2705   }
2706   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorLoad, viewer, 0, 0, 0));
2707   PetscFunctionReturn(PETSC_SUCCESS);
2708 }
2709 
2710 PetscErrorCode DMDestroy_Plex(DM dm)
2711 {
2712   DM_Plex *mesh = (DM_Plex *)dm->data;
2713 
2714   PetscFunctionBegin;
2715   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMSetUpGLVisViewer_C", NULL));
2716   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertBoundaryValues_C", NULL));
2717   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMCreateNeumannOverlap_C", NULL));
2718   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMInterpolateSolution_C", NULL));
2719   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertTimeDerivativeBoundaryValues_C", NULL));
2720   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", NULL));
2721   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeGetDefault_C", NULL));
2722   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeSetDefault_C", NULL));
2723   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "MatComputeNeumannOverlap_C", NULL));
2724   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderGetDefault_C", NULL));
2725   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderSetDefault_C", NULL));
2726   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionGetDefault_C", NULL));
2727   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionSetDefault_C", NULL));
2728   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionGetType_C", NULL));
2729   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionSetType_C", NULL));
2730   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", NULL));
2731   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexSetOverlap_C", NULL));
2732   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetUseCeed_C", NULL));
2733   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexSetUseCeed_C", NULL));
2734   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMGetIsoperiodicPointSF_C", NULL));
2735   if (--mesh->refct > 0) PetscFunctionReturn(PETSC_SUCCESS);
2736   PetscCall(PetscSectionDestroy(&mesh->coneSection));
2737   PetscCall(PetscFree(mesh->cones));
2738   PetscCall(PetscFree(mesh->coneOrientations));
2739   PetscCall(PetscSectionDestroy(&mesh->supportSection));
2740   PetscCall(PetscSectionDestroy(&mesh->subdomainSection));
2741   PetscCall(PetscFree(mesh->supports));
2742   PetscCall(PetscFree(mesh->cellTypes));
2743   PetscCall(DMPlexTransformDestroy(&mesh->tr));
2744   PetscCall(PetscFree(mesh->tetgenOpts));
2745   PetscCall(PetscFree(mesh->triangleOpts));
2746   PetscCall(PetscFree(mesh->transformType));
2747   PetscCall(PetscFree(mesh->distributionName));
2748   PetscCall(PetscPartitionerDestroy(&mesh->partitioner));
2749   PetscCall(DMLabelDestroy(&mesh->subpointMap));
2750   PetscCall(ISDestroy(&mesh->subpointIS));
2751   PetscCall(ISDestroy(&mesh->globalVertexNumbers));
2752   PetscCall(ISDestroy(&mesh->globalCellNumbers));
2753   if (mesh->periodic.face_sfs) {
2754     for (PetscInt i = 0; i < mesh->periodic.num_face_sfs; i++) PetscCall(PetscSFDestroy(&mesh->periodic.face_sfs[i]));
2755     PetscCall(PetscFree(mesh->periodic.face_sfs));
2756   }
2757   PetscCall(PetscSFDestroy(&mesh->periodic.composed_sf));
2758   if (mesh->periodic.periodic_points) {
2759     for (PetscInt i = 0; i < mesh->periodic.num_face_sfs; i++) PetscCall(ISDestroy(&mesh->periodic.periodic_points[i]));
2760     PetscCall(PetscFree(mesh->periodic.periodic_points));
2761   }
2762   if (mesh->periodic.transform) PetscCall(PetscFree(mesh->periodic.transform));
2763   PetscCall(PetscSectionDestroy(&mesh->anchorSection));
2764   PetscCall(ISDestroy(&mesh->anchorIS));
2765   PetscCall(PetscSectionDestroy(&mesh->parentSection));
2766   PetscCall(PetscFree(mesh->parents));
2767   PetscCall(PetscFree(mesh->childIDs));
2768   PetscCall(PetscSectionDestroy(&mesh->childSection));
2769   PetscCall(PetscFree(mesh->children));
2770   PetscCall(DMDestroy(&mesh->referenceTree));
2771   PetscCall(PetscGridHashDestroy(&mesh->lbox));
2772   PetscCall(PetscFree(mesh->neighbors));
2773   if (mesh->metricCtx) PetscCall(PetscFree(mesh->metricCtx));
2774   /* This was originally freed in DMDestroy(), but that prevents reference counting of backend objects */
2775   PetscCall(PetscFree(mesh));
2776   PetscFunctionReturn(PETSC_SUCCESS);
2777 }
2778 
2779 PetscErrorCode DMCreateMatrix_Plex(DM dm, Mat *J)
2780 {
2781   PetscSection           sectionGlobal, sectionLocal;
2782   PetscInt               bs = -1, mbs;
2783   PetscInt               localSize, localStart = 0;
2784   PetscBool              isShell, isBlock, isSeqBlock, isMPIBlock, isSymBlock, isSymSeqBlock, isSymMPIBlock, isMatIS;
2785   MatType                mtype;
2786   ISLocalToGlobalMapping ltog;
2787 
2788   PetscFunctionBegin;
2789   PetscCall(MatInitializePackage());
2790   mtype = dm->mattype;
2791   PetscCall(DMGetLocalSection(dm, &sectionLocal));
2792   PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
2793   /* PetscCall(PetscSectionGetStorageSize(sectionGlobal, &localSize)); */
2794   PetscCall(PetscSectionGetConstrainedStorageSize(sectionGlobal, &localSize));
2795   PetscCallMPI(MPI_Exscan(&localSize, &localStart, 1, MPIU_INT, MPI_SUM, PetscObjectComm((PetscObject)dm)));
2796   PetscCall(MatCreate(PetscObjectComm((PetscObject)dm), J));
2797   PetscCall(MatSetSizes(*J, localSize, localSize, PETSC_DETERMINE, PETSC_DETERMINE));
2798   PetscCall(MatSetType(*J, mtype));
2799   PetscCall(MatSetFromOptions(*J));
2800   PetscCall(MatGetBlockSize(*J, &mbs));
2801   if (mbs > 1) bs = mbs;
2802   PetscCall(PetscStrcmp(mtype, MATSHELL, &isShell));
2803   PetscCall(PetscStrcmp(mtype, MATBAIJ, &isBlock));
2804   PetscCall(PetscStrcmp(mtype, MATSEQBAIJ, &isSeqBlock));
2805   PetscCall(PetscStrcmp(mtype, MATMPIBAIJ, &isMPIBlock));
2806   PetscCall(PetscStrcmp(mtype, MATSBAIJ, &isSymBlock));
2807   PetscCall(PetscStrcmp(mtype, MATSEQSBAIJ, &isSymSeqBlock));
2808   PetscCall(PetscStrcmp(mtype, MATMPISBAIJ, &isSymMPIBlock));
2809   PetscCall(PetscStrcmp(mtype, MATIS, &isMatIS));
2810   if (!isShell) {
2811     // There are three states with pblocks, since block starts can have no dofs:
2812     // UNKNOWN) New Block:   An open block has been signalled by pblocks[p] == 1
2813     // TRUE)    Block Start: The first entry in a block has been added
2814     // FALSE)   Block Add:   An additional block entry has been added, since pblocks[p] == 0
2815     PetscBT         blst;
2816     PetscBool3      bstate     = PETSC_BOOL3_UNKNOWN;
2817     PetscBool       fillMatrix = (PetscBool)(!dm->prealloc_only && !isMatIS);
2818     const PetscInt *perm       = NULL;
2819     PetscInt       *dnz, *onz, *dnzu, *onzu, bsLocal[2], bsMinMax[2], *pblocks;
2820     PetscInt        pStart, pEnd, dof, cdof, num_fields;
2821 
2822     PetscCall(DMGetLocalToGlobalMapping(dm, &ltog));
2823     PetscCall(PetscSectionGetBlockStarts(sectionLocal, &blst));
2824     if (sectionLocal->perm) PetscCall(ISGetIndices(sectionLocal->perm, &perm));
2825 
2826     PetscCall(PetscCalloc1(localSize, &pblocks));
2827     PetscCall(PetscSectionGetChart(sectionGlobal, &pStart, &pEnd));
2828     PetscCall(PetscSectionGetNumFields(sectionGlobal, &num_fields));
2829     // We need to process in the permuted order to get block sizes right
2830     for (PetscInt point = pStart; point < pEnd; ++point) {
2831       const PetscInt p = perm ? perm[point] : point;
2832 
2833       switch (dm->blocking_type) {
2834       case DM_BLOCKING_TOPOLOGICAL_POINT: { // One block per topological point
2835         PetscInt bdof, offset;
2836 
2837         PetscCall(PetscSectionGetDof(sectionGlobal, p, &dof));
2838         PetscCall(PetscSectionGetOffset(sectionGlobal, p, &offset));
2839         PetscCall(PetscSectionGetConstraintDof(sectionGlobal, p, &cdof));
2840         if (blst && PetscBTLookup(blst, p)) bstate = PETSC_BOOL3_UNKNOWN;
2841         if (dof > 0) {
2842           // State change
2843           if (bstate == PETSC_BOOL3_UNKNOWN) bstate = PETSC_BOOL3_TRUE;
2844           else if (bstate == PETSC_BOOL3_TRUE && blst && !PetscBTLookup(blst, p)) bstate = PETSC_BOOL3_FALSE;
2845 
2846           for (PetscInt i = 0; i < dof - cdof; ++i) pblocks[offset - localStart + i] = dof - cdof;
2847           // Signal block concatenation
2848           if (bstate == PETSC_BOOL3_FALSE && dof - cdof) pblocks[offset - localStart] = -(dof - cdof);
2849         }
2850         dof  = dof < 0 ? -(dof + 1) : dof;
2851         bdof = cdof && (dof - cdof) ? 1 : dof;
2852         if (dof) {
2853           if (bs < 0) {
2854             bs = bdof;
2855           } else if (bs != bdof) {
2856             bs = 1;
2857           }
2858         }
2859       } break;
2860       case DM_BLOCKING_FIELD_NODE: {
2861         for (PetscInt field = 0; field < num_fields; field++) {
2862           PetscInt num_comp, bdof, offset;
2863           PetscCall(PetscSectionGetFieldComponents(sectionGlobal, field, &num_comp));
2864           PetscCall(PetscSectionGetFieldDof(sectionGlobal, p, field, &dof));
2865           if (dof < 0) continue;
2866           PetscCall(PetscSectionGetFieldOffset(sectionGlobal, p, field, &offset));
2867           PetscCall(PetscSectionGetFieldConstraintDof(sectionGlobal, p, field, &cdof));
2868           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);
2869           PetscInt num_nodes = dof / num_comp;
2870           for (PetscInt i = 0; i < dof - cdof; i++) pblocks[offset - localStart + i] = (dof - cdof) / num_nodes;
2871           // Handle possibly constant block size (unlikely)
2872           bdof = cdof && (dof - cdof) ? 1 : dof;
2873           if (dof) {
2874             if (bs < 0) {
2875               bs = bdof;
2876             } else if (bs != bdof) {
2877               bs = 1;
2878             }
2879           }
2880         }
2881       } break;
2882       }
2883     }
2884     if (sectionLocal->perm) PetscCall(ISRestoreIndices(sectionLocal->perm, &perm));
2885     /* Must have same blocksize on all procs (some might have no points) */
2886     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs;
2887     bsLocal[1] = bs;
2888     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
2889     if (bsMinMax[0] != bsMinMax[1]) bs = 1;
2890     else bs = bsMinMax[0];
2891     bs = PetscMax(1, bs);
2892     PetscCall(MatSetLocalToGlobalMapping(*J, ltog, ltog));
2893     if (dm->prealloc_skip) { // User will likely use MatSetPreallocationCOO(), but still set structural parameters
2894       PetscCall(MatSetBlockSize(*J, bs));
2895       PetscCall(MatSetUp(*J));
2896     } else {
2897       PetscCall(PetscCalloc4(localSize / bs, &dnz, localSize / bs, &onz, localSize / bs, &dnzu, localSize / bs, &onzu));
2898       PetscCall(DMPlexPreallocateOperator(dm, bs, dnz, onz, dnzu, onzu, *J, fillMatrix));
2899       PetscCall(PetscFree4(dnz, onz, dnzu, onzu));
2900     }
2901     if (pblocks) { // Consolidate blocks
2902       PetscInt nblocks = 0;
2903       pblocks[0]       = PetscAbs(pblocks[0]);
2904       for (PetscInt i = 0; i < localSize; i += PetscMax(1, pblocks[i])) {
2905         if (pblocks[i] == 0) continue;
2906         // Negative block size indicates the blocks should be concatenated
2907         if (pblocks[i] < 0) {
2908           pblocks[i] = -pblocks[i];
2909           pblocks[nblocks - 1] += pblocks[i];
2910         } else {
2911           pblocks[nblocks++] = pblocks[i]; // nblocks always <= i
2912         }
2913         for (PetscInt j = 1; j < pblocks[i]; j++)
2914           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);
2915       }
2916       PetscCall(MatSetVariableBlockSizes(*J, nblocks, pblocks));
2917     }
2918     PetscCall(PetscFree(pblocks));
2919   }
2920   PetscCall(MatSetDM(*J, dm));
2921   PetscFunctionReturn(PETSC_SUCCESS);
2922 }
2923 
2924 /*@
2925   DMPlexGetSubdomainSection - Returns the section associated with the subdomain
2926 
2927   Not Collective
2928 
2929   Input Parameter:
2930 . dm - The `DMPLEX`
2931 
2932   Output Parameter:
2933 . subsection - The subdomain section
2934 
2935   Level: developer
2936 
2937 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `PetscSection`
2938 @*/
2939 PetscErrorCode DMPlexGetSubdomainSection(DM dm, PetscSection *subsection)
2940 {
2941   DM_Plex *mesh = (DM_Plex *)dm->data;
2942 
2943   PetscFunctionBegin;
2944   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2945   if (!mesh->subdomainSection) {
2946     PetscSection section;
2947     PetscSF      sf;
2948 
2949     PetscCall(PetscSFCreate(PETSC_COMM_SELF, &sf));
2950     PetscCall(DMGetLocalSection(dm, &section));
2951     PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_TRUE, PETSC_FALSE, PETSC_TRUE, &mesh->subdomainSection));
2952     PetscCall(PetscSFDestroy(&sf));
2953   }
2954   *subsection = mesh->subdomainSection;
2955   PetscFunctionReturn(PETSC_SUCCESS);
2956 }
2957 
2958 /*@
2959   DMPlexGetChart - Return the interval for all mesh points [`pStart`, `pEnd`)
2960 
2961   Not Collective
2962 
2963   Input Parameter:
2964 . dm - The `DMPLEX`
2965 
2966   Output Parameters:
2967 + pStart - The first mesh point
2968 - pEnd   - The upper bound for mesh points
2969 
2970   Level: beginner
2971 
2972 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetChart()`
2973 @*/
2974 PetscErrorCode DMPlexGetChart(DM dm, PetscInt *pStart, PetscInt *pEnd)
2975 {
2976   DM_Plex *mesh = (DM_Plex *)dm->data;
2977 
2978   PetscFunctionBegin;
2979   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2980   if (mesh->tr) PetscCall(DMPlexTransformGetChart(mesh->tr, pStart, pEnd));
2981   else PetscCall(PetscSectionGetChart(mesh->coneSection, pStart, pEnd));
2982   PetscFunctionReturn(PETSC_SUCCESS);
2983 }
2984 
2985 /*@
2986   DMPlexSetChart - Set the interval for all mesh points [`pStart`, `pEnd`)
2987 
2988   Not Collective
2989 
2990   Input Parameters:
2991 + dm     - The `DMPLEX`
2992 . pStart - The first mesh point
2993 - pEnd   - The upper bound for mesh points
2994 
2995   Level: beginner
2996 
2997 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetChart()`
2998 @*/
2999 PetscErrorCode DMPlexSetChart(DM dm, PetscInt pStart, PetscInt pEnd)
3000 {
3001   DM_Plex *mesh = (DM_Plex *)dm->data;
3002 
3003   PetscFunctionBegin;
3004   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3005   PetscCall(PetscSectionSetChart(mesh->coneSection, pStart, pEnd));
3006   PetscCall(PetscSectionSetChart(mesh->supportSection, pStart, pEnd));
3007   PetscCall(PetscFree(mesh->cellTypes));
3008   PetscFunctionReturn(PETSC_SUCCESS);
3009 }
3010 
3011 /*@
3012   DMPlexGetConeSize - Return the number of in-edges for this point in the DAG
3013 
3014   Not Collective
3015 
3016   Input Parameters:
3017 + dm - The `DMPLEX`
3018 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3019 
3020   Output Parameter:
3021 . size - The cone size for point `p`
3022 
3023   Level: beginner
3024 
3025 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
3026 @*/
3027 PetscErrorCode DMPlexGetConeSize(DM dm, PetscInt p, PetscInt *size)
3028 {
3029   DM_Plex *mesh = (DM_Plex *)dm->data;
3030 
3031   PetscFunctionBegin;
3032   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3033   PetscAssertPointer(size, 3);
3034   if (mesh->tr) PetscCall(DMPlexTransformGetConeSize(mesh->tr, p, size));
3035   else PetscCall(PetscSectionGetDof(mesh->coneSection, p, size));
3036   PetscFunctionReturn(PETSC_SUCCESS);
3037 }
3038 
3039 /*@
3040   DMPlexSetConeSize - Set the number of in-edges for this point in the DAG
3041 
3042   Not Collective
3043 
3044   Input Parameters:
3045 + dm   - The `DMPLEX`
3046 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3047 - size - The cone size for point `p`
3048 
3049   Level: beginner
3050 
3051   Note:
3052   This should be called after `DMPlexSetChart()`.
3053 
3054 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetCone()`, `DMPlexCreate()`, `DMPlexGetConeSize()`, `DMPlexSetChart()`
3055 @*/
3056 PetscErrorCode DMPlexSetConeSize(DM dm, PetscInt p, PetscInt size)
3057 {
3058   DM_Plex *mesh = (DM_Plex *)dm->data;
3059 
3060   PetscFunctionBegin;
3061   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3062   PetscCheck(!mesh->tr, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Cannot call DMPlexSetConeSize() on a mesh with a transform defined.");
3063   PetscCall(PetscSectionSetDof(mesh->coneSection, p, size));
3064   PetscFunctionReturn(PETSC_SUCCESS);
3065 }
3066 
3067 /*@C
3068   DMPlexGetCone - Return the points on the in-edges for this point in the DAG
3069 
3070   Not Collective
3071 
3072   Input Parameters:
3073 + dm - The `DMPLEX`
3074 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3075 
3076   Output Parameter:
3077 . cone - An array of points which are on the in-edges for point `p`, the length of `cone` is the result of `DMPlexGetConeSize()`
3078 
3079   Level: beginner
3080 
3081   Fortran Notes:
3082   `cone` must be declared with
3083 .vb
3084   PetscInt, pointer :: cone(:)
3085 .ve
3086 
3087   You must also call `DMPlexRestoreCone()` after you finish using the array.
3088   `DMPlexRestoreCone()` is not needed/available in C.
3089 
3090 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSize()`, `DMPlexSetCone()`, `DMPlexGetConeTuple()`, `DMPlexSetChart()`, `DMPlexRestoreCone()`
3091 @*/
3092 PetscErrorCode DMPlexGetCone(DM dm, PetscInt p, const PetscInt *cone[])
3093 {
3094   DM_Plex *mesh = (DM_Plex *)dm->data;
3095   PetscInt off;
3096 
3097   PetscFunctionBegin;
3098   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3099   PetscAssertPointer(cone, 3);
3100   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3101   *cone = PetscSafePointerPlusOffset(mesh->cones, off);
3102   PetscFunctionReturn(PETSC_SUCCESS);
3103 }
3104 
3105 /*@
3106   DMPlexGetConeTuple - Return the points on the in-edges of several points in the DAG
3107 
3108   Not Collective
3109 
3110   Input Parameters:
3111 + dm - The `DMPLEX`
3112 - p  - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3113 
3114   Output Parameters:
3115 + pConesSection - `PetscSection` describing the layout of `pCones`
3116 - pCones        - An `IS` containing the points which are on the in-edges for the point set `p`
3117 
3118   Level: intermediate
3119 
3120 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeRecursive()`, `DMPlexSetChart()`, `PetscSection`, `IS`
3121 @*/
3122 PetscErrorCode DMPlexGetConeTuple(DM dm, IS p, PetscSection *pConesSection, IS *pCones)
3123 {
3124   PetscSection cs, newcs;
3125   PetscInt    *cones;
3126   PetscInt    *newarr = NULL;
3127   PetscInt     n;
3128 
3129   PetscFunctionBegin;
3130   PetscCall(DMPlexGetCones(dm, &cones));
3131   PetscCall(DMPlexGetConeSection(dm, &cs));
3132   PetscCall(PetscSectionExtractDofsFromArray(cs, MPIU_INT, cones, p, &newcs, pCones ? ((void **)&newarr) : NULL));
3133   if (pConesSection) *pConesSection = newcs;
3134   if (pCones) {
3135     PetscCall(PetscSectionGetStorageSize(newcs, &n));
3136     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)p), n, newarr, PETSC_OWN_POINTER, pCones));
3137   }
3138   PetscFunctionReturn(PETSC_SUCCESS);
3139 }
3140 
3141 /*@
3142   DMPlexGetConeRecursiveVertices - Expand each given point into its cone points and do that recursively until we end up just with vertices.
3143 
3144   Not Collective
3145 
3146   Input Parameters:
3147 + dm     - The `DMPLEX`
3148 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3149 
3150   Output Parameter:
3151 . expandedPoints - An `IS` containing the of vertices recursively expanded from input points
3152 
3153   Level: advanced
3154 
3155   Notes:
3156   Like `DMPlexGetConeRecursive()` but returns only the 0-depth `IS` (i.e. vertices only) and no sections.
3157 
3158   There is no corresponding Restore function, just call `ISDestroy()` on the returned `IS` to deallocate.
3159 
3160 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexRestoreConeRecursive()`,
3161           `DMPlexGetDepth()`, `IS`
3162 @*/
3163 PetscErrorCode DMPlexGetConeRecursiveVertices(DM dm, IS points, IS *expandedPoints)
3164 {
3165   IS      *expandedPointsAll;
3166   PetscInt depth;
3167 
3168   PetscFunctionBegin;
3169   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3170   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
3171   PetscAssertPointer(expandedPoints, 3);
3172   PetscCall(DMPlexGetConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
3173   *expandedPoints = expandedPointsAll[0];
3174   PetscCall(PetscObjectReference((PetscObject)expandedPointsAll[0]));
3175   PetscCall(DMPlexRestoreConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
3176   PetscFunctionReturn(PETSC_SUCCESS);
3177 }
3178 
3179 /*@
3180   DMPlexGetConeRecursive - Expand each given point into its cone points and do that recursively until we end up just with vertices
3181   (DAG points of depth 0, i.e., without cones).
3182 
3183   Not Collective
3184 
3185   Input Parameters:
3186 + dm     - The `DMPLEX`
3187 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3188 
3189   Output Parameters:
3190 + depth          - (optional) Size of the output arrays, equal to `DMPLEX` depth, returned by `DMPlexGetDepth()`
3191 . expandedPoints - (optional) An array of index sets with recursively expanded cones
3192 - sections       - (optional) An array of sections which describe mappings from points to their cone points
3193 
3194   Level: advanced
3195 
3196   Notes:
3197   Like `DMPlexGetConeTuple()` but recursive.
3198 
3199   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.
3200   For example, for d=0 it contains only vertices, for d=1 it can contain vertices and edges, etc.
3201 
3202   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\:
3203   (1) DAG points in `expandedPoints`[d+1] with `depth` d+1 to their cone points in `expandedPoints`[d];
3204   (2) DAG points in `expandedPoints`[d+1] with `depth` in [0,d] to the same points in `expandedPoints`[d].
3205 
3206 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexRestoreConeRecursive()`, `DMPlexGetConeRecursiveVertices()`,
3207           `DMPlexGetDepth()`, `PetscSection`, `IS`
3208 @*/
3209 PetscErrorCode DMPlexGetConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
3210 {
3211   const PetscInt *arr0 = NULL, *cone = NULL;
3212   PetscInt       *arr = NULL, *newarr = NULL;
3213   PetscInt        d, depth_, i, n, newn, cn, co, start, end;
3214   IS             *expandedPoints_;
3215   PetscSection   *sections_;
3216 
3217   PetscFunctionBegin;
3218   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3219   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
3220   if (depth) PetscAssertPointer(depth, 3);
3221   if (expandedPoints) PetscAssertPointer(expandedPoints, 4);
3222   if (sections) PetscAssertPointer(sections, 5);
3223   PetscCall(ISGetLocalSize(points, &n));
3224   PetscCall(ISGetIndices(points, &arr0));
3225   PetscCall(DMPlexGetDepth(dm, &depth_));
3226   PetscCall(PetscCalloc1(depth_, &expandedPoints_));
3227   PetscCall(PetscCalloc1(depth_, &sections_));
3228   arr = (PetscInt *)arr0; /* this is ok because first generation of arr is not modified */
3229   for (d = depth_ - 1; d >= 0; d--) {
3230     PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &sections_[d]));
3231     PetscCall(PetscSectionSetChart(sections_[d], 0, n));
3232     for (i = 0; i < n; i++) {
3233       PetscCall(DMPlexGetDepthStratum(dm, d + 1, &start, &end));
3234       if (arr[i] >= start && arr[i] < end) {
3235         PetscCall(DMPlexGetConeSize(dm, arr[i], &cn));
3236         PetscCall(PetscSectionSetDof(sections_[d], i, cn));
3237       } else {
3238         PetscCall(PetscSectionSetDof(sections_[d], i, 1));
3239       }
3240     }
3241     PetscCall(PetscSectionSetUp(sections_[d]));
3242     PetscCall(PetscSectionGetStorageSize(sections_[d], &newn));
3243     PetscCall(PetscMalloc1(newn, &newarr));
3244     for (i = 0; i < n; i++) {
3245       PetscCall(PetscSectionGetDof(sections_[d], i, &cn));
3246       PetscCall(PetscSectionGetOffset(sections_[d], i, &co));
3247       if (cn > 1) {
3248         PetscCall(DMPlexGetCone(dm, arr[i], &cone));
3249         PetscCall(PetscMemcpy(&newarr[co], cone, cn * sizeof(PetscInt)));
3250       } else {
3251         newarr[co] = arr[i];
3252       }
3253     }
3254     PetscCall(ISCreateGeneral(PETSC_COMM_SELF, newn, newarr, PETSC_OWN_POINTER, &expandedPoints_[d]));
3255     arr = newarr;
3256     n   = newn;
3257   }
3258   PetscCall(ISRestoreIndices(points, &arr0));
3259   *depth = depth_;
3260   if (expandedPoints) *expandedPoints = expandedPoints_;
3261   else {
3262     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&expandedPoints_[d]));
3263     PetscCall(PetscFree(expandedPoints_));
3264   }
3265   if (sections) *sections = sections_;
3266   else {
3267     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&sections_[d]));
3268     PetscCall(PetscFree(sections_));
3269   }
3270   PetscFunctionReturn(PETSC_SUCCESS);
3271 }
3272 
3273 /*@
3274   DMPlexRestoreConeRecursive - Deallocates arrays created by `DMPlexGetConeRecursive()`
3275 
3276   Not Collective
3277 
3278   Input Parameters:
3279 + dm     - The `DMPLEX`
3280 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3281 
3282   Output Parameters:
3283 + depth          - (optional) Size of the output arrays, equal to `DMPLEX` depth, returned by `DMPlexGetDepth()`
3284 . expandedPoints - (optional) An array of recursively expanded cones
3285 - sections       - (optional) An array of sections which describe mappings from points to their cone points
3286 
3287   Level: advanced
3288 
3289   Note:
3290   See `DMPlexGetConeRecursive()`
3291 
3292 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexGetConeRecursiveVertices()`,
3293           `DMPlexGetDepth()`, `IS`, `PetscSection`
3294 @*/
3295 PetscErrorCode DMPlexRestoreConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
3296 {
3297   PetscInt d, depth_;
3298 
3299   PetscFunctionBegin;
3300   PetscCall(DMPlexGetDepth(dm, &depth_));
3301   PetscCheck(!depth || *depth == depth_, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "depth changed since last call to DMPlexGetConeRecursive");
3302   if (depth) *depth = 0;
3303   if (expandedPoints) {
3304     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&((*expandedPoints)[d])));
3305     PetscCall(PetscFree(*expandedPoints));
3306   }
3307   if (sections) {
3308     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&((*sections)[d])));
3309     PetscCall(PetscFree(*sections));
3310   }
3311   PetscFunctionReturn(PETSC_SUCCESS);
3312 }
3313 
3314 /*@
3315   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
3316 
3317   Not Collective
3318 
3319   Input Parameters:
3320 + dm   - The `DMPLEX`
3321 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3322 - cone - An array of points which are on the in-edges for point `p`, its length must have been previously provided with `DMPlexSetConeSize()`
3323 
3324   Level: beginner
3325 
3326   Note:
3327   This should be called after all calls to `DMPlexSetConeSize()` and `DMSetUp()`.
3328 
3329 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`, `DMPlexSetSupport()`, `DMPlexSetSupportSize()`
3330 @*/
3331 PetscErrorCode DMPlexSetCone(DM dm, PetscInt p, const PetscInt cone[])
3332 {
3333   DM_Plex *mesh = (DM_Plex *)dm->data;
3334   PetscInt dof, off, c;
3335 
3336   PetscFunctionBegin;
3337   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3338   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3339   if (dof) PetscAssertPointer(cone, 3);
3340   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3341   if (PetscDefined(USE_DEBUG)) {
3342     PetscInt pStart, pEnd;
3343     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3344     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);
3345     for (c = 0; c < dof; ++c) {
3346       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);
3347       mesh->cones[off + c] = cone[c];
3348     }
3349   } else {
3350     for (c = 0; c < dof; ++c) mesh->cones[off + c] = cone[c];
3351   }
3352   PetscFunctionReturn(PETSC_SUCCESS);
3353 }
3354 
3355 /*@C
3356   DMPlexGetConeOrientation - Return the orientations on the in-edges for this point in the DAG
3357 
3358   Not Collective
3359 
3360   Input Parameters:
3361 + dm - The `DMPLEX`
3362 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3363 
3364   Output Parameter:
3365 . coneOrientation - An array of orientations which are on the in-edges for point `p`. An orientation is an
3366                     integer giving the prescription for cone traversal. Its length is given by the result of `DMPlexSetConeSize()`
3367 
3368   Level: beginner
3369 
3370   Note:
3371   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3372   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3373   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3374   with the identity.
3375 
3376   Fortran Notes:
3377   You must call `DMPlexRestoreConeOrientation()` after you finish using the returned array.
3378   `DMPlexRestoreConeOrientation()` is not needed/available in C.
3379 
3380 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetConeSize()`, `DMPolytopeTypeComposeOrientation()`, `DMPolytopeTypeComposeOrientationInv()`,
3381           `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetCone()`, `DMPlexSetChart()`
3382 @*/
3383 PetscErrorCode DMPlexGetConeOrientation(DM dm, PetscInt p, const PetscInt *coneOrientation[])
3384 {
3385   DM_Plex *mesh = (DM_Plex *)dm->data;
3386   PetscInt off;
3387 
3388   PetscFunctionBegin;
3389   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3390   if (PetscDefined(USE_DEBUG)) {
3391     PetscInt dof;
3392     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3393     if (dof) PetscAssertPointer(coneOrientation, 3);
3394   }
3395   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3396 
3397   *coneOrientation = &mesh->coneOrientations[off];
3398   PetscFunctionReturn(PETSC_SUCCESS);
3399 }
3400 
3401 /*@
3402   DMPlexSetConeOrientation - Set the orientations on the in-edges for this point in the DAG
3403 
3404   Not Collective
3405 
3406   Input Parameters:
3407 + dm              - The `DMPLEX`
3408 . p               - The point, which must lie in the chart set with `DMPlexSetChart()`
3409 - coneOrientation - An array of orientations. Its length is given by the result of `DMPlexSetConeSize()`
3410 
3411   Level: beginner
3412 
3413   Notes:
3414   This should be called after all calls to `DMPlexSetConeSize()` and `DMSetUp()`.
3415 
3416   The meaning of coneOrientation is detailed in `DMPlexGetConeOrientation()`.
3417 
3418 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetConeOrientation()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3419 @*/
3420 PetscErrorCode DMPlexSetConeOrientation(DM dm, PetscInt p, const PetscInt coneOrientation[])
3421 {
3422   DM_Plex *mesh = (DM_Plex *)dm->data;
3423   PetscInt pStart, pEnd;
3424   PetscInt dof, off, c;
3425 
3426   PetscFunctionBegin;
3427   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3428   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3429   if (dof) PetscAssertPointer(coneOrientation, 3);
3430   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3431   if (PetscDefined(USE_DEBUG)) {
3432     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3433     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);
3434     for (c = 0; c < dof; ++c) {
3435       PetscInt cdof, o = coneOrientation[c];
3436 
3437       PetscCall(PetscSectionGetDof(mesh->coneSection, mesh->cones[off + c], &cdof));
3438       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);
3439       mesh->coneOrientations[off + c] = o;
3440     }
3441   } else {
3442     for (c = 0; c < dof; ++c) mesh->coneOrientations[off + c] = coneOrientation[c];
3443   }
3444   PetscFunctionReturn(PETSC_SUCCESS);
3445 }
3446 
3447 /*@
3448   DMPlexInsertCone - Insert a point into the in-edges for the point p in the DAG
3449 
3450   Not Collective
3451 
3452   Input Parameters:
3453 + dm        - The `DMPLEX`
3454 . p         - The point, which must lie in the chart set with `DMPlexSetChart()`
3455 . conePos   - The local index in the cone where the point should be put
3456 - conePoint - The mesh point to insert
3457 
3458   Level: beginner
3459 
3460 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3461 @*/
3462 PetscErrorCode DMPlexInsertCone(DM dm, PetscInt p, PetscInt conePos, PetscInt conePoint)
3463 {
3464   DM_Plex *mesh = (DM_Plex *)dm->data;
3465   PetscInt pStart, pEnd;
3466   PetscInt dof, off;
3467 
3468   PetscFunctionBegin;
3469   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3470   if (PetscDefined(USE_DEBUG)) {
3471     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3472     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);
3473     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);
3474     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3475     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);
3476   }
3477   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3478   mesh->cones[off + conePos] = conePoint;
3479   PetscFunctionReturn(PETSC_SUCCESS);
3480 }
3481 
3482 /*@
3483   DMPlexInsertConeOrientation - Insert a point orientation for the in-edge for the point p in the DAG
3484 
3485   Not Collective
3486 
3487   Input Parameters:
3488 + dm              - The `DMPLEX`
3489 . p               - The point, which must lie in the chart set with `DMPlexSetChart()`
3490 . conePos         - The local index in the cone where the point should be put
3491 - coneOrientation - The point orientation to insert
3492 
3493   Level: beginner
3494 
3495   Note:
3496   The meaning of coneOrientation values is detailed in `DMPlexGetConeOrientation()`.
3497 
3498 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3499 @*/
3500 PetscErrorCode DMPlexInsertConeOrientation(DM dm, PetscInt p, PetscInt conePos, PetscInt coneOrientation)
3501 {
3502   DM_Plex *mesh = (DM_Plex *)dm->data;
3503   PetscInt pStart, pEnd;
3504   PetscInt dof, off;
3505 
3506   PetscFunctionBegin;
3507   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3508   if (PetscDefined(USE_DEBUG)) {
3509     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3510     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);
3511     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3512     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);
3513   }
3514   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3515   mesh->coneOrientations[off + conePos] = coneOrientation;
3516   PetscFunctionReturn(PETSC_SUCCESS);
3517 }
3518 
3519 /*@C
3520   DMPlexGetOrientedCone - Return the points and orientations on the in-edges for this point in the DAG
3521 
3522   Not collective
3523 
3524   Input Parameters:
3525 + dm - The DMPlex
3526 - p  - The point, which must lie in the chart set with DMPlexSetChart()
3527 
3528   Output Parameters:
3529 + cone - An array of points which are on the in-edges for point `p`
3530 - ornt - An array of orientations which are on the in-edges for point `p`. An orientation is an
3531          integer giving the prescription for cone traversal.
3532 
3533   Level: beginner
3534 
3535   Notes:
3536   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3537   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3538   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3539   with the identity.
3540 
3541   You must also call `DMPlexRestoreOrientedCone()` after you finish using the returned array.
3542 
3543   Fortran Notes:
3544   `cone` and `ornt` must be declared with
3545 .vb
3546   PetscInt, pointer :: cone(:)
3547   PetscInt, pointer :: ornt(:)
3548 .ve
3549 
3550 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreOrientedCone()`, `DMPlexGetConeSize()`, `DMPlexGetCone()`, `DMPlexGetChart()`
3551 @*/
3552 PetscErrorCode DMPlexGetOrientedCone(DM dm, PetscInt p, const PetscInt *cone[], const PetscInt *ornt[])
3553 {
3554   DM_Plex *mesh = (DM_Plex *)dm->data;
3555 
3556   PetscFunctionBegin;
3557   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3558   if (mesh->tr) {
3559     PetscCall(DMPlexTransformGetCone(mesh->tr, p, cone, ornt));
3560   } else {
3561     PetscInt off;
3562     if (PetscDefined(USE_DEBUG)) {
3563       PetscInt dof;
3564       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3565       if (dof) {
3566         if (cone) PetscAssertPointer(cone, 3);
3567         if (ornt) PetscAssertPointer(ornt, 4);
3568       }
3569     }
3570     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3571     if (cone) *cone = PetscSafePointerPlusOffset(mesh->cones, off);
3572     if (ornt) *ornt = PetscSafePointerPlusOffset(mesh->coneOrientations, off);
3573   }
3574   PetscFunctionReturn(PETSC_SUCCESS);
3575 }
3576 
3577 /*@C
3578   DMPlexRestoreOrientedCone - Restore the points and orientations on the in-edges for this point in the DAG obtained with `DMPlexGetOrientedCone()`
3579 
3580   Not Collective
3581 
3582   Input Parameters:
3583 + dm   - The DMPlex
3584 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3585 . cone - An array of points which are on the in-edges for point p
3586 - ornt - An array of orientations which are on the in-edges for point `p`. An orientation is an
3587          integer giving the prescription for cone traversal.
3588 
3589   Level: beginner
3590 
3591 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetOrientedCone()`, `DMPlexGetConeSize()`, `DMPlexGetCone()`, `DMPlexGetChart()`
3592 @*/
3593 PetscErrorCode DMPlexRestoreOrientedCone(DM dm, PetscInt p, const PetscInt *cone[], const PetscInt *ornt[])
3594 {
3595   DM_Plex *mesh = (DM_Plex *)dm->data;
3596 
3597   PetscFunctionBegin;
3598   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3599   if (mesh->tr) PetscCall(DMPlexTransformRestoreCone(mesh->tr, p, cone, ornt));
3600   PetscFunctionReturn(PETSC_SUCCESS);
3601 }
3602 
3603 /*@
3604   DMPlexGetSupportSize - Return the number of out-edges for this point in the DAG
3605 
3606   Not Collective
3607 
3608   Input Parameters:
3609 + dm - The `DMPLEX`
3610 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3611 
3612   Output Parameter:
3613 . size - The support size for point `p`
3614 
3615   Level: beginner
3616 
3617 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`, `DMPlexGetConeSize()`
3618 @*/
3619 PetscErrorCode DMPlexGetSupportSize(DM dm, PetscInt p, PetscInt *size)
3620 {
3621   DM_Plex *mesh = (DM_Plex *)dm->data;
3622 
3623   PetscFunctionBegin;
3624   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3625   PetscAssertPointer(size, 3);
3626   PetscCall(PetscSectionGetDof(mesh->supportSection, p, size));
3627   PetscFunctionReturn(PETSC_SUCCESS);
3628 }
3629 
3630 /*@
3631   DMPlexSetSupportSize - Set the number of out-edges for this point in the DAG
3632 
3633   Not Collective
3634 
3635   Input Parameters:
3636 + dm   - The `DMPLEX`
3637 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3638 - size - The support size for point `p`
3639 
3640   Level: beginner
3641 
3642   Note:
3643   This should be called after `DMPlexSetChart()`.
3644 
3645 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetSupportSize()`, `DMPlexSetChart()`
3646 @*/
3647 PetscErrorCode DMPlexSetSupportSize(DM dm, PetscInt p, PetscInt size)
3648 {
3649   DM_Plex *mesh = (DM_Plex *)dm->data;
3650 
3651   PetscFunctionBegin;
3652   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3653   PetscCall(PetscSectionSetDof(mesh->supportSection, p, size));
3654   PetscFunctionReturn(PETSC_SUCCESS);
3655 }
3656 
3657 /*@C
3658   DMPlexGetSupport - Return the points on the out-edges for this point in the DAG
3659 
3660   Not Collective
3661 
3662   Input Parameters:
3663 + dm - The `DMPLEX`
3664 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3665 
3666   Output Parameter:
3667 . support - An array of points which are on the out-edges for point `p`, its length is that obtained from `DMPlexGetSupportSize()`
3668 
3669   Level: beginner
3670 
3671   Fortran Notes:
3672   `support` must be declared with
3673 .vb
3674   PetscInt, pointer :: support(:)
3675 .ve
3676 
3677   You must also call `DMPlexRestoreSupport()` after you finish using the returned array.
3678   `DMPlexRestoreSupport()` is not needed/available in C.
3679 
3680 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSupportSize()`, `DMPlexSetSupport()`, `DMPlexGetCone()`, `DMPlexSetChart()`
3681 @*/
3682 PetscErrorCode DMPlexGetSupport(DM dm, PetscInt p, const PetscInt *support[])
3683 {
3684   DM_Plex *mesh = (DM_Plex *)dm->data;
3685   PetscInt off;
3686 
3687   PetscFunctionBegin;
3688   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3689   PetscAssertPointer(support, 3);
3690   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3691   *support = PetscSafePointerPlusOffset(mesh->supports, off);
3692   PetscFunctionReturn(PETSC_SUCCESS);
3693 }
3694 
3695 /*@
3696   DMPlexSetSupport - Set the points on the out-edges for this point in the DAG, that is the list of points that this point covers
3697 
3698   Not Collective
3699 
3700   Input Parameters:
3701 + dm      - The `DMPLEX`
3702 . p       - The point, which must lie in the chart set with `DMPlexSetChart()`
3703 - support - An array of points which are on the out-edges for point `p`, its length is that obtained from `DMPlexGetSupportSize()`
3704 
3705   Level: beginner
3706 
3707   Note:
3708   This should be called after all calls to `DMPlexSetSupportSize()` and `DMSetUp()`.
3709 
3710 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetCone()`, `DMPlexSetConeSize()`, `DMPlexCreate()`, `DMPlexGetSupport()`, `DMPlexSetChart()`, `DMPlexSetSupportSize()`, `DMSetUp()`
3711 @*/
3712 PetscErrorCode DMPlexSetSupport(DM dm, PetscInt p, const PetscInt support[])
3713 {
3714   DM_Plex *mesh = (DM_Plex *)dm->data;
3715   PetscInt pStart, pEnd;
3716   PetscInt dof, off, c;
3717 
3718   PetscFunctionBegin;
3719   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3720   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3721   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3722   if (dof) PetscAssertPointer(support, 3);
3723   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3724   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);
3725   for (c = 0; c < dof; ++c) {
3726     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);
3727     mesh->supports[off + c] = support[c];
3728   }
3729   PetscFunctionReturn(PETSC_SUCCESS);
3730 }
3731 
3732 /*@
3733   DMPlexInsertSupport - Insert a point into the out-edges for the point p in the DAG
3734 
3735   Not Collective
3736 
3737   Input Parameters:
3738 + dm           - The `DMPLEX`
3739 . p            - The point, which must lie in the chart set with `DMPlexSetChart()`
3740 . supportPos   - The local index in the cone where the point should be put
3741 - supportPoint - The mesh point to insert
3742 
3743   Level: beginner
3744 
3745 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3746 @*/
3747 PetscErrorCode DMPlexInsertSupport(DM dm, PetscInt p, PetscInt supportPos, PetscInt supportPoint)
3748 {
3749   DM_Plex *mesh = (DM_Plex *)dm->data;
3750   PetscInt pStart, pEnd;
3751   PetscInt dof, off;
3752 
3753   PetscFunctionBegin;
3754   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3755   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3756   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3757   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3758   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);
3759   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);
3760   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);
3761   mesh->supports[off + supportPos] = supportPoint;
3762   PetscFunctionReturn(PETSC_SUCCESS);
3763 }
3764 
3765 /* Converts an orientation o in the current numbering to the previous scheme used in Plex */
3766 PetscInt DMPolytopeConvertNewOrientation_Internal(DMPolytopeType ct, PetscInt o)
3767 {
3768   switch (ct) {
3769   case DM_POLYTOPE_SEGMENT:
3770     if (o == -1) return -2;
3771     break;
3772   case DM_POLYTOPE_TRIANGLE:
3773     if (o == -3) return -1;
3774     if (o == -2) return -3;
3775     if (o == -1) return -2;
3776     break;
3777   case DM_POLYTOPE_QUADRILATERAL:
3778     if (o == -4) return -2;
3779     if (o == -3) return -1;
3780     if (o == -2) return -4;
3781     if (o == -1) return -3;
3782     break;
3783   default:
3784     return o;
3785   }
3786   return o;
3787 }
3788 
3789 /* Converts an orientation o in the previous scheme used in Plex to the current numbering */
3790 PetscInt DMPolytopeConvertOldOrientation_Internal(DMPolytopeType ct, PetscInt o)
3791 {
3792   switch (ct) {
3793   case DM_POLYTOPE_SEGMENT:
3794     if ((o == -2) || (o == 1)) return -1;
3795     if (o == -1) return 0;
3796     break;
3797   case DM_POLYTOPE_TRIANGLE:
3798     if (o == -3) return -2;
3799     if (o == -2) return -1;
3800     if (o == -1) return -3;
3801     break;
3802   case DM_POLYTOPE_QUADRILATERAL:
3803     if (o == -4) return -2;
3804     if (o == -3) return -1;
3805     if (o == -2) return -4;
3806     if (o == -1) return -3;
3807     break;
3808   default:
3809     return o;
3810   }
3811   return o;
3812 }
3813 
3814 /* Takes in a mesh whose orientations are in the previous scheme and converts them all to the current numbering */
3815 PetscErrorCode DMPlexConvertOldOrientations_Internal(DM dm)
3816 {
3817   PetscInt pStart, pEnd, p;
3818 
3819   PetscFunctionBegin;
3820   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3821   for (p = pStart; p < pEnd; ++p) {
3822     const PetscInt *cone, *ornt;
3823     PetscInt        coneSize, c;
3824 
3825     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
3826     PetscCall(DMPlexGetCone(dm, p, &cone));
3827     PetscCall(DMPlexGetConeOrientation(dm, p, &ornt));
3828     for (c = 0; c < coneSize; ++c) {
3829       DMPolytopeType ct;
3830       const PetscInt o = ornt[c];
3831 
3832       PetscCall(DMPlexGetCellType(dm, cone[c], &ct));
3833       switch (ct) {
3834       case DM_POLYTOPE_SEGMENT:
3835         if ((o == -2) || (o == 1)) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3836         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, 0));
3837         break;
3838       case DM_POLYTOPE_TRIANGLE:
3839         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3840         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3841         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3842         break;
3843       case DM_POLYTOPE_QUADRILATERAL:
3844         if (o == -4) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3845         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3846         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -4));
3847         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3848         break;
3849       default:
3850         break;
3851       }
3852     }
3853   }
3854   PetscFunctionReturn(PETSC_SUCCESS);
3855 }
3856 
3857 static inline PetscErrorCode DMPlexGetTransitiveClosure_Hot_Private(DM dm, PetscInt p, PetscBool useCone, PetscInt *size, const PetscInt *arr[], const PetscInt *ornt[])
3858 {
3859   DM_Plex *mesh = (DM_Plex *)dm->data;
3860 
3861   PetscFunctionBeginHot;
3862   if (PetscDefined(USE_DEBUG) || mesh->tr) {
3863     if (useCone) {
3864       PetscCall(DMPlexGetConeSize(dm, p, size));
3865       PetscCall(DMPlexGetOrientedCone(dm, p, arr, ornt));
3866     } else {
3867       PetscCall(DMPlexGetSupportSize(dm, p, size));
3868       PetscCall(DMPlexGetSupport(dm, p, arr));
3869     }
3870   } else {
3871     if (useCone) {
3872       const PetscSection s   = mesh->coneSection;
3873       const PetscInt     ps  = p - s->pStart;
3874       const PetscInt     off = s->atlasOff[ps];
3875 
3876       *size = s->atlasDof[ps];
3877       *arr  = mesh->cones + off;
3878       *ornt = mesh->coneOrientations + off;
3879     } else {
3880       const PetscSection s   = mesh->supportSection;
3881       const PetscInt     ps  = p - s->pStart;
3882       const PetscInt     off = s->atlasOff[ps];
3883 
3884       *size = s->atlasDof[ps];
3885       *arr  = mesh->supports + off;
3886     }
3887   }
3888   PetscFunctionReturn(PETSC_SUCCESS);
3889 }
3890 
3891 static inline PetscErrorCode DMPlexRestoreTransitiveClosure_Hot_Private(DM dm, PetscInt p, PetscBool useCone, PetscInt *size, const PetscInt *arr[], const PetscInt *ornt[])
3892 {
3893   DM_Plex *mesh = (DM_Plex *)dm->data;
3894 
3895   PetscFunctionBeginHot;
3896   if (PetscDefined(USE_DEBUG) || mesh->tr) {
3897     if (useCone) PetscCall(DMPlexRestoreOrientedCone(dm, p, arr, ornt));
3898   }
3899   PetscFunctionReturn(PETSC_SUCCESS);
3900 }
3901 
3902 static PetscErrorCode DMPlexGetTransitiveClosure_Depth1_Private(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3903 {
3904   DMPolytopeType  ct = DM_POLYTOPE_UNKNOWN;
3905   PetscInt       *closure;
3906   const PetscInt *tmp = NULL, *tmpO = NULL;
3907   PetscInt        off = 0, tmpSize, t;
3908 
3909   PetscFunctionBeginHot;
3910   if (ornt) {
3911     PetscCall(DMPlexGetCellType(dm, p, &ct));
3912     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;
3913   }
3914   if (*points) {
3915     closure = *points;
3916   } else {
3917     PetscInt maxConeSize, maxSupportSize;
3918     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3919     PetscCall(DMGetWorkArray(dm, 2 * (PetscMax(maxConeSize, maxSupportSize) + 1), MPIU_INT, &closure));
3920   }
3921   PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, p, useCone, &tmpSize, &tmp, &tmpO));
3922   if (ct == DM_POLYTOPE_UNKNOWN) {
3923     closure[off++] = p;
3924     closure[off++] = 0;
3925     for (t = 0; t < tmpSize; ++t) {
3926       closure[off++] = tmp[t];
3927       closure[off++] = tmpO ? tmpO[t] : 0;
3928     }
3929   } else {
3930     const PetscInt *arr = DMPolytopeTypeGetArrangement(ct, ornt);
3931 
3932     /* We assume that cells with a valid type have faces with a valid type */
3933     closure[off++] = p;
3934     closure[off++] = ornt;
3935     for (t = 0; t < tmpSize; ++t) {
3936       DMPolytopeType ft;
3937 
3938       PetscCall(DMPlexGetCellType(dm, tmp[t], &ft));
3939       closure[off++] = tmp[arr[t]];
3940       closure[off++] = tmpO ? DMPolytopeTypeComposeOrientation(ft, ornt, tmpO[t]) : 0;
3941     }
3942   }
3943   PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, p, useCone, &tmpSize, &tmp, &tmpO));
3944   if (numPoints) *numPoints = tmpSize + 1;
3945   if (points) *points = closure;
3946   PetscFunctionReturn(PETSC_SUCCESS);
3947 }
3948 
3949 /* We need a special tensor version because we want to allow duplicate points in the endcaps for hybrid cells */
3950 static PetscErrorCode DMPlexTransitiveClosure_Tensor_Internal(DM dm, PetscInt point, DMPolytopeType ct, PetscInt o, PetscBool useCone, PetscInt *numPoints, PetscInt **points)
3951 {
3952   const PetscInt *arr = DMPolytopeTypeGetArrangement(ct, o);
3953   const PetscInt *cone, *ornt;
3954   PetscInt       *pts, *closure = NULL;
3955   DMPolytopeType  ft;
3956   PetscInt        maxConeSize, maxSupportSize, coneSeries, supportSeries, maxSize;
3957   PetscInt        dim, coneSize, c, d, clSize, cl;
3958 
3959   PetscFunctionBeginHot;
3960   PetscCall(DMGetDimension(dm, &dim));
3961   PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, point, PETSC_TRUE, &coneSize, &cone, &ornt));
3962   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3963   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, dim + 1) - 1) / (maxConeSize - 1)) : dim + 1;
3964   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, dim + 1) - 1) / (maxSupportSize - 1)) : dim + 1;
3965   maxSize       = PetscMax(coneSeries, supportSeries);
3966   if (*points) {
3967     pts = *points;
3968   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &pts));
3969   c        = 0;
3970   pts[c++] = point;
3971   pts[c++] = o;
3972   PetscCall(DMPlexGetCellType(dm, cone[arr[0 * 2 + 0]], &ft));
3973   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[0 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[0 * 2 + 1], ornt[0]), useCone, &clSize, &closure));
3974   for (cl = 0; cl < clSize * 2; cl += 2) {
3975     pts[c++] = closure[cl];
3976     pts[c++] = closure[cl + 1];
3977   }
3978   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[1 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[1 * 2 + 1], ornt[1]), useCone, &clSize, &closure));
3979   for (cl = 0; cl < clSize * 2; cl += 2) {
3980     pts[c++] = closure[cl];
3981     pts[c++] = closure[cl + 1];
3982   }
3983   PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[0], useCone, &clSize, &closure));
3984   for (d = 2; d < coneSize; ++d) {
3985     PetscCall(DMPlexGetCellType(dm, cone[arr[d * 2 + 0]], &ft));
3986     pts[c++] = cone[arr[d * 2 + 0]];
3987     pts[c++] = DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]);
3988   }
3989   PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, point, PETSC_TRUE, &coneSize, &cone, &ornt));
3990   if (dim >= 3) {
3991     for (d = 2; d < coneSize; ++d) {
3992       const PetscInt  fpoint = cone[arr[d * 2 + 0]];
3993       const PetscInt *fcone, *fornt;
3994       PetscInt        fconeSize, fc, i;
3995 
3996       PetscCall(DMPlexGetCellType(dm, fpoint, &ft));
3997       const PetscInt *farr = DMPolytopeTypeGetArrangement(ft, DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]));
3998       PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, fpoint, PETSC_TRUE, &fconeSize, &fcone, &fornt));
3999       for (fc = 0; fc < fconeSize; ++fc) {
4000         const PetscInt cp = fcone[farr[fc * 2 + 0]];
4001         const PetscInt co = farr[fc * 2 + 1];
4002 
4003         for (i = 0; i < c; i += 2)
4004           if (pts[i] == cp) break;
4005         if (i == c) {
4006           PetscCall(DMPlexGetCellType(dm, cp, &ft));
4007           pts[c++] = cp;
4008           pts[c++] = DMPolytopeTypeComposeOrientation(ft, co, fornt[farr[fc * 2 + 0]]);
4009         }
4010       }
4011       PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, fpoint, PETSC_TRUE, &fconeSize, &fcone, &fornt));
4012     }
4013   }
4014   *numPoints = c / 2;
4015   *points    = pts;
4016   PetscFunctionReturn(PETSC_SUCCESS);
4017 }
4018 
4019 PetscErrorCode DMPlexGetTransitiveClosure_Internal(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
4020 {
4021   DMPolytopeType ct;
4022   PetscInt      *closure, *fifo;
4023   PetscInt       closureSize = 0, fifoStart = 0, fifoSize = 0;
4024   PetscInt       maxConeSize, maxSupportSize, coneSeries, supportSeries;
4025   PetscInt       depth, maxSize;
4026 
4027   PetscFunctionBeginHot;
4028   PetscCall(DMPlexGetDepth(dm, &depth));
4029   if (depth == 1) {
4030     PetscCall(DMPlexGetTransitiveClosure_Depth1_Private(dm, p, ornt, useCone, numPoints, points));
4031     PetscFunctionReturn(PETSC_SUCCESS);
4032   }
4033   PetscCall(DMPlexGetCellType(dm, p, &ct));
4034   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;
4035   if (DMPolytopeTypeIsHybrid(ct) && ct != DM_POLYTOPE_POINT_PRISM_TENSOR) {
4036     PetscCall(DMPlexTransitiveClosure_Tensor_Internal(dm, p, ct, ornt, useCone, numPoints, points));
4037     PetscFunctionReturn(PETSC_SUCCESS);
4038   }
4039   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
4040   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, depth + 1) - 1) / (maxConeSize - 1)) : depth + 1;
4041   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, depth + 1) - 1) / (maxSupportSize - 1)) : depth + 1;
4042   maxSize       = PetscMax(coneSeries, supportSeries);
4043   PetscCall(DMGetWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
4044   if (*points) {
4045     closure = *points;
4046   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &closure));
4047   closure[closureSize++] = p;
4048   closure[closureSize++] = ornt;
4049   fifo[fifoSize++]       = p;
4050   fifo[fifoSize++]       = ornt;
4051   fifo[fifoSize++]       = ct;
4052   /* Should kick out early when depth is reached, rather than checking all vertices for empty cones */
4053   while (fifoSize - fifoStart) {
4054     const PetscInt       q    = fifo[fifoStart++];
4055     const PetscInt       o    = fifo[fifoStart++];
4056     const DMPolytopeType qt   = (DMPolytopeType)fifo[fifoStart++];
4057     const PetscInt      *qarr = DMPolytopeTypeGetArrangement(qt, o);
4058     const PetscInt      *tmp, *tmpO = NULL;
4059     PetscInt             tmpSize, t;
4060 
4061     if (PetscDefined(USE_DEBUG)) {
4062       PetscInt nO = DMPolytopeTypeGetNumArrangements(qt) / 2;
4063       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);
4064     }
4065     PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, q, useCone, &tmpSize, &tmp, &tmpO));
4066     for (t = 0; t < tmpSize; ++t) {
4067       const PetscInt ip = useCone && qarr ? qarr[t * 2] : t;
4068       const PetscInt io = useCone && qarr ? qarr[t * 2 + 1] : 0;
4069       const PetscInt cp = tmp[ip];
4070       PetscCall(DMPlexGetCellType(dm, cp, &ct));
4071       const PetscInt co = tmpO ? DMPolytopeTypeComposeOrientation(ct, io, tmpO[ip]) : 0;
4072       PetscInt       c;
4073 
4074       /* Check for duplicate */
4075       for (c = 0; c < closureSize; c += 2) {
4076         if (closure[c] == cp) break;
4077       }
4078       if (c == closureSize) {
4079         closure[closureSize++] = cp;
4080         closure[closureSize++] = co;
4081         fifo[fifoSize++]       = cp;
4082         fifo[fifoSize++]       = co;
4083         fifo[fifoSize++]       = ct;
4084       }
4085     }
4086     PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, q, useCone, &tmpSize, &tmp, &tmpO));
4087   }
4088   PetscCall(DMRestoreWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
4089   if (numPoints) *numPoints = closureSize / 2;
4090   if (points) *points = closure;
4091   PetscFunctionReturn(PETSC_SUCCESS);
4092 }
4093 
4094 /*@C
4095   DMPlexGetTransitiveClosure - Return the points on the transitive closure of the in-edges or out-edges for this point in the DAG
4096 
4097   Not Collective
4098 
4099   Input Parameters:
4100 + dm      - The `DMPLEX`
4101 . p       - The mesh point
4102 - useCone - `PETSC_TRUE` for the closure, otherwise return the star
4103 
4104   Input/Output Parameter:
4105 . points - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...];
4106            if *points is `NULL` on input, internal storage will be returned, use `DMPlexRestoreTransitiveClosure()`,
4107            otherwise the provided array is used to hold the values
4108 
4109   Output Parameter:
4110 . numPoints - The number of points in the closure, so `points` is of size 2*`numPoints`
4111 
4112   Level: beginner
4113 
4114   Note:
4115   If using internal storage (points is `NULL` on input), each call overwrites the last output.
4116 
4117   Fortran Notes:
4118   `points` must be declared with
4119 .vb
4120   PetscInt, pointer :: points(:)
4121 .ve
4122   and is always allocated by the function.
4123 
4124   The `numPoints` argument is not present in the Fortran binding.
4125 
4126 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
4127 @*/
4128 PetscErrorCode DMPlexGetTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
4129 {
4130   PetscFunctionBeginHot;
4131   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4132   if (numPoints) PetscAssertPointer(numPoints, 4);
4133   if (points) PetscAssertPointer(points, 5);
4134   if (PetscDefined(USE_DEBUG)) {
4135     PetscInt pStart, pEnd;
4136     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4137     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);
4138   }
4139   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, p, 0, useCone, numPoints, points));
4140   PetscFunctionReturn(PETSC_SUCCESS);
4141 }
4142 
4143 /*@C
4144   DMPlexRestoreTransitiveClosure - Restore the array of points on the transitive closure of the in-edges or out-edges for this point in the DAG
4145 
4146   Not Collective
4147 
4148   Input Parameters:
4149 + dm        - The `DMPLEX`
4150 . p         - The mesh point
4151 . useCone   - `PETSC_TRUE` for the closure, otherwise return the star
4152 . numPoints - The number of points in the closure, so points[] is of size 2*`numPoints`
4153 - points    - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...]
4154 
4155   Level: beginner
4156 
4157   Note:
4158   If not using internal storage (points is not `NULL` on input), this call is unnecessary
4159 
4160 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
4161 @*/
4162 PetscErrorCode DMPlexRestoreTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
4163 {
4164   PetscFunctionBeginHot;
4165   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4166   if (numPoints) *numPoints = 0;
4167   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, points));
4168   PetscFunctionReturn(PETSC_SUCCESS);
4169 }
4170 
4171 /*@
4172   DMPlexGetMaxSizes - Return the maximum number of in-edges (cone) and out-edges (support) for any point in the DAG
4173 
4174   Not Collective
4175 
4176   Input Parameter:
4177 . dm - The `DMPLEX`
4178 
4179   Output Parameters:
4180 + maxConeSize    - The maximum number of in-edges
4181 - maxSupportSize - The maximum number of out-edges
4182 
4183   Level: beginner
4184 
4185 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
4186 @*/
4187 PetscErrorCode DMPlexGetMaxSizes(DM dm, PetscInt *maxConeSize, PetscInt *maxSupportSize)
4188 {
4189   DM_Plex *mesh = (DM_Plex *)dm->data;
4190 
4191   PetscFunctionBegin;
4192   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4193   if (maxConeSize) PetscCall(PetscSectionGetMaxDof(mesh->coneSection, maxConeSize));
4194   if (maxSupportSize) PetscCall(PetscSectionGetMaxDof(mesh->supportSection, maxSupportSize));
4195   PetscFunctionReturn(PETSC_SUCCESS);
4196 }
4197 
4198 PetscErrorCode DMSetUp_Plex(DM dm)
4199 {
4200   DM_Plex *mesh = (DM_Plex *)dm->data;
4201   PetscInt size, maxSupportSize;
4202 
4203   PetscFunctionBegin;
4204   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4205   PetscCall(PetscSectionSetUp(mesh->coneSection));
4206   PetscCall(PetscSectionGetStorageSize(mesh->coneSection, &size));
4207   PetscCall(PetscMalloc1(size, &mesh->cones));
4208   PetscCall(PetscCalloc1(size, &mesh->coneOrientations));
4209   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
4210   if (maxSupportSize) {
4211     PetscCall(PetscSectionSetUp(mesh->supportSection));
4212     PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &size));
4213     PetscCall(PetscMalloc1(size, &mesh->supports));
4214   }
4215   PetscFunctionReturn(PETSC_SUCCESS);
4216 }
4217 
4218 PetscErrorCode DMCreateSubDM_Plex(DM dm, PetscInt numFields, const PetscInt fields[], IS *is, DM *subdm)
4219 {
4220   PetscFunctionBegin;
4221   if (subdm) PetscCall(DMClone(dm, subdm));
4222   PetscCall(DMCreateSectionSubDM(dm, numFields, fields, NULL, NULL, is, subdm));
4223   if (subdm) (*subdm)->useNatural = dm->useNatural;
4224   if (dm->useNatural && dm->sfMigration) {
4225     PetscSF sfNatural;
4226 
4227     (*subdm)->sfMigration = dm->sfMigration;
4228     PetscCall(PetscObjectReference((PetscObject)dm->sfMigration));
4229     PetscCall(DMPlexCreateGlobalToNaturalSF(*subdm, NULL, (*subdm)->sfMigration, &sfNatural));
4230     (*subdm)->sfNatural = sfNatural;
4231   }
4232   PetscFunctionReturn(PETSC_SUCCESS);
4233 }
4234 
4235 PetscErrorCode DMCreateSuperDM_Plex(DM dms[], PetscInt len, IS **is, DM *superdm)
4236 {
4237   PetscInt i = 0;
4238 
4239   PetscFunctionBegin;
4240   PetscCall(DMClone(dms[0], superdm));
4241   PetscCall(DMCreateSectionSuperDM(dms, len, is, superdm));
4242   (*superdm)->useNatural = PETSC_FALSE;
4243   for (i = 0; i < len; i++) {
4244     if (dms[i]->useNatural && dms[i]->sfMigration) {
4245       PetscSF sfNatural;
4246 
4247       (*superdm)->sfMigration = dms[i]->sfMigration;
4248       PetscCall(PetscObjectReference((PetscObject)dms[i]->sfMigration));
4249       (*superdm)->useNatural = PETSC_TRUE;
4250       PetscCall(DMPlexCreateGlobalToNaturalSF(*superdm, NULL, (*superdm)->sfMigration, &sfNatural));
4251       (*superdm)->sfNatural = sfNatural;
4252       break;
4253     }
4254   }
4255   PetscFunctionReturn(PETSC_SUCCESS);
4256 }
4257 
4258 /*@
4259   DMPlexSymmetrize - Create support (out-edge) information from cone (in-edge) information
4260 
4261   Not Collective
4262 
4263   Input Parameter:
4264 . dm - The `DMPLEX`
4265 
4266   Level: beginner
4267 
4268   Note:
4269   This should be called after all calls to `DMPlexSetCone()`
4270 
4271 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMPlexSetCone()`
4272 @*/
4273 PetscErrorCode DMPlexSymmetrize(DM dm)
4274 {
4275   DM_Plex  *mesh = (DM_Plex *)dm->data;
4276   PetscInt *offsets;
4277   PetscInt  supportSize;
4278   PetscInt  pStart, pEnd, p;
4279 
4280   PetscFunctionBegin;
4281   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4282   PetscCheck(!mesh->supports, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONGSTATE, "Supports were already setup in this DMPlex");
4283   PetscCall(PetscLogEventBegin(DMPLEX_Symmetrize, dm, 0, 0, 0));
4284   /* Calculate support sizes */
4285   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4286   for (p = pStart; p < pEnd; ++p) {
4287     PetscInt dof, off, c;
4288 
4289     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
4290     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
4291     for (c = off; c < off + dof; ++c) PetscCall(PetscSectionAddDof(mesh->supportSection, mesh->cones[c], 1));
4292   }
4293   PetscCall(PetscSectionSetUp(mesh->supportSection));
4294   /* Calculate supports */
4295   PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &supportSize));
4296   PetscCall(PetscMalloc1(supportSize, &mesh->supports));
4297   PetscCall(PetscCalloc1(pEnd - pStart, &offsets));
4298   for (p = pStart; p < pEnd; ++p) {
4299     PetscInt dof, off, c;
4300 
4301     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
4302     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
4303     for (c = off; c < off + dof; ++c) {
4304       const PetscInt q = mesh->cones[c];
4305       PetscInt       offS;
4306 
4307       PetscCall(PetscSectionGetOffset(mesh->supportSection, q, &offS));
4308 
4309       mesh->supports[offS + offsets[q]] = p;
4310       ++offsets[q];
4311     }
4312   }
4313   PetscCall(PetscFree(offsets));
4314   PetscCall(PetscLogEventEnd(DMPLEX_Symmetrize, dm, 0, 0, 0));
4315   PetscFunctionReturn(PETSC_SUCCESS);
4316 }
4317 
4318 static PetscErrorCode DMPlexCreateDepthStratum(DM dm, DMLabel label, PetscInt depth, PetscInt pStart, PetscInt pEnd)
4319 {
4320   IS stratumIS;
4321 
4322   PetscFunctionBegin;
4323   if (pStart >= pEnd) PetscFunctionReturn(PETSC_SUCCESS);
4324   if (PetscDefined(USE_DEBUG)) {
4325     PetscInt  qStart, qEnd, numLevels, level;
4326     PetscBool overlap = PETSC_FALSE;
4327     PetscCall(DMLabelGetNumValues(label, &numLevels));
4328     for (level = 0; level < numLevels; level++) {
4329       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4330       if ((pStart >= qStart && pStart < qEnd) || (pEnd > qStart && pEnd <= qEnd)) {
4331         overlap = PETSC_TRUE;
4332         break;
4333       }
4334     }
4335     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);
4336   }
4337   PetscCall(ISCreateStride(PETSC_COMM_SELF, pEnd - pStart, pStart, 1, &stratumIS));
4338   PetscCall(DMLabelSetStratumIS(label, depth, stratumIS));
4339   PetscCall(ISDestroy(&stratumIS));
4340   PetscFunctionReturn(PETSC_SUCCESS);
4341 }
4342 
4343 static PetscErrorCode DMPlexStratify_CellType_Private(DM dm, DMLabel label)
4344 {
4345   PetscInt *pMin, *pMax;
4346   PetscInt  pStart, pEnd;
4347   PetscInt  dmin = PETSC_MAX_INT, dmax = PETSC_MIN_INT;
4348 
4349   PetscFunctionBegin;
4350   {
4351     DMLabel label2;
4352 
4353     PetscCall(DMPlexGetCellTypeLabel(dm, &label2));
4354     PetscCall(PetscObjectViewFromOptions((PetscObject)label2, NULL, "-ct_view"));
4355   }
4356   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4357   for (PetscInt p = pStart; p < pEnd; ++p) {
4358     DMPolytopeType ct;
4359 
4360     PetscCall(DMPlexGetCellType(dm, p, &ct));
4361     dmin = PetscMin(DMPolytopeTypeGetDim(ct), dmin);
4362     dmax = PetscMax(DMPolytopeTypeGetDim(ct), dmax);
4363   }
4364   PetscCall(PetscMalloc2(dmax + 1, &pMin, dmax + 1, &pMax));
4365   for (PetscInt d = dmin; d <= dmax; ++d) {
4366     pMin[d] = PETSC_MAX_INT;
4367     pMax[d] = PETSC_MIN_INT;
4368   }
4369   for (PetscInt p = pStart; p < pEnd; ++p) {
4370     DMPolytopeType ct;
4371     PetscInt       d;
4372 
4373     PetscCall(DMPlexGetCellType(dm, p, &ct));
4374     d       = DMPolytopeTypeGetDim(ct);
4375     pMin[d] = PetscMin(p, pMin[d]);
4376     pMax[d] = PetscMax(p, pMax[d]);
4377   }
4378   for (PetscInt d = dmin; d <= dmax; ++d) {
4379     if (pMin[d] > pMax[d]) continue;
4380     PetscCall(DMPlexCreateDepthStratum(dm, label, d, pMin[d], pMax[d] + 1));
4381   }
4382   PetscCall(PetscFree2(pMin, pMax));
4383   PetscFunctionReturn(PETSC_SUCCESS);
4384 }
4385 
4386 static PetscErrorCode DMPlexStratify_Topological_Private(DM dm, DMLabel label)
4387 {
4388   PetscInt pStart, pEnd;
4389   PetscInt numRoots = 0, numLeaves = 0;
4390 
4391   PetscFunctionBegin;
4392   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4393   {
4394     /* Initialize roots and count leaves */
4395     PetscInt sMin = PETSC_MAX_INT;
4396     PetscInt sMax = PETSC_MIN_INT;
4397     PetscInt coneSize, supportSize;
4398 
4399     for (PetscInt p = pStart; p < pEnd; ++p) {
4400       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4401       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
4402       if (!coneSize && supportSize) {
4403         sMin = PetscMin(p, sMin);
4404         sMax = PetscMax(p, sMax);
4405         ++numRoots;
4406       } else if (!supportSize && coneSize) {
4407         ++numLeaves;
4408       } else if (!supportSize && !coneSize) {
4409         /* Isolated points */
4410         sMin = PetscMin(p, sMin);
4411         sMax = PetscMax(p, sMax);
4412       }
4413     }
4414     PetscCall(DMPlexCreateDepthStratum(dm, label, 0, sMin, sMax + 1));
4415   }
4416 
4417   if (numRoots + numLeaves == (pEnd - pStart)) {
4418     PetscInt sMin = PETSC_MAX_INT;
4419     PetscInt sMax = PETSC_MIN_INT;
4420     PetscInt coneSize, supportSize;
4421 
4422     for (PetscInt p = pStart; p < pEnd; ++p) {
4423       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4424       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
4425       if (!supportSize && coneSize) {
4426         sMin = PetscMin(p, sMin);
4427         sMax = PetscMax(p, sMax);
4428       }
4429     }
4430     PetscCall(DMPlexCreateDepthStratum(dm, label, 1, sMin, sMax + 1));
4431   } else {
4432     PetscInt level = 0;
4433     PetscInt qStart, qEnd;
4434 
4435     PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4436     while (qEnd > qStart) {
4437       PetscInt sMin = PETSC_MAX_INT;
4438       PetscInt sMax = PETSC_MIN_INT;
4439 
4440       for (PetscInt q = qStart; q < qEnd; ++q) {
4441         const PetscInt *support;
4442         PetscInt        supportSize;
4443 
4444         PetscCall(DMPlexGetSupportSize(dm, q, &supportSize));
4445         PetscCall(DMPlexGetSupport(dm, q, &support));
4446         for (PetscInt s = 0; s < supportSize; ++s) {
4447           sMin = PetscMin(support[s], sMin);
4448           sMax = PetscMax(support[s], sMax);
4449         }
4450       }
4451       PetscCall(DMLabelGetNumValues(label, &level));
4452       PetscCall(DMPlexCreateDepthStratum(dm, label, level, sMin, sMax + 1));
4453       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4454     }
4455   }
4456   PetscFunctionReturn(PETSC_SUCCESS);
4457 }
4458 
4459 /*@
4460   DMPlexStratify - Computes the strata for all points in the `DMPLEX`
4461 
4462   Collective
4463 
4464   Input Parameter:
4465 . dm - The `DMPLEX`
4466 
4467   Level: beginner
4468 
4469   Notes:
4470   The strata group all points of the same grade, and this function calculates the strata. This
4471   grade can be seen as the height (or depth) of the point in the DAG.
4472 
4473   The DAG for most topologies is a graded poset (https://en.wikipedia.org/wiki/Graded_poset), and
4474   can be illustrated by a Hasse Diagram (https://en.wikipedia.org/wiki/Hasse_diagram).
4475   Concretely, `DMPlexStratify()` creates a new label named "depth" containing the depth in the DAG of each point. For cell-vertex
4476   meshes, vertices are depth 0 and cells are depth 1. For fully interpolated meshes, depth 0 for vertices, 1 for edges, and so on
4477   until cells have depth equal to the dimension of the mesh. The depth label can be accessed through `DMPlexGetDepthLabel()` or `DMPlexGetDepthStratum()`, or
4478   manually via `DMGetLabel()`.  The height is defined implicitly by height = maxDimension - depth, and can be accessed
4479   via `DMPlexGetHeightStratum()`.  For example, cells have height 0 and faces have height 1.
4480 
4481   The depth of a point is calculated by executing a breadth-first search (BFS) on the DAG. This could produce surprising results
4482   if run on a partially interpolated mesh, meaning one that had some edges and faces, but not others. For example, suppose that
4483   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
4484   to interpolate only that one (e0), so that
4485 .vb
4486   cone(c0) = {e0, v2}
4487   cone(e0) = {v0, v1}
4488 .ve
4489   If `DMPlexStratify()` is run on this mesh, it will give depths
4490 .vb
4491    depth 0 = {v0, v1, v2}
4492    depth 1 = {e0, c0}
4493 .ve
4494   where the triangle has been given depth 1, instead of 2, because it is reachable from vertex v2.
4495 
4496   `DMPlexStratify()` should be called after all calls to `DMPlexSymmetrize()`
4497 
4498 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexComputeCellTypes()`
4499 @*/
4500 PetscErrorCode DMPlexStratify(DM dm)
4501 {
4502   DM_Plex  *mesh = (DM_Plex *)dm->data;
4503   DMLabel   label;
4504   PetscBool flg = PETSC_FALSE;
4505 
4506   PetscFunctionBegin;
4507   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4508   PetscCall(PetscLogEventBegin(DMPLEX_Stratify, dm, 0, 0, 0));
4509 
4510   // Create depth label
4511   PetscCall(DMRemoveLabel(dm, "depth", NULL));
4512   PetscCall(DMCreateLabel(dm, "depth"));
4513   PetscCall(DMPlexGetDepthLabel(dm, &label));
4514 
4515   PetscCall(PetscOptionsGetBool(NULL, dm->hdr.prefix, "-dm_plex_stratify_celltype", &flg, NULL));
4516   if (flg) PetscCall(DMPlexStratify_CellType_Private(dm, label));
4517   else PetscCall(DMPlexStratify_Topological_Private(dm, label));
4518 
4519   { /* just in case there is an empty process */
4520     PetscInt numValues, maxValues = 0, v;
4521 
4522     PetscCall(DMLabelGetNumValues(label, &numValues));
4523     PetscCall(MPIU_Allreduce(&numValues, &maxValues, 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
4524     for (v = numValues; v < maxValues; v++) PetscCall(DMLabelAddStratum(label, v));
4525   }
4526   PetscCall(PetscObjectStateGet((PetscObject)label, &mesh->depthState));
4527   PetscCall(PetscLogEventEnd(DMPLEX_Stratify, dm, 0, 0, 0));
4528   PetscFunctionReturn(PETSC_SUCCESS);
4529 }
4530 
4531 PetscErrorCode DMPlexComputeCellType_Internal(DM dm, PetscInt p, PetscInt pdepth, DMPolytopeType *pt)
4532 {
4533   DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4534   PetscInt       dim, depth, pheight, coneSize;
4535 
4536   PetscFunctionBeginHot;
4537   PetscCall(DMGetDimension(dm, &dim));
4538   PetscCall(DMPlexGetDepth(dm, &depth));
4539   PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4540   pheight = depth - pdepth;
4541   if (depth <= 1) {
4542     switch (pdepth) {
4543     case 0:
4544       ct = DM_POLYTOPE_POINT;
4545       break;
4546     case 1:
4547       switch (coneSize) {
4548       case 2:
4549         ct = DM_POLYTOPE_SEGMENT;
4550         break;
4551       case 3:
4552         ct = DM_POLYTOPE_TRIANGLE;
4553         break;
4554       case 4:
4555         switch (dim) {
4556         case 2:
4557           ct = DM_POLYTOPE_QUADRILATERAL;
4558           break;
4559         case 3:
4560           ct = DM_POLYTOPE_TETRAHEDRON;
4561           break;
4562         default:
4563           break;
4564         }
4565         break;
4566       case 5:
4567         ct = DM_POLYTOPE_PYRAMID;
4568         break;
4569       case 6:
4570         ct = DM_POLYTOPE_TRI_PRISM_TENSOR;
4571         break;
4572       case 8:
4573         ct = DM_POLYTOPE_HEXAHEDRON;
4574         break;
4575       default:
4576         break;
4577       }
4578     }
4579   } else {
4580     if (pdepth == 0) {
4581       ct = DM_POLYTOPE_POINT;
4582     } else if (pheight == 0) {
4583       switch (dim) {
4584       case 1:
4585         switch (coneSize) {
4586         case 2:
4587           ct = DM_POLYTOPE_SEGMENT;
4588           break;
4589         default:
4590           break;
4591         }
4592         break;
4593       case 2:
4594         switch (coneSize) {
4595         case 3:
4596           ct = DM_POLYTOPE_TRIANGLE;
4597           break;
4598         case 4:
4599           ct = DM_POLYTOPE_QUADRILATERAL;
4600           break;
4601         default:
4602           break;
4603         }
4604         break;
4605       case 3:
4606         switch (coneSize) {
4607         case 4:
4608           ct = DM_POLYTOPE_TETRAHEDRON;
4609           break;
4610         case 5: {
4611           const PetscInt *cone;
4612           PetscInt        faceConeSize;
4613 
4614           PetscCall(DMPlexGetCone(dm, p, &cone));
4615           PetscCall(DMPlexGetConeSize(dm, cone[0], &faceConeSize));
4616           switch (faceConeSize) {
4617           case 3:
4618             ct = DM_POLYTOPE_TRI_PRISM_TENSOR;
4619             break;
4620           case 4:
4621             ct = DM_POLYTOPE_PYRAMID;
4622             break;
4623           }
4624         } break;
4625         case 6:
4626           ct = DM_POLYTOPE_HEXAHEDRON;
4627           break;
4628         default:
4629           break;
4630         }
4631         break;
4632       default:
4633         break;
4634       }
4635     } else if (pheight > 0) {
4636       switch (coneSize) {
4637       case 2:
4638         ct = DM_POLYTOPE_SEGMENT;
4639         break;
4640       case 3:
4641         ct = DM_POLYTOPE_TRIANGLE;
4642         break;
4643       case 4:
4644         ct = DM_POLYTOPE_QUADRILATERAL;
4645         break;
4646       default:
4647         break;
4648       }
4649     }
4650   }
4651   *pt = ct;
4652   PetscFunctionReturn(PETSC_SUCCESS);
4653 }
4654 
4655 /*@
4656   DMPlexComputeCellTypes - Infer the polytope type of every cell using its dimension and cone size.
4657 
4658   Collective
4659 
4660   Input Parameter:
4661 . dm - The `DMPLEX`
4662 
4663   Level: developer
4664 
4665   Note:
4666   This function is normally called automatically when a cell type is requested. It creates an
4667   internal `DMLabel` named "celltype" which can be directly accessed using `DMGetLabel()`. A user may disable
4668   automatic creation by creating the label manually, using `DMCreateLabel`(dm, "celltype").
4669 
4670   `DMPlexComputeCellTypes()` should be called after all calls to `DMPlexSymmetrize()` and `DMPlexStratify()`
4671 
4672 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexStratify()`, `DMGetLabel()`, `DMCreateLabel()`
4673 @*/
4674 PetscErrorCode DMPlexComputeCellTypes(DM dm)
4675 {
4676   DM_Plex *mesh;
4677   DMLabel  ctLabel;
4678   PetscInt pStart, pEnd, p;
4679 
4680   PetscFunctionBegin;
4681   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4682   mesh = (DM_Plex *)dm->data;
4683   PetscCall(DMCreateLabel(dm, "celltype"));
4684   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
4685   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4686   PetscCall(PetscFree(mesh->cellTypes));
4687   PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
4688   for (p = pStart; p < pEnd; ++p) {
4689     DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4690     PetscInt       pdepth;
4691 
4692     PetscCall(DMPlexGetPointDepth(dm, p, &pdepth));
4693     PetscCall(DMPlexComputeCellType_Internal(dm, p, pdepth, &ct));
4694     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]);
4695     PetscCall(DMLabelSetValue(ctLabel, p, ct));
4696     mesh->cellTypes[p - pStart].value_as_uint8 = ct;
4697   }
4698   PetscCall(PetscObjectStateGet((PetscObject)ctLabel, &mesh->celltypeState));
4699   PetscCall(PetscObjectViewFromOptions((PetscObject)ctLabel, NULL, "-dm_plex_celltypes_view"));
4700   PetscFunctionReturn(PETSC_SUCCESS);
4701 }
4702 
4703 /*@C
4704   DMPlexGetJoin - Get an array for the join of the set of points
4705 
4706   Not Collective
4707 
4708   Input Parameters:
4709 + dm        - The `DMPLEX` object
4710 . numPoints - The number of input points for the join
4711 - points    - The input points
4712 
4713   Output Parameters:
4714 + numCoveredPoints - The number of points in the join
4715 - coveredPoints    - The points in the join
4716 
4717   Level: intermediate
4718 
4719   Note:
4720   Currently, this is restricted to a single level join
4721 
4722   Fortran Notes:
4723   `converedPoints` must be declared with
4724 .vb
4725   PetscInt, pointer :: coveredPints(:)
4726 .ve
4727 
4728   The `numCoveredPoints` argument is not present in the Fortran binding.
4729 
4730 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4731 @*/
4732 PetscErrorCode DMPlexGetJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt *coveredPoints[])
4733 {
4734   DM_Plex  *mesh = (DM_Plex *)dm->data;
4735   PetscInt *join[2];
4736   PetscInt  joinSize, i = 0;
4737   PetscInt  dof, off, p, c, m;
4738   PetscInt  maxSupportSize;
4739 
4740   PetscFunctionBegin;
4741   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4742   PetscAssertPointer(points, 3);
4743   PetscAssertPointer(numCoveredPoints, 4);
4744   PetscAssertPointer(coveredPoints, 5);
4745   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
4746   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[0]));
4747   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[1]));
4748   /* Copy in support of first point */
4749   PetscCall(PetscSectionGetDof(mesh->supportSection, points[0], &dof));
4750   PetscCall(PetscSectionGetOffset(mesh->supportSection, points[0], &off));
4751   for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = mesh->supports[off + joinSize];
4752   /* Check each successive support */
4753   for (p = 1; p < numPoints; ++p) {
4754     PetscInt newJoinSize = 0;
4755 
4756     PetscCall(PetscSectionGetDof(mesh->supportSection, points[p], &dof));
4757     PetscCall(PetscSectionGetOffset(mesh->supportSection, points[p], &off));
4758     for (c = 0; c < dof; ++c) {
4759       const PetscInt point = mesh->supports[off + c];
4760 
4761       for (m = 0; m < joinSize; ++m) {
4762         if (point == join[i][m]) {
4763           join[1 - i][newJoinSize++] = point;
4764           break;
4765         }
4766       }
4767     }
4768     joinSize = newJoinSize;
4769     i        = 1 - i;
4770   }
4771   *numCoveredPoints = joinSize;
4772   *coveredPoints    = join[i];
4773   PetscCall(DMRestoreWorkArray(dm, maxSupportSize, MPIU_INT, &join[1 - i]));
4774   PetscFunctionReturn(PETSC_SUCCESS);
4775 }
4776 
4777 /*@C
4778   DMPlexRestoreJoin - Restore an array for the join of the set of points obtained with `DMPlexGetJoin()`
4779 
4780   Not Collective
4781 
4782   Input Parameters:
4783 + dm        - The `DMPLEX` object
4784 . numPoints - The number of input points for the join
4785 - points    - The input points
4786 
4787   Output Parameters:
4788 + numCoveredPoints - The number of points in the join
4789 - coveredPoints    - The points in the join
4790 
4791   Level: intermediate
4792 
4793   Fortran Notes:
4794   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4795 
4796 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetJoin()`, `DMPlexGetFullJoin()`, `DMPlexGetMeet()`
4797 @*/
4798 PetscErrorCode DMPlexRestoreJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt *coveredPoints[])
4799 {
4800   PetscFunctionBegin;
4801   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4802   if (points) PetscAssertPointer(points, 3);
4803   if (numCoveredPoints) PetscAssertPointer(numCoveredPoints, 4);
4804   PetscAssertPointer(coveredPoints, 5);
4805   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
4806   if (numCoveredPoints) *numCoveredPoints = 0;
4807   PetscFunctionReturn(PETSC_SUCCESS);
4808 }
4809 
4810 /*@C
4811   DMPlexGetFullJoin - Get an array for the join of the set of points
4812 
4813   Not Collective
4814 
4815   Input Parameters:
4816 + dm        - The `DMPLEX` object
4817 . numPoints - The number of input points for the join
4818 - points    - The input points, its length is `numPoints`
4819 
4820   Output Parameters:
4821 + numCoveredPoints - The number of points in the join
4822 - coveredPoints    - The points in the join, its length is `numCoveredPoints`
4823 
4824   Level: intermediate
4825 
4826   Fortran Notes:
4827   `points` and `converedPoints` must be declared with
4828 .vb
4829   PetscInt, pointer :: points(:)
4830   PetscInt, pointer :: coveredPints(:)
4831 .ve
4832 
4833   The `numCoveredPoints` argument is not present in the Fortran binding.
4834 
4835 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetJoin()`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4836 @*/
4837 PetscErrorCode DMPlexGetFullJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt *coveredPoints[])
4838 {
4839   PetscInt *offsets, **closures;
4840   PetscInt *join[2];
4841   PetscInt  depth = 0, maxSize, joinSize = 0, i = 0;
4842   PetscInt  p, d, c, m, ms;
4843 
4844   PetscFunctionBegin;
4845   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4846   PetscAssertPointer(points, 3);
4847   PetscAssertPointer(numCoveredPoints, 4);
4848   PetscAssertPointer(coveredPoints, 5);
4849 
4850   PetscCall(DMPlexGetDepth(dm, &depth));
4851   PetscCall(PetscCalloc1(numPoints, &closures));
4852   PetscCall(DMGetWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
4853   PetscCall(DMPlexGetMaxSizes(dm, NULL, &ms));
4854   maxSize = (ms > 1) ? ((PetscPowInt(ms, depth + 1) - 1) / (ms - 1)) : depth + 1;
4855   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[0]));
4856   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[1]));
4857 
4858   for (p = 0; p < numPoints; ++p) {
4859     PetscInt closureSize;
4860 
4861     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_FALSE, &closureSize, &closures[p]));
4862 
4863     offsets[p * (depth + 2) + 0] = 0;
4864     for (d = 0; d < depth + 1; ++d) {
4865       PetscInt pStart, pEnd, i;
4866 
4867       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
4868       for (i = offsets[p * (depth + 2) + d]; i < closureSize; ++i) {
4869         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
4870           offsets[p * (depth + 2) + d + 1] = i;
4871           break;
4872         }
4873       }
4874       if (i == closureSize) offsets[p * (depth + 2) + d + 1] = i;
4875     }
4876     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);
4877   }
4878   for (d = 0; d < depth + 1; ++d) {
4879     PetscInt dof;
4880 
4881     /* Copy in support of first point */
4882     dof = offsets[d + 1] - offsets[d];
4883     for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = closures[0][(offsets[d] + joinSize) * 2];
4884     /* Check each successive cone */
4885     for (p = 1; p < numPoints && joinSize; ++p) {
4886       PetscInt newJoinSize = 0;
4887 
4888       dof = offsets[p * (depth + 2) + d + 1] - offsets[p * (depth + 2) + d];
4889       for (c = 0; c < dof; ++c) {
4890         const PetscInt point = closures[p][(offsets[p * (depth + 2) + d] + c) * 2];
4891 
4892         for (m = 0; m < joinSize; ++m) {
4893           if (point == join[i][m]) {
4894             join[1 - i][newJoinSize++] = point;
4895             break;
4896           }
4897         }
4898       }
4899       joinSize = newJoinSize;
4900       i        = 1 - i;
4901     }
4902     if (joinSize) break;
4903   }
4904   *numCoveredPoints = joinSize;
4905   *coveredPoints    = join[i];
4906   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_FALSE, NULL, &closures[p]));
4907   PetscCall(PetscFree(closures));
4908   PetscCall(DMRestoreWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
4909   PetscCall(DMRestoreWorkArray(dm, ms, MPIU_INT, &join[1 - i]));
4910   PetscFunctionReturn(PETSC_SUCCESS);
4911 }
4912 
4913 /*@C
4914   DMPlexGetMeet - Get an array for the meet of the set of points
4915 
4916   Not Collective
4917 
4918   Input Parameters:
4919 + dm        - The `DMPLEX` object
4920 . numPoints - The number of input points for the meet
4921 - points    - The input points, of length `numPoints`
4922 
4923   Output Parameters:
4924 + numCoveringPoints - The number of points in the meet
4925 - coveringPoints    - The points in the meet, of length `numCoveringPoints`
4926 
4927   Level: intermediate
4928 
4929   Note:
4930   Currently, this is restricted to a single level meet
4931 
4932   Fortran Notes:
4933   `coveringPoints` must be declared with
4934 .vb
4935   PetscInt, pointer :: coveringPoints(:)
4936 .ve
4937 
4938   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4939 
4940 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
4941 @*/
4942 PetscErrorCode DMPlexGetMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveringPoints, const PetscInt *coveringPoints[])
4943 {
4944   DM_Plex  *mesh = (DM_Plex *)dm->data;
4945   PetscInt *meet[2];
4946   PetscInt  meetSize, i = 0;
4947   PetscInt  dof, off, p, c, m;
4948   PetscInt  maxConeSize;
4949 
4950   PetscFunctionBegin;
4951   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4952   PetscAssertPointer(points, 3);
4953   PetscAssertPointer(numCoveringPoints, 4);
4954   PetscAssertPointer(coveringPoints, 5);
4955   PetscCall(PetscSectionGetMaxDof(mesh->coneSection, &maxConeSize));
4956   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[0]));
4957   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[1]));
4958   /* Copy in cone of first point */
4959   PetscCall(PetscSectionGetDof(mesh->coneSection, points[0], &dof));
4960   PetscCall(PetscSectionGetOffset(mesh->coneSection, points[0], &off));
4961   for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = mesh->cones[off + meetSize];
4962   /* Check each successive cone */
4963   for (p = 1; p < numPoints; ++p) {
4964     PetscInt newMeetSize = 0;
4965 
4966     PetscCall(PetscSectionGetDof(mesh->coneSection, points[p], &dof));
4967     PetscCall(PetscSectionGetOffset(mesh->coneSection, points[p], &off));
4968     for (c = 0; c < dof; ++c) {
4969       const PetscInt point = mesh->cones[off + c];
4970 
4971       for (m = 0; m < meetSize; ++m) {
4972         if (point == meet[i][m]) {
4973           meet[1 - i][newMeetSize++] = point;
4974           break;
4975         }
4976       }
4977     }
4978     meetSize = newMeetSize;
4979     i        = 1 - i;
4980   }
4981   *numCoveringPoints = meetSize;
4982   *coveringPoints    = meet[i];
4983   PetscCall(DMRestoreWorkArray(dm, maxConeSize, MPIU_INT, &meet[1 - i]));
4984   PetscFunctionReturn(PETSC_SUCCESS);
4985 }
4986 
4987 /*@C
4988   DMPlexRestoreMeet - Restore an array for the meet of the set of points obtained with `DMPlexGetMeet()`
4989 
4990   Not Collective
4991 
4992   Input Parameters:
4993 + dm        - The `DMPLEX` object
4994 . numPoints - The number of input points for the meet
4995 - points    - The input points
4996 
4997   Output Parameters:
4998 + numCoveredPoints - The number of points in the meet
4999 - coveredPoints    - The points in the meet
5000 
5001   Level: intermediate
5002 
5003   Fortran Notes:
5004   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
5005 
5006 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetMeet()`, `DMPlexGetFullMeet()`, `DMPlexGetJoin()`
5007 @*/
5008 PetscErrorCode DMPlexRestoreMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt *coveredPoints[])
5009 {
5010   PetscFunctionBegin;
5011   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5012   if (points) PetscAssertPointer(points, 3);
5013   if (numCoveredPoints) PetscAssertPointer(numCoveredPoints, 4);
5014   PetscAssertPointer(coveredPoints, 5);
5015   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
5016   if (numCoveredPoints) *numCoveredPoints = 0;
5017   PetscFunctionReturn(PETSC_SUCCESS);
5018 }
5019 
5020 /*@C
5021   DMPlexGetFullMeet - Get an array for the meet of the set of points
5022 
5023   Not Collective
5024 
5025   Input Parameters:
5026 + dm        - The `DMPLEX` object
5027 . numPoints - The number of input points for the meet
5028 - points    - The input points, of length  `numPoints`
5029 
5030   Output Parameters:
5031 + numCoveredPoints - The number of points in the meet
5032 - coveredPoints    - The points in the meet, of length  `numCoveredPoints`
5033 
5034   Level: intermediate
5035 
5036   Fortran Notes:
5037   `points` and `coveredPoints` must be declared with
5038 .vb
5039   PetscInt, pointer :: points(:)
5040   PetscInt, pointer :: coveredPoints(:)
5041 .ve
5042 
5043   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
5044 
5045 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetMeet()`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
5046 @*/
5047 PetscErrorCode DMPlexGetFullMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt *coveredPoints[])
5048 {
5049   PetscInt *offsets, **closures;
5050   PetscInt *meet[2];
5051   PetscInt  height = 0, maxSize, meetSize = 0, i = 0;
5052   PetscInt  p, h, c, m, mc;
5053 
5054   PetscFunctionBegin;
5055   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5056   PetscAssertPointer(points, 3);
5057   PetscAssertPointer(numCoveredPoints, 4);
5058   PetscAssertPointer(coveredPoints, 5);
5059 
5060   PetscCall(DMPlexGetDepth(dm, &height));
5061   PetscCall(PetscMalloc1(numPoints, &closures));
5062   PetscCall(DMGetWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
5063   PetscCall(DMPlexGetMaxSizes(dm, &mc, NULL));
5064   maxSize = (mc > 1) ? ((PetscPowInt(mc, height + 1) - 1) / (mc - 1)) : height + 1;
5065   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[0]));
5066   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[1]));
5067 
5068   for (p = 0; p < numPoints; ++p) {
5069     PetscInt closureSize;
5070 
5071     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_TRUE, &closureSize, &closures[p]));
5072 
5073     offsets[p * (height + 2) + 0] = 0;
5074     for (h = 0; h < height + 1; ++h) {
5075       PetscInt pStart, pEnd, i;
5076 
5077       PetscCall(DMPlexGetHeightStratum(dm, h, &pStart, &pEnd));
5078       for (i = offsets[p * (height + 2) + h]; i < closureSize; ++i) {
5079         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
5080           offsets[p * (height + 2) + h + 1] = i;
5081           break;
5082         }
5083       }
5084       if (i == closureSize) offsets[p * (height + 2) + h + 1] = i;
5085     }
5086     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);
5087   }
5088   for (h = 0; h < height + 1; ++h) {
5089     PetscInt dof;
5090 
5091     /* Copy in cone of first point */
5092     dof = offsets[h + 1] - offsets[h];
5093     for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = closures[0][(offsets[h] + meetSize) * 2];
5094     /* Check each successive cone */
5095     for (p = 1; p < numPoints && meetSize; ++p) {
5096       PetscInt newMeetSize = 0;
5097 
5098       dof = offsets[p * (height + 2) + h + 1] - offsets[p * (height + 2) + h];
5099       for (c = 0; c < dof; ++c) {
5100         const PetscInt point = closures[p][(offsets[p * (height + 2) + h] + c) * 2];
5101 
5102         for (m = 0; m < meetSize; ++m) {
5103           if (point == meet[i][m]) {
5104             meet[1 - i][newMeetSize++] = point;
5105             break;
5106           }
5107         }
5108       }
5109       meetSize = newMeetSize;
5110       i        = 1 - i;
5111     }
5112     if (meetSize) break;
5113   }
5114   *numCoveredPoints = meetSize;
5115   *coveredPoints    = meet[i];
5116   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_TRUE, NULL, &closures[p]));
5117   PetscCall(PetscFree(closures));
5118   PetscCall(DMRestoreWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
5119   PetscCall(DMRestoreWorkArray(dm, mc, MPIU_INT, &meet[1 - i]));
5120   PetscFunctionReturn(PETSC_SUCCESS);
5121 }
5122 
5123 /*@
5124   DMPlexEqual - Determine if two `DM` have the same topology
5125 
5126   Not Collective
5127 
5128   Input Parameters:
5129 + dmA - A `DMPLEX` object
5130 - dmB - A `DMPLEX` object
5131 
5132   Output Parameter:
5133 . equal - `PETSC_TRUE` if the topologies are identical
5134 
5135   Level: intermediate
5136 
5137   Note:
5138   We are not solving graph isomorphism, so we do not permute.
5139 
5140 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCone()`
5141 @*/
5142 PetscErrorCode DMPlexEqual(DM dmA, DM dmB, PetscBool *equal)
5143 {
5144   PetscInt depth, depthB, pStart, pEnd, pStartB, pEndB, p;
5145 
5146   PetscFunctionBegin;
5147   PetscValidHeaderSpecific(dmA, DM_CLASSID, 1);
5148   PetscValidHeaderSpecific(dmB, DM_CLASSID, 2);
5149   PetscAssertPointer(equal, 3);
5150 
5151   *equal = PETSC_FALSE;
5152   PetscCall(DMPlexGetDepth(dmA, &depth));
5153   PetscCall(DMPlexGetDepth(dmB, &depthB));
5154   if (depth != depthB) PetscFunctionReturn(PETSC_SUCCESS);
5155   PetscCall(DMPlexGetChart(dmA, &pStart, &pEnd));
5156   PetscCall(DMPlexGetChart(dmB, &pStartB, &pEndB));
5157   if ((pStart != pStartB) || (pEnd != pEndB)) PetscFunctionReturn(PETSC_SUCCESS);
5158   for (p = pStart; p < pEnd; ++p) {
5159     const PetscInt *cone, *coneB, *ornt, *orntB, *support, *supportB;
5160     PetscInt        coneSize, coneSizeB, c, supportSize, supportSizeB, s;
5161 
5162     PetscCall(DMPlexGetConeSize(dmA, p, &coneSize));
5163     PetscCall(DMPlexGetCone(dmA, p, &cone));
5164     PetscCall(DMPlexGetConeOrientation(dmA, p, &ornt));
5165     PetscCall(DMPlexGetConeSize(dmB, p, &coneSizeB));
5166     PetscCall(DMPlexGetCone(dmB, p, &coneB));
5167     PetscCall(DMPlexGetConeOrientation(dmB, p, &orntB));
5168     if (coneSize != coneSizeB) PetscFunctionReturn(PETSC_SUCCESS);
5169     for (c = 0; c < coneSize; ++c) {
5170       if (cone[c] != coneB[c]) PetscFunctionReturn(PETSC_SUCCESS);
5171       if (ornt[c] != orntB[c]) PetscFunctionReturn(PETSC_SUCCESS);
5172     }
5173     PetscCall(DMPlexGetSupportSize(dmA, p, &supportSize));
5174     PetscCall(DMPlexGetSupport(dmA, p, &support));
5175     PetscCall(DMPlexGetSupportSize(dmB, p, &supportSizeB));
5176     PetscCall(DMPlexGetSupport(dmB, p, &supportB));
5177     if (supportSize != supportSizeB) PetscFunctionReturn(PETSC_SUCCESS);
5178     for (s = 0; s < supportSize; ++s) {
5179       if (support[s] != supportB[s]) PetscFunctionReturn(PETSC_SUCCESS);
5180     }
5181   }
5182   *equal = PETSC_TRUE;
5183   PetscFunctionReturn(PETSC_SUCCESS);
5184 }
5185 
5186 /*@
5187   DMPlexGetNumFaceVertices - Returns the number of vertices on a face
5188 
5189   Not Collective
5190 
5191   Input Parameters:
5192 + dm         - The `DMPLEX`
5193 . cellDim    - The cell dimension
5194 - numCorners - The number of vertices on a cell
5195 
5196   Output Parameter:
5197 . numFaceVertices - The number of vertices on a face
5198 
5199   Level: developer
5200 
5201   Note:
5202   Of course this can only work for a restricted set of symmetric shapes
5203 
5204 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCone()`
5205 @*/
5206 PetscErrorCode DMPlexGetNumFaceVertices(DM dm, PetscInt cellDim, PetscInt numCorners, PetscInt *numFaceVertices)
5207 {
5208   MPI_Comm comm;
5209 
5210   PetscFunctionBegin;
5211   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
5212   PetscAssertPointer(numFaceVertices, 4);
5213   switch (cellDim) {
5214   case 0:
5215     *numFaceVertices = 0;
5216     break;
5217   case 1:
5218     *numFaceVertices = 1;
5219     break;
5220   case 2:
5221     switch (numCorners) {
5222     case 3:                 /* triangle */
5223       *numFaceVertices = 2; /* Edge has 2 vertices */
5224       break;
5225     case 4:                 /* quadrilateral */
5226       *numFaceVertices = 2; /* Edge has 2 vertices */
5227       break;
5228     case 6:                 /* quadratic triangle, tri and quad cohesive Lagrange cells */
5229       *numFaceVertices = 3; /* Edge has 3 vertices */
5230       break;
5231     case 9:                 /* quadratic quadrilateral, quadratic quad cohesive Lagrange cells */
5232       *numFaceVertices = 3; /* Edge has 3 vertices */
5233       break;
5234     default:
5235       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
5236     }
5237     break;
5238   case 3:
5239     switch (numCorners) {
5240     case 4:                 /* tetradehdron */
5241       *numFaceVertices = 3; /* Face has 3 vertices */
5242       break;
5243     case 6:                 /* tet cohesive cells */
5244       *numFaceVertices = 4; /* Face has 4 vertices */
5245       break;
5246     case 8:                 /* hexahedron */
5247       *numFaceVertices = 4; /* Face has 4 vertices */
5248       break;
5249     case 9:                 /* tet cohesive Lagrange cells */
5250       *numFaceVertices = 6; /* Face has 6 vertices */
5251       break;
5252     case 10:                /* quadratic tetrahedron */
5253       *numFaceVertices = 6; /* Face has 6 vertices */
5254       break;
5255     case 12:                /* hex cohesive Lagrange cells */
5256       *numFaceVertices = 6; /* Face has 6 vertices */
5257       break;
5258     case 18:                /* quadratic tet cohesive Lagrange cells */
5259       *numFaceVertices = 6; /* Face has 6 vertices */
5260       break;
5261     case 27:                /* quadratic hexahedron, quadratic hex cohesive Lagrange cells */
5262       *numFaceVertices = 9; /* Face has 9 vertices */
5263       break;
5264     default:
5265       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
5266     }
5267     break;
5268   default:
5269     SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid cell dimension %" PetscInt_FMT, cellDim);
5270   }
5271   PetscFunctionReturn(PETSC_SUCCESS);
5272 }
5273 
5274 /*@
5275   DMPlexGetDepthLabel - Get the `DMLabel` recording the depth of each point
5276 
5277   Not Collective
5278 
5279   Input Parameter:
5280 . dm - The `DMPLEX` object
5281 
5282   Output Parameter:
5283 . depthLabel - The `DMLabel` recording point depth
5284 
5285   Level: developer
5286 
5287 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepth()`, `DMPlexGetHeightStratum()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`,
5288 @*/
5289 PetscErrorCode DMPlexGetDepthLabel(DM dm, DMLabel *depthLabel)
5290 {
5291   PetscFunctionBegin;
5292   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5293   PetscAssertPointer(depthLabel, 2);
5294   *depthLabel = dm->depthLabel;
5295   PetscFunctionReturn(PETSC_SUCCESS);
5296 }
5297 
5298 /*@
5299   DMPlexGetDepth - Get the depth of the DAG representing this mesh
5300 
5301   Not Collective
5302 
5303   Input Parameter:
5304 . dm - The `DMPLEX` object
5305 
5306   Output Parameter:
5307 . depth - The number of strata (breadth first levels) in the DAG
5308 
5309   Level: developer
5310 
5311   Notes:
5312   This returns maximum of point depths over all points, i.e. maximum value of the label returned by `DMPlexGetDepthLabel()`.
5313 
5314   The point depth is described more in detail in `DMPlexGetDepthStratum()`.
5315 
5316   An empty mesh gives -1.
5317 
5318 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepthLabel()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`
5319 @*/
5320 PetscErrorCode DMPlexGetDepth(DM dm, PetscInt *depth)
5321 {
5322   DM_Plex *mesh = (DM_Plex *)dm->data;
5323   DMLabel  label;
5324   PetscInt d = -1;
5325 
5326   PetscFunctionBegin;
5327   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5328   PetscAssertPointer(depth, 2);
5329   if (mesh->tr) {
5330     PetscCall(DMPlexTransformGetDepth(mesh->tr, depth));
5331   } else {
5332     PetscCall(DMPlexGetDepthLabel(dm, &label));
5333     // Allow missing depths
5334     if (label) PetscCall(DMLabelGetValueBounds(label, NULL, &d));
5335     *depth = d;
5336   }
5337   PetscFunctionReturn(PETSC_SUCCESS);
5338 }
5339 
5340 /*@
5341   DMPlexGetDepthStratum - Get the bounds [`start`, `end`) for all points at a certain depth.
5342 
5343   Not Collective
5344 
5345   Input Parameters:
5346 + dm    - The `DMPLEX` object
5347 - depth - The requested depth
5348 
5349   Output Parameters:
5350 + start - The first point at this `depth`
5351 - end   - One beyond the last point at this `depth`
5352 
5353   Level: developer
5354 
5355   Notes:
5356   Depth indexing is related to topological dimension.  Depth stratum 0 contains the lowest topological dimension points,
5357   often "vertices".  If the mesh is "interpolated" (see `DMPlexInterpolate()`), then depth stratum 1 contains the next
5358   higher dimension, e.g., "edges".
5359 
5360 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetHeightStratum()`, `DMPlexGetCellTypeStratum()`, `DMPlexGetDepth()`, `DMPlexGetDepthLabel()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`, `DMPlexInterpolate()`
5361 @*/
5362 PetscErrorCode DMPlexGetDepthStratum(DM dm, PetscInt depth, PetscInt *start, PetscInt *end)
5363 {
5364   DM_Plex *mesh = (DM_Plex *)dm->data;
5365   DMLabel  label;
5366   PetscInt pStart, pEnd;
5367 
5368   PetscFunctionBegin;
5369   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5370   if (start) {
5371     PetscAssertPointer(start, 3);
5372     *start = 0;
5373   }
5374   if (end) {
5375     PetscAssertPointer(end, 4);
5376     *end = 0;
5377   }
5378   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
5379   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
5380   if (depth < 0) {
5381     if (start) *start = pStart;
5382     if (end) *end = pEnd;
5383     PetscFunctionReturn(PETSC_SUCCESS);
5384   }
5385   if (mesh->tr) {
5386     PetscCall(DMPlexTransformGetDepthStratum(mesh->tr, depth, start, end));
5387   } else {
5388     PetscCall(DMPlexGetDepthLabel(dm, &label));
5389     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named depth was found");
5390     PetscCall(DMLabelGetStratumBounds(label, depth, start, end));
5391   }
5392   PetscFunctionReturn(PETSC_SUCCESS);
5393 }
5394 
5395 /*@
5396   DMPlexGetHeightStratum - Get the bounds [`start`, `end`) for all points at a certain height.
5397 
5398   Not Collective
5399 
5400   Input Parameters:
5401 + dm     - The `DMPLEX` object
5402 - height - The requested height
5403 
5404   Output Parameters:
5405 + start - The first point at this `height`
5406 - end   - One beyond the last point at this `height`
5407 
5408   Level: developer
5409 
5410   Notes:
5411   Height indexing is related to topological codimension.  Height stratum 0 contains the highest topological dimension
5412   points, often called "cells" or "elements".  If the mesh is "interpolated" (see `DMPlexInterpolate()`), then height
5413   stratum 1 contains the boundary of these "cells", often called "faces" or "facets".
5414 
5415 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepthStratum()`, `DMPlexGetCellTypeStratum()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
5416 @*/
5417 PetscErrorCode DMPlexGetHeightStratum(DM dm, PetscInt height, PetscInt *start, PetscInt *end)
5418 {
5419   DMLabel  label;
5420   PetscInt depth, pStart, pEnd;
5421 
5422   PetscFunctionBegin;
5423   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5424   if (start) {
5425     PetscAssertPointer(start, 3);
5426     *start = 0;
5427   }
5428   if (end) {
5429     PetscAssertPointer(end, 4);
5430     *end = 0;
5431   }
5432   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
5433   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
5434   if (height < 0) {
5435     if (start) *start = pStart;
5436     if (end) *end = pEnd;
5437     PetscFunctionReturn(PETSC_SUCCESS);
5438   }
5439   PetscCall(DMPlexGetDepthLabel(dm, &label));
5440   if (label) PetscCall(DMLabelGetNumValues(label, &depth));
5441   else PetscCall(DMGetDimension(dm, &depth));
5442   PetscCheck(depth >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Depth not yet computed");
5443   PetscCall(DMPlexGetDepthStratum(dm, depth - 1 - height, start, end));
5444   PetscFunctionReturn(PETSC_SUCCESS);
5445 }
5446 
5447 /*@
5448   DMPlexGetPointDepth - Get the `depth` of a given point
5449 
5450   Not Collective
5451 
5452   Input Parameters:
5453 + dm    - The `DMPLEX` object
5454 - point - The point
5455 
5456   Output Parameter:
5457 . depth - The depth of the `point`
5458 
5459   Level: intermediate
5460 
5461 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
5462 @*/
5463 PetscErrorCode DMPlexGetPointDepth(DM dm, PetscInt point, PetscInt *depth)
5464 {
5465   PetscFunctionBegin;
5466   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5467   PetscAssertPointer(depth, 3);
5468   PetscCall(DMLabelGetValue(dm->depthLabel, point, depth));
5469   PetscFunctionReturn(PETSC_SUCCESS);
5470 }
5471 
5472 /*@
5473   DMPlexGetPointHeight - Get the `height` of a given point
5474 
5475   Not Collective
5476 
5477   Input Parameters:
5478 + dm    - The `DMPLEX` object
5479 - point - The point
5480 
5481   Output Parameter:
5482 . height - The height of the `point`
5483 
5484   Level: intermediate
5485 
5486 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointDepth()`
5487 @*/
5488 PetscErrorCode DMPlexGetPointHeight(DM dm, PetscInt point, PetscInt *height)
5489 {
5490   PetscInt n, pDepth;
5491 
5492   PetscFunctionBegin;
5493   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5494   PetscAssertPointer(height, 3);
5495   PetscCall(DMLabelGetNumValues(dm->depthLabel, &n));
5496   PetscCall(DMLabelGetValue(dm->depthLabel, point, &pDepth));
5497   *height = n - 1 - pDepth; /* DAG depth is n-1 */
5498   PetscFunctionReturn(PETSC_SUCCESS);
5499 }
5500 
5501 /*@
5502   DMPlexGetCellTypeLabel - Get the `DMLabel` recording the polytope type of each cell
5503 
5504   Not Collective
5505 
5506   Input Parameter:
5507 . dm - The `DMPLEX` object
5508 
5509   Output Parameter:
5510 . celltypeLabel - The `DMLabel` recording cell polytope type
5511 
5512   Level: developer
5513 
5514   Note:
5515   This function will trigger automatica computation of cell types. This can be disabled by calling
5516   `DMCreateLabel`(dm, "celltype") beforehand.
5517 
5518 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMCreateLabel()`
5519 @*/
5520 PetscErrorCode DMPlexGetCellTypeLabel(DM dm, DMLabel *celltypeLabel)
5521 {
5522   PetscFunctionBegin;
5523   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5524   PetscAssertPointer(celltypeLabel, 2);
5525   if (!dm->celltypeLabel) PetscCall(DMPlexComputeCellTypes(dm));
5526   *celltypeLabel = dm->celltypeLabel;
5527   PetscFunctionReturn(PETSC_SUCCESS);
5528 }
5529 
5530 /*@
5531   DMPlexGetCellType - Get the polytope type of a given cell
5532 
5533   Not Collective
5534 
5535   Input Parameters:
5536 + dm   - The `DMPLEX` object
5537 - cell - The cell
5538 
5539   Output Parameter:
5540 . celltype - The polytope type of the cell
5541 
5542   Level: intermediate
5543 
5544 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPolytopeType`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`
5545 @*/
5546 PetscErrorCode DMPlexGetCellType(DM dm, PetscInt cell, DMPolytopeType *celltype)
5547 {
5548   DM_Plex *mesh = (DM_Plex *)dm->data;
5549   DMLabel  label;
5550   PetscInt ct;
5551 
5552   PetscFunctionBegin;
5553   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5554   PetscAssertPointer(celltype, 3);
5555   if (mesh->tr) {
5556     PetscCall(DMPlexTransformGetCellType(mesh->tr, cell, celltype));
5557   } else {
5558     PetscInt pStart, pEnd;
5559 
5560     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, NULL));
5561     if (!mesh->cellTypes) { /* XXX remove? optimize? */
5562       PetscCall(PetscSectionGetChart(mesh->coneSection, NULL, &pEnd));
5563       PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
5564       PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5565       for (PetscInt p = pStart; p < pEnd; p++) {
5566         PetscCall(DMLabelGetValue(label, p, &ct));
5567         mesh->cellTypes[p - pStart].value_as_uint8 = (DMPolytopeType)ct;
5568       }
5569     }
5570     *celltype = (DMPolytopeType)mesh->cellTypes[cell - pStart].value_as_uint8;
5571     if (PetscDefined(USE_DEBUG)) {
5572       PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5573       PetscCall(DMLabelGetValue(label, cell, &ct));
5574       PetscCheck(ct >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Cell %" PetscInt_FMT " has not been assigned a cell type", cell);
5575       PetscCheck(ct == (PetscInt)*celltype, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid cellType for %" PetscInt_FMT ": %d != %" PetscInt_FMT, cell, (int)*celltype, ct);
5576     }
5577   }
5578   PetscFunctionReturn(PETSC_SUCCESS);
5579 }
5580 
5581 /*@
5582   DMPlexSetCellType - Set the polytope type of a given cell
5583 
5584   Not Collective
5585 
5586   Input Parameters:
5587 + dm       - The `DMPLEX` object
5588 . cell     - The cell
5589 - celltype - The polytope type of the cell
5590 
5591   Level: advanced
5592 
5593   Note:
5594   By default, cell types will be automatically computed using `DMPlexComputeCellTypes()` before this function
5595   is executed. This function will override the computed type. However, if automatic classification will not succeed
5596   and a user wants to manually specify all types, the classification must be disabled by calling
5597   DMCreateLabel(dm, "celltype") before getting or setting any cell types.
5598 
5599 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexComputeCellTypes()`, `DMCreateLabel()`
5600 @*/
5601 PetscErrorCode DMPlexSetCellType(DM dm, PetscInt cell, DMPolytopeType celltype)
5602 {
5603   DM_Plex *mesh = (DM_Plex *)dm->data;
5604   DMLabel  label;
5605   PetscInt pStart, pEnd;
5606 
5607   PetscFunctionBegin;
5608   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5609   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
5610   PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5611   PetscCall(DMLabelSetValue(label, cell, celltype));
5612   if (!mesh->cellTypes) PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
5613   mesh->cellTypes[cell - pStart].value_as_uint8 = celltype;
5614   PetscFunctionReturn(PETSC_SUCCESS);
5615 }
5616 
5617 PetscErrorCode DMCreateCoordinateDM_Plex(DM dm, DM *cdm)
5618 {
5619   PetscSection section;
5620   PetscInt     maxHeight;
5621   const char  *prefix;
5622 
5623   PetscFunctionBegin;
5624   PetscCall(DMClone(dm, cdm));
5625   PetscCall(PetscObjectGetOptionsPrefix((PetscObject)dm, &prefix));
5626   PetscCall(PetscObjectSetOptionsPrefix((PetscObject)*cdm, prefix));
5627   PetscCall(PetscObjectAppendOptionsPrefix((PetscObject)*cdm, "cdm_"));
5628   PetscCall(DMPlexGetMaxProjectionHeight(dm, &maxHeight));
5629   PetscCall(DMPlexSetMaxProjectionHeight(*cdm, maxHeight));
5630   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
5631   PetscCall(DMSetLocalSection(*cdm, section));
5632   PetscCall(PetscSectionDestroy(&section));
5633 
5634   PetscCall(DMSetNumFields(*cdm, 1));
5635   PetscCall(DMCreateDS(*cdm));
5636   (*cdm)->cloneOpts = PETSC_TRUE;
5637   if (dm->setfromoptionscalled) PetscCall(DMSetFromOptions(*cdm));
5638   PetscFunctionReturn(PETSC_SUCCESS);
5639 }
5640 
5641 PetscErrorCode DMCreateCoordinateField_Plex(DM dm, DMField *field)
5642 {
5643   Vec coordsLocal, cellCoordsLocal;
5644   DM  coordsDM, cellCoordsDM;
5645 
5646   PetscFunctionBegin;
5647   *field = NULL;
5648   PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
5649   PetscCall(DMGetCoordinateDM(dm, &coordsDM));
5650   PetscCall(DMGetCellCoordinatesLocal(dm, &cellCoordsLocal));
5651   PetscCall(DMGetCellCoordinateDM(dm, &cellCoordsDM));
5652   if (coordsLocal && coordsDM) {
5653     if (cellCoordsLocal && cellCoordsDM) PetscCall(DMFieldCreateDSWithDG(coordsDM, cellCoordsDM, 0, coordsLocal, cellCoordsLocal, field));
5654     else PetscCall(DMFieldCreateDS(coordsDM, 0, coordsLocal, field));
5655   }
5656   PetscFunctionReturn(PETSC_SUCCESS);
5657 }
5658 
5659 /*@
5660   DMPlexGetConeSection - Return a section which describes the layout of cone data
5661 
5662   Not Collective
5663 
5664   Input Parameter:
5665 . dm - The `DMPLEX` object
5666 
5667   Output Parameter:
5668 . section - The `PetscSection` object
5669 
5670   Level: developer
5671 
5672 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSupportSection()`, `DMPlexGetCones()`, `DMPlexGetConeOrientations()`, `PetscSection`
5673 @*/
5674 PetscErrorCode DMPlexGetConeSection(DM dm, PetscSection *section)
5675 {
5676   DM_Plex *mesh = (DM_Plex *)dm->data;
5677 
5678   PetscFunctionBegin;
5679   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5680   if (section) *section = mesh->coneSection;
5681   PetscFunctionReturn(PETSC_SUCCESS);
5682 }
5683 
5684 /*@
5685   DMPlexGetSupportSection - Return a section which describes the layout of support data
5686 
5687   Not Collective
5688 
5689   Input Parameter:
5690 . dm - The `DMPLEX` object
5691 
5692   Output Parameter:
5693 . section - The `PetscSection` object
5694 
5695   Level: developer
5696 
5697 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `PetscSection`
5698 @*/
5699 PetscErrorCode DMPlexGetSupportSection(DM dm, PetscSection *section)
5700 {
5701   DM_Plex *mesh = (DM_Plex *)dm->data;
5702 
5703   PetscFunctionBegin;
5704   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5705   if (section) *section = mesh->supportSection;
5706   PetscFunctionReturn(PETSC_SUCCESS);
5707 }
5708 
5709 /*@C
5710   DMPlexGetCones - Return cone data
5711 
5712   Not Collective
5713 
5714   Input Parameter:
5715 . dm - The `DMPLEX` object
5716 
5717   Output Parameter:
5718 . cones - The cone for each point
5719 
5720   Level: developer
5721 
5722 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`
5723 @*/
5724 PetscErrorCode DMPlexGetCones(DM dm, PetscInt *cones[])
5725 {
5726   DM_Plex *mesh = (DM_Plex *)dm->data;
5727 
5728   PetscFunctionBegin;
5729   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5730   if (cones) *cones = mesh->cones;
5731   PetscFunctionReturn(PETSC_SUCCESS);
5732 }
5733 
5734 /*@C
5735   DMPlexGetConeOrientations - Return cone orientation data
5736 
5737   Not Collective
5738 
5739   Input Parameter:
5740 . dm - The `DMPLEX` object
5741 
5742   Output Parameter:
5743 . coneOrientations - The array of cone orientations for all points
5744 
5745   Level: developer
5746 
5747   Notes:
5748   The `PetscSection` returned by `DMPlexGetConeSection()` partitions coneOrientations into cone orientations of particular points
5749   as returned by `DMPlexGetConeOrientation()`.
5750 
5751   The meaning of coneOrientations values is detailed in `DMPlexGetConeOrientation()`.
5752 
5753 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `DMPlexGetConeOrientation()`, `PetscSection`
5754 @*/
5755 PetscErrorCode DMPlexGetConeOrientations(DM dm, PetscInt *coneOrientations[])
5756 {
5757   DM_Plex *mesh = (DM_Plex *)dm->data;
5758 
5759   PetscFunctionBegin;
5760   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5761   if (coneOrientations) *coneOrientations = mesh->coneOrientations;
5762   PetscFunctionReturn(PETSC_SUCCESS);
5763 }
5764 
5765 /******************************** FEM Support **********************************/
5766 
5767 PetscErrorCode DMPlexGetAllCells_Internal(DM plex, IS *cellIS)
5768 {
5769   PetscInt depth;
5770 
5771   PetscFunctionBegin;
5772   PetscCall(DMPlexGetDepth(plex, &depth));
5773   PetscCall(DMGetStratumIS(plex, "dim", depth, cellIS));
5774   if (!*cellIS) PetscCall(DMGetStratumIS(plex, "depth", depth, cellIS));
5775   PetscFunctionReturn(PETSC_SUCCESS);
5776 }
5777 
5778 PetscErrorCode DMPlexGetAllFaces_Internal(DM plex, IS *faceIS)
5779 {
5780   PetscInt depth;
5781 
5782   PetscFunctionBegin;
5783   PetscCall(DMPlexGetDepth(plex, &depth));
5784   PetscCall(DMGetStratumIS(plex, "dim", depth - 1, faceIS));
5785   if (!*faceIS) PetscCall(DMGetStratumIS(plex, "depth", depth - 1, faceIS));
5786   PetscFunctionReturn(PETSC_SUCCESS);
5787 }
5788 
5789 /*
5790  Returns number of components and tensor degree for the field.  For interpolated meshes, line should be a point
5791  representing a line in the section.
5792 */
5793 static PetscErrorCode PetscSectionFieldGetTensorDegree_Private(DM dm, PetscSection section, PetscInt field, PetscInt line, PetscInt *Nc, PetscInt *k, PetscBool *continuous, PetscBool *tensor)
5794 {
5795   PetscObject  obj;
5796   PetscClassId id;
5797   PetscFE      fe = NULL;
5798 
5799   PetscFunctionBeginHot;
5800   PetscCall(PetscSectionGetFieldComponents(section, field, Nc));
5801   PetscCall(DMGetField(dm, field, NULL, &obj));
5802   PetscCall(PetscObjectGetClassId(obj, &id));
5803   if (id == PETSCFE_CLASSID) fe = (PetscFE)obj;
5804 
5805   if (!fe) {
5806     /* Assume the full interpolated mesh is in the chart; lines in particular */
5807     /* An order k SEM disc has k-1 dofs on an edge */
5808     PetscCall(PetscSectionGetFieldDof(section, line, field, k));
5809     *k = *k / *Nc + 1;
5810   } else {
5811     PetscInt       dual_space_size, dim;
5812     PetscDualSpace dsp;
5813 
5814     PetscCall(DMGetDimension(dm, &dim));
5815     PetscCall(PetscFEGetDualSpace(fe, &dsp));
5816     PetscCall(PetscDualSpaceGetDimension(dsp, &dual_space_size));
5817     *k = (PetscInt)PetscCeilReal(PetscPowReal(dual_space_size / *Nc, 1.0 / dim)) - 1;
5818     PetscCall(PetscDualSpaceLagrangeGetContinuity(dsp, continuous));
5819     PetscCall(PetscDualSpaceLagrangeGetTensor(dsp, tensor));
5820   }
5821   PetscFunctionReturn(PETSC_SUCCESS);
5822 }
5823 
5824 static PetscErrorCode GetFieldSize_Private(PetscInt dim, PetscInt k, PetscBool tensor, PetscInt *dof)
5825 {
5826   PetscFunctionBeginHot;
5827   if (tensor) {
5828     *dof = PetscPowInt(k + 1, dim);
5829   } else {
5830     switch (dim) {
5831     case 1:
5832       *dof = k + 1;
5833       break;
5834     case 2:
5835       *dof = ((k + 1) * (k + 2)) / 2;
5836       break;
5837     case 3:
5838       *dof = ((k + 1) * (k + 2) * (k + 3)) / 6;
5839       break;
5840     default:
5841       *dof = 0;
5842     }
5843   }
5844   PetscFunctionReturn(PETSC_SUCCESS);
5845 }
5846 
5847 /*@
5848   DMPlexSetClosurePermutationTensor - Create a permutation from the default (BFS) point ordering in the closure, to a
5849   lexicographic ordering over the tensor product cell (i.e., line, quad, hex, etc.), and set this permutation in the
5850   section provided (or the section of the `DM`).
5851 
5852   Input Parameters:
5853 + dm      - The `DM`
5854 . point   - Either a cell (highest dim point) or an edge (dim 1 point), or `PETSC_DETERMINE`
5855 - section - The `PetscSection` to reorder, or `NULL` for the default section
5856 
5857   Example:
5858   A typical interpolated single-quad mesh might order points as
5859 .vb
5860   [c0, v1, v2, v3, v4, e5, e6, e7, e8]
5861 
5862   v4 -- e6 -- v3
5863   |           |
5864   e7    c0    e8
5865   |           |
5866   v1 -- e5 -- v2
5867 .ve
5868 
5869   (There is no significance to the ordering described here.)  The default section for a Q3 quad might typically assign
5870   dofs in the order of points, e.g.,
5871 .vb
5872     c0 -> [0,1,2,3]
5873     v1 -> [4]
5874     ...
5875     e5 -> [8, 9]
5876 .ve
5877 
5878   which corresponds to the dofs
5879 .vb
5880     6   10  11  7
5881     13  2   3   15
5882     12  0   1   14
5883     4   8   9   5
5884 .ve
5885 
5886   The closure in BFS ordering works through height strata (cells, edges, vertices) to produce the ordering
5887 .vb
5888   0 1 2 3 8 9 14 15 11 10 13 12 4 5 7 6
5889 .ve
5890 
5891   After calling DMPlexSetClosurePermutationTensor(), the closure will be ordered lexicographically,
5892 .vb
5893    4 8 9 5 12 0 1 14 13 2 3 15 6 10 11 7
5894 .ve
5895 
5896   Level: developer
5897 
5898   Notes:
5899   The point is used to determine the number of dofs/field on an edge. For SEM, this is related to the polynomial
5900   degree of the basis.
5901 
5902   This is required to run with libCEED.
5903 
5904 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMGetLocalSection()`, `PetscSectionSetClosurePermutation()`, `DMSetGlobalSection()`
5905 @*/
5906 PetscErrorCode DMPlexSetClosurePermutationTensor(DM dm, PetscInt point, PetscSection section)
5907 {
5908   DMLabel   label;
5909   PetscInt  dim, depth = -1, eStart = -1, Nf;
5910   PetscBool continuous = PETSC_TRUE, tensor = PETSC_TRUE;
5911 
5912   PetscFunctionBegin;
5913   PetscCall(DMGetDimension(dm, &dim));
5914   if (dim < 1) PetscFunctionReturn(PETSC_SUCCESS);
5915   if (point < 0) {
5916     PetscInt sStart, sEnd;
5917 
5918     PetscCall(DMPlexGetDepthStratum(dm, 1, &sStart, &sEnd));
5919     point = sEnd - sStart ? sStart : point;
5920   }
5921   PetscCall(DMPlexGetDepthLabel(dm, &label));
5922   if (point >= 0) PetscCall(DMLabelGetValue(label, point, &depth));
5923   if (!section) PetscCall(DMGetLocalSection(dm, &section));
5924   if (depth == 1) {
5925     eStart = point;
5926   } else if (depth == dim) {
5927     const PetscInt *cone;
5928 
5929     PetscCall(DMPlexGetCone(dm, point, &cone));
5930     if (dim == 2) eStart = cone[0];
5931     else if (dim == 3) {
5932       const PetscInt *cone2;
5933       PetscCall(DMPlexGetCone(dm, cone[0], &cone2));
5934       eStart = cone2[0];
5935     } 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);
5936   } 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);
5937 
5938   PetscCall(PetscSectionGetNumFields(section, &Nf));
5939   for (PetscInt d = 1; d <= dim; d++) {
5940     PetscInt  k, f, Nc, c, i, j, size = 0, offset = 0, foffset = 0;
5941     PetscInt *perm;
5942 
5943     for (f = 0; f < Nf; ++f) {
5944       PetscInt dof;
5945 
5946       PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
5947       PetscCheck(dim == 1 || tensor || !continuous, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Continuous field %" PetscInt_FMT " must have a tensor product discretization", f);
5948       if (!continuous && d < dim) continue;
5949       PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
5950       size += dof * Nc;
5951     }
5952     PetscCall(PetscMalloc1(size, &perm));
5953     for (f = 0; f < Nf; ++f) {
5954       switch (d) {
5955       case 1:
5956         PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
5957         if (!continuous && d < dim) continue;
5958         /*
5959          Original ordering is [ edge of length k-1; vtx0; vtx1 ]
5960          We want              [ vtx0; edge of length k-1; vtx1 ]
5961          */
5962         if (continuous) {
5963           for (c = 0; c < Nc; c++, offset++) perm[offset] = (k - 1) * Nc + c + foffset;
5964           for (i = 0; i < k - 1; i++)
5965             for (c = 0; c < Nc; c++, offset++) perm[offset] = i * Nc + c + foffset;
5966           for (c = 0; c < Nc; c++, offset++) perm[offset] = k * Nc + c + foffset;
5967           foffset = offset;
5968         } else {
5969           PetscInt dof;
5970 
5971           PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
5972           for (i = 0; i < dof * Nc; ++i, ++offset) perm[offset] = i + foffset;
5973           foffset = offset;
5974         }
5975         break;
5976       case 2:
5977         /* The original quad closure is oriented clockwise, {f, e_b, e_r, e_t, e_l, v_lb, v_rb, v_tr, v_tl} */
5978         PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
5979         if (!continuous && d < dim) continue;
5980         /* The SEM order is
5981 
5982          v_lb, {e_b}, v_rb,
5983          e^{(k-1)-i}_l, {f^{i*(k-1)}}, e^i_r,
5984          v_lt, reverse {e_t}, v_rt
5985          */
5986         if (continuous) {
5987           const PetscInt of   = 0;
5988           const PetscInt oeb  = of + PetscSqr(k - 1);
5989           const PetscInt oer  = oeb + (k - 1);
5990           const PetscInt oet  = oer + (k - 1);
5991           const PetscInt oel  = oet + (k - 1);
5992           const PetscInt ovlb = oel + (k - 1);
5993           const PetscInt ovrb = ovlb + 1;
5994           const PetscInt ovrt = ovrb + 1;
5995           const PetscInt ovlt = ovrt + 1;
5996           PetscInt       o;
5997 
5998           /* bottom */
5999           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlb * Nc + c + foffset;
6000           for (o = oeb; o < oer; ++o)
6001             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6002           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrb * Nc + c + foffset;
6003           /* middle */
6004           for (i = 0; i < k - 1; ++i) {
6005             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oel + (k - 2) - i) * Nc + c + foffset;
6006             for (o = of + (k - 1) * i; o < of + (k - 1) * (i + 1); ++o)
6007               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6008             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oer + i) * Nc + c + foffset;
6009           }
6010           /* top */
6011           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlt * Nc + c + foffset;
6012           for (o = oel - 1; o >= oet; --o)
6013             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6014           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrt * Nc + c + foffset;
6015           foffset = offset;
6016         } else {
6017           PetscInt dof;
6018 
6019           PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
6020           for (i = 0; i < dof * Nc; ++i, ++offset) perm[offset] = i + foffset;
6021           foffset = offset;
6022         }
6023         break;
6024       case 3:
6025         /* The original hex closure is
6026 
6027          {c,
6028          f_b, f_t, f_f, f_b, f_r, f_l,
6029          e_bl, e_bb, e_br, e_bf,  e_tf, e_tr, e_tb, e_tl,  e_rf, e_lf, e_lb, e_rb,
6030          v_blf, v_blb, v_brb, v_brf, v_tlf, v_trf, v_trb, v_tlb}
6031          */
6032         PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
6033         if (!continuous && d < dim) continue;
6034         /* The SEM order is
6035          Bottom Slice
6036          v_blf, {e^{(k-1)-n}_bf}, v_brf,
6037          e^{i}_bl, f^{n*(k-1)+(k-1)-i}_b, e^{(k-1)-i}_br,
6038          v_blb, {e_bb}, v_brb,
6039 
6040          Middle Slice (j)
6041          {e^{(k-1)-j}_lf}, {f^{j*(k-1)+n}_f}, e^j_rf,
6042          f^{i*(k-1)+j}_l, {c^{(j*(k-1) + i)*(k-1)+n}_t}, f^{j*(k-1)+i}_r,
6043          e^j_lb, {f^{j*(k-1)+(k-1)-n}_b}, e^{(k-1)-j}_rb,
6044 
6045          Top Slice
6046          v_tlf, {e_tf}, v_trf,
6047          e^{(k-1)-i}_tl, {f^{i*(k-1)}_t}, e^{i}_tr,
6048          v_tlb, {e^{(k-1)-n}_tb}, v_trb,
6049          */
6050         if (continuous) {
6051           const PetscInt oc    = 0;
6052           const PetscInt ofb   = oc + PetscSqr(k - 1) * (k - 1);
6053           const PetscInt oft   = ofb + PetscSqr(k - 1);
6054           const PetscInt off   = oft + PetscSqr(k - 1);
6055           const PetscInt ofk   = off + PetscSqr(k - 1);
6056           const PetscInt ofr   = ofk + PetscSqr(k - 1);
6057           const PetscInt ofl   = ofr + PetscSqr(k - 1);
6058           const PetscInt oebl  = ofl + PetscSqr(k - 1);
6059           const PetscInt oebb  = oebl + (k - 1);
6060           const PetscInt oebr  = oebb + (k - 1);
6061           const PetscInt oebf  = oebr + (k - 1);
6062           const PetscInt oetf  = oebf + (k - 1);
6063           const PetscInt oetr  = oetf + (k - 1);
6064           const PetscInt oetb  = oetr + (k - 1);
6065           const PetscInt oetl  = oetb + (k - 1);
6066           const PetscInt oerf  = oetl + (k - 1);
6067           const PetscInt oelf  = oerf + (k - 1);
6068           const PetscInt oelb  = oelf + (k - 1);
6069           const PetscInt oerb  = oelb + (k - 1);
6070           const PetscInt ovblf = oerb + (k - 1);
6071           const PetscInt ovblb = ovblf + 1;
6072           const PetscInt ovbrb = ovblb + 1;
6073           const PetscInt ovbrf = ovbrb + 1;
6074           const PetscInt ovtlf = ovbrf + 1;
6075           const PetscInt ovtrf = ovtlf + 1;
6076           const PetscInt ovtrb = ovtrf + 1;
6077           const PetscInt ovtlb = ovtrb + 1;
6078           PetscInt       o, n;
6079 
6080           /* Bottom Slice */
6081           /*   bottom */
6082           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblf * Nc + c + foffset;
6083           for (o = oetf - 1; o >= oebf; --o)
6084             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6085           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrf * Nc + c + foffset;
6086           /*   middle */
6087           for (i = 0; i < k - 1; ++i) {
6088             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebl + i) * Nc + c + foffset;
6089             for (n = 0; n < k - 1; ++n) {
6090               o = ofb + n * (k - 1) + i;
6091               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6092             }
6093             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebr + (k - 2) - i) * Nc + c + foffset;
6094           }
6095           /*   top */
6096           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblb * Nc + c + foffset;
6097           for (o = oebb; o < oebr; ++o)
6098             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6099           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrb * Nc + c + foffset;
6100 
6101           /* Middle Slice */
6102           for (j = 0; j < k - 1; ++j) {
6103             /*   bottom */
6104             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelf + (k - 2) - j) * Nc + c + foffset;
6105             for (o = off + j * (k - 1); o < off + (j + 1) * (k - 1); ++o)
6106               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6107             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerf + j) * Nc + c + foffset;
6108             /*   middle */
6109             for (i = 0; i < k - 1; ++i) {
6110               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofl + i * (k - 1) + j) * Nc + c + foffset;
6111               for (n = 0; n < k - 1; ++n)
6112                 for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oc + (j * (k - 1) + i) * (k - 1) + n) * Nc + c + foffset;
6113               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofr + j * (k - 1) + i) * Nc + c + foffset;
6114             }
6115             /*   top */
6116             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelb + j) * Nc + c + foffset;
6117             for (o = ofk + j * (k - 1) + (k - 2); o >= ofk + j * (k - 1); --o)
6118               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6119             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerb + (k - 2) - j) * Nc + c + foffset;
6120           }
6121 
6122           /* Top Slice */
6123           /*   bottom */
6124           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlf * Nc + c + foffset;
6125           for (o = oetf; o < oetr; ++o)
6126             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6127           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrf * Nc + c + foffset;
6128           /*   middle */
6129           for (i = 0; i < k - 1; ++i) {
6130             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetl + (k - 2) - i) * Nc + c + foffset;
6131             for (n = 0; n < k - 1; ++n)
6132               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oft + i * (k - 1) + n) * Nc + c + foffset;
6133             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetr + i) * Nc + c + foffset;
6134           }
6135           /*   top */
6136           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlb * Nc + c + foffset;
6137           for (o = oetl - 1; o >= oetb; --o)
6138             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6139           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrb * Nc + c + foffset;
6140 
6141           foffset = offset;
6142         } else {
6143           PetscInt dof;
6144 
6145           PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
6146           for (i = 0; i < dof * Nc; ++i, ++offset) perm[offset] = i + foffset;
6147           foffset = offset;
6148         }
6149         break;
6150       default:
6151         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "No spectral ordering for dimension %" PetscInt_FMT, d);
6152       }
6153     }
6154     PetscCheck(offset == size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Number of permutation entries %" PetscInt_FMT " != %" PetscInt_FMT, offset, size);
6155     /* Check permutation */
6156     {
6157       PetscInt *check;
6158 
6159       PetscCall(PetscMalloc1(size, &check));
6160       for (i = 0; i < size; ++i) {
6161         check[i] = -1;
6162         PetscCheck(perm[i] >= 0 && perm[i] < size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid permutation index p[%" PetscInt_FMT "] = %" PetscInt_FMT, i, perm[i]);
6163       }
6164       for (i = 0; i < size; ++i) check[perm[i]] = i;
6165       for (i = 0; i < size; ++i) PetscCheck(check[i] >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Missing permutation index %" PetscInt_FMT, i);
6166       PetscCall(PetscFree(check));
6167     }
6168     PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size, PETSC_OWN_POINTER, perm));
6169     if (d == dim) { // Add permutation for localized (in case this is a coordinate DM)
6170       PetscInt *loc_perm;
6171       PetscCall(PetscMalloc1(size * 2, &loc_perm));
6172       for (PetscInt i = 0; i < size; i++) {
6173         loc_perm[i]        = perm[i];
6174         loc_perm[size + i] = size + perm[i];
6175       }
6176       PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size * 2, PETSC_OWN_POINTER, loc_perm));
6177     }
6178   }
6179   PetscFunctionReturn(PETSC_SUCCESS);
6180 }
6181 
6182 PetscErrorCode DMPlexGetPointDualSpaceFEM(DM dm, PetscInt point, PetscInt field, PetscDualSpace *dspace)
6183 {
6184   PetscDS  prob;
6185   PetscInt depth, Nf, h;
6186   DMLabel  label;
6187 
6188   PetscFunctionBeginHot;
6189   PetscCall(DMGetDS(dm, &prob));
6190   Nf      = prob->Nf;
6191   label   = dm->depthLabel;
6192   *dspace = NULL;
6193   if (field < Nf) {
6194     PetscObject disc = prob->disc[field];
6195 
6196     if (disc->classid == PETSCFE_CLASSID) {
6197       PetscDualSpace dsp;
6198 
6199       PetscCall(PetscFEGetDualSpace((PetscFE)disc, &dsp));
6200       PetscCall(DMLabelGetNumValues(label, &depth));
6201       PetscCall(DMLabelGetValue(label, point, &h));
6202       h = depth - 1 - h;
6203       if (h) {
6204         PetscCall(PetscDualSpaceGetHeightSubspace(dsp, h, dspace));
6205       } else {
6206         *dspace = dsp;
6207       }
6208     }
6209   }
6210   PetscFunctionReturn(PETSC_SUCCESS);
6211 }
6212 
6213 static inline PetscErrorCode DMPlexVecGetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6214 {
6215   PetscScalar       *array;
6216   const PetscScalar *vArray;
6217   const PetscInt    *cone, *coneO;
6218   PetscInt           pStart, pEnd, p, numPoints, size = 0, offset = 0;
6219 
6220   PetscFunctionBeginHot;
6221   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6222   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
6223   PetscCall(DMPlexGetCone(dm, point, &cone));
6224   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
6225   if (!values || !*values) {
6226     if ((point >= pStart) && (point < pEnd)) {
6227       PetscInt dof;
6228 
6229       PetscCall(PetscSectionGetDof(section, point, &dof));
6230       size += dof;
6231     }
6232     for (p = 0; p < numPoints; ++p) {
6233       const PetscInt cp = cone[p];
6234       PetscInt       dof;
6235 
6236       if ((cp < pStart) || (cp >= pEnd)) continue;
6237       PetscCall(PetscSectionGetDof(section, cp, &dof));
6238       size += dof;
6239     }
6240     if (!values) {
6241       if (csize) *csize = size;
6242       PetscFunctionReturn(PETSC_SUCCESS);
6243     }
6244     PetscCall(DMGetWorkArray(dm, size, MPIU_SCALAR, &array));
6245   } else {
6246     array = *values;
6247   }
6248   size = 0;
6249   PetscCall(VecGetArrayRead(v, &vArray));
6250   if ((point >= pStart) && (point < pEnd)) {
6251     PetscInt           dof, off, d;
6252     const PetscScalar *varr;
6253 
6254     PetscCall(PetscSectionGetDof(section, point, &dof));
6255     PetscCall(PetscSectionGetOffset(section, point, &off));
6256     varr = PetscSafePointerPlusOffset(vArray, off);
6257     for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
6258     size += dof;
6259   }
6260   for (p = 0; p < numPoints; ++p) {
6261     const PetscInt     cp = cone[p];
6262     PetscInt           o  = coneO[p];
6263     PetscInt           dof, off, d;
6264     const PetscScalar *varr;
6265 
6266     if ((cp < pStart) || (cp >= pEnd)) continue;
6267     PetscCall(PetscSectionGetDof(section, cp, &dof));
6268     PetscCall(PetscSectionGetOffset(section, cp, &off));
6269     varr = PetscSafePointerPlusOffset(vArray, off);
6270     if (o >= 0) {
6271       for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
6272     } else {
6273       for (d = dof - 1; d >= 0; --d, ++offset) array[offset] = varr[d];
6274     }
6275     size += dof;
6276   }
6277   PetscCall(VecRestoreArrayRead(v, &vArray));
6278   if (!*values) {
6279     if (csize) *csize = size;
6280     *values = array;
6281   } else {
6282     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
6283     *csize = size;
6284   }
6285   PetscFunctionReturn(PETSC_SUCCESS);
6286 }
6287 
6288 /* Compress out points not in the section */
6289 static inline PetscErrorCode CompressPoints_Private(PetscSection section, PetscInt *numPoints, PetscInt points[])
6290 {
6291   const PetscInt np = *numPoints;
6292   PetscInt       pStart, pEnd, p, q;
6293 
6294   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6295   for (p = 0, q = 0; p < np; ++p) {
6296     const PetscInt r = points[p * 2];
6297     if ((r >= pStart) && (r < pEnd)) {
6298       points[q * 2]     = r;
6299       points[q * 2 + 1] = points[p * 2 + 1];
6300       ++q;
6301     }
6302   }
6303   *numPoints = q;
6304   return PETSC_SUCCESS;
6305 }
6306 
6307 /* Compressed closure does not apply closure permutation */
6308 PetscErrorCode DMPlexGetCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt ornt, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
6309 {
6310   const PetscInt *cla = NULL;
6311   PetscInt        np, *pts = NULL;
6312 
6313   PetscFunctionBeginHot;
6314   PetscCall(PetscSectionGetClosureIndex(section, (PetscObject)dm, clSec, clPoints));
6315   if (!ornt && *clPoints) {
6316     PetscInt dof, off;
6317 
6318     PetscCall(PetscSectionGetDof(*clSec, point, &dof));
6319     PetscCall(PetscSectionGetOffset(*clSec, point, &off));
6320     PetscCall(ISGetIndices(*clPoints, &cla));
6321     np  = dof / 2;
6322     pts = PetscSafePointerPlusOffset((PetscInt *)cla, off);
6323   } else {
6324     PetscCall(DMPlexGetTransitiveClosure_Internal(dm, point, ornt, PETSC_TRUE, &np, &pts));
6325     PetscCall(CompressPoints_Private(section, &np, pts));
6326   }
6327   *numPoints = np;
6328   *points    = pts;
6329   *clp       = cla;
6330   PetscFunctionReturn(PETSC_SUCCESS);
6331 }
6332 
6333 PetscErrorCode DMPlexRestoreCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
6334 {
6335   PetscFunctionBeginHot;
6336   if (!*clPoints) {
6337     PetscCall(DMPlexRestoreTransitiveClosure(dm, point, PETSC_TRUE, numPoints, points));
6338   } else {
6339     PetscCall(ISRestoreIndices(*clPoints, clp));
6340   }
6341   *numPoints = 0;
6342   *points    = NULL;
6343   *clSec     = NULL;
6344   *clPoints  = NULL;
6345   *clp       = NULL;
6346   PetscFunctionReturn(PETSC_SUCCESS);
6347 }
6348 
6349 static inline PetscErrorCode DMPlexVecGetClosure_Static(DM dm, PetscSection section, PetscInt numPoints, const PetscInt points[], const PetscInt clperm[], const PetscScalar vArray[], PetscInt *size, PetscScalar array[])
6350 {
6351   PetscInt            offset = 0, p;
6352   const PetscInt    **perms  = NULL;
6353   const PetscScalar **flips  = NULL;
6354 
6355   PetscFunctionBeginHot;
6356   *size = 0;
6357   PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
6358   for (p = 0; p < numPoints; p++) {
6359     const PetscInt     point = points[2 * p];
6360     const PetscInt    *perm  = perms ? perms[p] : NULL;
6361     const PetscScalar *flip  = flips ? flips[p] : NULL;
6362     PetscInt           dof, off, d;
6363     const PetscScalar *varr;
6364 
6365     PetscCall(PetscSectionGetDof(section, point, &dof));
6366     PetscCall(PetscSectionGetOffset(section, point, &off));
6367     varr = PetscSafePointerPlusOffset(vArray, off);
6368     if (clperm) {
6369       if (perm) {
6370         for (d = 0; d < dof; d++) array[clperm[offset + perm[d]]] = varr[d];
6371       } else {
6372         for (d = 0; d < dof; d++) array[clperm[offset + d]] = varr[d];
6373       }
6374       if (flip) {
6375         for (d = 0; d < dof; d++) array[clperm[offset + d]] *= flip[d];
6376       }
6377     } else {
6378       if (perm) {
6379         for (d = 0; d < dof; d++) array[offset + perm[d]] = varr[d];
6380       } else {
6381         for (d = 0; d < dof; d++) array[offset + d] = varr[d];
6382       }
6383       if (flip) {
6384         for (d = 0; d < dof; d++) array[offset + d] *= flip[d];
6385       }
6386     }
6387     offset += dof;
6388   }
6389   PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
6390   *size = offset;
6391   PetscFunctionReturn(PETSC_SUCCESS);
6392 }
6393 
6394 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[])
6395 {
6396   PetscInt offset = 0, f;
6397 
6398   PetscFunctionBeginHot;
6399   *size = 0;
6400   for (f = 0; f < numFields; ++f) {
6401     PetscInt            p;
6402     const PetscInt    **perms = NULL;
6403     const PetscScalar **flips = NULL;
6404 
6405     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6406     for (p = 0; p < numPoints; p++) {
6407       const PetscInt     point = points[2 * p];
6408       PetscInt           fdof, foff, b;
6409       const PetscScalar *varr;
6410       const PetscInt    *perm = perms ? perms[p] : NULL;
6411       const PetscScalar *flip = flips ? flips[p] : NULL;
6412 
6413       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6414       PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6415       varr = &vArray[foff];
6416       if (clperm) {
6417         if (perm) {
6418           for (b = 0; b < fdof; b++) array[clperm[offset + perm[b]]] = varr[b];
6419         } else {
6420           for (b = 0; b < fdof; b++) array[clperm[offset + b]] = varr[b];
6421         }
6422         if (flip) {
6423           for (b = 0; b < fdof; b++) array[clperm[offset + b]] *= flip[b];
6424         }
6425       } else {
6426         if (perm) {
6427           for (b = 0; b < fdof; b++) array[offset + perm[b]] = varr[b];
6428         } else {
6429           for (b = 0; b < fdof; b++) array[offset + b] = varr[b];
6430         }
6431         if (flip) {
6432           for (b = 0; b < fdof; b++) array[offset + b] *= flip[b];
6433         }
6434       }
6435       offset += fdof;
6436     }
6437     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6438   }
6439   *size = offset;
6440   PetscFunctionReturn(PETSC_SUCCESS);
6441 }
6442 
6443 PetscErrorCode DMPlexVecGetOrientedClosure_Internal(DM dm, PetscSection section, PetscBool useClPerm, Vec v, PetscInt point, PetscInt ornt, PetscInt *csize, PetscScalar *values[])
6444 {
6445   PetscSection    clSection;
6446   IS              clPoints;
6447   PetscInt       *points = NULL;
6448   const PetscInt *clp, *perm = NULL;
6449   PetscInt        depth, numFields, numPoints, asize;
6450 
6451   PetscFunctionBeginHot;
6452   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6453   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6454   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6455   PetscValidHeaderSpecific(v, VEC_CLASSID, 4);
6456   PetscCall(DMPlexGetDepth(dm, &depth));
6457   PetscCall(PetscSectionGetNumFields(section, &numFields));
6458   if (depth == 1 && numFields < 2) {
6459     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6460     PetscFunctionReturn(PETSC_SUCCESS);
6461   }
6462   /* Get points */
6463   PetscCall(DMPlexGetCompressedClosure(dm, section, point, ornt, &numPoints, &points, &clSection, &clPoints, &clp));
6464   /* Get sizes */
6465   asize = 0;
6466   for (PetscInt p = 0; p < numPoints * 2; p += 2) {
6467     PetscInt dof;
6468     PetscCall(PetscSectionGetDof(section, points[p], &dof));
6469     asize += dof;
6470   }
6471   if (values) {
6472     const PetscScalar *vArray;
6473     PetscInt           size;
6474 
6475     if (*values) {
6476       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);
6477     } else PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, values));
6478     if (useClPerm) PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, asize, &perm));
6479     PetscCall(VecGetArrayRead(v, &vArray));
6480     /* Get values */
6481     if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, numPoints, points, numFields, perm, vArray, &size, *values));
6482     else PetscCall(DMPlexVecGetClosure_Static(dm, section, numPoints, points, perm, vArray, &size, *values));
6483     PetscCheck(asize == size, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Section size %" PetscInt_FMT " does not match Vec closure size %" PetscInt_FMT, asize, size);
6484     /* Cleanup array */
6485     PetscCall(VecRestoreArrayRead(v, &vArray));
6486   }
6487   if (csize) *csize = asize;
6488   /* Cleanup points */
6489   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6490   PetscFunctionReturn(PETSC_SUCCESS);
6491 }
6492 
6493 /*@C
6494   DMPlexVecGetClosure - Get an array of the values on the closure of 'point'
6495 
6496   Not collective
6497 
6498   Input Parameters:
6499 + dm      - The `DM`
6500 . section - The section describing the layout in `v`, or `NULL` to use the default section
6501 . v       - The local vector
6502 - point   - The point in the `DM`
6503 
6504   Input/Output Parameters:
6505 + csize  - The size of the input values array, or `NULL`; on output the number of values in the closure
6506 - values - An array to use for the values, or *values = `NULL` to have it allocated automatically;
6507            if the user provided `NULL`, it is a borrowed array and should not be freed, use  `DMPlexVecRestoreClosure()` to return it
6508 
6509   Level: intermediate
6510 
6511   Notes:
6512   `DMPlexVecGetClosure()`/`DMPlexVecRestoreClosure()` only allocates the values array if it set to `NULL` in the
6513   calling function. This is because `DMPlexVecGetClosure()` is typically called in the inner loop of a `Vec` or `Mat`
6514   assembly function, and a user may already have allocated storage for this operation.
6515 
6516   A typical use could be
6517 .vb
6518    values = NULL;
6519    PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
6520    for (cl = 0; cl < clSize; ++cl) {
6521      <Compute on closure>
6522    }
6523    PetscCall(DMPlexVecRestoreClosure(dm, NULL, v, p, &clSize, &values));
6524 .ve
6525   or
6526 .vb
6527    PetscMalloc1(clMaxSize, &values);
6528    for (p = pStart; p < pEnd; ++p) {
6529      clSize = clMaxSize;
6530      PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
6531      for (cl = 0; cl < clSize; ++cl) {
6532        <Compute on closure>
6533      }
6534    }
6535    PetscFree(values);
6536 .ve
6537 
6538   Fortran Notes:
6539   The `csize` argument is not present in the Fortran binding.
6540 
6541   `values` must be declared with
6542 .vb
6543   PetscScalar,dimension(:),pointer   :: values
6544 .ve
6545   and it will be allocated internally by PETSc to hold the values returned
6546 
6547 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecRestoreClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6548 @*/
6549 PetscErrorCode DMPlexVecGetClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6550 {
6551   PetscFunctionBeginHot;
6552   PetscCall(DMPlexVecGetOrientedClosure_Internal(dm, section, PETSC_TRUE, v, point, 0, csize, values));
6553   PetscFunctionReturn(PETSC_SUCCESS);
6554 }
6555 
6556 PetscErrorCode DMPlexVecGetClosureAtDepth_Internal(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt depth, PetscInt *csize, PetscScalar *values[])
6557 {
6558   DMLabel            depthLabel;
6559   PetscSection       clSection;
6560   IS                 clPoints;
6561   PetscScalar       *array;
6562   const PetscScalar *vArray;
6563   PetscInt          *points = NULL;
6564   const PetscInt    *clp, *perm = NULL;
6565   PetscInt           mdepth, numFields, numPoints, Np = 0, p, clsize, size;
6566 
6567   PetscFunctionBeginHot;
6568   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6569   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6570   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6571   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6572   PetscCall(DMPlexGetDepth(dm, &mdepth));
6573   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
6574   PetscCall(PetscSectionGetNumFields(section, &numFields));
6575   if (mdepth == 1 && numFields < 2) {
6576     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6577     PetscFunctionReturn(PETSC_SUCCESS);
6578   }
6579   /* Get points */
6580   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
6581   for (clsize = 0, p = 0; p < Np; p++) {
6582     PetscInt dof;
6583     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
6584     clsize += dof;
6585   }
6586   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &perm));
6587   /* Filter points */
6588   for (p = 0; p < numPoints * 2; p += 2) {
6589     PetscInt dep;
6590 
6591     PetscCall(DMLabelGetValue(depthLabel, points[p], &dep));
6592     if (dep != depth) continue;
6593     points[Np * 2 + 0] = points[p];
6594     points[Np * 2 + 1] = points[p + 1];
6595     ++Np;
6596   }
6597   /* Get array */
6598   if (!values || !*values) {
6599     PetscInt asize = 0, dof;
6600 
6601     for (p = 0; p < Np * 2; p += 2) {
6602       PetscCall(PetscSectionGetDof(section, points[p], &dof));
6603       asize += dof;
6604     }
6605     if (!values) {
6606       PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6607       if (csize) *csize = asize;
6608       PetscFunctionReturn(PETSC_SUCCESS);
6609     }
6610     PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, &array));
6611   } else {
6612     array = *values;
6613   }
6614   PetscCall(VecGetArrayRead(v, &vArray));
6615   /* Get values */
6616   if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, Np, points, numFields, perm, vArray, &size, array));
6617   else PetscCall(DMPlexVecGetClosure_Static(dm, section, Np, points, perm, vArray, &size, array));
6618   /* Cleanup points */
6619   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6620   /* Cleanup array */
6621   PetscCall(VecRestoreArrayRead(v, &vArray));
6622   if (!*values) {
6623     if (csize) *csize = size;
6624     *values = array;
6625   } else {
6626     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
6627     *csize = size;
6628   }
6629   PetscFunctionReturn(PETSC_SUCCESS);
6630 }
6631 
6632 /*@C
6633   DMPlexVecRestoreClosure - Restore the array of the values on the closure of 'point' obtained with `DMPlexVecGetClosure()`
6634 
6635   Not collective
6636 
6637   Input Parameters:
6638 + dm      - The `DM`
6639 . section - The section describing the layout in `v`, or `NULL` to use the default section
6640 . v       - The local vector
6641 . point   - The point in the `DM`
6642 . csize   - The number of values in the closure, or `NULL`
6643 - values  - The array of values
6644 
6645   Level: intermediate
6646 
6647   Note:
6648   The array values are discarded and not copied back into `v`. In order to copy values back to `v`, use `DMPlexVecSetClosure()`
6649 
6650   Fortran Note:
6651   The `csize` argument is not present in the Fortran binding since it is internal to the array.
6652 
6653 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6654 @*/
6655 PetscErrorCode DMPlexVecRestoreClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6656 {
6657   PetscInt size = 0;
6658 
6659   PetscFunctionBegin;
6660   /* Should work without recalculating size */
6661   PetscCall(DMRestoreWorkArray(dm, size, MPIU_SCALAR, (void *)values));
6662   *values = NULL;
6663   PetscFunctionReturn(PETSC_SUCCESS);
6664 }
6665 
6666 static inline void add(PetscScalar *x, PetscScalar y)
6667 {
6668   *x += y;
6669 }
6670 static inline void insert(PetscScalar *x, PetscScalar y)
6671 {
6672   *x = y;
6673 }
6674 
6675 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[])
6676 {
6677   PetscInt        cdof;  /* The number of constraints on this point */
6678   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6679   PetscScalar    *a;
6680   PetscInt        off, cind = 0, k;
6681 
6682   PetscFunctionBegin;
6683   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6684   PetscCall(PetscSectionGetOffset(section, point, &off));
6685   a = &array[off];
6686   if (!cdof || setBC) {
6687     if (clperm) {
6688       if (perm) {
6689         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6690       } else {
6691         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6692       }
6693     } else {
6694       if (perm) {
6695         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6696       } else {
6697         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6698       }
6699     }
6700   } else {
6701     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6702     if (clperm) {
6703       if (perm) {
6704         for (k = 0; k < dof; ++k) {
6705           if ((cind < cdof) && (k == cdofs[cind])) {
6706             ++cind;
6707             continue;
6708           }
6709           fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6710         }
6711       } else {
6712         for (k = 0; k < dof; ++k) {
6713           if ((cind < cdof) && (k == cdofs[cind])) {
6714             ++cind;
6715             continue;
6716           }
6717           fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6718         }
6719       }
6720     } else {
6721       if (perm) {
6722         for (k = 0; k < dof; ++k) {
6723           if ((cind < cdof) && (k == cdofs[cind])) {
6724             ++cind;
6725             continue;
6726           }
6727           fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6728         }
6729       } else {
6730         for (k = 0; k < dof; ++k) {
6731           if ((cind < cdof) && (k == cdofs[cind])) {
6732             ++cind;
6733             continue;
6734           }
6735           fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6736         }
6737       }
6738     }
6739   }
6740   PetscFunctionReturn(PETSC_SUCCESS);
6741 }
6742 
6743 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[])
6744 {
6745   PetscInt        cdof;  /* The number of constraints on this point */
6746   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6747   PetscScalar    *a;
6748   PetscInt        off, cind = 0, k;
6749 
6750   PetscFunctionBegin;
6751   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6752   PetscCall(PetscSectionGetOffset(section, point, &off));
6753   a = &array[off];
6754   if (cdof) {
6755     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6756     if (clperm) {
6757       if (perm) {
6758         for (k = 0; k < dof; ++k) {
6759           if ((cind < cdof) && (k == cdofs[cind])) {
6760             fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6761             cind++;
6762           }
6763         }
6764       } else {
6765         for (k = 0; k < dof; ++k) {
6766           if ((cind < cdof) && (k == cdofs[cind])) {
6767             fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6768             cind++;
6769           }
6770         }
6771       }
6772     } else {
6773       if (perm) {
6774         for (k = 0; k < dof; ++k) {
6775           if ((cind < cdof) && (k == cdofs[cind])) {
6776             fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6777             cind++;
6778           }
6779         }
6780       } else {
6781         for (k = 0; k < dof; ++k) {
6782           if ((cind < cdof) && (k == cdofs[cind])) {
6783             fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6784             cind++;
6785           }
6786         }
6787       }
6788     }
6789   }
6790   PetscFunctionReturn(PETSC_SUCCESS);
6791 }
6792 
6793 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[])
6794 {
6795   PetscScalar    *a;
6796   PetscInt        fdof, foff, fcdof, foffset = *offset;
6797   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6798   PetscInt        cind = 0, b;
6799 
6800   PetscFunctionBegin;
6801   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6802   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6803   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6804   a = &array[foff];
6805   if (!fcdof || setBC) {
6806     if (clperm) {
6807       if (perm) {
6808         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6809       } else {
6810         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6811       }
6812     } else {
6813       if (perm) {
6814         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6815       } else {
6816         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6817       }
6818     }
6819   } else {
6820     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6821     if (clperm) {
6822       if (perm) {
6823         for (b = 0; b < fdof; b++) {
6824           if ((cind < fcdof) && (b == fcdofs[cind])) {
6825             ++cind;
6826             continue;
6827           }
6828           fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6829         }
6830       } else {
6831         for (b = 0; b < fdof; b++) {
6832           if ((cind < fcdof) && (b == fcdofs[cind])) {
6833             ++cind;
6834             continue;
6835           }
6836           fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6837         }
6838       }
6839     } else {
6840       if (perm) {
6841         for (b = 0; b < fdof; b++) {
6842           if ((cind < fcdof) && (b == fcdofs[cind])) {
6843             ++cind;
6844             continue;
6845           }
6846           fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6847         }
6848       } else {
6849         for (b = 0; b < fdof; b++) {
6850           if ((cind < fcdof) && (b == fcdofs[cind])) {
6851             ++cind;
6852             continue;
6853           }
6854           fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6855         }
6856       }
6857     }
6858   }
6859   *offset += fdof;
6860   PetscFunctionReturn(PETSC_SUCCESS);
6861 }
6862 
6863 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[])
6864 {
6865   PetscScalar    *a;
6866   PetscInt        fdof, foff, fcdof, foffset = *offset;
6867   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6868   PetscInt        Nc, cind = 0, ncind = 0, b;
6869   PetscBool       ncSet, fcSet;
6870 
6871   PetscFunctionBegin;
6872   PetscCall(PetscSectionGetFieldComponents(section, f, &Nc));
6873   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6874   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6875   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6876   a = &array[foff];
6877   if (fcdof) {
6878     /* We just override fcdof and fcdofs with Ncc and comps */
6879     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6880     if (clperm) {
6881       if (perm) {
6882         if (comps) {
6883           for (b = 0; b < fdof; b++) {
6884             ncSet = fcSet = PETSC_FALSE;
6885             if (b % Nc == comps[ncind]) {
6886               ncind = (ncind + 1) % Ncc;
6887               ncSet = PETSC_TRUE;
6888             }
6889             if ((cind < fcdof) && (b == fcdofs[cind])) {
6890               ++cind;
6891               fcSet = PETSC_TRUE;
6892             }
6893             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6894           }
6895         } else {
6896           for (b = 0; b < fdof; b++) {
6897             if ((cind < fcdof) && (b == fcdofs[cind])) {
6898               fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6899               ++cind;
6900             }
6901           }
6902         }
6903       } else {
6904         if (comps) {
6905           for (b = 0; b < fdof; b++) {
6906             ncSet = fcSet = PETSC_FALSE;
6907             if (b % Nc == comps[ncind]) {
6908               ncind = (ncind + 1) % Ncc;
6909               ncSet = PETSC_TRUE;
6910             }
6911             if ((cind < fcdof) && (b == fcdofs[cind])) {
6912               ++cind;
6913               fcSet = PETSC_TRUE;
6914             }
6915             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6916           }
6917         } else {
6918           for (b = 0; b < fdof; b++) {
6919             if ((cind < fcdof) && (b == fcdofs[cind])) {
6920               fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6921               ++cind;
6922             }
6923           }
6924         }
6925       }
6926     } else {
6927       if (perm) {
6928         if (comps) {
6929           for (b = 0; b < fdof; b++) {
6930             ncSet = fcSet = PETSC_FALSE;
6931             if (b % Nc == comps[ncind]) {
6932               ncind = (ncind + 1) % Ncc;
6933               ncSet = PETSC_TRUE;
6934             }
6935             if ((cind < fcdof) && (b == fcdofs[cind])) {
6936               ++cind;
6937               fcSet = PETSC_TRUE;
6938             }
6939             if (ncSet && fcSet) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6940           }
6941         } else {
6942           for (b = 0; b < fdof; b++) {
6943             if ((cind < fcdof) && (b == fcdofs[cind])) {
6944               fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6945               ++cind;
6946             }
6947           }
6948         }
6949       } else {
6950         if (comps) {
6951           for (b = 0; b < fdof; b++) {
6952             ncSet = fcSet = PETSC_FALSE;
6953             if (b % Nc == comps[ncind]) {
6954               ncind = (ncind + 1) % Ncc;
6955               ncSet = PETSC_TRUE;
6956             }
6957             if ((cind < fcdof) && (b == fcdofs[cind])) {
6958               ++cind;
6959               fcSet = PETSC_TRUE;
6960             }
6961             if (ncSet && fcSet) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6962           }
6963         } else {
6964           for (b = 0; b < fdof; b++) {
6965             if ((cind < fcdof) && (b == fcdofs[cind])) {
6966               fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6967               ++cind;
6968             }
6969           }
6970         }
6971       }
6972     }
6973   }
6974   *offset += fdof;
6975   PetscFunctionReturn(PETSC_SUCCESS);
6976 }
6977 
6978 static inline PetscErrorCode DMPlexVecSetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6979 {
6980   PetscScalar    *array;
6981   const PetscInt *cone, *coneO;
6982   PetscInt        pStart, pEnd, p, numPoints, off, dof;
6983 
6984   PetscFunctionBeginHot;
6985   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6986   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
6987   PetscCall(DMPlexGetCone(dm, point, &cone));
6988   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
6989   PetscCall(VecGetArray(v, &array));
6990   for (p = 0, off = 0; p <= numPoints; ++p, off += dof) {
6991     const PetscInt cp = !p ? point : cone[p - 1];
6992     const PetscInt o  = !p ? 0 : coneO[p - 1];
6993 
6994     if ((cp < pStart) || (cp >= pEnd)) {
6995       dof = 0;
6996       continue;
6997     }
6998     PetscCall(PetscSectionGetDof(section, cp, &dof));
6999     /* ADD_VALUES */
7000     {
7001       const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
7002       PetscScalar    *a;
7003       PetscInt        cdof, coff, cind = 0, k;
7004 
7005       PetscCall(PetscSectionGetConstraintDof(section, cp, &cdof));
7006       PetscCall(PetscSectionGetOffset(section, cp, &coff));
7007       a = &array[coff];
7008       if (!cdof) {
7009         if (o >= 0) {
7010           for (k = 0; k < dof; ++k) a[k] += values[off + k];
7011         } else {
7012           for (k = 0; k < dof; ++k) a[k] += values[off + dof - k - 1];
7013         }
7014       } else {
7015         PetscCall(PetscSectionGetConstraintIndices(section, cp, &cdofs));
7016         if (o >= 0) {
7017           for (k = 0; k < dof; ++k) {
7018             if ((cind < cdof) && (k == cdofs[cind])) {
7019               ++cind;
7020               continue;
7021             }
7022             a[k] += values[off + k];
7023           }
7024         } else {
7025           for (k = 0; k < dof; ++k) {
7026             if ((cind < cdof) && (k == cdofs[cind])) {
7027               ++cind;
7028               continue;
7029             }
7030             a[k] += values[off + dof - k - 1];
7031           }
7032         }
7033       }
7034     }
7035   }
7036   PetscCall(VecRestoreArray(v, &array));
7037   PetscFunctionReturn(PETSC_SUCCESS);
7038 }
7039 
7040 /*@C
7041   DMPlexVecSetClosure - Set an array of the values on the closure of `point`
7042 
7043   Not collective
7044 
7045   Input Parameters:
7046 + dm      - The `DM`
7047 . section - The section describing the layout in `v`, or `NULL` to use the default section
7048 . v       - The local vector
7049 . point   - The point in the `DM`
7050 . values  - The array of values
7051 - mode    - The insert mode. One of `INSERT_ALL_VALUES`, `ADD_ALL_VALUES`, `INSERT_VALUES`, `ADD_VALUES`, `INSERT_BC_VALUES`, and `ADD_BC_VALUES`,
7052             where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions.
7053 
7054   Level: intermediate
7055 
7056   Note:
7057   Usually the input arrays were obtained with `DMPlexVecGetClosure()`
7058 
7059   Fortran Note:
7060   `values` must be declared with
7061 .vb
7062   PetscScalar,dimension(:),pointer   :: values
7063 .ve
7064 
7065 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`
7066 @*/
7067 PetscErrorCode DMPlexVecSetClosure(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
7068 {
7069   PetscSection    clSection;
7070   IS              clPoints;
7071   PetscScalar    *array;
7072   PetscInt       *points = NULL;
7073   const PetscInt *clp, *clperm = NULL;
7074   PetscInt        depth, numFields, numPoints, p, clsize;
7075 
7076   PetscFunctionBeginHot;
7077   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7078   if (!section) PetscCall(DMGetLocalSection(dm, &section));
7079   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7080   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
7081   PetscCall(DMPlexGetDepth(dm, &depth));
7082   PetscCall(PetscSectionGetNumFields(section, &numFields));
7083   if (depth == 1 && numFields < 2 && mode == ADD_VALUES) {
7084     PetscCall(DMPlexVecSetClosure_Depth1_Static(dm, section, v, point, values, mode));
7085     PetscFunctionReturn(PETSC_SUCCESS);
7086   }
7087   /* Get points */
7088   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
7089   for (clsize = 0, p = 0; p < numPoints; p++) {
7090     PetscInt dof;
7091     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
7092     clsize += dof;
7093   }
7094   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
7095   /* Get array */
7096   PetscCall(VecGetArray(v, &array));
7097   /* Get values */
7098   if (numFields > 0) {
7099     PetscInt offset = 0, f;
7100     for (f = 0; f < numFields; ++f) {
7101       const PetscInt    **perms = NULL;
7102       const PetscScalar **flips = NULL;
7103 
7104       PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7105       switch (mode) {
7106       case INSERT_VALUES:
7107         for (p = 0; p < numPoints; p++) {
7108           const PetscInt     point = points[2 * p];
7109           const PetscInt    *perm  = perms ? perms[p] : NULL;
7110           const PetscScalar *flip  = flips ? flips[p] : NULL;
7111           PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, clperm, values, &offset, array));
7112         }
7113         break;
7114       case INSERT_ALL_VALUES:
7115         for (p = 0; p < numPoints; p++) {
7116           const PetscInt     point = points[2 * p];
7117           const PetscInt    *perm  = perms ? perms[p] : NULL;
7118           const PetscScalar *flip  = flips ? flips[p] : NULL;
7119           PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, clperm, values, &offset, array));
7120         }
7121         break;
7122       case INSERT_BC_VALUES:
7123         for (p = 0; p < numPoints; p++) {
7124           const PetscInt     point = points[2 * p];
7125           const PetscInt    *perm  = perms ? perms[p] : NULL;
7126           const PetscScalar *flip  = flips ? flips[p] : NULL;
7127           PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, insert, clperm, values, &offset, array));
7128         }
7129         break;
7130       case ADD_VALUES:
7131         for (p = 0; p < numPoints; p++) {
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(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, clperm, values, &offset, array));
7136         }
7137         break;
7138       case ADD_ALL_VALUES:
7139         for (p = 0; p < numPoints; p++) {
7140           const PetscInt     point = points[2 * p];
7141           const PetscInt    *perm  = perms ? perms[p] : NULL;
7142           const PetscScalar *flip  = flips ? flips[p] : NULL;
7143           PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, clperm, values, &offset, array));
7144         }
7145         break;
7146       case ADD_BC_VALUES:
7147         for (p = 0; p < numPoints; p++) {
7148           const PetscInt     point = points[2 * p];
7149           const PetscInt    *perm  = perms ? perms[p] : NULL;
7150           const PetscScalar *flip  = flips ? flips[p] : NULL;
7151           PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, add, clperm, values, &offset, array));
7152         }
7153         break;
7154       default:
7155         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
7156       }
7157       PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7158     }
7159   } else {
7160     PetscInt            dof, off;
7161     const PetscInt    **perms = NULL;
7162     const PetscScalar **flips = NULL;
7163 
7164     PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
7165     switch (mode) {
7166     case INSERT_VALUES:
7167       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7168         const PetscInt     point = points[2 * p];
7169         const PetscInt    *perm  = perms ? perms[p] : NULL;
7170         const PetscScalar *flip  = flips ? flips[p] : NULL;
7171         PetscCall(PetscSectionGetDof(section, point, &dof));
7172         PetscCall(updatePoint_private(section, point, dof, insert, PETSC_FALSE, perm, flip, clperm, values, off, array));
7173       }
7174       break;
7175     case INSERT_ALL_VALUES:
7176       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7177         const PetscInt     point = points[2 * p];
7178         const PetscInt    *perm  = perms ? perms[p] : NULL;
7179         const PetscScalar *flip  = flips ? flips[p] : NULL;
7180         PetscCall(PetscSectionGetDof(section, point, &dof));
7181         PetscCall(updatePoint_private(section, point, dof, insert, PETSC_TRUE, perm, flip, clperm, values, off, array));
7182       }
7183       break;
7184     case INSERT_BC_VALUES:
7185       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7186         const PetscInt     point = points[2 * p];
7187         const PetscInt    *perm  = perms ? perms[p] : NULL;
7188         const PetscScalar *flip  = flips ? flips[p] : NULL;
7189         PetscCall(PetscSectionGetDof(section, point, &dof));
7190         PetscCall(updatePointBC_private(section, point, dof, insert, perm, flip, clperm, values, off, array));
7191       }
7192       break;
7193     case ADD_VALUES:
7194       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7195         const PetscInt     point = points[2 * p];
7196         const PetscInt    *perm  = perms ? perms[p] : NULL;
7197         const PetscScalar *flip  = flips ? flips[p] : NULL;
7198         PetscCall(PetscSectionGetDof(section, point, &dof));
7199         PetscCall(updatePoint_private(section, point, dof, add, PETSC_FALSE, perm, flip, clperm, values, off, array));
7200       }
7201       break;
7202     case ADD_ALL_VALUES:
7203       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7204         const PetscInt     point = points[2 * p];
7205         const PetscInt    *perm  = perms ? perms[p] : NULL;
7206         const PetscScalar *flip  = flips ? flips[p] : NULL;
7207         PetscCall(PetscSectionGetDof(section, point, &dof));
7208         PetscCall(updatePoint_private(section, point, dof, add, PETSC_TRUE, perm, flip, clperm, values, off, array));
7209       }
7210       break;
7211     case ADD_BC_VALUES:
7212       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7213         const PetscInt     point = points[2 * p];
7214         const PetscInt    *perm  = perms ? perms[p] : NULL;
7215         const PetscScalar *flip  = flips ? flips[p] : NULL;
7216         PetscCall(PetscSectionGetDof(section, point, &dof));
7217         PetscCall(updatePointBC_private(section, point, dof, add, perm, flip, clperm, values, off, array));
7218       }
7219       break;
7220     default:
7221       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
7222     }
7223     PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
7224   }
7225   /* Cleanup points */
7226   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
7227   /* Cleanup array */
7228   PetscCall(VecRestoreArray(v, &array));
7229   PetscFunctionReturn(PETSC_SUCCESS);
7230 }
7231 
7232 /* Check whether the given point is in the label. If not, update the offset to skip this point */
7233 static inline PetscErrorCode CheckPoint_Private(DMLabel label, PetscInt labelId, PetscSection section, PetscInt point, PetscInt f, PetscInt *offset, PetscBool *contains)
7234 {
7235   PetscFunctionBegin;
7236   *contains = PETSC_TRUE;
7237   if (label) {
7238     PetscInt fdof;
7239 
7240     PetscCall(DMLabelStratumHasPoint(label, labelId, point, contains));
7241     if (!*contains) {
7242       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7243       *offset += fdof;
7244       PetscFunctionReturn(PETSC_SUCCESS);
7245     }
7246   }
7247   PetscFunctionReturn(PETSC_SUCCESS);
7248 }
7249 
7250 /* Unlike DMPlexVecSetClosure(), this uses plex-native closure permutation, not a user-specified permutation such as DMPlexSetClosurePermutationTensor(). */
7251 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)
7252 {
7253   PetscSection    clSection;
7254   IS              clPoints;
7255   PetscScalar    *array;
7256   PetscInt       *points = NULL;
7257   const PetscInt *clp;
7258   PetscInt        numFields, numPoints, p;
7259   PetscInt        offset = 0, f;
7260 
7261   PetscFunctionBeginHot;
7262   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7263   if (!section) PetscCall(DMGetLocalSection(dm, &section));
7264   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7265   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
7266   PetscCall(PetscSectionGetNumFields(section, &numFields));
7267   /* Get points */
7268   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
7269   /* Get array */
7270   PetscCall(VecGetArray(v, &array));
7271   /* Get values */
7272   for (f = 0; f < numFields; ++f) {
7273     const PetscInt    **perms = NULL;
7274     const PetscScalar **flips = NULL;
7275     PetscBool           contains;
7276 
7277     if (!fieldActive[f]) {
7278       for (p = 0; p < numPoints * 2; p += 2) {
7279         PetscInt fdof;
7280         PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
7281         offset += fdof;
7282       }
7283       continue;
7284     }
7285     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7286     switch (mode) {
7287     case INSERT_VALUES:
7288       for (p = 0; p < numPoints; p++) {
7289         const PetscInt     point = points[2 * p];
7290         const PetscInt    *perm  = perms ? perms[p] : NULL;
7291         const PetscScalar *flip  = flips ? flips[p] : NULL;
7292         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7293         if (!contains) continue;
7294         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, NULL, values, &offset, array));
7295       }
7296       break;
7297     case INSERT_ALL_VALUES:
7298       for (p = 0; p < numPoints; p++) {
7299         const PetscInt     point = points[2 * p];
7300         const PetscInt    *perm  = perms ? perms[p] : NULL;
7301         const PetscScalar *flip  = flips ? flips[p] : NULL;
7302         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7303         if (!contains) continue;
7304         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, NULL, values, &offset, array));
7305       }
7306       break;
7307     case INSERT_BC_VALUES:
7308       for (p = 0; p < numPoints; p++) {
7309         const PetscInt     point = points[2 * p];
7310         const PetscInt    *perm  = perms ? perms[p] : NULL;
7311         const PetscScalar *flip  = flips ? flips[p] : NULL;
7312         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7313         if (!contains) continue;
7314         PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, Ncc, comps, insert, NULL, values, &offset, array));
7315       }
7316       break;
7317     case ADD_VALUES:
7318       for (p = 0; p < numPoints; p++) {
7319         const PetscInt     point = points[2 * p];
7320         const PetscInt    *perm  = perms ? perms[p] : NULL;
7321         const PetscScalar *flip  = flips ? flips[p] : NULL;
7322         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7323         if (!contains) continue;
7324         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, NULL, values, &offset, array));
7325       }
7326       break;
7327     case ADD_ALL_VALUES:
7328       for (p = 0; p < numPoints; p++) {
7329         const PetscInt     point = points[2 * p];
7330         const PetscInt    *perm  = perms ? perms[p] : NULL;
7331         const PetscScalar *flip  = flips ? flips[p] : NULL;
7332         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7333         if (!contains) continue;
7334         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, NULL, values, &offset, array));
7335       }
7336       break;
7337     default:
7338       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
7339     }
7340     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7341   }
7342   /* Cleanup points */
7343   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
7344   /* Cleanup array */
7345   PetscCall(VecRestoreArray(v, &array));
7346   PetscFunctionReturn(PETSC_SUCCESS);
7347 }
7348 
7349 static PetscErrorCode DMPlexPrintMatSetValues(PetscViewer viewer, Mat A, PetscInt point, PetscInt numRIndices, const PetscInt rindices[], PetscInt numCIndices, const PetscInt cindices[], const PetscScalar values[])
7350 {
7351   PetscMPIInt rank;
7352   PetscInt    i, j;
7353 
7354   PetscFunctionBegin;
7355   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7356   PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat for point %" PetscInt_FMT "\n", rank, point));
7357   for (i = 0; i < numRIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat row indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, rindices[i]));
7358   for (i = 0; i < numCIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat col indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, cindices[i]));
7359   numCIndices = numCIndices ? numCIndices : numRIndices;
7360   if (!values) PetscFunctionReturn(PETSC_SUCCESS);
7361   for (i = 0; i < numRIndices; i++) {
7362     PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]", rank));
7363     for (j = 0; j < numCIndices; j++) {
7364 #if defined(PETSC_USE_COMPLEX)
7365       PetscCall(PetscViewerASCIIPrintf(viewer, " (%g,%g)", (double)PetscRealPart(values[i * numCIndices + j]), (double)PetscImaginaryPart(values[i * numCIndices + j])));
7366 #else
7367       PetscCall(PetscViewerASCIIPrintf(viewer, " %g", (double)values[i * numCIndices + j]));
7368 #endif
7369     }
7370     PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
7371   }
7372   PetscFunctionReturn(PETSC_SUCCESS);
7373 }
7374 
7375 /*
7376   DMPlexGetIndicesPoint_Internal - Add the indices for dofs on a point to an index array
7377 
7378   Input Parameters:
7379 + section - The section for this data layout
7380 . islocal - Is the section (and thus indices being requested) local or global?
7381 . point   - The point contributing dofs with these indices
7382 . off     - The global offset of this point
7383 . loff    - The local offset of each field
7384 . setBC   - The flag determining whether to include indices of boundary values
7385 . perm    - A permutation of the dofs on this point, or NULL
7386 - indperm - A permutation of the entire indices array, or NULL
7387 
7388   Output Parameter:
7389 . indices - Indices for dofs on this point
7390 
7391   Level: developer
7392 
7393   Note: The indices could be local or global, depending on the value of 'off'.
7394 */
7395 PetscErrorCode DMPlexGetIndicesPoint_Internal(PetscSection section, PetscBool islocal, PetscInt point, PetscInt off, PetscInt *loff, PetscBool setBC, const PetscInt perm[], const PetscInt indperm[], PetscInt indices[])
7396 {
7397   PetscInt        dof;   /* The number of unknowns on this point */
7398   PetscInt        cdof;  /* The number of constraints on this point */
7399   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
7400   PetscInt        cind = 0, k;
7401 
7402   PetscFunctionBegin;
7403   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
7404   PetscCall(PetscSectionGetDof(section, point, &dof));
7405   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
7406   if (!cdof || setBC) {
7407     for (k = 0; k < dof; ++k) {
7408       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
7409       const PetscInt ind    = indperm ? indperm[preind] : preind;
7410 
7411       indices[ind] = off + k;
7412     }
7413   } else {
7414     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
7415     for (k = 0; k < dof; ++k) {
7416       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
7417       const PetscInt ind    = indperm ? indperm[preind] : preind;
7418 
7419       if ((cind < cdof) && (k == cdofs[cind])) {
7420         /* Insert check for returning constrained indices */
7421         indices[ind] = -(off + k + 1);
7422         ++cind;
7423       } else {
7424         indices[ind] = off + k - (islocal ? 0 : cind);
7425       }
7426     }
7427   }
7428   *loff += dof;
7429   PetscFunctionReturn(PETSC_SUCCESS);
7430 }
7431 
7432 /*
7433  DMPlexGetIndicesPointFields_Internal - gets section indices for a point in its canonical ordering.
7434 
7435  Input Parameters:
7436 + section - a section (global or local)
7437 - islocal - `PETSC_TRUE` if requesting local indices (i.e., section is local); `PETSC_FALSE` for global
7438 . point - point within section
7439 . off - The offset of this point in the (local or global) indexed space - should match islocal and (usually) the section
7440 . foffs - array of length numFields containing the offset in canonical point ordering (the location in indices) of each field
7441 . setBC - identify constrained (boundary condition) points via involution.
7442 . perms - perms[f][permsoff][:] is a permutation of dofs within each field
7443 . permsoff - offset
7444 - indperm - index permutation
7445 
7446  Output Parameter:
7447 . foffs - each entry is incremented by the number of (unconstrained if setBC=FALSE) dofs in that field
7448 . indices - array to hold indices (as defined by section) of each dof associated with point
7449 
7450  Notes:
7451  If section is local and setBC=true, there is no distinction between constrained and unconstrained dofs.
7452  If section is local and setBC=false, the indices for constrained points are the involution -(i+1) of their position
7453  in the local vector.
7454 
7455  If section is global and setBC=false, the indices for constrained points are negative (and their value is not
7456  significant).  It is invalid to call with a global section and setBC=true.
7457 
7458  Developer Note:
7459  The section is only used for field layout, so islocal is technically a statement about the offset (off).  At some point
7460  in the future, global sections may have fields set, in which case we could pass the global section and obtain the
7461  offset could be obtained from the section instead of passing it explicitly as we do now.
7462 
7463  Example:
7464  Suppose a point contains one field with three components, and for which the unconstrained indices are {10, 11, 12}.
7465  When the middle component is constrained, we get the array {10, -12, 12} for (islocal=TRUE, setBC=FALSE).
7466  Note that -12 is the involution of 11, so the user can involute negative indices to recover local indices.
7467  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.
7468 
7469  Level: developer
7470 */
7471 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[])
7472 {
7473   PetscInt numFields, foff, f;
7474 
7475   PetscFunctionBegin;
7476   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
7477   PetscCall(PetscSectionGetNumFields(section, &numFields));
7478   for (f = 0, foff = 0; f < numFields; ++f) {
7479     PetscInt        fdof, cfdof;
7480     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7481     PetscInt        cind = 0, b;
7482     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7483 
7484     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7485     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7486     if (!cfdof || setBC) {
7487       for (b = 0; b < fdof; ++b) {
7488         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7489         const PetscInt ind    = indperm ? indperm[preind] : preind;
7490 
7491         indices[ind] = off + foff + b;
7492       }
7493     } else {
7494       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7495       for (b = 0; b < fdof; ++b) {
7496         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7497         const PetscInt ind    = indperm ? indperm[preind] : preind;
7498 
7499         if ((cind < cfdof) && (b == fcdofs[cind])) {
7500           indices[ind] = -(off + foff + b + 1);
7501           ++cind;
7502         } else {
7503           indices[ind] = off + foff + b - (islocal ? 0 : cind);
7504         }
7505       }
7506     }
7507     foff += (setBC || islocal ? fdof : (fdof - cfdof));
7508     foffs[f] += fdof;
7509   }
7510   PetscFunctionReturn(PETSC_SUCCESS);
7511 }
7512 
7513 /*
7514   This version believes the globalSection offsets for each field, rather than just the point offset
7515 
7516  . foffs - The offset into 'indices' for each field, since it is segregated by field
7517 
7518  Notes:
7519  The semantics of this function relate to that of setBC=FALSE in DMPlexGetIndicesPointFields_Internal.
7520  Since this function uses global indices, setBC=TRUE would be invalid, so no such argument exists.
7521 */
7522 static PetscErrorCode DMPlexGetIndicesPointFieldsSplit_Internal(PetscSection section, PetscSection globalSection, PetscInt point, PetscInt foffs[], const PetscInt ***perms, PetscInt permsoff, const PetscInt indperm[], PetscInt indices[])
7523 {
7524   PetscInt numFields, foff, f;
7525 
7526   PetscFunctionBegin;
7527   PetscCall(PetscSectionGetNumFields(section, &numFields));
7528   for (f = 0; f < numFields; ++f) {
7529     PetscInt        fdof, cfdof;
7530     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7531     PetscInt        cind = 0, b;
7532     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7533 
7534     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7535     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7536     PetscCall(PetscSectionGetFieldOffset(globalSection, point, f, &foff));
7537     if (!cfdof) {
7538       for (b = 0; b < fdof; ++b) {
7539         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7540         const PetscInt ind    = indperm ? indperm[preind] : preind;
7541 
7542         indices[ind] = foff + b;
7543       }
7544     } else {
7545       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7546       for (b = 0; b < fdof; ++b) {
7547         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7548         const PetscInt ind    = indperm ? indperm[preind] : preind;
7549 
7550         if ((cind < cfdof) && (b == fcdofs[cind])) {
7551           indices[ind] = -(foff + b + 1);
7552           ++cind;
7553         } else {
7554           indices[ind] = foff + b - cind;
7555         }
7556       }
7557     }
7558     foffs[f] += fdof;
7559   }
7560   PetscFunctionReturn(PETSC_SUCCESS);
7561 }
7562 
7563 static PetscErrorCode DMPlexAnchorsGetSubMatIndices(PetscInt nPoints, const PetscInt pnts[], PetscSection section, PetscSection cSec, PetscInt tmpIndices[], PetscInt fieldOffsets[], PetscInt indices[], const PetscInt ***perms)
7564 {
7565   PetscInt numFields, sStart, sEnd, cStart, cEnd;
7566 
7567   PetscFunctionBegin;
7568   PetscCall(PetscSectionGetNumFields(section, &numFields));
7569   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
7570   PetscCall(PetscSectionGetChart(cSec, &cStart, &cEnd));
7571   for (PetscInt p = 0; p < nPoints; p++) {
7572     PetscInt     b       = pnts[2 * p];
7573     PetscInt     bSecDof = 0, bOff;
7574     PetscInt     cSecDof = 0;
7575     PetscSection indices_section;
7576 
7577     if (b >= sStart && b < sEnd) PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7578     if (!bSecDof) continue;
7579     if (b >= cStart && b < cEnd) PetscCall(PetscSectionGetDof(cSec, b, &cSecDof));
7580     indices_section = cSecDof > 0 ? cSec : section;
7581     if (numFields) {
7582       PetscInt fStart[32], fEnd[32];
7583 
7584       fStart[0] = 0;
7585       fEnd[0]   = 0;
7586       for (PetscInt f = 0; f < numFields; f++) {
7587         PetscInt fDof = 0;
7588 
7589         PetscCall(PetscSectionGetFieldDof(indices_section, b, f, &fDof));
7590         fStart[f + 1] = fStart[f] + fDof;
7591         fEnd[f + 1]   = fStart[f + 1];
7592       }
7593       PetscCall(PetscSectionGetOffset(indices_section, b, &bOff));
7594       // only apply permutations on one side
7595       PetscCall(DMPlexGetIndicesPointFields_Internal(indices_section, PETSC_TRUE, b, bOff, fEnd, PETSC_TRUE, perms, perms ? p : -1, NULL, tmpIndices));
7596       for (PetscInt f = 0; f < numFields; f++) {
7597         for (PetscInt i = fStart[f]; i < fEnd[f]; i++) { indices[fieldOffsets[f]++] = (cSecDof > 0) ? tmpIndices[i] : -(tmpIndices[i] + 1); }
7598       }
7599     } else {
7600       PetscInt bEnd = 0;
7601 
7602       PetscCall(PetscSectionGetOffset(indices_section, b, &bOff));
7603       PetscCall(DMPlexGetIndicesPoint_Internal(indices_section, PETSC_TRUE, b, bOff, &bEnd, PETSC_TRUE, (perms && perms[0]) ? perms[0][p] : NULL, NULL, tmpIndices));
7604 
7605       for (PetscInt i = 0; i < bEnd; i++) indices[fieldOffsets[0]++] = (cSecDof > 0) ? tmpIndices[i] : -(tmpIndices[i] + 1);
7606     }
7607   }
7608   PetscFunctionReturn(PETSC_SUCCESS);
7609 }
7610 
7611 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[])
7612 {
7613   Mat             cMat;
7614   PetscSection    aSec, cSec;
7615   IS              aIS;
7616   PetscInt        aStart = -1, aEnd = -1;
7617   PetscInt        sStart = -1, sEnd = -1;
7618   PetscInt        cStart = -1, cEnd = -1;
7619   const PetscInt *anchors;
7620   PetscInt        numFields, p;
7621   PetscInt        newNumPoints = 0, newNumIndices = 0;
7622   PetscInt       *newPoints, *indices, *newIndices, *tmpIndices, *tmpNewIndices;
7623   PetscInt        oldOffsets[32];
7624   PetscInt        newOffsets[32];
7625   PetscInt        oldOffsetsCopy[32];
7626   PetscInt        newOffsetsCopy[32];
7627   PetscScalar    *modMat         = NULL;
7628   PetscBool       anyConstrained = PETSC_FALSE;
7629 
7630   PetscFunctionBegin;
7631   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7632   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7633   PetscCall(PetscSectionGetNumFields(section, &numFields));
7634 
7635   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
7636   /* if there are point-to-point constraints */
7637   if (aSec) {
7638     PetscCall(PetscArrayzero(newOffsets, 32));
7639     PetscCall(PetscArrayzero(oldOffsets, 32));
7640     PetscCall(ISGetIndices(aIS, &anchors));
7641     PetscCall(PetscSectionGetChart(aSec, &aStart, &aEnd));
7642     PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
7643     /* figure out how many points are going to be in the new element matrix
7644      * (we allow double counting, because it's all just going to be summed
7645      * into the global matrix anyway) */
7646     for (p = 0; p < 2 * numPoints; p += 2) {
7647       PetscInt b    = points[p];
7648       PetscInt bDof = 0, bSecDof = 0;
7649 
7650       if (b >= sStart && b < sEnd) PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7651       if (!bSecDof) continue;
7652 
7653       for (PetscInt f = 0; f < numFields; f++) {
7654         PetscInt fDof = 0;
7655 
7656         PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7657         oldOffsets[f + 1] += fDof;
7658       }
7659       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7660       if (bDof) {
7661         /* this point is constrained */
7662         /* it is going to be replaced by its anchors */
7663         PetscInt bOff, q;
7664 
7665         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7666         for (q = 0; q < bDof; q++) {
7667           PetscInt a    = anchors[bOff + q];
7668           PetscInt aDof = 0;
7669 
7670           if (a >= sStart && a < sEnd) PetscCall(PetscSectionGetDof(section, a, &aDof));
7671           if (aDof) {
7672             anyConstrained = PETSC_TRUE;
7673             newNumPoints += 1;
7674           }
7675           newNumIndices += aDof;
7676           for (PetscInt f = 0; f < numFields; ++f) {
7677             PetscInt fDof = 0;
7678 
7679             if (a >= sStart && a < sEnd) PetscCall(PetscSectionGetFieldDof(section, a, f, &fDof));
7680             newOffsets[f + 1] += fDof;
7681           }
7682         }
7683       } else {
7684         /* this point is not constrained */
7685         newNumPoints++;
7686         newNumIndices += bSecDof;
7687         for (PetscInt f = 0; f < numFields; ++f) {
7688           PetscInt fDof;
7689 
7690           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7691           newOffsets[f + 1] += fDof;
7692         }
7693       }
7694     }
7695   }
7696   if (!anyConstrained) {
7697     if (outNumPoints) *outNumPoints = 0;
7698     if (outNumIndices) *outNumIndices = 0;
7699     if (outPoints) *outPoints = NULL;
7700     if (outMat) *outMat = NULL;
7701     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7702     PetscFunctionReturn(PETSC_SUCCESS);
7703   }
7704 
7705   if (outNumPoints) *outNumPoints = newNumPoints;
7706   if (outNumIndices) *outNumIndices = newNumIndices;
7707 
7708   for (PetscInt f = 0; f < numFields; ++f) newOffsets[f + 1] += newOffsets[f];
7709   for (PetscInt f = 0; f < numFields; ++f) oldOffsets[f + 1] += oldOffsets[f];
7710 
7711   if (!outPoints && !outMat) {
7712     if (offsets) {
7713       for (PetscInt f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7714     }
7715     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7716     PetscFunctionReturn(PETSC_SUCCESS);
7717   }
7718 
7719   PetscCheck(!numFields || newOffsets[numFields] == newNumIndices, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, newOffsets[numFields], newNumIndices);
7720   PetscCheck(!numFields || oldOffsets[numFields] == numIndices, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, oldOffsets[numFields], numIndices);
7721 
7722   PetscCall(DMGetDefaultConstraints(dm, &cSec, &cMat, NULL));
7723   PetscCall(PetscSectionGetChart(cSec, &cStart, &cEnd));
7724 
7725   /* output arrays */
7726   PetscCall(DMGetWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7727   PetscCall(PetscArrayzero(newPoints, 2 * newNumPoints));
7728 
7729   // get the new Points
7730   for (PetscInt p = 0, newP = 0; p < numPoints; p++) {
7731     PetscInt b    = points[2 * p];
7732     PetscInt bDof = 0, bSecDof = 0, bOff;
7733 
7734     if (b >= sStart && b < sEnd) PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7735     if (!bSecDof) continue;
7736     if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7737     if (bDof) {
7738       PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7739       for (PetscInt q = 0; q < bDof; q++) {
7740         PetscInt a = anchors[bOff + q], aDof = 0;
7741 
7742         if (a >= sStart && a < sEnd) PetscCall(PetscSectionGetDof(section, a, &aDof));
7743         if (aDof) {
7744           newPoints[2 * newP]     = a;
7745           newPoints[2 * newP + 1] = 0; // orientations are accounted for in constructing the matrix, newly added points are in default orientation
7746           newP++;
7747         }
7748       }
7749     } else {
7750       newPoints[2 * newP]     = b;
7751       newPoints[2 * newP + 1] = points[2 * p + 1];
7752       newP++;
7753     }
7754   }
7755 
7756   if (outMat) {
7757     PetscScalar *tmpMat;
7758     PetscCall(PetscArraycpy(oldOffsetsCopy, oldOffsets, 32));
7759     PetscCall(PetscArraycpy(newOffsetsCopy, newOffsets, 32));
7760 
7761     PetscCall(DMGetWorkArray(dm, numIndices, MPIU_INT, &indices));
7762     PetscCall(DMGetWorkArray(dm, numIndices, MPIU_INT, &tmpIndices));
7763     PetscCall(DMGetWorkArray(dm, newNumIndices, MPIU_INT, &newIndices));
7764     PetscCall(DMGetWorkArray(dm, newNumIndices, MPIU_INT, &tmpNewIndices));
7765 
7766     for (PetscInt i = 0; i < numIndices; i++) indices[i] = -1;
7767     for (PetscInt i = 0; i < newNumIndices; i++) newIndices[i] = -1;
7768 
7769     PetscCall(DMPlexAnchorsGetSubMatIndices(numPoints, points, section, cSec, tmpIndices, oldOffsetsCopy, indices, perms));
7770     PetscCall(DMPlexAnchorsGetSubMatIndices(newNumPoints, newPoints, section, section, tmpNewIndices, newOffsetsCopy, newIndices, NULL));
7771 
7772     PetscCall(DMGetWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &modMat));
7773     PetscCall(DMGetWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &tmpMat));
7774     PetscCall(PetscArrayzero(modMat, newNumIndices * numIndices));
7775     // for each field, insert the anchor modification into modMat
7776     for (PetscInt f = 0; f < PetscMax(1, numFields); f++) {
7777       PetscInt fStart    = oldOffsets[f];
7778       PetscInt fNewStart = newOffsets[f];
7779       for (PetscInt p = 0, newP = 0, o = fStart, oNew = fNewStart; p < numPoints; p++) {
7780         PetscInt b    = points[2 * p];
7781         PetscInt bDof = 0, bSecDof = 0, bOff;
7782 
7783         if (b >= sStart && b < sEnd) {
7784           if (numFields) {
7785             PetscCall(PetscSectionGetFieldDof(section, b, f, &bSecDof));
7786           } else {
7787             PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7788           }
7789         }
7790         if (!bSecDof) continue;
7791         if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7792         if (bDof) {
7793           PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7794           for (PetscInt q = 0; q < bDof; q++, newP++) {
7795             PetscInt a = anchors[bOff + q], aDof = 0;
7796 
7797             if (a >= sStart && a < sEnd) {
7798               if (numFields) {
7799                 PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
7800               } else {
7801                 PetscCall(PetscSectionGetDof(section, a, &aDof));
7802               }
7803             }
7804             if (aDof) {
7805               PetscCall(MatGetValues(cMat, bSecDof, &indices[o], aDof, &newIndices[oNew], tmpMat));
7806               for (PetscInt d = 0; d < bSecDof; d++) {
7807                 for (PetscInt e = 0; e < aDof; e++) modMat[(o + d) * newNumIndices + oNew + e] = tmpMat[d * aDof + e];
7808               }
7809             }
7810             oNew += aDof;
7811           }
7812         } else {
7813           // Insert the identity matrix in this block
7814           for (PetscInt d = 0; d < bSecDof; d++) modMat[(o + d) * newNumIndices + oNew + d] = 1;
7815           oNew += bSecDof;
7816           newP++;
7817         }
7818         o += bSecDof;
7819       }
7820     }
7821 
7822     *outMat = modMat;
7823 
7824     PetscCall(DMRestoreWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &tmpMat));
7825     PetscCall(DMRestoreWorkArray(dm, newNumIndices, MPIU_INT, &tmpNewIndices));
7826     PetscCall(DMRestoreWorkArray(dm, newNumIndices, MPIU_INT, &newIndices));
7827     PetscCall(DMRestoreWorkArray(dm, numIndices, MPIU_INT, &tmpIndices));
7828     PetscCall(DMRestoreWorkArray(dm, numIndices, MPIU_INT, &indices));
7829   }
7830   PetscCall(ISRestoreIndices(aIS, &anchors));
7831 
7832   /* output */
7833   if (outPoints) {
7834     *outPoints = newPoints;
7835   } else {
7836     PetscCall(DMRestoreWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7837   }
7838   for (PetscInt f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7839   PetscFunctionReturn(PETSC_SUCCESS);
7840 }
7841 
7842 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)
7843 {
7844   PetscScalar *modMat        = NULL;
7845   PetscInt     newNumIndices = -1;
7846 
7847   PetscFunctionBegin;
7848   /* 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.
7849      modMat is that matrix C */
7850   PetscCall(DMPlexAnchorsGetSubMatModification(dm, section, numPoints, numIndices, points, perms, outNumPoints, &newNumIndices, outPoints, offsets, outValues ? &modMat : NULL));
7851   if (outNumIndices) *outNumIndices = newNumIndices;
7852   if (modMat) {
7853     const PetscScalar *newValues = values;
7854 
7855     if (multiplyRight) {
7856       PetscScalar *newNewValues = NULL;
7857       PetscBLASInt M            = newNumIndices;
7858       PetscBLASInt N            = numRows;
7859       PetscBLASInt K            = numIndices;
7860       PetscScalar  a = 1.0, b = 0.0;
7861 
7862       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);
7863 
7864       PetscCall(DMGetWorkArray(dm, numRows * newNumIndices, MPIU_SCALAR, &newNewValues));
7865       // row-major to column-major conversion, right multiplication becomes left multiplication
7866       PetscCallBLAS("BLASgemm", BLASgemm_("N", "N", &M, &N, &K, &a, modMat, &M, newValues, &K, &b, newNewValues, &M));
7867 
7868       numCols   = newNumIndices;
7869       newValues = newNewValues;
7870     }
7871 
7872     if (multiplyLeft) {
7873       PetscScalar *newNewValues = NULL;
7874       PetscBLASInt M            = numCols;
7875       PetscBLASInt N            = newNumIndices;
7876       PetscBLASInt K            = numIndices;
7877       PetscScalar  a = 1.0, b = 0.0;
7878 
7879       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);
7880 
7881       PetscCall(DMGetWorkArray(dm, newNumIndices * numCols, MPIU_SCALAR, &newNewValues));
7882       // row-major to column-major conversion, left multiplication becomes right multiplication
7883       PetscCallBLAS("BLASgemm", BLASgemm_("N", "T", &M, &N, &K, &a, newValues, &M, modMat, &N, &b, newNewValues, &M));
7884       if (newValues != values) PetscCall(DMRestoreWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &newValues));
7885       newValues = newNewValues;
7886     }
7887     *outValues = (PetscScalar *)newValues;
7888     PetscCall(DMRestoreWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &modMat));
7889   }
7890   PetscFunctionReturn(PETSC_SUCCESS);
7891 }
7892 
7893 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)
7894 {
7895   PetscFunctionBegin;
7896   PetscCall(DMPlexAnchorsModifyMat_Internal(dm, section, numPoints, numIndices, points, perms, numIndices, numIndices, values, outNumPoints, outNumIndices, outPoints, outValues, offsets, PETSC_TRUE, multiplyLeft));
7897   PetscFunctionReturn(PETSC_SUCCESS);
7898 }
7899 
7900 static PetscErrorCode DMPlexGetClosureIndicesSize_Internal(DM dm, PetscSection section, PetscInt point, PetscInt *closureSize)
7901 {
7902   /* Closure ordering */
7903   PetscSection    clSection;
7904   IS              clPoints;
7905   const PetscInt *clp;
7906   PetscInt       *points;
7907   PetscInt        Ncl, Ni = 0;
7908 
7909   PetscFunctionBeginHot;
7910   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &Ncl, &points, &clSection, &clPoints, &clp));
7911   for (PetscInt p = 0; p < Ncl * 2; p += 2) {
7912     PetscInt dof;
7913 
7914     PetscCall(PetscSectionGetDof(section, points[p], &dof));
7915     Ni += dof;
7916   }
7917   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7918   *closureSize = Ni;
7919   PetscFunctionReturn(PETSC_SUCCESS);
7920 }
7921 
7922 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)
7923 {
7924   /* Closure ordering */
7925   PetscSection    clSection;
7926   IS              clPoints;
7927   const PetscInt *clp;
7928   PetscInt       *points;
7929   const PetscInt *clperm = NULL;
7930   /* Dof permutation and sign flips */
7931   const PetscInt    **perms[32] = {NULL};
7932   const PetscScalar **flips[32] = {NULL};
7933   PetscScalar        *valCopy   = NULL;
7934   /* Hanging node constraints */
7935   PetscInt    *pointsC = NULL;
7936   PetscScalar *valuesC = NULL;
7937   PetscInt     NclC, NiC;
7938 
7939   PetscInt *idx;
7940   PetscInt  Nf, Ncl, Ni = 0, offsets[32], p, f;
7941   PetscBool isLocal = (section == idxSection) ? PETSC_TRUE : PETSC_FALSE;
7942   PetscInt  idxStart, idxEnd;
7943   PetscInt  nRows, nCols;
7944 
7945   PetscFunctionBeginHot;
7946   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7947   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7948   PetscValidHeaderSpecific(idxSection, PETSC_SECTION_CLASSID, 3);
7949   PetscAssertPointer(numRows, 6);
7950   PetscAssertPointer(numCols, 7);
7951   if (indices) PetscAssertPointer(indices, 8);
7952   if (outOffsets) PetscAssertPointer(outOffsets, 9);
7953   if (values) PetscAssertPointer(values, 10);
7954   PetscCall(PetscSectionGetNumFields(section, &Nf));
7955   PetscCheck(Nf <= 31, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", Nf);
7956   PetscCall(PetscArrayzero(offsets, 32));
7957   /* 1) Get points in closure */
7958   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &Ncl, &points, &clSection, &clPoints, &clp));
7959   if (useClPerm) {
7960     PetscInt depth, clsize;
7961     PetscCall(DMPlexGetPointDepth(dm, point, &depth));
7962     for (clsize = 0, p = 0; p < Ncl; p++) {
7963       PetscInt dof;
7964       PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
7965       clsize += dof;
7966     }
7967     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
7968   }
7969   /* 2) Get number of indices on these points and field offsets from section */
7970   for (p = 0; p < Ncl * 2; p += 2) {
7971     PetscInt dof, fdof;
7972 
7973     PetscCall(PetscSectionGetDof(section, points[p], &dof));
7974     for (f = 0; f < Nf; ++f) {
7975       PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
7976       offsets[f + 1] += fdof;
7977     }
7978     Ni += dof;
7979   }
7980   if (*numRows == -1) *numRows = Ni;
7981   if (*numCols == -1) *numCols = Ni;
7982   nRows = *numRows;
7983   nCols = *numCols;
7984   for (f = 1; f < Nf; ++f) offsets[f + 1] += offsets[f];
7985   PetscCheck(!Nf || offsets[Nf] == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, offsets[Nf], Ni);
7986   /* 3) Get symmetries and sign flips. Apply sign flips to values if passed in (only works for square values matrix) */
7987   if (multiplyRight) PetscCheck(nCols == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Expected %" PetscInt_FMT " columns, got %" PetscInt_FMT, Ni, nCols);
7988   if (multiplyLeft) PetscCheck(nRows == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Expected %" PetscInt_FMT " rows, got %" PetscInt_FMT, Ni, nRows);
7989   for (f = 0; f < PetscMax(1, Nf); ++f) {
7990     if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7991     else PetscCall(PetscSectionGetPointSyms(section, Ncl, points, &perms[f], &flips[f]));
7992     /* may need to apply sign changes to the element matrix */
7993     if (values && flips[f]) {
7994       PetscInt foffset = offsets[f];
7995 
7996       for (p = 0; p < Ncl; ++p) {
7997         PetscInt           pnt  = points[2 * p], fdof;
7998         const PetscScalar *flip = flips[f] ? flips[f][p] : NULL;
7999 
8000         if (!Nf) PetscCall(PetscSectionGetDof(section, pnt, &fdof));
8001         else PetscCall(PetscSectionGetFieldDof(section, pnt, f, &fdof));
8002         if (flip) {
8003           PetscInt i, j, k;
8004 
8005           if (!valCopy) {
8006             PetscCall(DMGetWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
8007             for (j = 0; j < Ni * Ni; ++j) valCopy[j] = (*values)[j];
8008             *values = valCopy;
8009           }
8010           for (i = 0; i < fdof; ++i) {
8011             PetscScalar fval = flip[i];
8012 
8013             if (multiplyRight) {
8014               for (k = 0; k < nRows; ++k) { valCopy[Ni * k + (foffset + i)] *= fval; }
8015             }
8016             if (multiplyLeft) {
8017               for (k = 0; k < nCols; ++k) { valCopy[nCols * (foffset + i) + k] *= fval; }
8018             }
8019           }
8020         }
8021         foffset += fdof;
8022       }
8023     }
8024   }
8025   /* 4) Apply hanging node constraints. Get new symmetries and replace all storage with constrained storage */
8026   PetscCall(DMPlexAnchorsModifyMat_Internal(dm, section, Ncl, Ni, points, perms, nRows, nCols, values ? *values : NULL, &NclC, &NiC, &pointsC, values ? &valuesC : NULL, offsets, multiplyRight, multiplyLeft));
8027   if (NclC) {
8028     if (multiplyRight) { *numCols = nCols = NiC; }
8029     if (multiplyLeft) { *numRows = nRows = NiC; }
8030     if (valCopy) PetscCall(DMRestoreWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
8031     for (f = 0; f < PetscMax(1, Nf); ++f) {
8032       if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
8033       else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
8034     }
8035     for (f = 0; f < PetscMax(1, Nf); ++f) {
8036       if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, NclC, pointsC, &perms[f], &flips[f]));
8037       else PetscCall(PetscSectionGetPointSyms(section, NclC, pointsC, &perms[f], &flips[f]));
8038     }
8039     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
8040     Ncl    = NclC;
8041     Ni     = NiC;
8042     points = pointsC;
8043     if (values) *values = valuesC;
8044   }
8045   /* 5) Calculate indices */
8046   PetscCall(DMGetWorkArray(dm, Ni, MPIU_INT, &idx));
8047   PetscCall(PetscSectionGetChart(idxSection, &idxStart, &idxEnd));
8048   if (Nf) {
8049     PetscInt  idxOff;
8050     PetscBool useFieldOffsets;
8051 
8052     if (outOffsets) {
8053       for (f = 0; f <= Nf; f++) outOffsets[f] = offsets[f];
8054     }
8055     PetscCall(PetscSectionGetUseFieldOffsets(idxSection, &useFieldOffsets));
8056     if (useFieldOffsets) {
8057       for (p = 0; p < Ncl; ++p) {
8058         const PetscInt pnt = points[p * 2];
8059 
8060         PetscCall(DMPlexGetIndicesPointFieldsSplit_Internal(section, idxSection, pnt, offsets, perms, p, clperm, idx));
8061       }
8062     } else {
8063       for (p = 0; p < Ncl; ++p) {
8064         const PetscInt pnt = points[p * 2];
8065 
8066         if (pnt < idxStart || pnt >= idxEnd) continue;
8067         PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
8068         /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
8069          * not (at the time of this writing) have fields set. They probably should, in which case we would pass the
8070          * global section. */
8071         PetscCall(DMPlexGetIndicesPointFields_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, offsets, PETSC_FALSE, perms, p, clperm, idx));
8072       }
8073     }
8074   } else {
8075     PetscInt off = 0, idxOff;
8076 
8077     for (p = 0; p < Ncl; ++p) {
8078       const PetscInt  pnt  = points[p * 2];
8079       const PetscInt *perm = perms[0] ? perms[0][p] : NULL;
8080 
8081       if (pnt < idxStart || pnt >= idxEnd) continue;
8082       PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
8083       /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
8084        * not (at the time of this writing) have fields set. They probably should, in which case we would pass the global section. */
8085       PetscCall(DMPlexGetIndicesPoint_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, &off, PETSC_FALSE, perm, clperm, idx));
8086     }
8087   }
8088   /* 6) Cleanup */
8089   for (f = 0; f < PetscMax(1, Nf); ++f) {
8090     if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
8091     else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
8092   }
8093   if (NclC) {
8094     PetscCall(DMRestoreWorkArray(dm, NclC * 2, MPIU_INT, &pointsC));
8095   } else {
8096     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
8097   }
8098 
8099   if (indices) *indices = idx;
8100   PetscFunctionReturn(PETSC_SUCCESS);
8101 }
8102 
8103 /*@C
8104   DMPlexGetClosureIndices - Gets the global dof indices associated with the closure of the given point within the provided sections.
8105 
8106   Not collective
8107 
8108   Input Parameters:
8109 + dm         - The `DM`
8110 . section    - The `PetscSection` describing the points (a local section)
8111 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
8112 . point      - The point defining the closure
8113 - useClPerm  - Use the closure point permutation if available
8114 
8115   Output Parameters:
8116 + numIndices - The number of dof indices in the closure of point with the input sections
8117 . indices    - The dof indices
8118 . outOffsets - Array to write the field offsets into, or `NULL`
8119 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or `NULL`
8120 
8121   Level: advanced
8122 
8123   Notes:
8124   Call `DMPlexRestoreClosureIndices()` to free allocated memory
8125 
8126   If `idxSection` is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
8127   of those indices is not significant.  If `idxSection` is local, the constrained dofs will yield the involution -(idx+1)
8128   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
8129   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when `idxSection` == section, otherwise global
8130   indices (with the above semantics) are implied.
8131 
8132 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`,
8133           `PetscSection`, `DMGetGlobalSection()`
8134 @*/
8135 PetscErrorCode DMPlexGetClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
8136 {
8137   PetscInt numRows = -1, numCols = -1;
8138 
8139   PetscFunctionBeginHot;
8140   PetscCall(DMPlexGetClosureIndices_Internal(dm, section, idxSection, point, useClPerm, &numRows, &numCols, indices, outOffsets, values, PETSC_TRUE, PETSC_TRUE));
8141   PetscCheck(numRows == numCols, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Symmetric matrix transformation produces rectangular dimensions (%" PetscInt_FMT ", %" PetscInt_FMT ")", numRows, numCols);
8142   *numIndices = numRows;
8143   PetscFunctionReturn(PETSC_SUCCESS);
8144 }
8145 
8146 /*@C
8147   DMPlexRestoreClosureIndices - Restores the global dof indices associated with the closure of the given point within the provided sections.
8148 
8149   Not collective
8150 
8151   Input Parameters:
8152 + dm         - The `DM`
8153 . section    - The `PetscSection` describing the points (a local section)
8154 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
8155 . point      - The point defining the closure
8156 - useClPerm  - Use the closure point permutation if available
8157 
8158   Output Parameters:
8159 + numIndices - The number of dof indices in the closure of point with the input sections
8160 . indices    - The dof indices
8161 . outOffsets - Array to write the field offsets into, or `NULL`
8162 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or `NULL`
8163 
8164   Level: advanced
8165 
8166   Notes:
8167   If values were modified, the user is responsible for calling `DMRestoreWorkArray`(dm, 0, `MPIU_SCALAR`, &values).
8168 
8169   If idxSection is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
8170   of those indices is not significant.  If idxSection is local, the constrained dofs will yield the involution -(idx+1)
8171   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
8172   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when idxSection == section, otherwise global
8173   indices (with the above semantics) are implied.
8174 
8175 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`, `DMGetGlobalSection()`
8176 @*/
8177 PetscErrorCode DMPlexRestoreClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
8178 {
8179   PetscFunctionBegin;
8180   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8181   PetscAssertPointer(indices, 7);
8182   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, indices));
8183   PetscFunctionReturn(PETSC_SUCCESS);
8184 }
8185 
8186 PetscErrorCode DMPlexMatSetClosure_Internal(DM dm, PetscSection section, PetscSection globalSection, PetscBool useClPerm, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8187 {
8188   DM_Plex           *mesh = (DM_Plex *)dm->data;
8189   PetscInt          *indices;
8190   PetscInt           numIndices;
8191   const PetscScalar *valuesOrig = values;
8192   PetscErrorCode     ierr;
8193 
8194   PetscFunctionBegin;
8195   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8196   if (!section) PetscCall(DMGetLocalSection(dm, &section));
8197   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
8198   if (!globalSection) PetscCall(DMGetGlobalSection(dm, &globalSection));
8199   PetscValidHeaderSpecific(globalSection, PETSC_SECTION_CLASSID, 3);
8200   PetscValidHeaderSpecific(A, MAT_CLASSID, 5);
8201 
8202   PetscCall(DMPlexGetClosureIndices(dm, section, globalSection, point, useClPerm, &numIndices, &indices, NULL, (PetscScalar **)&values));
8203 
8204   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndices, indices, 0, NULL, values));
8205   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8206   ierr = MatSetValues(A, numIndices, indices, numIndices, indices, values, mode);
8207   if (ierr) {
8208     PetscMPIInt rank;
8209 
8210     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8211     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8212     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndices, indices, 0, NULL, values));
8213     PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
8214     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
8215     SETERRQ(PetscObjectComm((PetscObject)dm), ierr, "Not possible to set matrix values");
8216   }
8217   if (mesh->printFEM > 1) {
8218     PetscInt i;
8219     PetscCall(PetscPrintf(PETSC_COMM_SELF, "  Indices:"));
8220     for (i = 0; i < numIndices; ++i) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, indices[i]));
8221     PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8222   }
8223 
8224   PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
8225   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
8226   PetscFunctionReturn(PETSC_SUCCESS);
8227 }
8228 
8229 /*@C
8230   DMPlexMatSetClosure - Set an array of the values on the closure of 'point'
8231 
8232   Not collective
8233 
8234   Input Parameters:
8235 + dm            - The `DM`
8236 . section       - The section describing the layout in `v`, or `NULL` to use the default section
8237 . globalSection - The section describing the layout in `v`, or `NULL` to use the default global section
8238 . A             - The matrix
8239 . point         - The point in the `DM`
8240 . values        - The array of values
8241 - mode          - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
8242 
8243   Level: intermediate
8244 
8245 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosureGeneral()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
8246 @*/
8247 PetscErrorCode DMPlexMatSetClosure(DM dm, PetscSection section, PetscSection globalSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8248 {
8249   PetscFunctionBegin;
8250   PetscCall(DMPlexMatSetClosure_Internal(dm, section, globalSection, PETSC_TRUE, A, point, values, mode));
8251   PetscFunctionReturn(PETSC_SUCCESS);
8252 }
8253 
8254 /*@C
8255   DMPlexMatSetClosureGeneral - Set an array of the values on the closure of 'point' using a different row and column section
8256 
8257   Not collective
8258 
8259   Input Parameters:
8260 + dmRow            - The `DM` for the row fields
8261 . sectionRow       - The section describing the layout, or `NULL` to use the default section in `dmRow`
8262 . useRowPerm       - The flag to use the closure permutation of the `dmRow` if available
8263 . globalSectionRow - The section describing the layout, or `NULL` to use the default global section in `dmRow`
8264 . dmCol            - The `DM` for the column fields
8265 . sectionCol       - The section describing the layout, or `NULL` to use the default section in `dmCol`
8266 . useColPerm       - The flag to use the closure permutation of the `dmCol` if available
8267 . globalSectionCol - The section describing the layout, or `NULL` to use the default global section in `dmCol`
8268 . A                - The matrix
8269 . point            - The point in the `DM`
8270 . values           - The array of values
8271 - mode             - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
8272 
8273   Level: intermediate
8274 
8275 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosure()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
8276 @*/
8277 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)
8278 {
8279   DM_Plex           *mesh = (DM_Plex *)dmRow->data;
8280   PetscInt          *indicesRow, *indicesCol;
8281   PetscInt           numIndicesRow = -1, numIndicesCol = -1;
8282   const PetscScalar *valuesV0 = values, *valuesV1, *valuesV2;
8283 
8284   PetscErrorCode ierr;
8285 
8286   PetscFunctionBegin;
8287   PetscValidHeaderSpecific(dmRow, DM_CLASSID, 1);
8288   if (!sectionRow) PetscCall(DMGetLocalSection(dmRow, &sectionRow));
8289   PetscValidHeaderSpecific(sectionRow, PETSC_SECTION_CLASSID, 2);
8290   if (!globalSectionRow) PetscCall(DMGetGlobalSection(dmRow, &globalSectionRow));
8291   PetscValidHeaderSpecific(globalSectionRow, PETSC_SECTION_CLASSID, 3);
8292   PetscValidHeaderSpecific(dmCol, DM_CLASSID, 5);
8293   if (!sectionCol) PetscCall(DMGetLocalSection(dmCol, &sectionCol));
8294   PetscValidHeaderSpecific(sectionCol, PETSC_SECTION_CLASSID, 6);
8295   if (!globalSectionCol) PetscCall(DMGetGlobalSection(dmCol, &globalSectionCol));
8296   PetscValidHeaderSpecific(globalSectionCol, PETSC_SECTION_CLASSID, 7);
8297   PetscValidHeaderSpecific(A, MAT_CLASSID, 9);
8298 
8299   PetscCall(DMPlexGetClosureIndicesSize_Internal(dmRow, sectionRow, point, &numIndicesRow));
8300   PetscCall(DMPlexGetClosureIndicesSize_Internal(dmCol, sectionCol, point, &numIndicesCol));
8301   valuesV1 = valuesV0;
8302   PetscCall(DMPlexGetClosureIndices_Internal(dmRow, sectionRow, globalSectionRow, point, useRowPerm, &numIndicesRow, &numIndicesCol, &indicesRow, NULL, (PetscScalar **)&valuesV1, PETSC_FALSE, PETSC_TRUE));
8303   valuesV2 = valuesV1;
8304   PetscCall(DMPlexGetClosureIndices_Internal(dmCol, sectionCol, globalSectionCol, point, useColPerm, &numIndicesRow, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&valuesV2, PETSC_TRUE, PETSC_FALSE));
8305 
8306   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, valuesV2));
8307   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8308   ierr = MatSetValues(A, numIndicesRow, indicesRow, numIndicesCol, indicesCol, valuesV2, mode);
8309   if (ierr) {
8310     PetscMPIInt rank;
8311 
8312     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8313     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8314     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
8315     PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesRow, NULL, (PetscScalar **)&valuesV2));
8316     PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&valuesV1));
8317     if (valuesV2 != valuesV1) PetscCall(DMRestoreWorkArray(dmCol, 0, MPIU_SCALAR, &valuesV2));
8318     if (valuesV1 != valuesV0) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &valuesV1));
8319   }
8320 
8321   PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, useColPerm, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&valuesV2));
8322   PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, useRowPerm, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&valuesV1));
8323   if (valuesV2 != valuesV1) PetscCall(DMRestoreWorkArray(dmCol, 0, MPIU_SCALAR, &valuesV2));
8324   if (valuesV1 != valuesV0) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &valuesV1));
8325   PetscFunctionReturn(PETSC_SUCCESS);
8326 }
8327 
8328 PetscErrorCode DMPlexMatSetClosureRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8329 {
8330   DM_Plex        *mesh    = (DM_Plex *)dmf->data;
8331   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
8332   PetscInt       *cpoints = NULL;
8333   PetscInt       *findices, *cindices;
8334   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
8335   PetscInt        foffsets[32], coffsets[32];
8336   DMPolytopeType  ct;
8337   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
8338   PetscErrorCode  ierr;
8339 
8340   PetscFunctionBegin;
8341   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
8342   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
8343   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
8344   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
8345   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
8346   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
8347   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
8348   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
8349   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
8350   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
8351   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
8352   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
8353   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
8354   PetscCall(PetscArrayzero(foffsets, 32));
8355   PetscCall(PetscArrayzero(coffsets, 32));
8356   /* Column indices */
8357   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8358   maxFPoints = numCPoints;
8359   /* Compress out points not in the section */
8360   /*   TODO: Squeeze out points with 0 dof as well */
8361   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
8362   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
8363     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
8364       cpoints[q * 2]     = cpoints[p];
8365       cpoints[q * 2 + 1] = cpoints[p + 1];
8366       ++q;
8367     }
8368   }
8369   numCPoints = q;
8370   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
8371     PetscInt fdof;
8372 
8373     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
8374     if (!dof) continue;
8375     for (f = 0; f < numFields; ++f) {
8376       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
8377       coffsets[f + 1] += fdof;
8378     }
8379     numCIndices += dof;
8380   }
8381   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
8382   /* Row indices */
8383   PetscCall(DMPlexGetCellType(dmc, point, &ct));
8384   {
8385     DMPlexTransform tr;
8386     DMPolytopeType *rct;
8387     PetscInt       *rsize, *rcone, *rornt, Nt;
8388 
8389     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8390     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8391     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8392     numSubcells = rsize[Nt - 1];
8393     PetscCall(DMPlexTransformDestroy(&tr));
8394   }
8395   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8396   for (r = 0, q = 0; r < numSubcells; ++r) {
8397     /* TODO Map from coarse to fine cells */
8398     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8399     /* Compress out points not in the section */
8400     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8401     for (p = 0; p < numFPoints * 2; p += 2) {
8402       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8403         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8404         if (!dof) continue;
8405         for (s = 0; s < q; ++s)
8406           if (fpoints[p] == ftotpoints[s * 2]) break;
8407         if (s < q) continue;
8408         ftotpoints[q * 2]     = fpoints[p];
8409         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8410         ++q;
8411       }
8412     }
8413     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8414   }
8415   numFPoints = q;
8416   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8417     PetscInt fdof;
8418 
8419     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8420     if (!dof) continue;
8421     for (f = 0; f < numFields; ++f) {
8422       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8423       foffsets[f + 1] += fdof;
8424     }
8425     numFIndices += dof;
8426   }
8427   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8428 
8429   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8430   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8431   PetscCall(DMGetWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8432   PetscCall(DMGetWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8433   if (numFields) {
8434     const PetscInt **permsF[32] = {NULL};
8435     const PetscInt **permsC[32] = {NULL};
8436 
8437     for (f = 0; f < numFields; f++) {
8438       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8439       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8440     }
8441     for (p = 0; p < numFPoints; p++) {
8442       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8443       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8444     }
8445     for (p = 0; p < numCPoints; p++) {
8446       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8447       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8448     }
8449     for (f = 0; f < numFields; f++) {
8450       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8451       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8452     }
8453   } else {
8454     const PetscInt **permsF = NULL;
8455     const PetscInt **permsC = NULL;
8456 
8457     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8458     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8459     for (p = 0, off = 0; p < numFPoints; p++) {
8460       const PetscInt *perm = permsF ? permsF[p] : NULL;
8461 
8462       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8463       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8464     }
8465     for (p = 0, off = 0; p < numCPoints; p++) {
8466       const PetscInt *perm = permsC ? permsC[p] : NULL;
8467 
8468       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8469       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8470     }
8471     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8472     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8473   }
8474   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8475   /* TODO: flips */
8476   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8477   ierr = MatSetValues(A, numFIndices, findices, numCIndices, cindices, values, mode);
8478   if (ierr) {
8479     PetscMPIInt rank;
8480 
8481     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8482     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8483     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8484     PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8485     PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8486   }
8487   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8488   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8489   PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8490   PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8491   PetscFunctionReturn(PETSC_SUCCESS);
8492 }
8493 
8494 PetscErrorCode DMPlexMatGetClosureIndicesRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, PetscInt point, PetscInt cindices[], PetscInt findices[])
8495 {
8496   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
8497   PetscInt       *cpoints      = NULL;
8498   PetscInt        foffsets[32] = {0}, coffsets[32] = {0};
8499   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
8500   DMPolytopeType  ct;
8501   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
8502 
8503   PetscFunctionBegin;
8504   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
8505   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
8506   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
8507   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
8508   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
8509   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
8510   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
8511   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
8512   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
8513   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
8514   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
8515   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
8516   /* Column indices */
8517   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8518   maxFPoints = numCPoints;
8519   /* Compress out points not in the section */
8520   /*   TODO: Squeeze out points with 0 dof as well */
8521   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
8522   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
8523     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
8524       cpoints[q * 2]     = cpoints[p];
8525       cpoints[q * 2 + 1] = cpoints[p + 1];
8526       ++q;
8527     }
8528   }
8529   numCPoints = q;
8530   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
8531     PetscInt fdof;
8532 
8533     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
8534     if (!dof) continue;
8535     for (f = 0; f < numFields; ++f) {
8536       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
8537       coffsets[f + 1] += fdof;
8538     }
8539     numCIndices += dof;
8540   }
8541   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
8542   /* Row indices */
8543   PetscCall(DMPlexGetCellType(dmc, point, &ct));
8544   {
8545     DMPlexTransform tr;
8546     DMPolytopeType *rct;
8547     PetscInt       *rsize, *rcone, *rornt, Nt;
8548 
8549     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8550     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8551     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8552     numSubcells = rsize[Nt - 1];
8553     PetscCall(DMPlexTransformDestroy(&tr));
8554   }
8555   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8556   for (r = 0, q = 0; r < numSubcells; ++r) {
8557     /* TODO Map from coarse to fine cells */
8558     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8559     /* Compress out points not in the section */
8560     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8561     for (p = 0; p < numFPoints * 2; p += 2) {
8562       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8563         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8564         if (!dof) continue;
8565         for (s = 0; s < q; ++s)
8566           if (fpoints[p] == ftotpoints[s * 2]) break;
8567         if (s < q) continue;
8568         ftotpoints[q * 2]     = fpoints[p];
8569         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8570         ++q;
8571       }
8572     }
8573     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8574   }
8575   numFPoints = q;
8576   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8577     PetscInt fdof;
8578 
8579     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8580     if (!dof) continue;
8581     for (f = 0; f < numFields; ++f) {
8582       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8583       foffsets[f + 1] += fdof;
8584     }
8585     numFIndices += dof;
8586   }
8587   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8588 
8589   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8590   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8591   if (numFields) {
8592     const PetscInt **permsF[32] = {NULL};
8593     const PetscInt **permsC[32] = {NULL};
8594 
8595     for (f = 0; f < numFields; f++) {
8596       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8597       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8598     }
8599     for (p = 0; p < numFPoints; p++) {
8600       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8601       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8602     }
8603     for (p = 0; p < numCPoints; p++) {
8604       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8605       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8606     }
8607     for (f = 0; f < numFields; f++) {
8608       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8609       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8610     }
8611   } else {
8612     const PetscInt **permsF = NULL;
8613     const PetscInt **permsC = NULL;
8614 
8615     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8616     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8617     for (p = 0, off = 0; p < numFPoints; p++) {
8618       const PetscInt *perm = permsF ? permsF[p] : NULL;
8619 
8620       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8621       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8622     }
8623     for (p = 0, off = 0; p < numCPoints; p++) {
8624       const PetscInt *perm = permsC ? permsC[p] : NULL;
8625 
8626       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8627       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8628     }
8629     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8630     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8631   }
8632   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8633   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8634   PetscFunctionReturn(PETSC_SUCCESS);
8635 }
8636 
8637 /*@
8638   DMPlexGetVTKCellHeight - Returns the height in the DAG used to determine which points are cells (normally 0)
8639 
8640   Input Parameter:
8641 . dm - The `DMPLEX` object
8642 
8643   Output Parameter:
8644 . cellHeight - The height of a cell
8645 
8646   Level: developer
8647 
8648 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetVTKCellHeight()`
8649 @*/
8650 PetscErrorCode DMPlexGetVTKCellHeight(DM dm, PetscInt *cellHeight)
8651 {
8652   DM_Plex *mesh = (DM_Plex *)dm->data;
8653 
8654   PetscFunctionBegin;
8655   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8656   PetscAssertPointer(cellHeight, 2);
8657   *cellHeight = mesh->vtkCellHeight;
8658   PetscFunctionReturn(PETSC_SUCCESS);
8659 }
8660 
8661 /*@
8662   DMPlexSetVTKCellHeight - Sets the height in the DAG used to determine which points are cells (normally 0)
8663 
8664   Input Parameters:
8665 + dm         - The `DMPLEX` object
8666 - cellHeight - The height of a cell
8667 
8668   Level: developer
8669 
8670 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetVTKCellHeight()`
8671 @*/
8672 PetscErrorCode DMPlexSetVTKCellHeight(DM dm, PetscInt cellHeight)
8673 {
8674   DM_Plex *mesh = (DM_Plex *)dm->data;
8675 
8676   PetscFunctionBegin;
8677   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8678   mesh->vtkCellHeight = cellHeight;
8679   PetscFunctionReturn(PETSC_SUCCESS);
8680 }
8681 
8682 /*@
8683   DMPlexGetCellTypeStratum - Get the range of cells of a given celltype
8684 
8685   Input Parameters:
8686 + dm - The `DMPLEX` object
8687 - ct - The `DMPolytopeType` of the cell
8688 
8689   Output Parameters:
8690 + start - The first cell of this type, or `NULL`
8691 - end   - The upper bound on this celltype, or `NULL`
8692 
8693   Level: advanced
8694 
8695 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexConstructGhostCells()`, `DMPlexGetDepthStratum()`, `DMPlexGetHeightStratum()`
8696 @*/
8697 PetscErrorCode DMPlexGetCellTypeStratum(DM dm, DMPolytopeType ct, PetscInt *start, PetscInt *end)
8698 {
8699   DM_Plex *mesh = (DM_Plex *)dm->data;
8700   DMLabel  label;
8701   PetscInt pStart, pEnd;
8702 
8703   PetscFunctionBegin;
8704   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8705   if (start) {
8706     PetscAssertPointer(start, 3);
8707     *start = 0;
8708   }
8709   if (end) {
8710     PetscAssertPointer(end, 4);
8711     *end = 0;
8712   }
8713   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8714   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
8715   if (mesh->tr) {
8716     PetscCall(DMPlexTransformGetCellTypeStratum(mesh->tr, ct, start, end));
8717   } else {
8718     PetscCall(DMPlexGetCellTypeLabel(dm, &label));
8719     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named celltype was found");
8720     PetscCall(DMLabelGetStratumBounds(label, ct, start, end));
8721   }
8722   PetscFunctionReturn(PETSC_SUCCESS);
8723 }
8724 
8725 PetscErrorCode DMPlexCreateNumbering_Plex(DM dm, PetscInt pStart, PetscInt pEnd, PetscInt shift, PetscInt *globalSize, PetscSF sf, IS *numbering)
8726 {
8727   PetscSection section, globalSection;
8728   PetscInt    *numbers, p;
8729 
8730   PetscFunctionBegin;
8731   if (PetscDefined(USE_DEBUG)) PetscCall(DMPlexCheckPointSF(dm, sf, PETSC_TRUE));
8732   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
8733   PetscCall(PetscSectionSetChart(section, pStart, pEnd));
8734   for (p = pStart; p < pEnd; ++p) PetscCall(PetscSectionSetDof(section, p, 1));
8735   PetscCall(PetscSectionSetUp(section));
8736   PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &globalSection));
8737   PetscCall(PetscMalloc1(pEnd - pStart, &numbers));
8738   for (p = pStart; p < pEnd; ++p) {
8739     PetscCall(PetscSectionGetOffset(globalSection, p, &numbers[p - pStart]));
8740     if (numbers[p - pStart] < 0) numbers[p - pStart] -= shift;
8741     else numbers[p - pStart] += shift;
8742   }
8743   PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), pEnd - pStart, numbers, PETSC_OWN_POINTER, numbering));
8744   if (globalSize) {
8745     PetscLayout layout;
8746     PetscCall(PetscSectionGetPointLayout(PetscObjectComm((PetscObject)dm), globalSection, &layout));
8747     PetscCall(PetscLayoutGetSize(layout, globalSize));
8748     PetscCall(PetscLayoutDestroy(&layout));
8749   }
8750   PetscCall(PetscSectionDestroy(&section));
8751   PetscCall(PetscSectionDestroy(&globalSection));
8752   PetscFunctionReturn(PETSC_SUCCESS);
8753 }
8754 
8755 /*@
8756   DMPlexCreateCellNumbering - Get a global cell numbering for all cells on this process
8757 
8758   Input Parameter:
8759 + dm         - The `DMPLEX` object
8760 - includeAll - Whether to include all cells, or just the simplex and box cells
8761 
8762   Output Parameter:
8763 . globalCellNumbers - Global cell numbers for all cells on this process
8764 
8765   Level: developer
8766 
8767 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`, `DMPlexGetVertexNumbering()`
8768 @*/
8769 PetscErrorCode DMPlexCreateCellNumbering(DM dm, PetscBool includeAll, IS *globalCellNumbers)
8770 {
8771   PetscInt cellHeight, cStart, cEnd;
8772 
8773   PetscFunctionBegin;
8774   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8775   if (includeAll) PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8776   else PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd));
8777   PetscCall(DMPlexCreateNumbering_Plex(dm, cStart, cEnd, 0, NULL, dm->sf, globalCellNumbers));
8778   PetscFunctionReturn(PETSC_SUCCESS);
8779 }
8780 
8781 /*@
8782   DMPlexGetCellNumbering - Get a global cell numbering for all cells on this process
8783 
8784   Input Parameter:
8785 . dm - The `DMPLEX` object
8786 
8787   Output Parameter:
8788 . globalCellNumbers - Global cell numbers for all cells on this process
8789 
8790   Level: developer
8791 
8792 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreateCellNumbering()`, `DMPlexGetVertexNumbering()`
8793 @*/
8794 PetscErrorCode DMPlexGetCellNumbering(DM dm, IS *globalCellNumbers)
8795 {
8796   DM_Plex *mesh = (DM_Plex *)dm->data;
8797 
8798   PetscFunctionBegin;
8799   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8800   if (!mesh->globalCellNumbers) PetscCall(DMPlexCreateCellNumbering(dm, PETSC_FALSE, &mesh->globalCellNumbers));
8801   *globalCellNumbers = mesh->globalCellNumbers;
8802   PetscFunctionReturn(PETSC_SUCCESS);
8803 }
8804 
8805 PetscErrorCode DMPlexCreateVertexNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalVertexNumbers)
8806 {
8807   PetscInt vStart, vEnd;
8808 
8809   PetscFunctionBegin;
8810   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8811   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8812   PetscCall(DMPlexCreateNumbering_Plex(dm, vStart, vEnd, 0, NULL, dm->sf, globalVertexNumbers));
8813   PetscFunctionReturn(PETSC_SUCCESS);
8814 }
8815 
8816 /*@
8817   DMPlexGetVertexNumbering - Get a global vertex numbering for all vertices on this process
8818 
8819   Input Parameter:
8820 . dm - The `DMPLEX` object
8821 
8822   Output Parameter:
8823 . globalVertexNumbers - Global vertex numbers for all vertices on this process
8824 
8825   Level: developer
8826 
8827 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
8828 @*/
8829 PetscErrorCode DMPlexGetVertexNumbering(DM dm, IS *globalVertexNumbers)
8830 {
8831   DM_Plex *mesh = (DM_Plex *)dm->data;
8832 
8833   PetscFunctionBegin;
8834   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8835   if (!mesh->globalVertexNumbers) PetscCall(DMPlexCreateVertexNumbering_Internal(dm, PETSC_FALSE, &mesh->globalVertexNumbers));
8836   *globalVertexNumbers = mesh->globalVertexNumbers;
8837   PetscFunctionReturn(PETSC_SUCCESS);
8838 }
8839 
8840 /*@
8841   DMPlexCreatePointNumbering - Create a global numbering for all points.
8842 
8843   Collective
8844 
8845   Input Parameter:
8846 . dm - The `DMPLEX` object
8847 
8848   Output Parameter:
8849 . globalPointNumbers - Global numbers for all points on this process
8850 
8851   Level: developer
8852 
8853   Notes:
8854   The point numbering `IS` is parallel, with local portion indexed by local points (see `DMGetLocalSection()`). The global
8855   points are taken as stratified, with each MPI rank owning a contiguous subset of each stratum. In the IS, owned points
8856   will have their non-negative value while points owned by different ranks will be involuted -(idx+1). As an example,
8857   consider a parallel mesh in which the first two elements and first two vertices are owned by rank 0.
8858 
8859   The partitioned mesh is
8860   ```
8861   (2)--0--(3)--1--(4)    (1)--0--(2)
8862   ```
8863   and its global numbering is
8864   ```
8865   (3)--0--(4)--1--(5)--2--(6)
8866   ```
8867   Then the global numbering is provided as
8868   ```
8869   [0] Number of indices in set 5
8870   [0] 0 0
8871   [0] 1 1
8872   [0] 2 3
8873   [0] 3 4
8874   [0] 4 -6
8875   [1] Number of indices in set 3
8876   [1] 0 2
8877   [1] 1 5
8878   [1] 2 6
8879   ```
8880 
8881 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
8882 @*/
8883 PetscErrorCode DMPlexCreatePointNumbering(DM dm, IS *globalPointNumbers)
8884 {
8885   IS        nums[4];
8886   PetscInt  depths[4], gdepths[4], starts[4];
8887   PetscInt  depth, d, shift = 0;
8888   PetscBool empty = PETSC_FALSE;
8889 
8890   PetscFunctionBegin;
8891   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8892   PetscCall(DMPlexGetDepth(dm, &depth));
8893   // For unstratified meshes use dim instead of depth
8894   if (depth < 0) PetscCall(DMGetDimension(dm, &depth));
8895   // If any stratum is empty, we must mark all empty
8896   for (d = 0; d <= depth; ++d) {
8897     PetscInt end;
8898 
8899     depths[d] = depth - d;
8900     PetscCall(DMPlexGetDepthStratum(dm, depths[d], &starts[d], &end));
8901     if (!(starts[d] - end)) empty = PETSC_TRUE;
8902   }
8903   if (empty)
8904     for (d = 0; d <= depth; ++d) {
8905       depths[d] = -1;
8906       starts[d] = -1;
8907     }
8908   else PetscCall(PetscSortIntWithArray(depth + 1, starts, depths));
8909   PetscCall(MPIU_Allreduce(depths, gdepths, depth + 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
8910   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]);
8911   // Note here that 'shift' is collective, so that the numbering is stratified by depth
8912   for (d = 0; d <= depth; ++d) {
8913     PetscInt pStart, pEnd, gsize;
8914 
8915     PetscCall(DMPlexGetDepthStratum(dm, gdepths[d], &pStart, &pEnd));
8916     PetscCall(DMPlexCreateNumbering_Plex(dm, pStart, pEnd, shift, &gsize, dm->sf, &nums[d]));
8917     shift += gsize;
8918   }
8919   PetscCall(ISConcatenate(PETSC_COMM_SELF, depth + 1, nums, globalPointNumbers));
8920   for (d = 0; d <= depth; ++d) PetscCall(ISDestroy(&nums[d]));
8921   PetscFunctionReturn(PETSC_SUCCESS);
8922 }
8923 
8924 /*@
8925   DMPlexCreateEdgeNumbering - Create a global numbering for edges.
8926 
8927   Collective
8928 
8929   Input Parameter:
8930 . dm - The `DMPLEX` object
8931 
8932   Output Parameter:
8933 . globalEdgeNumbers - Global numbers for all edges on this process
8934 
8935   Level: developer
8936 
8937   Notes:
8938   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).
8939 
8940 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`, `DMPlexGetVertexNumbering()`, `DMPlexCreatePointNumbering()`
8941 @*/
8942 PetscErrorCode DMPlexCreateEdgeNumbering(DM dm, IS *globalEdgeNumbers)
8943 {
8944   PetscSF  sf;
8945   PetscInt eStart, eEnd;
8946 
8947   PetscFunctionBegin;
8948   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8949   PetscCall(DMGetPointSF(dm, &sf));
8950   PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
8951   PetscCall(DMPlexCreateNumbering_Plex(dm, eStart, eEnd, 0, NULL, sf, globalEdgeNumbers));
8952   PetscFunctionReturn(PETSC_SUCCESS);
8953 }
8954 
8955 /*@
8956   DMPlexCreateRankField - Create a cell field whose value is the rank of the owner
8957 
8958   Input Parameter:
8959 . dm - The `DMPLEX` object
8960 
8961   Output Parameter:
8962 . ranks - The rank field
8963 
8964   Options Database Key:
8965 . -dm_partition_view - Adds the rank field into the `DM` output from `-dm_view` using the same viewer
8966 
8967   Level: intermediate
8968 
8969 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`
8970 @*/
8971 PetscErrorCode DMPlexCreateRankField(DM dm, Vec *ranks)
8972 {
8973   DM             rdm;
8974   PetscFE        fe;
8975   PetscScalar   *r;
8976   PetscMPIInt    rank;
8977   DMPolytopeType ct;
8978   PetscInt       dim, cStart, cEnd, c;
8979   PetscBool      simplex;
8980 
8981   PetscFunctionBeginUser;
8982   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8983   PetscAssertPointer(ranks, 2);
8984   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
8985   PetscCall(DMClone(dm, &rdm));
8986   PetscCall(DMGetDimension(rdm, &dim));
8987   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
8988   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
8989   simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct) + 1 ? PETSC_TRUE : PETSC_FALSE;
8990   PetscCall(PetscFECreateDefault(PETSC_COMM_SELF, dim, 1, simplex, "PETSc___rank_", -1, &fe));
8991   PetscCall(PetscObjectSetName((PetscObject)fe, "rank"));
8992   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
8993   PetscCall(PetscFEDestroy(&fe));
8994   PetscCall(DMCreateDS(rdm));
8995   PetscCall(DMCreateGlobalVector(rdm, ranks));
8996   PetscCall(PetscObjectSetName((PetscObject)*ranks, "partition"));
8997   PetscCall(VecGetArray(*ranks, &r));
8998   for (c = cStart; c < cEnd; ++c) {
8999     PetscScalar *lr;
9000 
9001     PetscCall(DMPlexPointGlobalRef(rdm, c, r, &lr));
9002     if (lr) *lr = rank;
9003   }
9004   PetscCall(VecRestoreArray(*ranks, &r));
9005   PetscCall(DMDestroy(&rdm));
9006   PetscFunctionReturn(PETSC_SUCCESS);
9007 }
9008 
9009 /*@
9010   DMPlexCreateLabelField - Create a field whose value is the label value for that point
9011 
9012   Input Parameters:
9013 + dm    - The `DMPLEX`
9014 - label - The `DMLabel`
9015 
9016   Output Parameter:
9017 . val - The label value field
9018 
9019   Options Database Key:
9020 . -dm_label_view - Adds the label value field into the `DM` output from `-dm_view` using the same viewer
9021 
9022   Level: intermediate
9023 
9024 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`
9025 @*/
9026 PetscErrorCode DMPlexCreateLabelField(DM dm, DMLabel label, Vec *val)
9027 {
9028   DM             rdm, plex;
9029   Vec            lval;
9030   PetscSection   section;
9031   PetscFE        fe;
9032   PetscScalar   *v;
9033   PetscInt       dim, pStart, pEnd, p, cStart;
9034   DMPolytopeType ct;
9035   char           name[PETSC_MAX_PATH_LEN];
9036   const char    *lname, *prefix;
9037 
9038   PetscFunctionBeginUser;
9039   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9040   PetscAssertPointer(label, 2);
9041   PetscAssertPointer(val, 3);
9042   PetscCall(DMClone(dm, &rdm));
9043   PetscCall(DMConvert(rdm, DMPLEX, &plex));
9044   PetscCall(DMPlexGetHeightStratum(plex, 0, &cStart, NULL));
9045   PetscCall(DMPlexGetCellType(plex, cStart, &ct));
9046   PetscCall(DMDestroy(&plex));
9047   PetscCall(DMGetDimension(rdm, &dim));
9048   PetscCall(DMGetOptionsPrefix(dm, &prefix));
9049   PetscCall(PetscObjectGetName((PetscObject)label, &lname));
9050   PetscCall(PetscSNPrintf(name, sizeof(name), "%s%s_", prefix ? prefix : "", lname));
9051   PetscCall(PetscFECreateByCell(PETSC_COMM_SELF, dim, 1, ct, name, -1, &fe));
9052   PetscCall(PetscObjectSetName((PetscObject)fe, ""));
9053   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
9054   PetscCall(PetscFEDestroy(&fe));
9055   PetscCall(DMCreateDS(rdm));
9056   PetscCall(DMCreateGlobalVector(rdm, val));
9057   PetscCall(DMCreateLocalVector(rdm, &lval));
9058   PetscCall(PetscObjectSetName((PetscObject)*val, lname));
9059   PetscCall(DMGetLocalSection(rdm, &section));
9060   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
9061   PetscCall(VecGetArray(lval, &v));
9062   for (p = pStart; p < pEnd; ++p) {
9063     PetscInt cval, dof, off;
9064 
9065     PetscCall(PetscSectionGetDof(section, p, &dof));
9066     if (!dof) continue;
9067     PetscCall(DMLabelGetValue(label, p, &cval));
9068     PetscCall(PetscSectionGetOffset(section, p, &off));
9069     for (PetscInt d = 0; d < dof; d++) v[off + d] = cval;
9070   }
9071   PetscCall(VecRestoreArray(lval, &v));
9072   PetscCall(DMLocalToGlobal(rdm, lval, INSERT_VALUES, *val));
9073   PetscCall(VecDestroy(&lval));
9074   PetscCall(DMDestroy(&rdm));
9075   PetscFunctionReturn(PETSC_SUCCESS);
9076 }
9077 
9078 /*@
9079   DMPlexCheckSymmetry - Check that the adjacency information in the mesh is symmetric.
9080 
9081   Input Parameter:
9082 . dm - The `DMPLEX` object
9083 
9084   Level: developer
9085 
9086   Notes:
9087   This is a useful diagnostic when creating meshes programmatically.
9088 
9089   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9090 
9091 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9092 @*/
9093 PetscErrorCode DMPlexCheckSymmetry(DM dm)
9094 {
9095   PetscSection    coneSection, supportSection;
9096   const PetscInt *cone, *support;
9097   PetscInt        coneSize, c, supportSize, s;
9098   PetscInt        pStart, pEnd, p, pp, csize, ssize;
9099   PetscBool       storagecheck = PETSC_TRUE;
9100 
9101   PetscFunctionBegin;
9102   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9103   PetscCall(DMViewFromOptions(dm, NULL, "-sym_dm_view"));
9104   PetscCall(DMPlexGetConeSection(dm, &coneSection));
9105   PetscCall(DMPlexGetSupportSection(dm, &supportSection));
9106   /* Check that point p is found in the support of its cone points, and vice versa */
9107   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
9108   for (p = pStart; p < pEnd; ++p) {
9109     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
9110     PetscCall(DMPlexGetCone(dm, p, &cone));
9111     for (c = 0; c < coneSize; ++c) {
9112       PetscBool dup = PETSC_FALSE;
9113       PetscInt  d;
9114       for (d = c - 1; d >= 0; --d) {
9115         if (cone[c] == cone[d]) {
9116           dup = PETSC_TRUE;
9117           break;
9118         }
9119       }
9120       PetscCall(DMPlexGetSupportSize(dm, cone[c], &supportSize));
9121       PetscCall(DMPlexGetSupport(dm, cone[c], &support));
9122       for (s = 0; s < supportSize; ++s) {
9123         if (support[s] == p) break;
9124       }
9125       if ((s >= supportSize) || (dup && (support[s + 1] != p))) {
9126         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", p));
9127         for (s = 0; s < coneSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[s]));
9128         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9129         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", cone[c]));
9130         for (s = 0; s < supportSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[s]));
9131         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9132         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]);
9133         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in support of cone point %" PetscInt_FMT, p, cone[c]);
9134       }
9135     }
9136     PetscCall(DMPlexGetTreeParent(dm, p, &pp, NULL));
9137     if (p != pp) {
9138       storagecheck = PETSC_FALSE;
9139       continue;
9140     }
9141     PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
9142     PetscCall(DMPlexGetSupport(dm, p, &support));
9143     for (s = 0; s < supportSize; ++s) {
9144       PetscCall(DMPlexGetConeSize(dm, support[s], &coneSize));
9145       PetscCall(DMPlexGetCone(dm, support[s], &cone));
9146       for (c = 0; c < coneSize; ++c) {
9147         PetscCall(DMPlexGetTreeParent(dm, cone[c], &pp, NULL));
9148         if (cone[c] != pp) {
9149           c = 0;
9150           break;
9151         }
9152         if (cone[c] == p) break;
9153       }
9154       if (c >= coneSize) {
9155         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", p));
9156         for (c = 0; c < supportSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[c]));
9157         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9158         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", support[s]));
9159         for (c = 0; c < coneSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[c]));
9160         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9161         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in cone of support point %" PetscInt_FMT, p, support[s]);
9162       }
9163     }
9164   }
9165   if (storagecheck) {
9166     PetscCall(PetscSectionGetStorageSize(coneSection, &csize));
9167     PetscCall(PetscSectionGetStorageSize(supportSection, &ssize));
9168     PetscCheck(csize == ssize, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Total cone size %" PetscInt_FMT " != Total support size %" PetscInt_FMT, csize, ssize);
9169   }
9170   PetscFunctionReturn(PETSC_SUCCESS);
9171 }
9172 
9173 /*
9174   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.
9175 */
9176 static PetscErrorCode DMPlexCellUnsplitVertices_Private(DM dm, PetscInt c, DMPolytopeType ct, PetscInt *unsplit)
9177 {
9178   DMPolytopeType  cct;
9179   PetscInt        ptpoints[4];
9180   const PetscInt *cone, *ccone, *ptcone;
9181   PetscInt        coneSize, cp, cconeSize, ccp, npt = 0, pt;
9182 
9183   PetscFunctionBegin;
9184   *unsplit = 0;
9185   switch (ct) {
9186   case DM_POLYTOPE_POINT_PRISM_TENSOR:
9187     ptpoints[npt++] = c;
9188     break;
9189   case DM_POLYTOPE_SEG_PRISM_TENSOR:
9190     PetscCall(DMPlexGetCone(dm, c, &cone));
9191     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9192     for (cp = 0; cp < coneSize; ++cp) {
9193       PetscCall(DMPlexGetCellType(dm, cone[cp], &cct));
9194       if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) ptpoints[npt++] = cone[cp];
9195     }
9196     break;
9197   case DM_POLYTOPE_TRI_PRISM_TENSOR:
9198   case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9199     PetscCall(DMPlexGetCone(dm, c, &cone));
9200     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9201     for (cp = 0; cp < coneSize; ++cp) {
9202       PetscCall(DMPlexGetCone(dm, cone[cp], &ccone));
9203       PetscCall(DMPlexGetConeSize(dm, cone[cp], &cconeSize));
9204       for (ccp = 0; ccp < cconeSize; ++ccp) {
9205         PetscCall(DMPlexGetCellType(dm, ccone[ccp], &cct));
9206         if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) {
9207           PetscInt p;
9208           for (p = 0; p < npt; ++p)
9209             if (ptpoints[p] == ccone[ccp]) break;
9210           if (p == npt) ptpoints[npt++] = ccone[ccp];
9211         }
9212       }
9213     }
9214     break;
9215   default:
9216     break;
9217   }
9218   for (pt = 0; pt < npt; ++pt) {
9219     PetscCall(DMPlexGetCone(dm, ptpoints[pt], &ptcone));
9220     if (ptcone[0] == ptcone[1]) ++(*unsplit);
9221   }
9222   PetscFunctionReturn(PETSC_SUCCESS);
9223 }
9224 
9225 /*@
9226   DMPlexCheckSkeleton - Check that each cell has the correct number of vertices
9227 
9228   Input Parameters:
9229 + dm         - The `DMPLEX` object
9230 - cellHeight - Normally 0
9231 
9232   Level: developer
9233 
9234   Notes:
9235   This is a useful diagnostic when creating meshes programmatically.
9236   Currently applicable only to homogeneous simplex or tensor meshes.
9237 
9238   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9239 
9240 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9241 @*/
9242 PetscErrorCode DMPlexCheckSkeleton(DM dm, PetscInt cellHeight)
9243 {
9244   DMPlexInterpolatedFlag interp;
9245   DMPolytopeType         ct;
9246   PetscInt               vStart, vEnd, cStart, cEnd, c;
9247 
9248   PetscFunctionBegin;
9249   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9250   PetscCall(DMPlexIsInterpolated(dm, &interp));
9251   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9252   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
9253   for (c = cStart; c < cEnd; ++c) {
9254     PetscInt *closure = NULL;
9255     PetscInt  coneSize, closureSize, cl, Nv = 0;
9256 
9257     PetscCall(DMPlexGetCellType(dm, c, &ct));
9258     PetscCheck((PetscInt)ct >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell %" PetscInt_FMT " has no cell type", c);
9259     if (ct == DM_POLYTOPE_UNKNOWN) continue;
9260     if (interp == DMPLEX_INTERPOLATED_FULL) {
9261       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9262       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));
9263     }
9264     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9265     for (cl = 0; cl < closureSize * 2; cl += 2) {
9266       const PetscInt p = closure[cl];
9267       if ((p >= vStart) && (p < vEnd)) ++Nv;
9268     }
9269     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9270     /* Special Case: Tensor faces with identified vertices */
9271     if (Nv < DMPolytopeTypeGetNumVertices(ct)) {
9272       PetscInt unsplit;
9273 
9274       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9275       if (Nv + unsplit == DMPolytopeTypeGetNumVertices(ct)) continue;
9276     }
9277     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));
9278   }
9279   PetscFunctionReturn(PETSC_SUCCESS);
9280 }
9281 
9282 /*@
9283   DMPlexCheckFaces - Check that the faces of each cell give a vertex order this is consistent with what we expect from the cell type
9284 
9285   Collective
9286 
9287   Input Parameters:
9288 + dm         - The `DMPLEX` object
9289 - cellHeight - Normally 0
9290 
9291   Level: developer
9292 
9293   Notes:
9294   This is a useful diagnostic when creating meshes programmatically.
9295   This routine is only relevant for meshes that are fully interpolated across all ranks.
9296   It will error out if a partially interpolated mesh is given on some rank.
9297   It will do nothing for locally uninterpolated mesh (as there is nothing to check).
9298 
9299   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9300 
9301 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMPlexGetVTKCellHeight()`, `DMSetFromOptions()`
9302 @*/
9303 PetscErrorCode DMPlexCheckFaces(DM dm, PetscInt cellHeight)
9304 {
9305   PetscInt               dim, depth, vStart, vEnd, cStart, cEnd, c, h;
9306   DMPlexInterpolatedFlag interpEnum;
9307 
9308   PetscFunctionBegin;
9309   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9310   PetscCall(DMPlexIsInterpolatedCollective(dm, &interpEnum));
9311   if (interpEnum == DMPLEX_INTERPOLATED_NONE) PetscFunctionReturn(PETSC_SUCCESS);
9312   if (interpEnum != DMPLEX_INTERPOLATED_FULL) {
9313     PetscCall(PetscPrintf(PetscObjectComm((PetscObject)dm), "DMPlexCheckFaces() warning: Mesh is only partially interpolated, this is currently not supported"));
9314     PetscFunctionReturn(PETSC_SUCCESS);
9315   }
9316 
9317   PetscCall(DMGetDimension(dm, &dim));
9318   PetscCall(DMPlexGetDepth(dm, &depth));
9319   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
9320   for (h = cellHeight; h < PetscMin(depth, dim); ++h) {
9321     PetscCall(DMPlexGetHeightStratum(dm, h, &cStart, &cEnd));
9322     for (c = cStart; c < cEnd; ++c) {
9323       const PetscInt       *cone, *ornt, *faceSizes, *faces;
9324       const DMPolytopeType *faceTypes;
9325       DMPolytopeType        ct;
9326       PetscInt              numFaces, coneSize, f;
9327       PetscInt             *closure = NULL, closureSize, cl, numCorners = 0, fOff = 0, unsplit;
9328 
9329       PetscCall(DMPlexGetCellType(dm, c, &ct));
9330       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9331       if (unsplit) continue;
9332       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9333       PetscCall(DMPlexGetCone(dm, c, &cone));
9334       PetscCall(DMPlexGetConeOrientation(dm, c, &ornt));
9335       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9336       for (cl = 0; cl < closureSize * 2; cl += 2) {
9337         const PetscInt p = closure[cl];
9338         if ((p >= vStart) && (p < vEnd)) closure[numCorners++] = p;
9339       }
9340       PetscCall(DMPlexGetRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
9341       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);
9342       for (f = 0; f < numFaces; ++f) {
9343         DMPolytopeType fct;
9344         PetscInt      *fclosure = NULL, fclosureSize, cl, fnumCorners = 0, v;
9345 
9346         PetscCall(DMPlexGetCellType(dm, cone[f], &fct));
9347         PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[f], ornt[f], PETSC_TRUE, &fclosureSize, &fclosure));
9348         for (cl = 0; cl < fclosureSize * 2; cl += 2) {
9349           const PetscInt p = fclosure[cl];
9350           if ((p >= vStart) && (p < vEnd)) fclosure[fnumCorners++] = p;
9351         }
9352         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]);
9353         for (v = 0; v < fnumCorners; ++v) {
9354           if (fclosure[v] != faces[fOff + v]) {
9355             PetscInt v1;
9356 
9357             PetscCall(PetscPrintf(PETSC_COMM_SELF, "face closure:"));
9358             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, fclosure[v1]));
9359             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\ncell face:"));
9360             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, faces[fOff + v1]));
9361             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9362             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]);
9363           }
9364         }
9365         PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[f], PETSC_TRUE, &fclosureSize, &fclosure));
9366         fOff += faceSizes[f];
9367       }
9368       PetscCall(DMPlexRestoreRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
9369       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9370     }
9371   }
9372   PetscFunctionReturn(PETSC_SUCCESS);
9373 }
9374 
9375 /*@
9376   DMPlexCheckGeometry - Check the geometry of mesh cells
9377 
9378   Input Parameter:
9379 . dm - The `DMPLEX` object
9380 
9381   Level: developer
9382 
9383   Notes:
9384   This is a useful diagnostic when creating meshes programmatically.
9385 
9386   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9387 
9388 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9389 @*/
9390 PetscErrorCode DMPlexCheckGeometry(DM dm)
9391 {
9392   Vec       coordinates;
9393   PetscReal detJ, J[9], refVol = 1.0;
9394   PetscReal vol;
9395   PetscInt  dim, depth, dE, d, cStart, cEnd, c;
9396 
9397   PetscFunctionBegin;
9398   PetscCall(DMGetDimension(dm, &dim));
9399   PetscCall(DMGetCoordinateDim(dm, &dE));
9400   if (dim != dE) PetscFunctionReturn(PETSC_SUCCESS);
9401   PetscCall(DMPlexGetDepth(dm, &depth));
9402   for (d = 0; d < dim; ++d) refVol *= 2.0;
9403   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
9404   /* Make sure local coordinates are created, because that step is collective */
9405   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
9406   if (!coordinates) PetscFunctionReturn(PETSC_SUCCESS);
9407   for (c = cStart; c < cEnd; ++c) {
9408     DMPolytopeType ct;
9409     PetscInt       unsplit;
9410     PetscBool      ignoreZeroVol = PETSC_FALSE;
9411 
9412     PetscCall(DMPlexGetCellType(dm, c, &ct));
9413     switch (ct) {
9414     case DM_POLYTOPE_SEG_PRISM_TENSOR:
9415     case DM_POLYTOPE_TRI_PRISM_TENSOR:
9416     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9417       ignoreZeroVol = PETSC_TRUE;
9418       break;
9419     default:
9420       break;
9421     }
9422     switch (ct) {
9423     case DM_POLYTOPE_TRI_PRISM:
9424     case DM_POLYTOPE_TRI_PRISM_TENSOR:
9425     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9426     case DM_POLYTOPE_PYRAMID:
9427       continue;
9428     default:
9429       break;
9430     }
9431     PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9432     if (unsplit) continue;
9433     PetscCall(DMPlexComputeCellGeometryFEM(dm, c, NULL, NULL, J, NULL, &detJ));
9434     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);
9435     PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FEM Volume %g\n", c, (double)(detJ * refVol)));
9436     /* This should work with periodicity since DG coordinates should be used */
9437     if (depth > 1) {
9438       PetscCall(DMPlexComputeCellGeometryFVM(dm, c, &vol, NULL, NULL));
9439       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);
9440       PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FVM Volume %g\n", c, (double)vol));
9441     }
9442   }
9443   PetscFunctionReturn(PETSC_SUCCESS);
9444 }
9445 
9446 /*@
9447   DMPlexCheckPointSF - Check that several necessary conditions are met for the point `PetscSF` of this plex.
9448 
9449   Collective
9450 
9451   Input Parameters:
9452 + dm              - The `DMPLEX` object
9453 . pointSF         - The `PetscSF`, or `NULL` for `PointSF` attached to `DM`
9454 - allowExtraRoots - Flag to allow extra points not present in the `DM`
9455 
9456   Level: developer
9457 
9458   Notes:
9459   This is mainly intended for debugging/testing purposes.
9460 
9461   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9462 
9463   Extra roots can come from periodic cuts, where additional points appear on the boundary
9464 
9465 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMGetPointSF()`, `DMSetFromOptions()`
9466 @*/
9467 PetscErrorCode DMPlexCheckPointSF(DM dm, PetscSF pointSF, PetscBool allowExtraRoots)
9468 {
9469   PetscInt           l, nleaves, nroots, overlap;
9470   const PetscInt    *locals;
9471   const PetscSFNode *remotes;
9472   PetscBool          distributed;
9473   MPI_Comm           comm;
9474   PetscMPIInt        rank;
9475 
9476   PetscFunctionBegin;
9477   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9478   if (pointSF) PetscValidHeaderSpecific(pointSF, PETSCSF_CLASSID, 2);
9479   else pointSF = dm->sf;
9480   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9481   PetscCheck(pointSF, comm, PETSC_ERR_ARG_WRONGSTATE, "DMPlex must have Point SF attached");
9482   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9483   {
9484     PetscMPIInt mpiFlag;
9485 
9486     PetscCallMPI(MPI_Comm_compare(comm, PetscObjectComm((PetscObject)pointSF), &mpiFlag));
9487     PetscCheck(mpiFlag == MPI_CONGRUENT || mpiFlag == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "DM and Point SF have different communicators (flag %d)", mpiFlag);
9488   }
9489   PetscCall(PetscSFGetGraph(pointSF, &nroots, &nleaves, &locals, &remotes));
9490   PetscCall(DMPlexIsDistributed(dm, &distributed));
9491   if (!distributed) {
9492     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);
9493     PetscFunctionReturn(PETSC_SUCCESS);
9494   }
9495   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);
9496   PetscCall(DMPlexGetOverlap(dm, &overlap));
9497 
9498   /* Check SF graph is compatible with DMPlex chart */
9499   {
9500     PetscInt pStart, pEnd, maxLeaf;
9501 
9502     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
9503     PetscCall(PetscSFGetLeafRange(pointSF, NULL, &maxLeaf));
9504     PetscCheck(allowExtraRoots || pEnd - pStart == nroots, PETSC_COMM_SELF, PETSC_ERR_PLIB, "pEnd - pStart = %" PetscInt_FMT " != nroots = %" PetscInt_FMT, pEnd - pStart, nroots);
9505     PetscCheck(maxLeaf < pEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "maxLeaf = %" PetscInt_FMT " >= pEnd = %" PetscInt_FMT, maxLeaf, pEnd);
9506   }
9507 
9508   /* Check Point SF has no local points referenced */
9509   for (l = 0; l < nleaves; l++) {
9510     PetscAssert(remotes[l].rank != (PetscInt)rank, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains local point %" PetscInt_FMT " <- (%" PetscInt_FMT ",%" PetscInt_FMT ")", locals ? locals[l] : l, remotes[l].rank, remotes[l].index);
9511   }
9512 
9513   /* Check there are no cells in interface */
9514   if (!overlap) {
9515     PetscInt cellHeight, cStart, cEnd;
9516 
9517     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9518     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9519     for (l = 0; l < nleaves; ++l) {
9520       const PetscInt point = locals ? locals[l] : l;
9521 
9522       PetscCheck(point < cStart || point >= cEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " which is a cell", point);
9523     }
9524   }
9525 
9526   /* If some point is in interface, then all its cone points must be also in interface (either as leaves or roots) */
9527   {
9528     const PetscInt *rootdegree;
9529 
9530     PetscCall(PetscSFComputeDegreeBegin(pointSF, &rootdegree));
9531     PetscCall(PetscSFComputeDegreeEnd(pointSF, &rootdegree));
9532     for (l = 0; l < nleaves; ++l) {
9533       const PetscInt  point = locals ? locals[l] : l;
9534       const PetscInt *cone;
9535       PetscInt        coneSize, c, idx;
9536 
9537       PetscCall(DMPlexGetConeSize(dm, point, &coneSize));
9538       PetscCall(DMPlexGetCone(dm, point, &cone));
9539       for (c = 0; c < coneSize; ++c) {
9540         if (!rootdegree[cone[c]]) {
9541           if (locals) {
9542             PetscCall(PetscFindInt(cone[c], nleaves, locals, &idx));
9543           } else {
9544             idx = (cone[c] < nleaves) ? cone[c] : -1;
9545           }
9546           PetscCheck(idx >= 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " but not %" PetscInt_FMT " from its cone", point, cone[c]);
9547         }
9548       }
9549     }
9550   }
9551   PetscFunctionReturn(PETSC_SUCCESS);
9552 }
9553 
9554 /*@
9555   DMPlexCheckOrphanVertices - Check that no vertices are disconnected from the mesh, unless the mesh only consists of disconnected vertices.
9556 
9557   Collective
9558 
9559   Input Parameter:
9560 . dm - The `DMPLEX` object
9561 
9562   Level: developer
9563 
9564   Notes:
9565   This is mainly intended for debugging/testing purposes.
9566 
9567   Other cell types which are disconnected would be caught by the symmetry and face checks.
9568 
9569   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9570 
9571 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCheck()`, `DMSetFromOptions()`
9572 @*/
9573 PetscErrorCode DMPlexCheckOrphanVertices(DM dm)
9574 {
9575   PetscInt pStart, pEnd, vStart, vEnd;
9576 
9577   PetscFunctionBegin;
9578   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
9579   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
9580   if (pStart == vStart && pEnd == vEnd) PetscFunctionReturn(PETSC_SUCCESS);
9581   for (PetscInt v = vStart; v < vEnd; ++v) {
9582     PetscInt suppSize;
9583 
9584     PetscCall(DMPlexGetSupportSize(dm, v, &suppSize));
9585     PetscCheck(suppSize, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Vertex %" PetscInt_FMT " is disconnected from the mesh", v);
9586   }
9587   PetscFunctionReturn(PETSC_SUCCESS);
9588 }
9589 
9590 /*@
9591   DMPlexCheck - Perform various checks of `DMPLEX` sanity
9592 
9593   Input Parameter:
9594 . dm - The `DMPLEX` object
9595 
9596   Level: developer
9597 
9598   Notes:
9599   This is a useful diagnostic when creating meshes programmatically.
9600 
9601   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9602 
9603   Currently does not include `DMPlexCheckCellShape()`.
9604 
9605 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9606 @*/
9607 PetscErrorCode DMPlexCheck(DM dm)
9608 {
9609   PetscInt cellHeight;
9610 
9611   PetscFunctionBegin;
9612   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9613   PetscCall(DMPlexCheckSymmetry(dm));
9614   PetscCall(DMPlexCheckSkeleton(dm, cellHeight));
9615   PetscCall(DMPlexCheckFaces(dm, cellHeight));
9616   PetscCall(DMPlexCheckGeometry(dm));
9617   PetscCall(DMPlexCheckPointSF(dm, NULL, PETSC_FALSE));
9618   PetscCall(DMPlexCheckInterfaceCones(dm));
9619   PetscCall(DMPlexCheckOrphanVertices(dm));
9620   PetscFunctionReturn(PETSC_SUCCESS);
9621 }
9622 
9623 typedef struct cell_stats {
9624   PetscReal min, max, sum, squaresum;
9625   PetscInt  count;
9626 } cell_stats_t;
9627 
9628 static void MPIAPI cell_stats_reduce(void *a, void *b, int *len, MPI_Datatype *datatype)
9629 {
9630   PetscInt i, N = *len;
9631 
9632   for (i = 0; i < N; i++) {
9633     cell_stats_t *A = (cell_stats_t *)a;
9634     cell_stats_t *B = (cell_stats_t *)b;
9635 
9636     B->min = PetscMin(A->min, B->min);
9637     B->max = PetscMax(A->max, B->max);
9638     B->sum += A->sum;
9639     B->squaresum += A->squaresum;
9640     B->count += A->count;
9641   }
9642 }
9643 
9644 /*@
9645   DMPlexCheckCellShape - Checks the Jacobian of the mapping from reference to real cells and computes some minimal statistics.
9646 
9647   Collective
9648 
9649   Input Parameters:
9650 + dm        - The `DMPLEX` object
9651 . output    - If true, statistics will be displayed on `stdout`
9652 - condLimit - Display all cells above this condition number, or `PETSC_DETERMINE` for no cell output
9653 
9654   Level: developer
9655 
9656   Notes:
9657   This is mainly intended for debugging/testing purposes.
9658 
9659   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9660 
9661 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexComputeOrthogonalQuality()`
9662 @*/
9663 PetscErrorCode DMPlexCheckCellShape(DM dm, PetscBool output, PetscReal condLimit)
9664 {
9665   DM           dmCoarse;
9666   cell_stats_t stats, globalStats;
9667   MPI_Comm     comm = PetscObjectComm((PetscObject)dm);
9668   PetscReal   *J, *invJ, min = 0, max = 0, mean = 0, stdev = 0;
9669   PetscReal    limit = condLimit > 0 ? condLimit : PETSC_MAX_REAL;
9670   PetscInt     cdim, cStart, cEnd, c, eStart, eEnd, count = 0;
9671   PetscMPIInt  rank, size;
9672 
9673   PetscFunctionBegin;
9674   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9675   stats.min = PETSC_MAX_REAL;
9676   stats.max = PETSC_MIN_REAL;
9677   stats.sum = stats.squaresum = 0.;
9678   stats.count                 = 0;
9679 
9680   PetscCallMPI(MPI_Comm_size(comm, &size));
9681   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9682   PetscCall(DMGetCoordinateDim(dm, &cdim));
9683   PetscCall(PetscMalloc2(PetscSqr(cdim), &J, PetscSqr(cdim), &invJ));
9684   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
9685   PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
9686   for (c = cStart; c < cEnd; c++) {
9687     PetscInt  i;
9688     PetscReal frobJ = 0., frobInvJ = 0., cond2, cond, detJ;
9689 
9690     PetscCall(DMPlexComputeCellGeometryAffineFEM(dm, c, NULL, J, invJ, &detJ));
9691     PetscCheck(detJ >= 0.0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Mesh cell %" PetscInt_FMT " is inverted", c);
9692     for (i = 0; i < PetscSqr(cdim); ++i) {
9693       frobJ += J[i] * J[i];
9694       frobInvJ += invJ[i] * invJ[i];
9695     }
9696     cond2 = frobJ * frobInvJ;
9697     cond  = PetscSqrtReal(cond2);
9698 
9699     stats.min = PetscMin(stats.min, cond);
9700     stats.max = PetscMax(stats.max, cond);
9701     stats.sum += cond;
9702     stats.squaresum += cond2;
9703     stats.count++;
9704     if (output && cond > limit) {
9705       PetscSection coordSection;
9706       Vec          coordsLocal;
9707       PetscScalar *coords = NULL;
9708       PetscInt     Nv, d, clSize, cl, *closure = NULL;
9709 
9710       PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
9711       PetscCall(DMGetCoordinateSection(dm, &coordSection));
9712       PetscCall(DMPlexVecGetClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9713       PetscCall(PetscSynchronizedPrintf(comm, "[%d] Cell %" PetscInt_FMT " cond %g\n", rank, c, (double)cond));
9714       for (i = 0; i < Nv / cdim; ++i) {
9715         PetscCall(PetscSynchronizedPrintf(comm, "  Vertex %" PetscInt_FMT ": (", i));
9716         for (d = 0; d < cdim; ++d) {
9717           if (d > 0) PetscCall(PetscSynchronizedPrintf(comm, ", "));
9718           PetscCall(PetscSynchronizedPrintf(comm, "%g", (double)PetscRealPart(coords[i * cdim + d])));
9719         }
9720         PetscCall(PetscSynchronizedPrintf(comm, ")\n"));
9721       }
9722       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9723       for (cl = 0; cl < clSize * 2; cl += 2) {
9724         const PetscInt edge = closure[cl];
9725 
9726         if ((edge >= eStart) && (edge < eEnd)) {
9727           PetscReal len;
9728 
9729           PetscCall(DMPlexComputeCellGeometryFVM(dm, edge, &len, NULL, NULL));
9730           PetscCall(PetscSynchronizedPrintf(comm, "  Edge %" PetscInt_FMT ": length %g\n", edge, (double)len));
9731         }
9732       }
9733       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9734       PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9735     }
9736   }
9737   if (output) PetscCall(PetscSynchronizedFlush(comm, NULL));
9738 
9739   if (size > 1) {
9740     PetscMPIInt  blockLengths[2] = {4, 1};
9741     MPI_Aint     blockOffsets[2] = {offsetof(cell_stats_t, min), offsetof(cell_stats_t, count)};
9742     MPI_Datatype blockTypes[2]   = {MPIU_REAL, MPIU_INT}, statType;
9743     MPI_Op       statReduce;
9744 
9745     PetscCallMPI(MPI_Type_create_struct(2, blockLengths, blockOffsets, blockTypes, &statType));
9746     PetscCallMPI(MPI_Type_commit(&statType));
9747     PetscCallMPI(MPI_Op_create(cell_stats_reduce, PETSC_TRUE, &statReduce));
9748     PetscCallMPI(MPI_Reduce(&stats, &globalStats, 1, statType, statReduce, 0, comm));
9749     PetscCallMPI(MPI_Op_free(&statReduce));
9750     PetscCallMPI(MPI_Type_free(&statType));
9751   } else {
9752     PetscCall(PetscArraycpy(&globalStats, &stats, 1));
9753   }
9754   if (rank == 0) {
9755     count = globalStats.count;
9756     min   = globalStats.min;
9757     max   = globalStats.max;
9758     mean  = globalStats.sum / globalStats.count;
9759     stdev = globalStats.count > 1 ? PetscSqrtReal(PetscMax((globalStats.squaresum - globalStats.count * mean * mean) / (globalStats.count - 1), 0)) : 0.0;
9760   }
9761 
9762   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));
9763   PetscCall(PetscFree2(J, invJ));
9764 
9765   PetscCall(DMGetCoarseDM(dm, &dmCoarse));
9766   if (dmCoarse) {
9767     PetscBool isplex;
9768 
9769     PetscCall(PetscObjectTypeCompare((PetscObject)dmCoarse, DMPLEX, &isplex));
9770     if (isplex) PetscCall(DMPlexCheckCellShape(dmCoarse, output, condLimit));
9771   }
9772   PetscFunctionReturn(PETSC_SUCCESS);
9773 }
9774 
9775 /*@
9776   DMPlexComputeOrthogonalQuality - Compute cell-wise orthogonal quality mesh statistic. Optionally tags all cells with
9777   orthogonal quality below given tolerance.
9778 
9779   Collective
9780 
9781   Input Parameters:
9782 + dm   - The `DMPLEX` object
9783 . fv   - Optional `PetscFV` object for pre-computed cell/face centroid information
9784 - atol - [0, 1] Absolute tolerance for tagging cells.
9785 
9786   Output Parameters:
9787 + OrthQual      - `Vec` containing orthogonal quality per cell
9788 - OrthQualLabel - `DMLabel` tagging cells below atol with `DM_ADAPT_REFINE`
9789 
9790   Options Database Keys:
9791 + -dm_plex_orthogonal_quality_label_view - view OrthQualLabel if label is requested. Currently only `PETSCVIEWERASCII` is supported.
9792 - -dm_plex_orthogonal_quality_vec_view   - view OrthQual vector.
9793 
9794   Level: intermediate
9795 
9796   Notes:
9797   Orthogonal quality is given by the following formula\:
9798 
9799   $ \min \left[ \frac{A_i \cdot f_i}{\|A_i\| \|f_i\|} , \frac{A_i \cdot c_i}{\|A_i\| \|c_i\|} \right]$
9800 
9801   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
9802   is the vector from the current cells centroid to the centroid of its i'th neighbor (which shares a face with the
9803   current cell). This computes the vector similarity between each cell face and its corresponding neighbor centroid by
9804   calculating the cosine of the angle between these vectors.
9805 
9806   Orthogonal quality ranges from 1 (best) to 0 (worst).
9807 
9808   This routine is mainly useful for FVM, however is not restricted to only FVM. The `PetscFV` object is optionally used to check for
9809   pre-computed FVM cell data, but if it is not passed in then this data will be computed.
9810 
9811   Cells are tagged if they have an orthogonal quality less than or equal to the absolute tolerance.
9812 
9813 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCheckCellShape()`, `DMCreateLabel()`, `PetscFV`, `DMLabel`, `Vec`
9814 @*/
9815 PetscErrorCode DMPlexComputeOrthogonalQuality(DM dm, PetscFV fv, PetscReal atol, Vec *OrthQual, DMLabel *OrthQualLabel)
9816 {
9817   PetscInt               nc, cellHeight, cStart, cEnd, cell, cellIter = 0;
9818   PetscInt              *idx;
9819   PetscScalar           *oqVals;
9820   const PetscScalar     *cellGeomArr, *faceGeomArr;
9821   PetscReal             *ci, *fi, *Ai;
9822   MPI_Comm               comm;
9823   Vec                    cellgeom, facegeom;
9824   DM                     dmFace, dmCell;
9825   IS                     glob;
9826   ISLocalToGlobalMapping ltog;
9827   PetscViewer            vwr;
9828 
9829   PetscFunctionBegin;
9830   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9831   if (fv) PetscValidHeaderSpecific(fv, PETSCFV_CLASSID, 2);
9832   PetscAssertPointer(OrthQual, 4);
9833   PetscCheck(atol >= 0.0 && atol <= 1.0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Absolute tolerance %g not in [0,1]", (double)atol);
9834   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9835   PetscCall(DMGetDimension(dm, &nc));
9836   PetscCheck(nc >= 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must have dimension >= 2 (current %" PetscInt_FMT ")", nc);
9837   {
9838     DMPlexInterpolatedFlag interpFlag;
9839 
9840     PetscCall(DMPlexIsInterpolated(dm, &interpFlag));
9841     if (interpFlag != DMPLEX_INTERPOLATED_FULL) {
9842       PetscMPIInt rank;
9843 
9844       PetscCallMPI(MPI_Comm_rank(comm, &rank));
9845       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must be fully interpolated, DM on rank %d is not fully interpolated", rank);
9846     }
9847   }
9848   if (OrthQualLabel) {
9849     PetscAssertPointer(OrthQualLabel, 5);
9850     PetscCall(DMCreateLabel(dm, "Orthogonal_Quality"));
9851     PetscCall(DMGetLabel(dm, "Orthogonal_Quality", OrthQualLabel));
9852   } else {
9853     *OrthQualLabel = NULL;
9854   }
9855   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9856   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9857   PetscCall(DMPlexCreateCellNumbering(dm, PETSC_TRUE, &glob));
9858   PetscCall(ISLocalToGlobalMappingCreateIS(glob, &ltog));
9859   PetscCall(ISLocalToGlobalMappingSetType(ltog, ISLOCALTOGLOBALMAPPINGHASH));
9860   PetscCall(VecCreate(comm, OrthQual));
9861   PetscCall(VecSetType(*OrthQual, VECSTANDARD));
9862   PetscCall(VecSetSizes(*OrthQual, cEnd - cStart, PETSC_DETERMINE));
9863   PetscCall(VecSetLocalToGlobalMapping(*OrthQual, ltog));
9864   PetscCall(VecSetUp(*OrthQual));
9865   PetscCall(ISDestroy(&glob));
9866   PetscCall(ISLocalToGlobalMappingDestroy(&ltog));
9867   PetscCall(DMPlexGetDataFVM(dm, fv, &cellgeom, &facegeom, NULL));
9868   PetscCall(VecGetArrayRead(cellgeom, &cellGeomArr));
9869   PetscCall(VecGetArrayRead(facegeom, &faceGeomArr));
9870   PetscCall(VecGetDM(cellgeom, &dmCell));
9871   PetscCall(VecGetDM(facegeom, &dmFace));
9872   PetscCall(PetscMalloc5(cEnd - cStart, &idx, cEnd - cStart, &oqVals, nc, &ci, nc, &fi, nc, &Ai));
9873   for (cell = cStart; cell < cEnd; cellIter++, cell++) {
9874     PetscInt         cellneigh, cellneighiter = 0, adjSize = PETSC_DETERMINE;
9875     PetscInt         cellarr[2], *adj = NULL;
9876     PetscScalar     *cArr, *fArr;
9877     PetscReal        minvalc = 1.0, minvalf = 1.0;
9878     PetscFVCellGeom *cg;
9879 
9880     idx[cellIter] = cell - cStart;
9881     cellarr[0]    = cell;
9882     /* Make indexing into cellGeom easier */
9883     PetscCall(DMPlexPointLocalRead(dmCell, cell, cellGeomArr, &cg));
9884     PetscCall(DMPlexGetAdjacency_Internal(dm, cell, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &adjSize, &adj));
9885     /* Technically 1 too big, but easier than fiddling with empty adjacency array */
9886     PetscCall(PetscCalloc2(adjSize, &cArr, adjSize, &fArr));
9887     for (cellneigh = 0; cellneigh < adjSize; cellneighiter++, cellneigh++) {
9888       PetscInt         i;
9889       const PetscInt   neigh  = adj[cellneigh];
9890       PetscReal        normci = 0, normfi = 0, normai = 0;
9891       PetscFVCellGeom *cgneigh;
9892       PetscFVFaceGeom *fg;
9893 
9894       /* Don't count ourselves in the neighbor list */
9895       if (neigh == cell) continue;
9896       PetscCall(DMPlexPointLocalRead(dmCell, neigh, cellGeomArr, &cgneigh));
9897       cellarr[1] = neigh;
9898       {
9899         PetscInt        numcovpts;
9900         const PetscInt *covpts;
9901 
9902         PetscCall(DMPlexGetMeet(dm, 2, cellarr, &numcovpts, &covpts));
9903         PetscCall(DMPlexPointLocalRead(dmFace, covpts[0], faceGeomArr, &fg));
9904         PetscCall(DMPlexRestoreMeet(dm, 2, cellarr, &numcovpts, &covpts));
9905       }
9906 
9907       /* Compute c_i, f_i and their norms */
9908       for (i = 0; i < nc; i++) {
9909         ci[i] = cgneigh->centroid[i] - cg->centroid[i];
9910         fi[i] = fg->centroid[i] - cg->centroid[i];
9911         Ai[i] = fg->normal[i];
9912         normci += PetscPowReal(ci[i], 2);
9913         normfi += PetscPowReal(fi[i], 2);
9914         normai += PetscPowReal(Ai[i], 2);
9915       }
9916       normci = PetscSqrtReal(normci);
9917       normfi = PetscSqrtReal(normfi);
9918       normai = PetscSqrtReal(normai);
9919 
9920       /* Normalize and compute for each face-cell-normal pair */
9921       for (i = 0; i < nc; i++) {
9922         ci[i] = ci[i] / normci;
9923         fi[i] = fi[i] / normfi;
9924         Ai[i] = Ai[i] / normai;
9925         /* PetscAbs because I don't know if normals are guaranteed to point out */
9926         cArr[cellneighiter] += PetscAbs(Ai[i] * ci[i]);
9927         fArr[cellneighiter] += PetscAbs(Ai[i] * fi[i]);
9928       }
9929       if (PetscRealPart(cArr[cellneighiter]) < minvalc) minvalc = PetscRealPart(cArr[cellneighiter]);
9930       if (PetscRealPart(fArr[cellneighiter]) < minvalf) minvalf = PetscRealPart(fArr[cellneighiter]);
9931     }
9932     PetscCall(PetscFree(adj));
9933     PetscCall(PetscFree2(cArr, fArr));
9934     /* Defer to cell if they're equal */
9935     oqVals[cellIter] = PetscMin(minvalf, minvalc);
9936     if (OrthQualLabel) {
9937       if (PetscRealPart(oqVals[cellIter]) <= atol) PetscCall(DMLabelSetValue(*OrthQualLabel, cell, DM_ADAPT_REFINE));
9938     }
9939   }
9940   PetscCall(VecSetValuesLocal(*OrthQual, cEnd - cStart, idx, oqVals, INSERT_VALUES));
9941   PetscCall(VecAssemblyBegin(*OrthQual));
9942   PetscCall(VecAssemblyEnd(*OrthQual));
9943   PetscCall(VecRestoreArrayRead(cellgeom, &cellGeomArr));
9944   PetscCall(VecRestoreArrayRead(facegeom, &faceGeomArr));
9945   PetscCall(PetscOptionsCreateViewer(comm, NULL, NULL, "-dm_plex_orthogonal_quality_label_view", &vwr, NULL, NULL));
9946   if (OrthQualLabel) {
9947     if (vwr) PetscCall(DMLabelView(*OrthQualLabel, vwr));
9948   }
9949   PetscCall(PetscFree5(idx, oqVals, ci, fi, Ai));
9950   PetscCall(PetscViewerDestroy(&vwr));
9951   PetscCall(VecViewFromOptions(*OrthQual, NULL, "-dm_plex_orthogonal_quality_vec_view"));
9952   PetscFunctionReturn(PETSC_SUCCESS);
9953 }
9954 
9955 /* this is here instead of DMGetOutputDM because output DM still has constraints in the local indices that affect
9956  * interpolator construction */
9957 static PetscErrorCode DMGetFullDM(DM dm, DM *odm)
9958 {
9959   PetscSection section, newSection, gsection;
9960   PetscSF      sf;
9961   PetscBool    hasConstraints, ghasConstraints;
9962 
9963   PetscFunctionBegin;
9964   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9965   PetscAssertPointer(odm, 2);
9966   PetscCall(DMGetLocalSection(dm, &section));
9967   PetscCall(PetscSectionHasConstraints(section, &hasConstraints));
9968   PetscCall(MPIU_Allreduce(&hasConstraints, &ghasConstraints, 1, MPIU_BOOL, MPI_LOR, PetscObjectComm((PetscObject)dm)));
9969   if (!ghasConstraints) {
9970     PetscCall(PetscObjectReference((PetscObject)dm));
9971     *odm = dm;
9972     PetscFunctionReturn(PETSC_SUCCESS);
9973   }
9974   PetscCall(DMClone(dm, odm));
9975   PetscCall(DMCopyFields(dm, *odm));
9976   PetscCall(DMGetLocalSection(*odm, &newSection));
9977   PetscCall(DMGetPointSF(*odm, &sf));
9978   PetscCall(PetscSectionCreateGlobalSection(newSection, sf, PETSC_TRUE, PETSC_TRUE, PETSC_FALSE, &gsection));
9979   PetscCall(DMSetGlobalSection(*odm, gsection));
9980   PetscCall(PetscSectionDestroy(&gsection));
9981   PetscFunctionReturn(PETSC_SUCCESS);
9982 }
9983 
9984 static PetscErrorCode DMCreateAffineInterpolationCorrection_Plex(DM dmc, DM dmf, Vec *shift)
9985 {
9986   DM        dmco, dmfo;
9987   Mat       interpo;
9988   Vec       rscale;
9989   Vec       cglobalo, clocal;
9990   Vec       fglobal, fglobalo, flocal;
9991   PetscBool regular;
9992 
9993   PetscFunctionBegin;
9994   PetscCall(DMGetFullDM(dmc, &dmco));
9995   PetscCall(DMGetFullDM(dmf, &dmfo));
9996   PetscCall(DMSetCoarseDM(dmfo, dmco));
9997   PetscCall(DMPlexGetRegularRefinement(dmf, &regular));
9998   PetscCall(DMPlexSetRegularRefinement(dmfo, regular));
9999   PetscCall(DMCreateInterpolation(dmco, dmfo, &interpo, &rscale));
10000   PetscCall(DMCreateGlobalVector(dmco, &cglobalo));
10001   PetscCall(DMCreateLocalVector(dmc, &clocal));
10002   PetscCall(VecSet(cglobalo, 0.));
10003   PetscCall(VecSet(clocal, 0.));
10004   PetscCall(DMCreateGlobalVector(dmf, &fglobal));
10005   PetscCall(DMCreateGlobalVector(dmfo, &fglobalo));
10006   PetscCall(DMCreateLocalVector(dmf, &flocal));
10007   PetscCall(VecSet(fglobal, 0.));
10008   PetscCall(VecSet(fglobalo, 0.));
10009   PetscCall(VecSet(flocal, 0.));
10010   PetscCall(DMPlexInsertBoundaryValues(dmc, PETSC_TRUE, clocal, 0., NULL, NULL, NULL));
10011   PetscCall(DMLocalToGlobalBegin(dmco, clocal, INSERT_VALUES, cglobalo));
10012   PetscCall(DMLocalToGlobalEnd(dmco, clocal, INSERT_VALUES, cglobalo));
10013   PetscCall(MatMult(interpo, cglobalo, fglobalo));
10014   PetscCall(DMGlobalToLocalBegin(dmfo, fglobalo, INSERT_VALUES, flocal));
10015   PetscCall(DMGlobalToLocalEnd(dmfo, fglobalo, INSERT_VALUES, flocal));
10016   PetscCall(DMLocalToGlobalBegin(dmf, flocal, INSERT_VALUES, fglobal));
10017   PetscCall(DMLocalToGlobalEnd(dmf, flocal, INSERT_VALUES, fglobal));
10018   *shift = fglobal;
10019   PetscCall(VecDestroy(&flocal));
10020   PetscCall(VecDestroy(&fglobalo));
10021   PetscCall(VecDestroy(&clocal));
10022   PetscCall(VecDestroy(&cglobalo));
10023   PetscCall(VecDestroy(&rscale));
10024   PetscCall(MatDestroy(&interpo));
10025   PetscCall(DMDestroy(&dmfo));
10026   PetscCall(DMDestroy(&dmco));
10027   PetscFunctionReturn(PETSC_SUCCESS);
10028 }
10029 
10030 PETSC_INTERN PetscErrorCode DMInterpolateSolution_Plex(DM coarse, DM fine, Mat interp, Vec coarseSol, Vec fineSol)
10031 {
10032   PetscObject shifto;
10033   Vec         shift;
10034 
10035   PetscFunctionBegin;
10036   if (!interp) {
10037     Vec rscale;
10038 
10039     PetscCall(DMCreateInterpolation(coarse, fine, &interp, &rscale));
10040     PetscCall(VecDestroy(&rscale));
10041   } else {
10042     PetscCall(PetscObjectReference((PetscObject)interp));
10043   }
10044   PetscCall(PetscObjectQuery((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", &shifto));
10045   if (!shifto) {
10046     PetscCall(DMCreateAffineInterpolationCorrection_Plex(coarse, fine, &shift));
10047     PetscCall(PetscObjectCompose((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", (PetscObject)shift));
10048     shifto = (PetscObject)shift;
10049     PetscCall(VecDestroy(&shift));
10050   }
10051   shift = (Vec)shifto;
10052   PetscCall(MatInterpolate(interp, coarseSol, fineSol));
10053   PetscCall(VecAXPY(fineSol, 1.0, shift));
10054   PetscCall(MatDestroy(&interp));
10055   PetscFunctionReturn(PETSC_SUCCESS);
10056 }
10057 
10058 /* Pointwise interpolation
10059      Just code FEM for now
10060      u^f = I u^c
10061      sum_k u^f_k phi^f_k = I sum_j u^c_j phi^c_j
10062      u^f_i = sum_j psi^f_i I phi^c_j u^c_j
10063      I_{ij} = psi^f_i phi^c_j
10064 */
10065 PetscErrorCode DMCreateInterpolation_Plex(DM dmCoarse, DM dmFine, Mat *interpolation, Vec *scaling)
10066 {
10067   PetscSection gsc, gsf;
10068   PetscInt     m, n;
10069   void        *ctx;
10070   DM           cdm;
10071   PetscBool    regular, ismatis, isRefined = dmCoarse->data == dmFine->data ? PETSC_FALSE : PETSC_TRUE;
10072 
10073   PetscFunctionBegin;
10074   PetscCall(DMGetGlobalSection(dmFine, &gsf));
10075   PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
10076   PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
10077   PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
10078 
10079   PetscCall(PetscStrcmp(dmCoarse->mattype, MATIS, &ismatis));
10080   PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), interpolation));
10081   PetscCall(MatSetSizes(*interpolation, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
10082   PetscCall(MatSetType(*interpolation, ismatis ? MATAIJ : dmCoarse->mattype));
10083   PetscCall(DMGetApplicationContext(dmFine, &ctx));
10084 
10085   PetscCall(DMGetCoarseDM(dmFine, &cdm));
10086   PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
10087   if (!isRefined || (regular && cdm == dmCoarse)) PetscCall(DMPlexComputeInterpolatorNested(dmCoarse, dmFine, isRefined, *interpolation, ctx));
10088   else PetscCall(DMPlexComputeInterpolatorGeneral(dmCoarse, dmFine, *interpolation, ctx));
10089   PetscCall(MatViewFromOptions(*interpolation, NULL, "-interp_mat_view"));
10090   if (scaling) {
10091     /* Use naive scaling */
10092     PetscCall(DMCreateInterpolationScale(dmCoarse, dmFine, *interpolation, scaling));
10093   }
10094   PetscFunctionReturn(PETSC_SUCCESS);
10095 }
10096 
10097 PetscErrorCode DMCreateInjection_Plex(DM dmCoarse, DM dmFine, Mat *mat)
10098 {
10099   VecScatter ctx;
10100 
10101   PetscFunctionBegin;
10102   PetscCall(DMPlexComputeInjectorFEM(dmCoarse, dmFine, &ctx, NULL));
10103   PetscCall(MatCreateScatter(PetscObjectComm((PetscObject)ctx), ctx, mat));
10104   PetscCall(VecScatterDestroy(&ctx));
10105   PetscFunctionReturn(PETSC_SUCCESS);
10106 }
10107 
10108 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[])
10109 {
10110   const PetscInt f  = (PetscInt)PetscRealPart(constants[numConstants]);
10111   const PetscInt Nc = uOff[f + 1] - uOff[f];
10112   for (PetscInt c = 0; c < Nc; ++c) g0[c * Nc + c] = 1.0;
10113 }
10114 
10115 PetscErrorCode DMCreateMassMatrixLumped_Plex(DM dm, Vec *mass)
10116 {
10117   DM           dmc;
10118   PetscDS      ds;
10119   Vec          ones, locmass;
10120   IS           cellIS;
10121   PetscFormKey key;
10122   PetscInt     depth;
10123 
10124   PetscFunctionBegin;
10125   PetscCall(DMClone(dm, &dmc));
10126   PetscCall(DMCopyDisc(dm, dmc));
10127   PetscCall(DMGetDS(dmc, &ds));
10128   for (PetscInt f = 0; f < dmc->Nf; ++f) PetscCall(PetscDSSetJacobian(ds, f, f, g0_identity_private, NULL, NULL, NULL));
10129   PetscCall(DMCreateGlobalVector(dmc, mass));
10130   PetscCall(DMGetLocalVector(dmc, &ones));
10131   PetscCall(DMGetLocalVector(dmc, &locmass));
10132   PetscCall(DMPlexGetDepth(dmc, &depth));
10133   PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
10134   PetscCall(VecSet(locmass, 0.0));
10135   PetscCall(VecSet(ones, 1.0));
10136   key.label = NULL;
10137   key.value = 0;
10138   key.field = 0;
10139   key.part  = 0;
10140   PetscCall(DMPlexComputeJacobian_Action_Internal(dmc, key, cellIS, 0.0, 0.0, ones, NULL, ones, locmass, NULL));
10141   PetscCall(ISDestroy(&cellIS));
10142   PetscCall(VecSet(*mass, 0.0));
10143   PetscCall(DMLocalToGlobalBegin(dmc, locmass, ADD_VALUES, *mass));
10144   PetscCall(DMLocalToGlobalEnd(dmc, locmass, ADD_VALUES, *mass));
10145   PetscCall(DMRestoreLocalVector(dmc, &ones));
10146   PetscCall(DMRestoreLocalVector(dmc, &locmass));
10147   PetscCall(DMDestroy(&dmc));
10148   PetscFunctionReturn(PETSC_SUCCESS);
10149 }
10150 
10151 PetscErrorCode DMCreateMassMatrix_Plex(DM dmCoarse, DM dmFine, Mat *mass)
10152 {
10153   PetscSection gsc, gsf;
10154   PetscInt     m, n;
10155   void        *ctx;
10156   DM           cdm;
10157   PetscBool    regular;
10158 
10159   PetscFunctionBegin;
10160   if (dmFine == dmCoarse) {
10161     DM            dmc;
10162     PetscDS       ds;
10163     PetscWeakForm wf;
10164     Vec           u;
10165     IS            cellIS;
10166     PetscFormKey  key;
10167     PetscInt      depth;
10168 
10169     PetscCall(DMClone(dmFine, &dmc));
10170     PetscCall(DMCopyDisc(dmFine, dmc));
10171     PetscCall(DMGetDS(dmc, &ds));
10172     PetscCall(PetscDSGetWeakForm(ds, &wf));
10173     PetscCall(PetscWeakFormClear(wf));
10174     for (PetscInt f = 0; f < dmc->Nf; ++f) PetscCall(PetscDSSetJacobian(ds, f, f, g0_identity_private, NULL, NULL, NULL));
10175     PetscCall(DMCreateMatrix(dmc, mass));
10176     PetscCall(DMGetLocalVector(dmc, &u));
10177     PetscCall(DMPlexGetDepth(dmc, &depth));
10178     PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
10179     PetscCall(MatZeroEntries(*mass));
10180     key.label = NULL;
10181     key.value = 0;
10182     key.field = 0;
10183     key.part  = 0;
10184     PetscCall(DMPlexComputeJacobian_Internal(dmc, key, cellIS, 0.0, 0.0, u, NULL, *mass, *mass, NULL));
10185     PetscCall(ISDestroy(&cellIS));
10186     PetscCall(DMRestoreLocalVector(dmc, &u));
10187     PetscCall(DMDestroy(&dmc));
10188   } else {
10189     PetscCall(DMGetGlobalSection(dmFine, &gsf));
10190     PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
10191     PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
10192     PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
10193 
10194     PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), mass));
10195     PetscCall(MatSetSizes(*mass, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
10196     PetscCall(MatSetType(*mass, dmCoarse->mattype));
10197     PetscCall(DMGetApplicationContext(dmFine, &ctx));
10198 
10199     PetscCall(DMGetCoarseDM(dmFine, &cdm));
10200     PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
10201     if (regular && cdm == dmCoarse) PetscCall(DMPlexComputeMassMatrixNested(dmCoarse, dmFine, *mass, ctx));
10202     else PetscCall(DMPlexComputeMassMatrixGeneral(dmCoarse, dmFine, *mass, ctx));
10203   }
10204   PetscCall(MatViewFromOptions(*mass, NULL, "-mass_mat_view"));
10205   PetscFunctionReturn(PETSC_SUCCESS);
10206 }
10207 
10208 /*@
10209   DMPlexGetRegularRefinement - Get the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
10210 
10211   Input Parameter:
10212 . dm - The `DMPLEX` object
10213 
10214   Output Parameter:
10215 . regular - The flag
10216 
10217   Level: intermediate
10218 
10219 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetRegularRefinement()`
10220 @*/
10221 PetscErrorCode DMPlexGetRegularRefinement(DM dm, PetscBool *regular)
10222 {
10223   PetscFunctionBegin;
10224   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10225   PetscAssertPointer(regular, 2);
10226   *regular = ((DM_Plex *)dm->data)->regularRefinement;
10227   PetscFunctionReturn(PETSC_SUCCESS);
10228 }
10229 
10230 /*@
10231   DMPlexSetRegularRefinement - Set the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
10232 
10233   Input Parameters:
10234 + dm      - The `DMPLEX` object
10235 - regular - The flag
10236 
10237   Level: intermediate
10238 
10239 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetRegularRefinement()`
10240 @*/
10241 PetscErrorCode DMPlexSetRegularRefinement(DM dm, PetscBool regular)
10242 {
10243   PetscFunctionBegin;
10244   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10245   ((DM_Plex *)dm->data)->regularRefinement = regular;
10246   PetscFunctionReturn(PETSC_SUCCESS);
10247 }
10248 
10249 /*@
10250   DMPlexGetAnchors - Get the layout of the anchor (point-to-point) constraints.  Typically, the user will not have to
10251   call DMPlexGetAnchors() directly: if there are anchors, then `DMPlexGetAnchors()` is called during `DMGetDefaultConstraints()`.
10252 
10253   Not Collective
10254 
10255   Input Parameter:
10256 . dm - The `DMPLEX` object
10257 
10258   Output Parameters:
10259 + anchorSection - If not `NULL`, set to the section describing which points anchor the constrained points.
10260 - anchorIS      - If not `NULL`, set to the list of anchors indexed by `anchorSection`
10261 
10262   Level: intermediate
10263 
10264 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`, `IS`, `PetscSection`
10265 @*/
10266 PetscErrorCode DMPlexGetAnchors(DM dm, PetscSection *anchorSection, IS *anchorIS)
10267 {
10268   DM_Plex *plex = (DM_Plex *)dm->data;
10269 
10270   PetscFunctionBegin;
10271   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10272   if (!plex->anchorSection && !plex->anchorIS && plex->createanchors) PetscCall((*plex->createanchors)(dm));
10273   if (anchorSection) *anchorSection = plex->anchorSection;
10274   if (anchorIS) *anchorIS = plex->anchorIS;
10275   PetscFunctionReturn(PETSC_SUCCESS);
10276 }
10277 
10278 /*@
10279   DMPlexSetAnchors - Set the layout of the local anchor (point-to-point) constraints.
10280 
10281   Collective
10282 
10283   Input Parameters:
10284 + dm            - The `DMPLEX` object
10285 . anchorSection - The section that describes the mapping from constrained points to the anchor points listed in anchorIS.
10286                   Must have a local communicator (`PETSC_COMM_SELF` or derivative).
10287 - anchorIS      - The list of all anchor points.  Must have a local communicator (`PETSC_COMM_SELF` or derivative).
10288 
10289   Level: intermediate
10290 
10291   Notes:
10292   Unlike boundary conditions, when a point's degrees of freedom in a section are constrained to
10293   an outside value, the anchor constraints set a point's degrees of freedom to be a linear
10294   combination of other points' degrees of freedom.
10295 
10296   After specifying the layout of constraints with `DMPlexSetAnchors()`, one specifies the constraints by calling
10297   `DMGetDefaultConstraints()` and filling in the entries in the constraint matrix.
10298 
10299   The reference counts of `anchorSection` and `anchorIS` are incremented.
10300 
10301 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`
10302 @*/
10303 PetscErrorCode DMPlexSetAnchors(DM dm, PetscSection anchorSection, IS anchorIS)
10304 {
10305   DM_Plex    *plex = (DM_Plex *)dm->data;
10306   PetscMPIInt result;
10307 
10308   PetscFunctionBegin;
10309   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10310   if (anchorSection) {
10311     PetscValidHeaderSpecific(anchorSection, PETSC_SECTION_CLASSID, 2);
10312     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorSection), &result));
10313     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor section must have local communicator");
10314   }
10315   if (anchorIS) {
10316     PetscValidHeaderSpecific(anchorIS, IS_CLASSID, 3);
10317     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorIS), &result));
10318     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor IS must have local communicator");
10319   }
10320 
10321   PetscCall(PetscObjectReference((PetscObject)anchorSection));
10322   PetscCall(PetscSectionDestroy(&plex->anchorSection));
10323   plex->anchorSection = anchorSection;
10324 
10325   PetscCall(PetscObjectReference((PetscObject)anchorIS));
10326   PetscCall(ISDestroy(&plex->anchorIS));
10327   plex->anchorIS = anchorIS;
10328 
10329   if (PetscUnlikelyDebug(anchorIS && anchorSection)) {
10330     PetscInt        size, a, pStart, pEnd;
10331     const PetscInt *anchors;
10332 
10333     PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
10334     PetscCall(ISGetLocalSize(anchorIS, &size));
10335     PetscCall(ISGetIndices(anchorIS, &anchors));
10336     for (a = 0; a < size; a++) {
10337       PetscInt p;
10338 
10339       p = anchors[a];
10340       if (p >= pStart && p < pEnd) {
10341         PetscInt dof;
10342 
10343         PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
10344         if (dof) {
10345           PetscCall(ISRestoreIndices(anchorIS, &anchors));
10346           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Point %" PetscInt_FMT " cannot be constrained and an anchor", p);
10347         }
10348       }
10349     }
10350     PetscCall(ISRestoreIndices(anchorIS, &anchors));
10351   }
10352   /* reset the generic constraints */
10353   PetscCall(DMSetDefaultConstraints(dm, NULL, NULL, NULL));
10354   PetscFunctionReturn(PETSC_SUCCESS);
10355 }
10356 
10357 static PetscErrorCode DMPlexCreateConstraintSection_Anchors(DM dm, PetscSection section, PetscSection *cSec)
10358 {
10359   PetscSection anchorSection;
10360   PetscInt     pStart, pEnd, sStart, sEnd, p, dof, numFields, f;
10361 
10362   PetscFunctionBegin;
10363   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10364   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
10365   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, cSec));
10366   PetscCall(PetscSectionGetNumFields(section, &numFields));
10367   if (numFields) {
10368     PetscInt f;
10369     PetscCall(PetscSectionSetNumFields(*cSec, numFields));
10370 
10371     for (f = 0; f < numFields; f++) {
10372       PetscInt numComp;
10373 
10374       PetscCall(PetscSectionGetFieldComponents(section, f, &numComp));
10375       PetscCall(PetscSectionSetFieldComponents(*cSec, f, numComp));
10376     }
10377   }
10378   PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
10379   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
10380   pStart = PetscMax(pStart, sStart);
10381   pEnd   = PetscMin(pEnd, sEnd);
10382   pEnd   = PetscMax(pStart, pEnd);
10383   PetscCall(PetscSectionSetChart(*cSec, pStart, pEnd));
10384   for (p = pStart; p < pEnd; p++) {
10385     PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
10386     if (dof) {
10387       PetscCall(PetscSectionGetDof(section, p, &dof));
10388       PetscCall(PetscSectionSetDof(*cSec, p, dof));
10389       for (f = 0; f < numFields; f++) {
10390         PetscCall(PetscSectionGetFieldDof(section, p, f, &dof));
10391         PetscCall(PetscSectionSetFieldDof(*cSec, p, f, dof));
10392       }
10393     }
10394   }
10395   PetscCall(PetscSectionSetUp(*cSec));
10396   PetscCall(PetscObjectSetName((PetscObject)*cSec, "Constraint Section"));
10397   PetscFunctionReturn(PETSC_SUCCESS);
10398 }
10399 
10400 static PetscErrorCode DMPlexCreateConstraintMatrix_Anchors(DM dm, PetscSection section, PetscSection cSec, Mat *cMat)
10401 {
10402   PetscSection    aSec;
10403   PetscInt        pStart, pEnd, p, sStart, sEnd, dof, aDof, aOff, off, nnz, annz, m, n, q, a, offset, *i, *j;
10404   const PetscInt *anchors;
10405   PetscInt        numFields, f;
10406   IS              aIS;
10407   MatType         mtype;
10408   PetscBool       iscuda, iskokkos;
10409 
10410   PetscFunctionBegin;
10411   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10412   PetscCall(PetscSectionGetStorageSize(cSec, &m));
10413   PetscCall(PetscSectionGetStorageSize(section, &n));
10414   PetscCall(MatCreate(PETSC_COMM_SELF, cMat));
10415   PetscCall(MatSetSizes(*cMat, m, n, m, n));
10416   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJCUSPARSE, &iscuda));
10417   if (!iscuda) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJCUSPARSE, &iscuda));
10418   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJKOKKOS, &iskokkos));
10419   if (!iskokkos) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJKOKKOS, &iskokkos));
10420   if (iscuda) mtype = MATSEQAIJCUSPARSE;
10421   else if (iskokkos) mtype = MATSEQAIJKOKKOS;
10422   else mtype = MATSEQAIJ;
10423   PetscCall(MatSetType(*cMat, mtype));
10424   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
10425   PetscCall(ISGetIndices(aIS, &anchors));
10426   /* cSec will be a subset of aSec and section */
10427   PetscCall(PetscSectionGetChart(cSec, &pStart, &pEnd));
10428   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
10429   PetscCall(PetscMalloc1(m + 1, &i));
10430   i[0] = 0;
10431   PetscCall(PetscSectionGetNumFields(section, &numFields));
10432   for (p = pStart; p < pEnd; p++) {
10433     PetscInt rDof, rOff, r;
10434 
10435     PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10436     if (!rDof) continue;
10437     PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10438     if (numFields) {
10439       for (f = 0; f < numFields; f++) {
10440         annz = 0;
10441         for (r = 0; r < rDof; r++) {
10442           a = anchors[rOff + r];
10443           if (a < sStart || a >= sEnd) continue;
10444           PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
10445           annz += aDof;
10446         }
10447         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
10448         PetscCall(PetscSectionGetFieldOffset(cSec, p, f, &off));
10449         for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
10450       }
10451     } else {
10452       annz = 0;
10453       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10454       for (q = 0; q < dof; q++) {
10455         a = anchors[rOff + q];
10456         if (a < sStart || a >= sEnd) continue;
10457         PetscCall(PetscSectionGetDof(section, a, &aDof));
10458         annz += aDof;
10459       }
10460       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10461       PetscCall(PetscSectionGetOffset(cSec, p, &off));
10462       for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
10463     }
10464   }
10465   nnz = i[m];
10466   PetscCall(PetscMalloc1(nnz, &j));
10467   offset = 0;
10468   for (p = pStart; p < pEnd; p++) {
10469     if (numFields) {
10470       for (f = 0; f < numFields; f++) {
10471         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
10472         for (q = 0; q < dof; q++) {
10473           PetscInt rDof, rOff, r;
10474           PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10475           PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10476           for (r = 0; r < rDof; r++) {
10477             PetscInt s;
10478 
10479             a = anchors[rOff + r];
10480             if (a < sStart || a >= sEnd) continue;
10481             PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
10482             PetscCall(PetscSectionGetFieldOffset(section, a, f, &aOff));
10483             for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
10484           }
10485         }
10486       }
10487     } else {
10488       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10489       for (q = 0; q < dof; q++) {
10490         PetscInt rDof, rOff, r;
10491         PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10492         PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10493         for (r = 0; r < rDof; r++) {
10494           PetscInt s;
10495 
10496           a = anchors[rOff + r];
10497           if (a < sStart || a >= sEnd) continue;
10498           PetscCall(PetscSectionGetDof(section, a, &aDof));
10499           PetscCall(PetscSectionGetOffset(section, a, &aOff));
10500           for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
10501         }
10502       }
10503     }
10504   }
10505   PetscCall(MatSeqAIJSetPreallocationCSR(*cMat, i, j, NULL));
10506   PetscCall(PetscFree(i));
10507   PetscCall(PetscFree(j));
10508   PetscCall(ISRestoreIndices(aIS, &anchors));
10509   PetscFunctionReturn(PETSC_SUCCESS);
10510 }
10511 
10512 PetscErrorCode DMCreateDefaultConstraints_Plex(DM dm)
10513 {
10514   DM_Plex     *plex = (DM_Plex *)dm->data;
10515   PetscSection anchorSection, section, cSec;
10516   Mat          cMat;
10517 
10518   PetscFunctionBegin;
10519   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10520   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
10521   if (anchorSection) {
10522     PetscInt Nf;
10523 
10524     PetscCall(DMGetLocalSection(dm, &section));
10525     PetscCall(DMPlexCreateConstraintSection_Anchors(dm, section, &cSec));
10526     PetscCall(DMPlexCreateConstraintMatrix_Anchors(dm, section, cSec, &cMat));
10527     PetscCall(DMGetNumFields(dm, &Nf));
10528     if (Nf && plex->computeanchormatrix) PetscCall((*plex->computeanchormatrix)(dm, section, cSec, cMat));
10529     PetscCall(DMSetDefaultConstraints(dm, cSec, cMat, NULL));
10530     PetscCall(PetscSectionDestroy(&cSec));
10531     PetscCall(MatDestroy(&cMat));
10532   }
10533   PetscFunctionReturn(PETSC_SUCCESS);
10534 }
10535 
10536 PetscErrorCode DMCreateSubDomainDM_Plex(DM dm, DMLabel label, PetscInt value, IS *is, DM *subdm)
10537 {
10538   IS           subis;
10539   PetscSection section, subsection;
10540 
10541   PetscFunctionBegin;
10542   PetscCall(DMGetLocalSection(dm, &section));
10543   PetscCheck(section, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set default section for DM before splitting subdomain");
10544   PetscCheck(subdm, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set output subDM for splitting subdomain");
10545   /* Create subdomain */
10546   PetscCall(DMPlexFilter(dm, label, value, PETSC_FALSE, PETSC_FALSE, NULL, subdm));
10547   /* Create submodel */
10548   PetscCall(DMPlexGetSubpointIS(*subdm, &subis));
10549   PetscCall(PetscSectionCreateSubmeshSection(section, subis, &subsection));
10550   PetscCall(DMSetLocalSection(*subdm, subsection));
10551   PetscCall(PetscSectionDestroy(&subsection));
10552   PetscCall(DMCopyDisc(dm, *subdm));
10553   /* Create map from submodel to global model */
10554   if (is) {
10555     PetscSection    sectionGlobal, subsectionGlobal;
10556     IS              spIS;
10557     const PetscInt *spmap;
10558     PetscInt       *subIndices;
10559     PetscInt        subSize = 0, subOff = 0, pStart, pEnd, p;
10560     PetscInt        Nf, f, bs = -1, bsLocal[2], bsMinMax[2];
10561 
10562     PetscCall(DMPlexGetSubpointIS(*subdm, &spIS));
10563     PetscCall(ISGetIndices(spIS, &spmap));
10564     PetscCall(PetscSectionGetNumFields(section, &Nf));
10565     PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
10566     PetscCall(DMGetGlobalSection(*subdm, &subsectionGlobal));
10567     PetscCall(PetscSectionGetChart(subsection, &pStart, &pEnd));
10568     for (p = pStart; p < pEnd; ++p) {
10569       PetscInt gdof, pSubSize = 0;
10570 
10571       PetscCall(PetscSectionGetDof(sectionGlobal, p, &gdof));
10572       if (gdof > 0) {
10573         for (f = 0; f < Nf; ++f) {
10574           PetscInt fdof, fcdof;
10575 
10576           PetscCall(PetscSectionGetFieldDof(subsection, p, f, &fdof));
10577           PetscCall(PetscSectionGetFieldConstraintDof(subsection, p, f, &fcdof));
10578           pSubSize += fdof - fcdof;
10579         }
10580         subSize += pSubSize;
10581         if (pSubSize) {
10582           if (bs < 0) {
10583             bs = pSubSize;
10584           } else if (bs != pSubSize) {
10585             /* Layout does not admit a pointwise block size */
10586             bs = 1;
10587           }
10588         }
10589       }
10590     }
10591     /* Must have same blocksize on all procs (some might have no points) */
10592     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs;
10593     bsLocal[1] = bs;
10594     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
10595     if (bsMinMax[0] != bsMinMax[1]) {
10596       bs = 1;
10597     } else {
10598       bs = bsMinMax[0];
10599     }
10600     PetscCall(PetscMalloc1(subSize, &subIndices));
10601     for (p = pStart; p < pEnd; ++p) {
10602       PetscInt gdof, goff;
10603 
10604       PetscCall(PetscSectionGetDof(subsectionGlobal, p, &gdof));
10605       if (gdof > 0) {
10606         const PetscInt point = spmap[p];
10607 
10608         PetscCall(PetscSectionGetOffset(sectionGlobal, point, &goff));
10609         for (f = 0; f < Nf; ++f) {
10610           PetscInt fdof, fcdof, fc, f2, poff = 0;
10611 
10612           /* Can get rid of this loop by storing field information in the global section */
10613           for (f2 = 0; f2 < f; ++f2) {
10614             PetscCall(PetscSectionGetFieldDof(section, p, f2, &fdof));
10615             PetscCall(PetscSectionGetFieldConstraintDof(section, p, f2, &fcdof));
10616             poff += fdof - fcdof;
10617           }
10618           PetscCall(PetscSectionGetFieldDof(section, p, f, &fdof));
10619           PetscCall(PetscSectionGetFieldConstraintDof(section, p, f, &fcdof));
10620           for (fc = 0; fc < fdof - fcdof; ++fc, ++subOff) subIndices[subOff] = goff + poff + fc;
10621         }
10622       }
10623     }
10624     PetscCall(ISRestoreIndices(spIS, &spmap));
10625     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), subSize, subIndices, PETSC_OWN_POINTER, is));
10626     if (bs > 1) {
10627       /* We need to check that the block size does not come from non-contiguous fields */
10628       PetscInt i, j, set = 1;
10629       for (i = 0; i < subSize; i += bs) {
10630         for (j = 0; j < bs; ++j) {
10631           if (subIndices[i + j] != subIndices[i] + j) {
10632             set = 0;
10633             break;
10634           }
10635         }
10636       }
10637       if (set) PetscCall(ISSetBlockSize(*is, bs));
10638     }
10639     /* Attach nullspace */
10640     for (f = 0; f < Nf; ++f) {
10641       (*subdm)->nullspaceConstructors[f] = dm->nullspaceConstructors[f];
10642       if ((*subdm)->nullspaceConstructors[f]) break;
10643     }
10644     if (f < Nf) {
10645       MatNullSpace nullSpace;
10646       PetscCall((*(*subdm)->nullspaceConstructors[f])(*subdm, f, f, &nullSpace));
10647 
10648       PetscCall(PetscObjectCompose((PetscObject)*is, "nullspace", (PetscObject)nullSpace));
10649       PetscCall(MatNullSpaceDestroy(&nullSpace));
10650     }
10651   }
10652   PetscFunctionReturn(PETSC_SUCCESS);
10653 }
10654 
10655 /*@
10656   DMPlexMonitorThroughput - Report the cell throughput of FE integration
10657 
10658   Input Parameters:
10659 + dm    - The `DM`
10660 - dummy - unused argument
10661 
10662   Options Database Key:
10663 . -dm_plex_monitor_throughput - Activate the monitor
10664 
10665   Level: developer
10666 
10667 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexCreate()`
10668 @*/
10669 PetscErrorCode DMPlexMonitorThroughput(DM dm, void *dummy)
10670 {
10671   PetscLogHandler default_handler;
10672 
10673   PetscFunctionBegin;
10674   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10675   PetscCall(PetscLogGetDefaultHandler(&default_handler));
10676   if (default_handler) {
10677     PetscLogEvent      event;
10678     PetscEventPerfInfo eventInfo;
10679     PetscReal          cellRate, flopRate;
10680     PetscInt           cStart, cEnd, Nf, N;
10681     const char        *name;
10682 
10683     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
10684     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
10685     PetscCall(DMGetNumFields(dm, &Nf));
10686     PetscCall(PetscLogEventGetId("DMPlexResidualFE", &event));
10687     PetscCall(PetscLogEventGetPerfInfo(PETSC_DEFAULT, event, &eventInfo));
10688     N        = (cEnd - cStart) * Nf * eventInfo.count;
10689     flopRate = eventInfo.flops / eventInfo.time;
10690     cellRate = N / eventInfo.time;
10691     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)));
10692   } else {
10693     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.");
10694   }
10695   PetscFunctionReturn(PETSC_SUCCESS);
10696 }
10697