xref: /petsc/src/dm/impls/plex/plex.c (revision a253b78679873401fddd34f394d8a7e168ec579c)
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_CreateBoxSFC, DMPLEX_RebalanceSharedPoints, DMPLEX_PartSelf, DMPLEX_PartLabelInvert, DMPLEX_PartLabelCreateSF, DMPLEX_PartStratSF, DMPLEX_CreatePointSF, DMPLEX_LocatePoints, DMPLEX_TopologyView, DMPLEX_LabelsView, DMPLEX_CoordinatesView, DMPLEX_SectionView, DMPLEX_GlobalVectorView, DMPLEX_LocalVectorView, DMPLEX_TopologyLoad, DMPLEX_LabelsLoad, DMPLEX_CoordinatesLoad, DMPLEX_SectionLoad, DMPLEX_GlobalVectorLoad, DMPLEX_LocalVectorLoad;
15 PetscLogEvent DMPLEX_RebalBuildGraph, DMPLEX_RebalRewriteSF, DMPLEX_RebalGatherGraph, DMPLEX_RebalPartition, DMPLEX_RebalScatterPart, DMPLEX_Generate, DMPLEX_Transform, DMPLEX_GetLocalOffsets, DMPLEX_Uninterpolate;
16 
17 PetscBool  Plexcite       = PETSC_FALSE;
18 const char PlexCitation[] = "@article{LangeMitchellKnepleyGorman2015,\n"
19                             "title     = {Efficient mesh management in {Firedrake} using {PETSc-DMPlex}},\n"
20                             "author    = {Michael Lange and Lawrence Mitchell and Matthew G. Knepley and Gerard J. Gorman},\n"
21                             "journal   = {SIAM Journal on Scientific Computing},\n"
22                             "volume    = {38},\n"
23                             "number    = {5},\n"
24                             "pages     = {S143--S155},\n"
25                             "eprint    = {http://arxiv.org/abs/1506.07749},\n"
26                             "doi       = {10.1137/15M1026092},\n"
27                             "year      = {2016},\n"
28                             "petsc_uses={DMPlex},\n}\n";
29 
30 PETSC_EXTERN PetscErrorCode VecView_MPI(Vec, PetscViewer);
31 
32 /*@
33   DMPlexIsSimplex - Is the first cell in this mesh a simplex?
34 
35   Input Parameter:
36 . dm - The `DMPLEX` object
37 
38   Output Parameter:
39 . simplex - Flag checking for a simplex
40 
41   Level: intermediate
42 
43   Note:
44   This just gives the first range of cells found. If the mesh has several cell types, it will only give the first.
45   If the mesh has no cells, this returns `PETSC_FALSE`.
46 
47 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSimplexOrBoxCells()`, `DMPlexGetCellType()`, `DMPlexGetHeightStratum()`, `DMPolytopeTypeGetNumVertices()`
48 @*/
49 PetscErrorCode DMPlexIsSimplex(DM dm, PetscBool *simplex)
50 {
51   DMPolytopeType ct;
52   PetscInt       cStart, cEnd;
53 
54   PetscFunctionBegin;
55   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
56   if (cEnd <= cStart) {
57     *simplex = PETSC_FALSE;
58     PetscFunctionReturn(PETSC_SUCCESS);
59   }
60   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
61   *simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct) + 1 ? PETSC_TRUE : PETSC_FALSE;
62   PetscFunctionReturn(PETSC_SUCCESS);
63 }
64 
65 /*@
66   DMPlexGetSimplexOrBoxCells - Get the range of cells which are neither prisms nor ghost FV cells
67 
68   Input Parameters:
69 + dm     - The `DMPLEX` object
70 - height - The cell height in the Plex, 0 is the default
71 
72   Output Parameters:
73 + cStart - The first "normal" cell
74 - cEnd   - The upper bound on "normal" cells
75 
76   Level: developer
77 
78   Note:
79   This function requires that tensor cells are ordered last.
80 
81 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexConstructGhostCells()`, `DMPlexGetCellTypeStratum()`
82 @*/
83 PetscErrorCode DMPlexGetSimplexOrBoxCells(DM dm, PetscInt height, PetscInt *cStart, PetscInt *cEnd)
84 {
85   DMLabel         ctLabel;
86   IS              valueIS;
87   const PetscInt *ctypes;
88   PetscBool       found = PETSC_FALSE;
89   PetscInt        Nct, cS = PETSC_INT_MAX, cE = 0;
90 
91   PetscFunctionBegin;
92   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
93   PetscCall(DMLabelGetValueIS(ctLabel, &valueIS));
94   PetscCall(ISGetLocalSize(valueIS, &Nct));
95   PetscCall(ISGetIndices(valueIS, &ctypes));
96   for (PetscInt t = 0; t < Nct; ++t) {
97     const DMPolytopeType ct = (DMPolytopeType)ctypes[t];
98     PetscInt             ctS, ctE, ht;
99 
100     if (ct == DM_POLYTOPE_UNKNOWN) {
101       // If any cells are not typed, just use all cells
102       PetscCall(DMPlexGetHeightStratum(dm, PetscMax(height, 0), cStart, cEnd));
103       break;
104     }
105     if (DMPolytopeTypeIsHybrid(ct) || ct == DM_POLYTOPE_FV_GHOST) continue;
106     PetscCall(DMLabelGetStratumBounds(ctLabel, ct, &ctS, &ctE));
107     if (ctS >= ctE) continue;
108     // Check that a point has the right height
109     PetscCall(DMPlexGetPointHeight(dm, ctS, &ht));
110     if (ht != height) continue;
111     cS    = PetscMin(cS, ctS);
112     cE    = PetscMax(cE, ctE);
113     found = PETSC_TRUE;
114   }
115   if (!Nct || !found) cS = cE = 0;
116   PetscCall(ISDestroy(&valueIS));
117   // Reset label for fast lookup
118   PetscCall(DMLabelMakeAllInvalid_Internal(ctLabel));
119   if (cStart) *cStart = cS;
120   if (cEnd) *cEnd = cE;
121   PetscFunctionReturn(PETSC_SUCCESS);
122 }
123 
124 PetscErrorCode 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   PetscCallMPI(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   PetscCallMPI(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(dm, "Could not classify VTK output type of section field %" PetscInt_FMT " \"%s\"\n", field, fieldname));
252     } else {
253       PetscCall(PetscInfo(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         DMPolytopeType     ct;
430         PetscScalar       *coords = NULL, *a = NULL;
431         const PetscScalar *coords_arr;
432         PetscBool          isDG;
433         PetscInt           numCoords;
434         int                color[4] = {-1, -1, -1, -1};
435 
436         PetscCall(DMPlexGetCellType(dm, c, &ct));
437         PetscCall(DMPlexPointLocalRead(fdm, c, array, &a));
438         if (a) {
439           color[0] = PetscDrawRealToColor(PetscRealPart(a[comp]), vbound[0], vbound[1]);
440           color[1] = color[2] = color[3] = color[0];
441         } else {
442           PetscScalar *vals = NULL;
443           PetscInt     numVals, va;
444 
445           PetscCall(DMPlexVecGetClosure(fdm, NULL, fv, c, &numVals, &vals));
446           if (!numVals) {
447             PetscCall(DMPlexVecRestoreClosure(fdm, NULL, fv, c, &numVals, &vals));
448             continue;
449           }
450           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);
451           switch (numVals / Nc) {
452           case 1: /* P1 Clamped Segment Prism */
453           case 2: /* P1 Segment Prism, P2 Clamped Segment Prism */
454             PetscCheck(ct == DM_POLYTOPE_SEG_PRISM_TENSOR, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell should be a tensor segment, but it is a %s", DMPolytopeTypes[ct]);
455             for (va = 0; va < numVals / Nc; ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp]), vbound[0], vbound[1]);
456             break;
457           case 3: /* P1 Triangle */
458           case 4: /* P1 Quadrangle */
459             PetscCheck(ct == DM_POLYTOPE_TRIANGLE || ct == DM_POLYTOPE_QUADRILATERAL || ct == DM_POLYTOPE_SEG_PRISM_TENSOR, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell should be a triangle or quad, but it is a %s", DMPolytopeTypes[ct]);
460             for (va = 0; va < numVals / Nc; ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp]), vbound[0], vbound[1]);
461             break;
462           case 6: /* P2 Triangle */
463           case 8: /* P2 Quadrangle */
464             PetscCheck(ct == DM_POLYTOPE_TRIANGLE || ct == DM_POLYTOPE_QUADRILATERAL || ct == DM_POLYTOPE_SEG_PRISM_TENSOR, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell should be a triangle or quad, but it is a %s", DMPolytopeTypes[ct]);
465             for (va = 0; va < numVals / (Nc * 2); ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp + numVals / (Nc * 2)]), vbound[0], vbound[1]);
466             break;
467           default:
468             SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of values for cell closure %" PetscInt_FMT " cannot be handled", numVals / Nc);
469           }
470           PetscCall(DMPlexVecRestoreClosure(fdm, NULL, fv, c, &numVals, &vals));
471         }
472         PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
473         switch (numCoords) {
474         case 6:
475         case 12: /* Localized triangle */
476           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]));
477           break;
478         case 8:
479         case 16: /* Localized quadrilateral */
480           if (ct == DM_POLYTOPE_SEG_PRISM_TENSOR) {
481             PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscMax(color[0], color[1])));
482           } else {
483             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]));
484             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]));
485           }
486           break;
487         default:
488           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells with %" PetscInt_FMT " coordinates", numCoords);
489         }
490         PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
491       }
492       PetscCall(VecRestoreArrayRead(fv, &array));
493       PetscCall(PetscDrawFlush(draw));
494       PetscCall(PetscDrawPause(draw));
495       PetscCall(PetscDrawSave(draw));
496     }
497     if (Nf > 1) {
498       PetscCall(VecRestoreSubVector(v, fis, &fv));
499       PetscCall(ISDestroy(&fis));
500       PetscCall(DMDestroy(&fdm));
501     }
502   }
503   PetscFunctionReturn(PETSC_SUCCESS);
504 }
505 
506 static PetscErrorCode VecView_Plex_Local_Draw(Vec v, PetscViewer viewer)
507 {
508   DM        dm;
509   PetscDraw draw;
510   PetscInt  dim;
511   PetscBool isnull;
512 
513   PetscFunctionBegin;
514   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
515   PetscCall(PetscDrawIsNull(draw, &isnull));
516   if (isnull) PetscFunctionReturn(PETSC_SUCCESS);
517 
518   PetscCall(VecGetDM(v, &dm));
519   PetscCall(DMGetCoordinateDim(dm, &dim));
520   switch (dim) {
521   case 1:
522     PetscCall(VecView_Plex_Local_Draw_1D(v, viewer));
523     break;
524   case 2:
525     PetscCall(VecView_Plex_Local_Draw_2D(v, viewer));
526     break;
527   default:
528     SETERRQ(PetscObjectComm((PetscObject)v), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT ". Try PETSCVIEWERGLVIS", dim);
529   }
530   PetscFunctionReturn(PETSC_SUCCESS);
531 }
532 
533 static PetscErrorCode VecView_Plex_Local_VTK(Vec v, PetscViewer viewer)
534 {
535   DM                      dm;
536   Vec                     locv;
537   const char             *name;
538   PetscSection            section;
539   PetscInt                pStart, pEnd;
540   PetscInt                numFields;
541   PetscViewerVTKFieldType ft;
542 
543   PetscFunctionBegin;
544   PetscCall(VecGetDM(v, &dm));
545   PetscCall(DMCreateLocalVector(dm, &locv)); /* VTK viewer requires exclusive ownership of the vector */
546   PetscCall(PetscObjectGetName((PetscObject)v, &name));
547   PetscCall(PetscObjectSetName((PetscObject)locv, name));
548   PetscCall(VecCopy(v, locv));
549   PetscCall(DMGetLocalSection(dm, &section));
550   PetscCall(PetscSectionGetNumFields(section, &numFields));
551   if (!numFields) {
552     PetscCall(DMPlexGetFieldType_Internal(dm, section, PETSC_DETERMINE, &pStart, &pEnd, &ft));
553     PetscCall(PetscViewerVTKAddField(viewer, (PetscObject)dm, DMPlexVTKWriteAll, PETSC_DEFAULT, ft, PETSC_TRUE, (PetscObject)locv));
554   } else {
555     PetscInt f;
556 
557     for (f = 0; f < numFields; f++) {
558       PetscCall(DMPlexGetFieldType_Internal(dm, section, f, &pStart, &pEnd, &ft));
559       if (ft == PETSC_VTK_INVALID) continue;
560       PetscCall(PetscObjectReference((PetscObject)locv));
561       PetscCall(PetscViewerVTKAddField(viewer, (PetscObject)dm, DMPlexVTKWriteAll, f, ft, PETSC_TRUE, (PetscObject)locv));
562     }
563     PetscCall(VecDestroy(&locv));
564   }
565   PetscFunctionReturn(PETSC_SUCCESS);
566 }
567 
568 PetscErrorCode VecView_Plex_Local(Vec v, PetscViewer viewer)
569 {
570   DM        dm;
571   PetscBool isvtk, ishdf5, isdraw, isglvis, iscgns;
572 
573   PetscFunctionBegin;
574   PetscCall(VecGetDM(v, &dm));
575   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
576   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
577   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
578   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
579   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
580   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
581   if (isvtk || ishdf5 || isdraw || isglvis || iscgns) {
582     PetscInt    i, numFields;
583     PetscObject fe;
584     PetscBool   fem  = PETSC_FALSE;
585     Vec         locv = v;
586     const char *name;
587     PetscInt    step;
588     PetscReal   time;
589 
590     PetscCall(DMGetNumFields(dm, &numFields));
591     for (i = 0; i < numFields; i++) {
592       PetscCall(DMGetField(dm, i, NULL, &fe));
593       if (fe->classid == PETSCFE_CLASSID) {
594         fem = PETSC_TRUE;
595         break;
596       }
597     }
598     if (fem) {
599       PetscObject isZero;
600 
601       PetscCall(DMGetLocalVector(dm, &locv));
602       PetscCall(PetscObjectGetName((PetscObject)v, &name));
603       PetscCall(PetscObjectSetName((PetscObject)locv, name));
604       PetscCall(PetscObjectQuery((PetscObject)v, "__Vec_bc_zero__", &isZero));
605       PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", isZero));
606       PetscCall(VecCopy(v, locv));
607       PetscCall(DMGetOutputSequenceNumber(dm, NULL, &time));
608       PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_TRUE, locv, time, NULL, NULL, NULL));
609     }
610     if (isvtk) {
611       PetscCall(VecView_Plex_Local_VTK(locv, viewer));
612     } else if (ishdf5) {
613 #if defined(PETSC_HAVE_HDF5)
614       PetscCall(VecView_Plex_Local_HDF5_Internal(locv, viewer));
615 #else
616       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
617 #endif
618     } else if (isdraw) {
619       PetscCall(VecView_Plex_Local_Draw(locv, viewer));
620     } else if (isglvis) {
621       PetscCall(DMGetOutputSequenceNumber(dm, &step, NULL));
622       PetscCall(PetscViewerGLVisSetSnapId(viewer, step));
623       PetscCall(VecView_GLVis(locv, viewer));
624     } else if (iscgns) {
625 #if defined(PETSC_HAVE_CGNS)
626       PetscCall(VecView_Plex_Local_CGNS(locv, viewer));
627 #else
628       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "CGNS not supported in this build.\nPlease reconfigure using --download-cgns");
629 #endif
630     }
631     if (fem) {
632       PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", NULL));
633       PetscCall(DMRestoreLocalVector(dm, &locv));
634     }
635   } else {
636     PetscBool isseq;
637 
638     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
639     if (isseq) PetscCall(VecView_Seq(v, viewer));
640     else PetscCall(VecView_MPI(v, viewer));
641   }
642   PetscFunctionReturn(PETSC_SUCCESS);
643 }
644 
645 PetscErrorCode VecView_Plex(Vec v, PetscViewer viewer)
646 {
647   DM        dm;
648   PetscBool isvtk, ishdf5, isdraw, isglvis, isexodusii, iscgns;
649 
650   PetscFunctionBegin;
651   PetscCall(VecGetDM(v, &dm));
652   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
653   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
654   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
655   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
656   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
657   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
658   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodusii));
659   if (isvtk || isdraw || isglvis || iscgns) {
660     Vec         locv;
661     PetscObject isZero;
662     const char *name;
663 
664     PetscCall(DMGetLocalVector(dm, &locv));
665     PetscCall(PetscObjectGetName((PetscObject)v, &name));
666     PetscCall(PetscObjectSetName((PetscObject)locv, name));
667     PetscCall(DMGlobalToLocalBegin(dm, v, INSERT_VALUES, locv));
668     PetscCall(DMGlobalToLocalEnd(dm, v, INSERT_VALUES, locv));
669     PetscCall(PetscObjectQuery((PetscObject)v, "__Vec_bc_zero__", &isZero));
670     PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", isZero));
671     PetscCall(VecView_Plex_Local(locv, viewer));
672     PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", NULL));
673     PetscCall(DMRestoreLocalVector(dm, &locv));
674   } else if (ishdf5) {
675 #if defined(PETSC_HAVE_HDF5)
676     PetscCall(VecView_Plex_HDF5_Internal(v, viewer));
677 #else
678     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
679 #endif
680   } else if (isexodusii) {
681 #if defined(PETSC_HAVE_EXODUSII)
682     PetscCall(VecView_PlexExodusII_Internal(v, viewer));
683 #else
684     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
685 #endif
686   } else {
687     PetscBool isseq;
688 
689     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
690     if (isseq) PetscCall(VecView_Seq(v, viewer));
691     else PetscCall(VecView_MPI(v, viewer));
692   }
693   PetscFunctionReturn(PETSC_SUCCESS);
694 }
695 
696 PetscErrorCode VecView_Plex_Native(Vec originalv, PetscViewer viewer)
697 {
698   DM                dm;
699   MPI_Comm          comm;
700   PetscViewerFormat format;
701   Vec               v;
702   PetscBool         isvtk, ishdf5;
703 
704   PetscFunctionBegin;
705   PetscCall(VecGetDM(originalv, &dm));
706   PetscCall(PetscObjectGetComm((PetscObject)originalv, &comm));
707   PetscCheck(dm, comm, PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
708   PetscCall(PetscViewerGetFormat(viewer, &format));
709   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
710   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
711   if (format == PETSC_VIEWER_NATIVE) {
712     /* Natural ordering is the common case for DMDA, NATIVE means plain vector, for PLEX is the opposite */
713     /* this need a better fix */
714     if (dm->useNatural) {
715       if (dm->sfNatural) {
716         const char *vecname;
717         PetscInt    n, nroots;
718 
719         PetscCall(VecGetLocalSize(originalv, &n));
720         PetscCall(PetscSFGetGraph(dm->sfNatural, &nroots, NULL, NULL, NULL));
721         if (n == nroots) {
722           PetscCall(DMPlexCreateNaturalVector(dm, &v));
723           PetscCall(DMPlexGlobalToNaturalBegin(dm, originalv, v));
724           PetscCall(DMPlexGlobalToNaturalEnd(dm, originalv, v));
725           PetscCall(PetscObjectGetName((PetscObject)originalv, &vecname));
726           PetscCall(PetscObjectSetName((PetscObject)v, vecname));
727         } else SETERRQ(comm, PETSC_ERR_ARG_WRONG, "DM global to natural SF only handles global vectors");
728       } else SETERRQ(comm, PETSC_ERR_ARG_WRONGSTATE, "DM global to natural SF was not created");
729     } else v = originalv;
730   } else v = originalv;
731 
732   if (ishdf5) {
733 #if defined(PETSC_HAVE_HDF5)
734     PetscCall(VecView_Plex_HDF5_Native_Internal(v, viewer));
735 #else
736     SETERRQ(comm, PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
737 #endif
738   } else if (isvtk) {
739     SETERRQ(comm, PETSC_ERR_SUP, "VTK format does not support viewing in natural order. Please switch to HDF5.");
740   } else {
741     PetscBool isseq;
742 
743     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
744     if (isseq) PetscCall(VecView_Seq(v, viewer));
745     else PetscCall(VecView_MPI(v, viewer));
746   }
747   if (v != originalv) PetscCall(VecDestroy(&v));
748   PetscFunctionReturn(PETSC_SUCCESS);
749 }
750 
751 PetscErrorCode VecLoad_Plex_Local(Vec v, PetscViewer viewer)
752 {
753   DM        dm;
754   PetscBool ishdf5;
755 
756   PetscFunctionBegin;
757   PetscCall(VecGetDM(v, &dm));
758   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
759   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
760   if (ishdf5) {
761     DM          dmBC;
762     Vec         gv;
763     const char *name;
764 
765     PetscCall(DMGetOutputDM(dm, &dmBC));
766     PetscCall(DMGetGlobalVector(dmBC, &gv));
767     PetscCall(PetscObjectGetName((PetscObject)v, &name));
768     PetscCall(PetscObjectSetName((PetscObject)gv, name));
769     PetscCall(VecLoad_Default(gv, viewer));
770     PetscCall(DMGlobalToLocalBegin(dmBC, gv, INSERT_VALUES, v));
771     PetscCall(DMGlobalToLocalEnd(dmBC, gv, INSERT_VALUES, v));
772     PetscCall(DMRestoreGlobalVector(dmBC, &gv));
773   } else PetscCall(VecLoad_Default(v, viewer));
774   PetscFunctionReturn(PETSC_SUCCESS);
775 }
776 
777 PetscErrorCode VecLoad_Plex(Vec v, PetscViewer viewer)
778 {
779   DM        dm;
780   PetscBool ishdf5, isexodusii, iscgns;
781 
782   PetscFunctionBegin;
783   PetscCall(VecGetDM(v, &dm));
784   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
785   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
786   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodusii));
787   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
788   if (ishdf5) {
789 #if defined(PETSC_HAVE_HDF5)
790     PetscCall(VecLoad_Plex_HDF5_Internal(v, viewer));
791 #else
792     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
793 #endif
794   } else if (isexodusii) {
795 #if defined(PETSC_HAVE_EXODUSII)
796     PetscCall(VecLoad_PlexExodusII_Internal(v, viewer));
797 #else
798     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
799 #endif
800   } else if (iscgns) {
801 #if defined(PETSC_HAVE_CGNS)
802     PetscCall(VecLoad_Plex_CGNS_Internal(v, viewer));
803 #else
804     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "CGNS not supported in this build.\nPlease reconfigure using --download-cgns");
805 #endif
806   } else PetscCall(VecLoad_Default(v, viewer));
807   PetscFunctionReturn(PETSC_SUCCESS);
808 }
809 
810 PetscErrorCode VecLoad_Plex_Native(Vec originalv, PetscViewer viewer)
811 {
812   DM                dm;
813   PetscViewerFormat format;
814   PetscBool         ishdf5;
815 
816   PetscFunctionBegin;
817   PetscCall(VecGetDM(originalv, &dm));
818   PetscCheck(dm, PetscObjectComm((PetscObject)originalv), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
819   PetscCall(PetscViewerGetFormat(viewer, &format));
820   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
821   if (format == PETSC_VIEWER_NATIVE) {
822     if (dm->useNatural) {
823       if (dm->sfNatural) {
824         if (ishdf5) {
825 #if defined(PETSC_HAVE_HDF5)
826           Vec         v;
827           const char *vecname;
828 
829           PetscCall(DMPlexCreateNaturalVector(dm, &v));
830           PetscCall(PetscObjectGetName((PetscObject)originalv, &vecname));
831           PetscCall(PetscObjectSetName((PetscObject)v, vecname));
832           PetscCall(VecLoad_Plex_HDF5_Native_Internal(v, viewer));
833           PetscCall(DMPlexNaturalToGlobalBegin(dm, v, originalv));
834           PetscCall(DMPlexNaturalToGlobalEnd(dm, v, originalv));
835           PetscCall(VecDestroy(&v));
836 #else
837           SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
838 #endif
839         } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Reading in natural order is not supported for anything but HDF5.");
840       }
841     } else PetscCall(VecLoad_Default(originalv, viewer));
842   }
843   PetscFunctionReturn(PETSC_SUCCESS);
844 }
845 
846 PETSC_UNUSED static PetscErrorCode DMPlexView_Ascii_Geometry(DM dm, PetscViewer viewer)
847 {
848   PetscSection       coordSection;
849   Vec                coordinates;
850   DMLabel            depthLabel, celltypeLabel;
851   const char        *name[4];
852   const PetscScalar *a;
853   PetscInt           dim, pStart, pEnd, cStart, cEnd, c;
854 
855   PetscFunctionBegin;
856   PetscCall(DMGetDimension(dm, &dim));
857   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
858   PetscCall(DMGetCoordinateSection(dm, &coordSection));
859   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
860   PetscCall(DMPlexGetCellTypeLabel(dm, &celltypeLabel));
861   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
862   PetscCall(PetscSectionGetChart(coordSection, &pStart, &pEnd));
863   PetscCall(VecGetArrayRead(coordinates, &a));
864   name[0]       = "vertex";
865   name[1]       = "edge";
866   name[dim - 1] = "face";
867   name[dim]     = "cell";
868   for (c = cStart; c < cEnd; ++c) {
869     PetscInt *closure = NULL;
870     PetscInt  closureSize, cl, ct;
871 
872     PetscCall(DMLabelGetValue(celltypeLabel, c, &ct));
873     PetscCall(PetscViewerASCIIPrintf(viewer, "Geometry for cell %" PetscInt_FMT " polytope type %s:\n", c, DMPolytopeTypes[ct]));
874     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
875     PetscCall(PetscViewerASCIIPushTab(viewer));
876     for (cl = 0; cl < closureSize * 2; cl += 2) {
877       PetscInt point = closure[cl], depth, dof, off, d, p;
878 
879       if ((point < pStart) || (point >= pEnd)) continue;
880       PetscCall(PetscSectionGetDof(coordSection, point, &dof));
881       if (!dof) continue;
882       PetscCall(DMLabelGetValue(depthLabel, point, &depth));
883       PetscCall(PetscSectionGetOffset(coordSection, point, &off));
884       PetscCall(PetscViewerASCIIPrintf(viewer, "%s %" PetscInt_FMT " coords:", name[depth], point));
885       for (p = 0; p < dof / dim; ++p) {
886         PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
887         for (d = 0; d < dim; ++d) {
888           if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
889           PetscCall(PetscViewerASCIIPrintf(viewer, "%g", (double)PetscRealPart(a[off + p * dim + d])));
890         }
891         PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
892       }
893       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
894     }
895     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
896     PetscCall(PetscViewerASCIIPopTab(viewer));
897   }
898   PetscCall(VecRestoreArrayRead(coordinates, &a));
899   PetscFunctionReturn(PETSC_SUCCESS);
900 }
901 
902 typedef enum {
903   CS_CARTESIAN,
904   CS_POLAR,
905   CS_CYLINDRICAL,
906   CS_SPHERICAL
907 } CoordSystem;
908 const char *CoordSystems[] = {"cartesian", "polar", "cylindrical", "spherical", "CoordSystem", "CS_", NULL};
909 
910 static PetscErrorCode DMPlexView_Ascii_Coordinates(PetscViewer viewer, CoordSystem cs, PetscInt dim, const PetscScalar x[])
911 {
912   PetscInt i;
913 
914   PetscFunctionBegin;
915   if (dim > 3) {
916     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double)PetscRealPart(x[i])));
917   } else {
918     PetscReal coords[3], trcoords[3] = {0., 0., 0.};
919 
920     for (i = 0; i < dim; ++i) coords[i] = PetscRealPart(x[i]);
921     switch (cs) {
922     case CS_CARTESIAN:
923       for (i = 0; i < dim; ++i) trcoords[i] = coords[i];
924       break;
925     case CS_POLAR:
926       PetscCheck(dim == 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Polar coordinates are for 2 dimension, not %" PetscInt_FMT, dim);
927       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
928       trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
929       break;
930     case CS_CYLINDRICAL:
931       PetscCheck(dim == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cylindrical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
932       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
933       trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
934       trcoords[2] = coords[2];
935       break;
936     case CS_SPHERICAL:
937       PetscCheck(dim == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Spherical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
938       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]) + PetscSqr(coords[2]));
939       trcoords[1] = PetscAtan2Real(PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1])), coords[2]);
940       trcoords[2] = PetscAtan2Real(coords[1], coords[0]);
941       break;
942     }
943     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double)trcoords[i]));
944   }
945   PetscFunctionReturn(PETSC_SUCCESS);
946 }
947 
948 static PetscErrorCode DMPlexView_Ascii(DM dm, PetscViewer viewer)
949 {
950   DM_Plex          *mesh = (DM_Plex *)dm->data;
951   DM                cdm, cdmCell;
952   PetscSection      coordSection, coordSectionCell;
953   Vec               coordinates, coordinatesCell;
954   PetscViewerFormat format;
955 
956   PetscFunctionBegin;
957   PetscCall(PetscViewerGetFormat(viewer, &format));
958   if (format == PETSC_VIEWER_ASCII_INFO_DETAIL) {
959     const char *name;
960     PetscInt    dim, cellHeight, maxConeSize, maxSupportSize;
961     PetscInt    pStart, pEnd, p, numLabels, l;
962     PetscMPIInt rank, size;
963 
964     PetscCall(DMGetCoordinateDM(dm, &cdm));
965     PetscCall(DMGetCoordinateSection(dm, &coordSection));
966     PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
967     PetscCall(DMGetCellCoordinateDM(dm, &cdmCell));
968     PetscCall(DMGetCellCoordinateSection(dm, &coordSectionCell));
969     PetscCall(DMGetCellCoordinatesLocal(dm, &coordinatesCell));
970     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
971     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
972     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
973     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
974     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
975     PetscCall(DMGetDimension(dm, &dim));
976     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
977     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
978     else PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
979     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
980     PetscCall(PetscViewerASCIIPrintf(viewer, "Supports:\n"));
981     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
982     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max support size: %" PetscInt_FMT "\n", rank, maxSupportSize));
983     for (p = pStart; p < pEnd; ++p) {
984       PetscInt dof, off, s;
985 
986       PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
987       PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
988       for (s = off; s < off + dof; ++s) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d]: %" PetscInt_FMT " ----> %" PetscInt_FMT "\n", rank, p, mesh->supports[s]));
989     }
990     PetscCall(PetscViewerFlush(viewer));
991     PetscCall(PetscViewerASCIIPrintf(viewer, "Cones:\n"));
992     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max cone size: %" PetscInt_FMT "\n", rank, maxConeSize));
993     for (p = pStart; p < pEnd; ++p) {
994       PetscInt dof, off, c;
995 
996       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
997       PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
998       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]));
999     }
1000     PetscCall(PetscViewerFlush(viewer));
1001     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
1002     if (coordSection && coordinates) {
1003       CoordSystem        cs = CS_CARTESIAN;
1004       const PetscScalar *array, *arrayCell = NULL;
1005       PetscInt           Nf, Nc, pvStart, pvEnd, pcStart = PETSC_INT_MAX, pcEnd = PETSC_INT_MIN, pStart, pEnd, p;
1006       PetscMPIInt        rank;
1007       const char        *name;
1008 
1009       PetscCall(PetscOptionsGetEnum(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_coord_system", CoordSystems, (PetscEnum *)&cs, NULL));
1010       PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)viewer), &rank));
1011       PetscCall(PetscSectionGetNumFields(coordSection, &Nf));
1012       PetscCheck(Nf == 1, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Coordinate section should have 1 field, not %" PetscInt_FMT, Nf);
1013       PetscCall(PetscSectionGetFieldComponents(coordSection, 0, &Nc));
1014       PetscCall(PetscSectionGetChart(coordSection, &pvStart, &pvEnd));
1015       if (coordSectionCell) PetscCall(PetscSectionGetChart(coordSectionCell, &pcStart, &pcEnd));
1016       pStart = PetscMin(pvStart, pcStart);
1017       pEnd   = PetscMax(pvEnd, pcEnd);
1018       PetscCall(PetscObjectGetName((PetscObject)coordinates, &name));
1019       PetscCall(PetscViewerASCIIPrintf(viewer, "%s with %" PetscInt_FMT " fields\n", name, Nf));
1020       PetscCall(PetscViewerASCIIPrintf(viewer, "  field 0 with %" PetscInt_FMT " components\n", Nc));
1021       if (cs != CS_CARTESIAN) PetscCall(PetscViewerASCIIPrintf(viewer, "  output coordinate system: %s\n", CoordSystems[cs]));
1022 
1023       PetscCall(VecGetArrayRead(coordinates, &array));
1024       if (coordinatesCell) PetscCall(VecGetArrayRead(coordinatesCell, &arrayCell));
1025       PetscCall(PetscViewerASCIIPushSynchronized(viewer));
1026       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "Process %d:\n", rank));
1027       for (p = pStart; p < pEnd; ++p) {
1028         PetscInt dof, off;
1029 
1030         if (p >= pvStart && p < pvEnd) {
1031           PetscCall(PetscSectionGetDof(coordSection, p, &dof));
1032           PetscCall(PetscSectionGetOffset(coordSection, p, &off));
1033           if (dof) {
1034             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dof %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
1035             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &array[off]));
1036             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
1037           }
1038         }
1039         if (cdmCell && p >= pcStart && p < pcEnd) {
1040           PetscCall(PetscSectionGetDof(coordSectionCell, p, &dof));
1041           PetscCall(PetscSectionGetOffset(coordSectionCell, p, &off));
1042           if (dof) {
1043             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dof %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
1044             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &arrayCell[off]));
1045             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
1046           }
1047         }
1048       }
1049       PetscCall(PetscViewerFlush(viewer));
1050       PetscCall(PetscViewerASCIIPopSynchronized(viewer));
1051       PetscCall(VecRestoreArrayRead(coordinates, &array));
1052       if (coordinatesCell) PetscCall(VecRestoreArrayRead(coordinatesCell, &arrayCell));
1053     }
1054     PetscCall(DMGetNumLabels(dm, &numLabels));
1055     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
1056     for (l = 0; l < numLabels; ++l) {
1057       DMLabel     label;
1058       PetscBool   isdepth;
1059       const char *name;
1060 
1061       PetscCall(DMGetLabelName(dm, l, &name));
1062       PetscCall(PetscStrcmp(name, "depth", &isdepth));
1063       if (isdepth) continue;
1064       PetscCall(DMGetLabel(dm, name, &label));
1065       PetscCall(DMLabelView(label, viewer));
1066     }
1067     if (size > 1) {
1068       PetscSF sf;
1069 
1070       PetscCall(DMGetPointSF(dm, &sf));
1071       PetscCall(PetscSFView(sf, viewer));
1072     }
1073     if (mesh->periodic.face_sfs)
1074       for (PetscInt i = 0; i < mesh->periodic.num_face_sfs; i++) PetscCall(PetscSFView(mesh->periodic.face_sfs[i], viewer));
1075     PetscCall(PetscViewerFlush(viewer));
1076   } else if (format == PETSC_VIEWER_ASCII_LATEX) {
1077     const char  *name, *color;
1078     const char  *defcolors[3]  = {"gray", "orange", "green"};
1079     const char  *deflcolors[4] = {"blue", "cyan", "red", "magenta"};
1080     char         lname[PETSC_MAX_PATH_LEN];
1081     PetscReal    scale      = 2.0;
1082     PetscReal    tikzscale  = 1.0;
1083     PetscBool    useNumbers = PETSC_TRUE, drawNumbers[4], drawColors[4], useLabels, useColors, plotEdges, drawHasse = PETSC_FALSE;
1084     double       tcoords[3];
1085     PetscScalar *coords;
1086     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;
1087     PetscMPIInt  rank, size;
1088     char       **names, **colors, **lcolors;
1089     PetscBool    flg, lflg;
1090     PetscBT      wp = NULL;
1091     PetscInt     pEnd, pStart;
1092 
1093     PetscCall(DMGetCoordinateDM(dm, &cdm));
1094     PetscCall(DMGetCoordinateSection(dm, &coordSection));
1095     PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1096     PetscCall(DMGetCellCoordinateDM(dm, &cdmCell));
1097     PetscCall(DMGetCellCoordinateSection(dm, &coordSectionCell));
1098     PetscCall(DMGetCellCoordinatesLocal(dm, &coordinatesCell));
1099     PetscCall(DMGetDimension(dm, &dim));
1100     PetscCall(DMPlexGetDepth(dm, &depth));
1101     PetscCall(DMGetNumLabels(dm, &numLabels));
1102     numLabels  = PetscMax(numLabels, 10);
1103     numColors  = 10;
1104     numLColors = 10;
1105     PetscCall(PetscCalloc3(numLabels, &names, numColors, &colors, numLColors, &lcolors));
1106     PetscCall(PetscOptionsGetReal(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_scale", &scale, NULL));
1107     PetscCall(PetscOptionsGetReal(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_tikzscale", &tikzscale, NULL));
1108     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_numbers", &useNumbers, NULL));
1109     for (d = 0; d < 4; ++d) drawNumbers[d] = useNumbers;
1110     for (d = 0; d < 4; ++d) drawColors[d] = PETSC_TRUE;
1111     n = 4;
1112     PetscCall(PetscOptionsGetBoolArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_numbers_depth", drawNumbers, &n, &flg));
1113     PetscCheck(!flg || n == dim + 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim + 1);
1114     n = 4;
1115     PetscCall(PetscOptionsGetBoolArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_colors_depth", drawColors, &n, &flg));
1116     PetscCheck(!flg || n == dim + 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim + 1);
1117     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_labels", names, &numLabels, &useLabels));
1118     if (!useLabels) numLabels = 0;
1119     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_colors", colors, &numColors, &useColors));
1120     if (!useColors) {
1121       numColors = 3;
1122       for (c = 0; c < numColors; ++c) PetscCall(PetscStrallocpy(defcolors[c], &colors[c]));
1123     }
1124     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_lcolors", lcolors, &numLColors, &useColors));
1125     if (!useColors) {
1126       numLColors = 4;
1127       for (c = 0; c < numLColors; ++c) PetscCall(PetscStrallocpy(deflcolors[c], &lcolors[c]));
1128     }
1129     PetscCall(PetscOptionsGetString(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_label_filter", lname, sizeof(lname), &lflg));
1130     plotEdges = (PetscBool)(depth > 1 && drawNumbers[1] && dim < 3);
1131     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_edges", &plotEdges, &flg));
1132     PetscCheck(!flg || !plotEdges || depth >= dim, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Mesh must be interpolated");
1133     if (depth < dim) plotEdges = PETSC_FALSE;
1134     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_hasse", &drawHasse, NULL));
1135 
1136     /* filter points with labelvalue != labeldefaultvalue */
1137     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
1138     PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1139     PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
1140     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1141     PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd));
1142     if (lflg) {
1143       DMLabel lbl;
1144 
1145       PetscCall(DMGetLabel(dm, lname, &lbl));
1146       if (lbl) {
1147         PetscInt val, defval;
1148 
1149         PetscCall(DMLabelGetDefaultValue(lbl, &defval));
1150         PetscCall(PetscBTCreate(pEnd - pStart, &wp));
1151         for (c = pStart; c < pEnd; c++) {
1152           PetscInt *closure = NULL;
1153           PetscInt  closureSize;
1154 
1155           PetscCall(DMLabelGetValue(lbl, c, &val));
1156           if (val == defval) continue;
1157 
1158           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1159           for (p = 0; p < closureSize * 2; p += 2) PetscCall(PetscBTSet(wp, closure[p] - pStart));
1160           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1161         }
1162       }
1163     }
1164 
1165     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1166     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
1167     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1168     PetscCall(PetscViewerASCIIPrintf(viewer, "\
1169 \\documentclass[tikz]{standalone}\n\n\
1170 \\usepackage{pgflibraryshapes}\n\
1171 \\usetikzlibrary{backgrounds}\n\
1172 \\usetikzlibrary{arrows}\n\
1173 \\begin{document}\n"));
1174     if (size > 1) {
1175       PetscCall(PetscViewerASCIIPrintf(viewer, "%s for process ", name));
1176       for (p = 0; p < size; ++p) {
1177         if (p) PetscCall(PetscViewerASCIIPrintf(viewer, (p == size - 1) ? ", and " : ", "));
1178         PetscCall(PetscViewerASCIIPrintf(viewer, "{\\textcolor{%s}%" PetscInt_FMT "}", colors[p % numColors], p));
1179       }
1180       PetscCall(PetscViewerASCIIPrintf(viewer, ".\n\n\n"));
1181     }
1182     if (drawHasse) {
1183       PetscInt maxStratum = PetscMax(vEnd - vStart, PetscMax(eEnd - eStart, PetscMax(fEnd - fStart, cEnd - cStart)));
1184 
1185       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vStart}{%" PetscInt_FMT "}\n", vStart));
1186       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vEnd}{%" PetscInt_FMT "}\n", vEnd - 1));
1187       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numVertices}{%" PetscInt_FMT "}\n", vEnd - vStart));
1188       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vShift}{%.2f}\n", 3 + (maxStratum - (vEnd - vStart)) / 2.));
1189       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eStart}{%" PetscInt_FMT "}\n", eStart));
1190       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eEnd}{%" PetscInt_FMT "}\n", eEnd - 1));
1191       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eShift}{%.2f}\n", 3 + (maxStratum - (eEnd - eStart)) / 2.));
1192       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numEdges}{%" PetscInt_FMT "}\n", eEnd - eStart));
1193       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\fStart}{%" PetscInt_FMT "}\n", fStart));
1194       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\fEnd}{%" PetscInt_FMT "}\n", fEnd - 1));
1195       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\fShift}{%.2f}\n", 3 + (maxStratum - (fEnd - fStart)) / 2.));
1196       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numFaces}{%" PetscInt_FMT "}\n", fEnd - fStart));
1197       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cStart}{%" PetscInt_FMT "}\n", cStart));
1198       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cEnd}{%" PetscInt_FMT "}\n", cEnd - 1));
1199       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numCells}{%" PetscInt_FMT "}\n", cEnd - cStart));
1200       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cShift}{%.2f}\n", 3 + (maxStratum - (cEnd - cStart)) / 2.));
1201     }
1202     PetscCall(PetscViewerASCIIPrintf(viewer, "\\begin{tikzpicture}[scale = %g,font=\\fontsize{8}{8}\\selectfont]\n", (double)tikzscale));
1203 
1204     /* Plot vertices */
1205     PetscCall(VecGetArray(coordinates, &coords));
1206     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
1207     for (v = vStart; v < vEnd; ++v) {
1208       PetscInt  off, dof, d;
1209       PetscBool isLabeled = PETSC_FALSE;
1210 
1211       if (wp && !PetscBTLookup(wp, v - pStart)) continue;
1212       PetscCall(PetscSectionGetDof(coordSection, v, &dof));
1213       PetscCall(PetscSectionGetOffset(coordSection, v, &off));
1214       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1215       PetscCheck(dof <= 3, PETSC_COMM_SELF, PETSC_ERR_PLIB, "coordSection vertex %" PetscInt_FMT " has dof %" PetscInt_FMT " > 3", v, dof);
1216       for (d = 0; d < dof; ++d) {
1217         tcoords[d] = (double)(scale * PetscRealPart(coords[off + d]));
1218         tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1219       }
1220       /* Rotate coordinates since PGF makes z point out of the page instead of up */
1221       if (dim == 3) {
1222         PetscReal tmp = tcoords[1];
1223         tcoords[1]    = tcoords[2];
1224         tcoords[2]    = -tmp;
1225       }
1226       for (d = 0; d < dof; ++d) {
1227         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1228         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", tcoords[d]));
1229       }
1230       if (drawHasse) color = colors[0 % numColors];
1231       else color = colors[rank % numColors];
1232       for (l = 0; l < numLabels; ++l) {
1233         PetscInt val;
1234         PetscCall(DMGetLabelValue(dm, names[l], v, &val));
1235         if (val >= 0) {
1236           color     = lcolors[l % numLColors];
1237           isLabeled = PETSC_TRUE;
1238           break;
1239         }
1240       }
1241       if (drawNumbers[0]) {
1242         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", v, rank, color, v));
1243       } else if (drawColors[0]) {
1244         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", v, rank, !isLabeled ? 1 : 2, color));
1245       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", v, rank));
1246     }
1247     PetscCall(VecRestoreArray(coordinates, &coords));
1248     PetscCall(PetscViewerFlush(viewer));
1249     /* Plot edges */
1250     if (plotEdges) {
1251       PetscCall(VecGetArray(coordinates, &coords));
1252       PetscCall(PetscViewerASCIIPrintf(viewer, "\\path\n"));
1253       for (e = eStart; e < eEnd; ++e) {
1254         const PetscInt *cone;
1255         PetscInt        coneSize, offA, offB, dof, d;
1256 
1257         if (wp && !PetscBTLookup(wp, e - pStart)) continue;
1258         PetscCall(DMPlexGetConeSize(dm, e, &coneSize));
1259         PetscCheck(coneSize == 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Edge %" PetscInt_FMT " cone should have two vertices, not %" PetscInt_FMT, e, coneSize);
1260         PetscCall(DMPlexGetCone(dm, e, &cone));
1261         PetscCall(PetscSectionGetDof(coordSection, cone[0], &dof));
1262         PetscCall(PetscSectionGetOffset(coordSection, cone[0], &offA));
1263         PetscCall(PetscSectionGetOffset(coordSection, cone[1], &offB));
1264         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "("));
1265         for (d = 0; d < dof; ++d) {
1266           tcoords[d] = (double)(scale * PetscRealPart(coords[offA + d] + coords[offB + d]) / 2);
1267           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1268         }
1269         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1270         if (dim == 3) {
1271           PetscReal tmp = tcoords[1];
1272           tcoords[1]    = tcoords[2];
1273           tcoords[2]    = -tmp;
1274         }
1275         for (d = 0; d < dof; ++d) {
1276           if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1277           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", tcoords[d]));
1278         }
1279         if (drawHasse) color = colors[1 % numColors];
1280         else color = colors[rank % numColors];
1281         for (l = 0; l < numLabels; ++l) {
1282           PetscInt val;
1283           PetscCall(DMGetLabelValue(dm, names[l], e, &val));
1284           if (val >= 0) {
1285             color = lcolors[l % numLColors];
1286             break;
1287           }
1288         }
1289         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "} --\n", e, rank, color, e));
1290       }
1291       PetscCall(VecRestoreArray(coordinates, &coords));
1292       PetscCall(PetscViewerFlush(viewer));
1293       PetscCall(PetscViewerASCIIPrintf(viewer, "(0,0);\n"));
1294     }
1295     /* Plot cells */
1296     if (dim == 3 || !drawNumbers[1]) {
1297       for (e = eStart; e < eEnd; ++e) {
1298         const PetscInt *cone;
1299 
1300         if (wp && !PetscBTLookup(wp, e - pStart)) continue;
1301         color = colors[rank % numColors];
1302         for (l = 0; l < numLabels; ++l) {
1303           PetscInt val;
1304           PetscCall(DMGetLabelValue(dm, names[l], e, &val));
1305           if (val >= 0) {
1306             color = lcolors[l % numLColors];
1307             break;
1308           }
1309         }
1310         PetscCall(DMPlexGetCone(dm, e, &cone));
1311         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", color, cone[0], rank, cone[1], rank));
1312       }
1313     } else {
1314       DMPolytopeType ct;
1315 
1316       /* Drawing a 2D polygon */
1317       for (c = cStart; c < cEnd; ++c) {
1318         if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1319         PetscCall(DMPlexGetCellType(dm, c, &ct));
1320         if (DMPolytopeTypeIsHybrid(ct)) {
1321           const PetscInt *cone;
1322           PetscInt        coneSize, e;
1323 
1324           PetscCall(DMPlexGetCone(dm, c, &cone));
1325           PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
1326           for (e = 0; e < coneSize; ++e) {
1327             const PetscInt *econe;
1328 
1329             PetscCall(DMPlexGetCone(dm, cone[e], &econe));
1330             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));
1331           }
1332         } else {
1333           PetscInt *closure = NULL;
1334           PetscInt  closureSize, Nv = 0, v;
1335 
1336           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1337           for (p = 0; p < closureSize * 2; p += 2) {
1338             const PetscInt point = closure[p];
1339 
1340             if ((point >= vStart) && (point < vEnd)) closure[Nv++] = point;
1341           }
1342           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] ", colors[rank % numColors]));
1343           for (v = 0; v <= Nv; ++v) {
1344             const PetscInt vertex = closure[v % Nv];
1345 
1346             if (v > 0) {
1347               if (plotEdges) {
1348                 const PetscInt *edge;
1349                 PetscInt        endpoints[2], ne;
1350 
1351                 endpoints[0] = closure[v - 1];
1352                 endpoints[1] = vertex;
1353                 PetscCall(DMPlexGetJoin(dm, 2, endpoints, &ne, &edge));
1354                 PetscCheck(ne == 1, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Could not find edge for vertices %" PetscInt_FMT ", %" PetscInt_FMT, endpoints[0], endpoints[1]);
1355                 PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- (%" PetscInt_FMT "_%d) -- ", edge[0], rank));
1356                 PetscCall(DMPlexRestoreJoin(dm, 2, endpoints, &ne, &edge));
1357               } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- "));
1358             }
1359             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "(%" PetscInt_FMT "_%d)", vertex, rank));
1360           }
1361           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ";\n"));
1362           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1363         }
1364       }
1365     }
1366     for (c = cStart; c < cEnd; ++c) {
1367       double             ccoords[3] = {0.0, 0.0, 0.0};
1368       PetscBool          isLabeled  = PETSC_FALSE;
1369       PetscScalar       *cellCoords = NULL;
1370       const PetscScalar *array;
1371       PetscInt           numCoords, cdim, d;
1372       PetscBool          isDG;
1373 
1374       if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1375       PetscCall(DMGetCoordinateDim(dm, &cdim));
1376       PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1377       PetscCheck(!(numCoords % cdim), PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "coordinate dim %" PetscInt_FMT " does not divide numCoords %" PetscInt_FMT, cdim, numCoords);
1378       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1379       for (p = 0; p < numCoords / cdim; ++p) {
1380         for (d = 0; d < cdim; ++d) {
1381           tcoords[d] = (double)(scale * PetscRealPart(cellCoords[p * cdim + d]));
1382           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1383         }
1384         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1385         if (cdim == 3) {
1386           PetscReal tmp = tcoords[1];
1387           tcoords[1]    = tcoords[2];
1388           tcoords[2]    = -tmp;
1389         }
1390         for (d = 0; d < dim; ++d) ccoords[d] += tcoords[d];
1391       }
1392       for (d = 0; d < cdim; ++d) ccoords[d] /= (numCoords / cdim);
1393       PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1394       for (d = 0; d < cdim; ++d) {
1395         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1396         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", ccoords[d]));
1397       }
1398       if (drawHasse) color = colors[depth % numColors];
1399       else color = colors[rank % numColors];
1400       for (l = 0; l < numLabels; ++l) {
1401         PetscInt val;
1402         PetscCall(DMGetLabelValue(dm, names[l], c, &val));
1403         if (val >= 0) {
1404           color     = lcolors[l % numLColors];
1405           isLabeled = PETSC_TRUE;
1406           break;
1407         }
1408       }
1409       if (drawNumbers[dim]) {
1410         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", c, rank, color, c));
1411       } else if (drawColors[dim]) {
1412         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", c, rank, !isLabeled ? 1 : 2, color));
1413       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", c, rank));
1414     }
1415     if (drawHasse) {
1416       int height = 0;
1417 
1418       color = colors[depth % numColors];
1419       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Cells\n"));
1420       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\c in {\\cStart,...,\\cEnd}\n"));
1421       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1422       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\c_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\cShift+\\c-\\cStart,%d) {\\c};\n", rank, color, height++));
1423       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1424 
1425       if (depth > 2) {
1426         color = colors[1 % numColors];
1427         PetscCall(PetscViewerASCIIPrintf(viewer, "%% Faces\n"));
1428         PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\f in {\\fStart,...,\\fEnd}\n"));
1429         PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1430         PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\f_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\fShift+\\f-\\fStart,%d) {\\f};\n", rank, color, height++));
1431         PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1432       }
1433 
1434       color = colors[1 % numColors];
1435       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Edges\n"));
1436       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\e in {\\eStart,...,\\eEnd}\n"));
1437       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1438       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\e_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\eShift+\\e-\\eStart,%d) {\\e};\n", rank, color, height++));
1439       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1440 
1441       color = colors[0 % numColors];
1442       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Vertices\n"));
1443       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\v in {\\vStart,...,\\vEnd}\n"));
1444       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1445       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\v_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\vShift+\\v-\\vStart,%d) {\\v};\n", rank, color, height++));
1446       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1447 
1448       for (p = pStart; p < pEnd; ++p) {
1449         const PetscInt *cone;
1450         PetscInt        coneSize, cp;
1451 
1452         PetscCall(DMPlexGetCone(dm, p, &cone));
1453         PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
1454         for (cp = 0; cp < coneSize; ++cp) PetscCall(PetscViewerASCIIPrintf(viewer, "\\draw[->, shorten >=1pt] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", cone[cp], rank, p, rank));
1455       }
1456     }
1457     PetscCall(PetscViewerFlush(viewer));
1458     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
1459     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{tikzpicture}\n"));
1460     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{document}\n"));
1461     for (l = 0; l < numLabels; ++l) PetscCall(PetscFree(names[l]));
1462     for (c = 0; c < numColors; ++c) PetscCall(PetscFree(colors[c]));
1463     for (c = 0; c < numLColors; ++c) PetscCall(PetscFree(lcolors[c]));
1464     PetscCall(PetscFree3(names, colors, lcolors));
1465     PetscCall(PetscBTDestroy(&wp));
1466   } else if (format == PETSC_VIEWER_LOAD_BALANCE) {
1467     Vec                    cown, acown;
1468     VecScatter             sct;
1469     ISLocalToGlobalMapping g2l;
1470     IS                     gid, acis;
1471     MPI_Comm               comm, ncomm = MPI_COMM_NULL;
1472     MPI_Group              ggroup, ngroup;
1473     PetscScalar           *array, nid;
1474     const PetscInt        *idxs;
1475     PetscInt              *idxs2, *start, *adjacency, *work;
1476     PetscInt64             lm[3], gm[3];
1477     PetscInt               i, c, cStart, cEnd, cum, numVertices, ect, ectn, cellHeight;
1478     PetscMPIInt            d1, d2, rank;
1479 
1480     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1481     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1482 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1483     PetscCallMPI(MPI_Comm_split_type(comm, MPI_COMM_TYPE_SHARED, rank, MPI_INFO_NULL, &ncomm));
1484 #endif
1485     if (ncomm != MPI_COMM_NULL) {
1486       PetscCallMPI(MPI_Comm_group(comm, &ggroup));
1487       PetscCallMPI(MPI_Comm_group(ncomm, &ngroup));
1488       d1 = 0;
1489       PetscCallMPI(MPI_Group_translate_ranks(ngroup, 1, &d1, ggroup, &d2));
1490       nid = d2;
1491       PetscCallMPI(MPI_Group_free(&ggroup));
1492       PetscCallMPI(MPI_Group_free(&ngroup));
1493       PetscCallMPI(MPI_Comm_free(&ncomm));
1494     } else nid = 0.0;
1495 
1496     /* Get connectivity */
1497     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1498     PetscCall(DMPlexCreatePartitionerGraph(dm, cellHeight, &numVertices, &start, &adjacency, &gid));
1499 
1500     /* filter overlapped local cells */
1501     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
1502     PetscCall(ISGetIndices(gid, &idxs));
1503     PetscCall(ISGetLocalSize(gid, &cum));
1504     PetscCall(PetscMalloc1(cum, &idxs2));
1505     for (c = cStart, cum = 0; c < cEnd; c++) {
1506       if (idxs[c - cStart] < 0) continue;
1507       idxs2[cum++] = idxs[c - cStart];
1508     }
1509     PetscCall(ISRestoreIndices(gid, &idxs));
1510     PetscCheck(numVertices == cum, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Unexpected %" PetscInt_FMT " != %" PetscInt_FMT, numVertices, cum);
1511     PetscCall(ISDestroy(&gid));
1512     PetscCall(ISCreateGeneral(comm, numVertices, idxs2, PETSC_OWN_POINTER, &gid));
1513 
1514     /* support for node-aware cell locality */
1515     PetscCall(ISCreateGeneral(comm, start[numVertices], adjacency, PETSC_USE_POINTER, &acis));
1516     PetscCall(VecCreateSeq(PETSC_COMM_SELF, start[numVertices], &acown));
1517     PetscCall(VecCreateMPI(comm, numVertices, PETSC_DECIDE, &cown));
1518     PetscCall(VecGetArray(cown, &array));
1519     for (c = 0; c < numVertices; c++) array[c] = nid;
1520     PetscCall(VecRestoreArray(cown, &array));
1521     PetscCall(VecScatterCreate(cown, acis, acown, NULL, &sct));
1522     PetscCall(VecScatterBegin(sct, cown, acown, INSERT_VALUES, SCATTER_FORWARD));
1523     PetscCall(VecScatterEnd(sct, cown, acown, INSERT_VALUES, SCATTER_FORWARD));
1524     PetscCall(ISDestroy(&acis));
1525     PetscCall(VecScatterDestroy(&sct));
1526     PetscCall(VecDestroy(&cown));
1527 
1528     /* compute edgeCut */
1529     for (c = 0, cum = 0; c < numVertices; c++) cum = PetscMax(cum, start[c + 1] - start[c]);
1530     PetscCall(PetscMalloc1(cum, &work));
1531     PetscCall(ISLocalToGlobalMappingCreateIS(gid, &g2l));
1532     PetscCall(ISLocalToGlobalMappingSetType(g2l, ISLOCALTOGLOBALMAPPINGHASH));
1533     PetscCall(ISDestroy(&gid));
1534     PetscCall(VecGetArray(acown, &array));
1535     for (c = 0, ect = 0, ectn = 0; c < numVertices; c++) {
1536       PetscInt totl;
1537 
1538       totl = start[c + 1] - start[c];
1539       PetscCall(ISGlobalToLocalMappingApply(g2l, IS_GTOLM_MASK, totl, adjacency + start[c], NULL, work));
1540       for (i = 0; i < totl; i++) {
1541         if (work[i] < 0) {
1542           ect += 1;
1543           ectn += (array[i + start[c]] != nid) ? 0 : 1;
1544         }
1545       }
1546     }
1547     PetscCall(PetscFree(work));
1548     PetscCall(VecRestoreArray(acown, &array));
1549     lm[0] = numVertices > 0 ? numVertices : PETSC_INT_MAX;
1550     lm[1] = -numVertices;
1551     PetscCallMPI(MPIU_Allreduce(lm, gm, 2, MPIU_INT64, MPI_MIN, comm));
1552     PetscCall(PetscViewerASCIIPrintf(viewer, "  Cell balance: %.2f (max %" PetscInt64_FMT ", min %" PetscInt64_FMT, -((double)gm[1]) / ((double)gm[0]), -gm[1], gm[0]));
1553     lm[0] = ect;                     /* edgeCut */
1554     lm[1] = ectn;                    /* node-aware edgeCut */
1555     lm[2] = numVertices > 0 ? 0 : 1; /* empty processes */
1556     PetscCallMPI(MPIU_Allreduce(lm, gm, 3, MPIU_INT64, MPI_SUM, comm));
1557     PetscCall(PetscViewerASCIIPrintf(viewer, ", empty %" PetscInt64_FMT ")\n", gm[2]));
1558 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1559     PetscCall(PetscViewerASCIIPrintf(viewer, "  Edge Cut: %" PetscInt64_FMT " (on node %.3f)\n", gm[0] / 2, gm[0] ? ((double)gm[1]) / ((double)gm[0]) : 1.));
1560 #else
1561     PetscCall(PetscViewerASCIIPrintf(viewer, "  Edge Cut: %" PetscInt64_FMT " (on node %.3f)\n", gm[0] / 2, 0.0));
1562 #endif
1563     PetscCall(ISLocalToGlobalMappingDestroy(&g2l));
1564     PetscCall(PetscFree(start));
1565     PetscCall(PetscFree(adjacency));
1566     PetscCall(VecDestroy(&acown));
1567   } else {
1568     const char    *name;
1569     PetscInt      *sizes, *hybsizes, *ghostsizes;
1570     PetscInt       locDepth, depth, cellHeight, dim, d;
1571     PetscInt       pStart, pEnd, p, gcStart, gcEnd, gcNum;
1572     PetscInt       numLabels, l, maxSize = 17;
1573     DMPolytopeType ct0 = DM_POLYTOPE_UNKNOWN;
1574     MPI_Comm       comm;
1575     PetscMPIInt    size, rank;
1576 
1577     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1578     PetscCallMPI(MPI_Comm_size(comm, &size));
1579     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1580     PetscCall(DMGetDimension(dm, &dim));
1581     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1582     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1583     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
1584     else PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
1585     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
1586     PetscCall(DMPlexGetDepth(dm, &locDepth));
1587     PetscCallMPI(MPIU_Allreduce(&locDepth, &depth, 1, MPIU_INT, MPI_MAX, comm));
1588     PetscCall(DMPlexGetCellTypeStratum(dm, DM_POLYTOPE_FV_GHOST, &gcStart, &gcEnd));
1589     gcNum = gcEnd - gcStart;
1590     if (size < maxSize) PetscCall(PetscCalloc3(size, &sizes, size, &hybsizes, size, &ghostsizes));
1591     else PetscCall(PetscCalloc3(3, &sizes, 3, &hybsizes, 3, &ghostsizes));
1592     for (d = 0; d <= depth; d++) {
1593       PetscInt Nc[2] = {0, 0}, ict;
1594 
1595       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
1596       if (pStart < pEnd) PetscCall(DMPlexGetCellType(dm, pStart, &ct0));
1597       ict = ct0;
1598       PetscCallMPI(MPI_Bcast(&ict, 1, MPIU_INT, 0, comm));
1599       ct0 = (DMPolytopeType)ict;
1600       for (p = pStart; p < pEnd; ++p) {
1601         DMPolytopeType ct;
1602 
1603         PetscCall(DMPlexGetCellType(dm, p, &ct));
1604         if (ct == ct0) ++Nc[0];
1605         else ++Nc[1];
1606       }
1607       if (size < maxSize) {
1608         PetscCallMPI(MPI_Gather(&Nc[0], 1, MPIU_INT, sizes, 1, MPIU_INT, 0, comm));
1609         PetscCallMPI(MPI_Gather(&Nc[1], 1, MPIU_INT, hybsizes, 1, MPIU_INT, 0, comm));
1610         if (d == depth) PetscCallMPI(MPI_Gather(&gcNum, 1, MPIU_INT, ghostsizes, 1, MPIU_INT, 0, comm));
1611         PetscCall(PetscViewerASCIIPrintf(viewer, "  Number of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1612         for (p = 0; p < size; ++p) {
1613           if (rank == 0) {
1614             PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT, sizes[p] + hybsizes[p]));
1615             if (hybsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT ")", hybsizes[p]));
1616             if (ghostsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "]", ghostsizes[p]));
1617           }
1618         }
1619       } else {
1620         PetscInt locMinMax[2];
1621 
1622         locMinMax[0] = Nc[0] + Nc[1];
1623         locMinMax[1] = Nc[0] + Nc[1];
1624         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, sizes));
1625         locMinMax[0] = Nc[1];
1626         locMinMax[1] = Nc[1];
1627         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, hybsizes));
1628         if (d == depth) {
1629           locMinMax[0] = gcNum;
1630           locMinMax[1] = gcNum;
1631           PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, ghostsizes));
1632         }
1633         PetscCall(PetscViewerASCIIPrintf(viewer, "  Min/Max of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1634         PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT "/%" PetscInt_FMT, sizes[0], sizes[1]));
1635         if (hybsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT "/%" PetscInt_FMT ")", hybsizes[0], hybsizes[1]));
1636         if (ghostsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "/%" PetscInt_FMT "]", ghostsizes[0], ghostsizes[1]));
1637       }
1638       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
1639     }
1640     PetscCall(PetscFree3(sizes, hybsizes, ghostsizes));
1641     {
1642       const PetscReal *maxCell;
1643       const PetscReal *L;
1644       PetscBool        localized;
1645 
1646       PetscCall(DMGetPeriodicity(dm, &maxCell, NULL, &L));
1647       PetscCall(DMGetCoordinatesLocalized(dm, &localized));
1648       if (L || localized) {
1649         PetscCall(PetscViewerASCIIPrintf(viewer, "Periodic mesh"));
1650         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1651         if (L) {
1652           PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
1653           for (d = 0; d < dim; ++d) {
1654             if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1655             PetscCall(PetscViewerASCIIPrintf(viewer, "%s", L[d] > 0.0 ? "PERIODIC" : "NONE"));
1656           }
1657           PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
1658         }
1659         PetscCall(PetscViewerASCIIPrintf(viewer, " coordinates %s\n", localized ? "localized" : "not localized"));
1660         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1661       }
1662     }
1663     PetscCall(DMGetNumLabels(dm, &numLabels));
1664     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
1665     for (l = 0; l < numLabels; ++l) {
1666       DMLabel     label;
1667       const char *name;
1668       PetscInt   *values;
1669       PetscInt    numValues, v;
1670 
1671       PetscCall(DMGetLabelName(dm, l, &name));
1672       PetscCall(DMGetLabel(dm, name, &label));
1673       PetscCall(DMLabelGetNumValues(label, &numValues));
1674       PetscCall(PetscViewerASCIIPrintf(viewer, "  %s: %" PetscInt_FMT " strata with value/size (", name, numValues));
1675 
1676       { // Extract array of DMLabel values so it can be sorted
1677         IS              is_values;
1678         const PetscInt *is_values_local = NULL;
1679 
1680         PetscCall(DMLabelGetValueIS(label, &is_values));
1681         PetscCall(ISGetIndices(is_values, &is_values_local));
1682         PetscCall(PetscMalloc1(numValues, &values));
1683         PetscCall(PetscArraycpy(values, is_values_local, numValues));
1684         PetscCall(PetscSortInt(numValues, values));
1685         PetscCall(ISRestoreIndices(is_values, &is_values_local));
1686         PetscCall(ISDestroy(&is_values));
1687       }
1688       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1689       for (v = 0; v < numValues; ++v) {
1690         PetscInt size;
1691 
1692         PetscCall(DMLabelGetStratumSize(label, values[v], &size));
1693         if (v > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1694         PetscCall(PetscViewerASCIIPrintf(viewer, "%" PetscInt_FMT " (%" PetscInt_FMT ")", values[v], size));
1695       }
1696       PetscCall(PetscViewerASCIIPrintf(viewer, ")\n"));
1697       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1698       PetscCall(PetscFree(values));
1699     }
1700     {
1701       char    **labelNames;
1702       PetscInt  Nl = numLabels;
1703       PetscBool flg;
1704 
1705       PetscCall(PetscMalloc1(Nl, &labelNames));
1706       PetscCall(PetscOptionsGetStringArray(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_plex_view_labels", labelNames, &Nl, &flg));
1707       for (l = 0; l < Nl; ++l) {
1708         DMLabel label;
1709 
1710         PetscCall(DMHasLabel(dm, labelNames[l], &flg));
1711         if (flg) {
1712           PetscCall(DMGetLabel(dm, labelNames[l], &label));
1713           PetscCall(DMLabelView(label, viewer));
1714         }
1715         PetscCall(PetscFree(labelNames[l]));
1716       }
1717       PetscCall(PetscFree(labelNames));
1718     }
1719     /* If no fields are specified, people do not want to see adjacency */
1720     if (dm->Nf) {
1721       PetscInt f;
1722 
1723       for (f = 0; f < dm->Nf; ++f) {
1724         const char *name;
1725 
1726         PetscCall(PetscObjectGetName(dm->fields[f].disc, &name));
1727         if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Field %s:\n", name));
1728         PetscCall(PetscViewerASCIIPushTab(viewer));
1729         if (dm->fields[f].label) PetscCall(DMLabelView(dm->fields[f].label, viewer));
1730         if (dm->fields[f].adjacency[0]) {
1731           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM++\n"));
1732           else PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM\n"));
1733         } else {
1734           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FEM\n"));
1735           else PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FUNKY\n"));
1736         }
1737         PetscCall(PetscViewerASCIIPopTab(viewer));
1738       }
1739     }
1740     PetscCall(DMGetCoarseDM(dm, &cdm));
1741     if (cdm) {
1742       PetscCall(PetscViewerASCIIPushTab(viewer));
1743       PetscCall(PetscViewerASCIIPrintf(viewer, "Defined by transform from:\n"));
1744       PetscCall(DMPlexView_Ascii(cdm, viewer));
1745       PetscCall(PetscViewerASCIIPopTab(viewer));
1746     }
1747   }
1748   PetscFunctionReturn(PETSC_SUCCESS);
1749 }
1750 
1751 static PetscErrorCode DMPlexDrawCell(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[])
1752 {
1753   DMPolytopeType ct;
1754   PetscMPIInt    rank;
1755   PetscInt       cdim;
1756 
1757   PetscFunctionBegin;
1758   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1759   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1760   PetscCall(DMGetCoordinateDim(dm, &cdim));
1761   switch (ct) {
1762   case DM_POLYTOPE_SEGMENT:
1763   case DM_POLYTOPE_POINT_PRISM_TENSOR:
1764     switch (cdim) {
1765     case 1: {
1766       const PetscReal y  = 0.5;  /* TODO Put it in the middle of the viewport */
1767       const PetscReal dy = 0.05; /* TODO Make it a fraction of the total length */
1768 
1769       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y, PetscRealPart(coords[1]), y, PETSC_DRAW_BLACK));
1770       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y + dy, PetscRealPart(coords[0]), y - dy, PETSC_DRAW_BLACK));
1771       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[1]), y + dy, PetscRealPart(coords[1]), y - dy, PETSC_DRAW_BLACK));
1772     } break;
1773     case 2: {
1774       const PetscReal dx = (PetscRealPart(coords[3]) - PetscRealPart(coords[1]));
1775       const PetscReal dy = (PetscRealPart(coords[2]) - PetscRealPart(coords[0]));
1776       const PetscReal l  = 0.1 / PetscSqrtReal(dx * dx + dy * dy);
1777 
1778       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1779       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));
1780       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));
1781     } break;
1782     default:
1783       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of dimension %" PetscInt_FMT, cdim);
1784     }
1785     break;
1786   case DM_POLYTOPE_TRIANGLE:
1787     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));
1788     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1789     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1790     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1791     break;
1792   case DM_POLYTOPE_QUADRILATERAL:
1793     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));
1794     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));
1795     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1796     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1797     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_BLACK));
1798     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1799     break;
1800   case DM_POLYTOPE_SEG_PRISM_TENSOR:
1801     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));
1802     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));
1803     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1804     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_BLACK));
1805     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1806     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1807     break;
1808   case DM_POLYTOPE_FV_GHOST:
1809     break;
1810   default:
1811     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1812   }
1813   PetscFunctionReturn(PETSC_SUCCESS);
1814 }
1815 
1816 static PetscErrorCode DrawPolygon_Private(DM dm, PetscDraw draw, PetscInt cell, PetscInt Nv, const PetscReal refVertices[], const PetscScalar coords[], PetscInt edgeDiv, PetscReal refCoords[], PetscReal edgeCoords[])
1817 {
1818   PetscReal   centroid[2] = {0., 0.};
1819   PetscMPIInt rank;
1820   PetscMPIInt fillColor;
1821 
1822   PetscFunctionBegin;
1823   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1824   fillColor = PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2;
1825   for (PetscInt v = 0; v < Nv; ++v) {
1826     centroid[0] += PetscRealPart(coords[v * 2 + 0]) / Nv;
1827     centroid[1] += PetscRealPart(coords[v * 2 + 1]) / Nv;
1828   }
1829   for (PetscInt e = 0; e < Nv; ++e) {
1830     refCoords[0] = refVertices[e * 2 + 0];
1831     refCoords[1] = refVertices[e * 2 + 1];
1832     for (PetscInt d = 1; d <= edgeDiv; ++d) {
1833       refCoords[d * 2 + 0] = refCoords[0] + (refVertices[(e + 1) % Nv * 2 + 0] - refCoords[0]) * d / edgeDiv;
1834       refCoords[d * 2 + 1] = refCoords[1] + (refVertices[(e + 1) % Nv * 2 + 1] - refCoords[1]) * d / edgeDiv;
1835     }
1836     PetscCall(DMPlexReferenceToCoordinates(dm, cell, edgeDiv + 1, refCoords, edgeCoords));
1837     for (PetscInt d = 0; d < edgeDiv; ++d) {
1838       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));
1839       PetscCall(PetscDrawLine(draw, edgeCoords[d * 2 + 0], edgeCoords[d * 2 + 1], edgeCoords[(d + 1) * 2 + 0], edgeCoords[(d + 1) * 2 + 1], PETSC_DRAW_BLACK));
1840     }
1841   }
1842   PetscFunctionReturn(PETSC_SUCCESS);
1843 }
1844 
1845 static PetscErrorCode DMPlexDrawCellHighOrder(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[], PetscInt edgeDiv, PetscReal refCoords[], PetscReal edgeCoords[])
1846 {
1847   DMPolytopeType ct;
1848 
1849   PetscFunctionBegin;
1850   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1851   switch (ct) {
1852   case DM_POLYTOPE_TRIANGLE: {
1853     PetscReal refVertices[6] = {-1., -1., 1., -1., -1., 1.};
1854 
1855     PetscCall(DrawPolygon_Private(dm, draw, cell, 3, refVertices, coords, edgeDiv, refCoords, edgeCoords));
1856   } break;
1857   case DM_POLYTOPE_QUADRILATERAL: {
1858     PetscReal refVertices[8] = {-1., -1., 1., -1., 1., 1., -1., 1.};
1859 
1860     PetscCall(DrawPolygon_Private(dm, draw, cell, 4, refVertices, coords, edgeDiv, refCoords, edgeCoords));
1861   } break;
1862   default:
1863     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1864   }
1865   PetscFunctionReturn(PETSC_SUCCESS);
1866 }
1867 
1868 static PetscErrorCode DMPlexView_Draw(DM dm, PetscViewer viewer)
1869 {
1870   PetscDraw    draw;
1871   DM           cdm;
1872   PetscSection coordSection;
1873   Vec          coordinates;
1874   PetscReal    xyl[3], xyr[3];
1875   PetscReal   *refCoords, *edgeCoords;
1876   PetscBool    isnull, drawAffine;
1877   PetscInt     dim, vStart, vEnd, cStart, cEnd, c, cDegree, edgeDiv;
1878 
1879   PetscFunctionBegin;
1880   PetscCall(DMGetCoordinateDim(dm, &dim));
1881   PetscCheck(dim <= 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT, dim);
1882   PetscCall(DMGetCoordinateDegree_Internal(dm, &cDegree));
1883   drawAffine = cDegree > 1 ? PETSC_FALSE : PETSC_TRUE;
1884   edgeDiv    = cDegree + 1;
1885   PetscCall(PetscOptionsGetBool(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_view_draw_affine", &drawAffine, NULL));
1886   if (!drawAffine) PetscCall(PetscMalloc2((edgeDiv + 1) * dim, &refCoords, (edgeDiv + 1) * dim, &edgeCoords));
1887   PetscCall(DMGetCoordinateDM(dm, &cdm));
1888   PetscCall(DMGetLocalSection(cdm, &coordSection));
1889   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1890   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1891   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1892 
1893   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
1894   PetscCall(PetscDrawIsNull(draw, &isnull));
1895   if (isnull) PetscFunctionReturn(PETSC_SUCCESS);
1896   PetscCall(PetscDrawSetTitle(draw, "Mesh"));
1897 
1898   PetscCall(DMGetBoundingBox(dm, xyl, xyr));
1899   PetscCall(PetscDrawSetCoordinates(draw, xyl[0], xyl[1], xyr[0], xyr[1]));
1900   PetscCall(PetscDrawClear(draw));
1901 
1902   for (c = cStart; c < cEnd; ++c) {
1903     PetscScalar       *coords = NULL;
1904     const PetscScalar *coords_arr;
1905     PetscInt           numCoords;
1906     PetscBool          isDG;
1907 
1908     PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
1909     if (drawAffine) PetscCall(DMPlexDrawCell(dm, draw, c, coords));
1910     else PetscCall(DMPlexDrawCellHighOrder(dm, draw, c, coords, edgeDiv, refCoords, edgeCoords));
1911     PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
1912   }
1913   if (!drawAffine) PetscCall(PetscFree2(refCoords, edgeCoords));
1914   PetscCall(PetscDrawFlush(draw));
1915   PetscCall(PetscDrawPause(draw));
1916   PetscCall(PetscDrawSave(draw));
1917   PetscFunctionReturn(PETSC_SUCCESS);
1918 }
1919 
1920 static PetscErrorCode DMPlexCreateHighOrderSurrogate_Internal(DM dm, DM *hdm)
1921 {
1922   DM           odm = dm, rdm = dm, cdm;
1923   PetscFE      fe;
1924   PetscSpace   sp;
1925   PetscClassId id;
1926   PetscInt     degree;
1927   PetscBool    hoView = PETSC_TRUE;
1928 
1929   PetscFunctionBegin;
1930   PetscObjectOptionsBegin((PetscObject)dm);
1931   PetscCall(PetscOptionsBool("-dm_plex_high_order_view", "Subsample to view meshes with high order coordinates", "DMPlexCreateHighOrderSurrogate_Internal", hoView, &hoView, NULL));
1932   PetscOptionsEnd();
1933   PetscCall(PetscObjectReference((PetscObject)dm));
1934   *hdm = dm;
1935   if (!hoView) PetscFunctionReturn(PETSC_SUCCESS);
1936   PetscCall(DMGetCoordinateDM(dm, &cdm));
1937   PetscCall(DMGetField(cdm, 0, NULL, (PetscObject *)&fe));
1938   PetscCall(PetscObjectGetClassId((PetscObject)fe, &id));
1939   if (id != PETSCFE_CLASSID) PetscFunctionReturn(PETSC_SUCCESS);
1940   PetscCall(PetscFEGetBasisSpace(fe, &sp));
1941   PetscCall(PetscSpaceGetDegree(sp, &degree, NULL));
1942   for (PetscInt r = 0, rd = PetscCeilReal(((PetscReal)degree) / 2.); r < (PetscInt)PetscCeilReal(PetscLog2Real(degree)); ++r, rd = PetscCeilReal(((PetscReal)rd) / 2.)) {
1943     DM  cdm, rcdm;
1944     Mat In;
1945     Vec cl, rcl;
1946 
1947     PetscCall(DMRefine(odm, PetscObjectComm((PetscObject)odm), &rdm));
1948     PetscCall(DMPlexCreateCoordinateSpace(rdm, rd, PETSC_FALSE, NULL));
1949     PetscCall(PetscObjectSetName((PetscObject)rdm, "Refined Mesh with Linear Coordinates"));
1950     PetscCall(DMGetCoordinateDM(odm, &cdm));
1951     PetscCall(DMGetCoordinateDM(rdm, &rcdm));
1952     PetscCall(DMGetCoordinatesLocal(odm, &cl));
1953     PetscCall(DMGetCoordinatesLocal(rdm, &rcl));
1954     PetscCall(DMSetCoarseDM(rcdm, cdm));
1955     PetscCall(DMCreateInterpolation(cdm, rcdm, &In, NULL));
1956     PetscCall(MatMult(In, cl, rcl));
1957     PetscCall(MatDestroy(&In));
1958     PetscCall(DMSetCoordinatesLocal(rdm, rcl));
1959     PetscCall(DMDestroy(&odm));
1960     odm = rdm;
1961   }
1962   *hdm = rdm;
1963   PetscFunctionReturn(PETSC_SUCCESS);
1964 }
1965 
1966 #if defined(PETSC_HAVE_EXODUSII)
1967   #include <exodusII.h>
1968   #include <petscviewerexodusii.h>
1969 #endif
1970 
1971 PetscErrorCode DMView_Plex(DM dm, PetscViewer viewer)
1972 {
1973   PetscBool iascii, ishdf5, isvtk, isdraw, flg, isglvis, isexodus, iscgns, ispython;
1974   char      name[PETSC_MAX_PATH_LEN];
1975 
1976   PetscFunctionBegin;
1977   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1978   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1979   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &iascii));
1980   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
1981   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1982   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
1983   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
1984   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodus));
1985   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
1986   PetscCall(PetscObjectHasFunction((PetscObject)viewer, "PetscViewerPythonViewObject_C", &ispython));
1987   if (iascii) {
1988     PetscViewerFormat format;
1989     PetscCall(PetscViewerGetFormat(viewer, &format));
1990     if (format == PETSC_VIEWER_ASCII_GLVIS) PetscCall(DMPlexView_GLVis(dm, viewer));
1991     else PetscCall(DMPlexView_Ascii(dm, viewer));
1992   } else if (ishdf5) {
1993 #if defined(PETSC_HAVE_HDF5)
1994     PetscCall(DMPlexView_HDF5_Internal(dm, viewer));
1995 #else
1996     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1997 #endif
1998   } else if (isvtk) {
1999     PetscCall(DMPlexVTKWriteAll((PetscObject)dm, viewer));
2000   } else if (isdraw) {
2001     DM hdm;
2002 
2003     PetscCall(DMPlexCreateHighOrderSurrogate_Internal(dm, &hdm));
2004     PetscCall(DMPlexView_Draw(hdm, viewer));
2005     PetscCall(DMDestroy(&hdm));
2006   } else if (isglvis) {
2007     PetscCall(DMPlexView_GLVis(dm, viewer));
2008 #if defined(PETSC_HAVE_EXODUSII)
2009   } else if (isexodus) {
2010     /*
2011       exodusII requires that all sets be part of exactly one cell set.
2012       If the dm does not have a "Cell Sets" label defined, we create one
2013       with ID 1, containing all cells.
2014       Note that if the Cell Sets label is defined but does not cover all cells,
2015       we may still have a problem. This should probably be checked here or in the viewer;
2016     */
2017     PetscInt numCS;
2018     PetscCall(DMGetLabelSize(dm, "Cell Sets", &numCS));
2019     if (!numCS) {
2020       PetscInt cStart, cEnd, c;
2021       PetscCall(DMCreateLabel(dm, "Cell Sets"));
2022       PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
2023       for (c = cStart; c < cEnd; ++c) PetscCall(DMSetLabelValue(dm, "Cell Sets", c, 1));
2024     }
2025     PetscCall(DMView_PlexExodusII(dm, viewer));
2026 #endif
2027 #if defined(PETSC_HAVE_CGNS)
2028   } else if (iscgns) {
2029     PetscCall(DMView_PlexCGNS(dm, viewer));
2030 #endif
2031   } else if (ispython) {
2032     PetscCall(PetscViewerPythonViewObject(viewer, (PetscObject)dm));
2033   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex writing", ((PetscObject)viewer)->type_name);
2034   /* Optionally view the partition */
2035   PetscCall(PetscOptionsHasName(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_partition_view", &flg));
2036   if (flg) {
2037     Vec ranks;
2038     PetscCall(DMPlexCreateRankField(dm, &ranks));
2039     PetscCall(VecView(ranks, viewer));
2040     PetscCall(VecDestroy(&ranks));
2041   }
2042   /* Optionally view a label */
2043   PetscCall(PetscOptionsGetString(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_label_view", name, sizeof(name), &flg));
2044   if (flg) {
2045     DMLabel label;
2046     Vec     val;
2047 
2048     PetscCall(DMGetLabel(dm, name, &label));
2049     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Label %s provided to -dm_label_view does not exist in this DM", name);
2050     PetscCall(DMPlexCreateLabelField(dm, label, &val));
2051     PetscCall(VecView(val, viewer));
2052     PetscCall(VecDestroy(&val));
2053   }
2054   PetscFunctionReturn(PETSC_SUCCESS);
2055 }
2056 
2057 /*@
2058   DMPlexTopologyView - Saves a `DMPLEX` topology into a file
2059 
2060   Collective
2061 
2062   Input Parameters:
2063 + dm     - The `DM` whose topology is to be saved
2064 - viewer - The `PetscViewer` to save it in
2065 
2066   Level: advanced
2067 
2068 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexTopologyLoad()`, `PetscViewer`
2069 @*/
2070 PetscErrorCode DMPlexTopologyView(DM dm, PetscViewer viewer)
2071 {
2072   PetscBool ishdf5;
2073 
2074   PetscFunctionBegin;
2075   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2076   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2077   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2078   PetscCall(PetscLogEventBegin(DMPLEX_TopologyView, viewer, 0, 0, 0));
2079   if (ishdf5) {
2080 #if defined(PETSC_HAVE_HDF5)
2081     PetscViewerFormat format;
2082     PetscCall(PetscViewerGetFormat(viewer, &format));
2083     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2084       IS globalPointNumbering;
2085 
2086       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
2087       PetscCall(DMPlexTopologyView_HDF5_Internal(dm, globalPointNumbering, viewer));
2088       PetscCall(ISDestroy(&globalPointNumbering));
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_TopologyView, viewer, 0, 0, 0));
2095   PetscFunctionReturn(PETSC_SUCCESS);
2096 }
2097 
2098 /*@
2099   DMPlexCoordinatesView - Saves `DMPLEX` coordinates into a file
2100 
2101   Collective
2102 
2103   Input Parameters:
2104 + dm     - The `DM` whose coordinates are to be saved
2105 - viewer - The `PetscViewer` for saving
2106 
2107   Level: advanced
2108 
2109 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexLabelsView()`, `DMPlexCoordinatesLoad()`, `PetscViewer`
2110 @*/
2111 PetscErrorCode DMPlexCoordinatesView(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_CoordinatesView, viewer, 0, 0, 0));
2120   if (ishdf5) {
2121 #if defined(PETSC_HAVE_HDF5)
2122     PetscViewerFormat format;
2123     PetscCall(PetscViewerGetFormat(viewer, &format));
2124     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2125       PetscCall(DMPlexCoordinatesView_HDF5_Internal(dm, viewer));
2126     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
2127 #else
2128     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2129 #endif
2130   }
2131   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
2132   PetscFunctionReturn(PETSC_SUCCESS);
2133 }
2134 
2135 /*@
2136   DMPlexLabelsView - Saves `DMPLEX` labels into a file
2137 
2138   Collective
2139 
2140   Input Parameters:
2141 + dm     - The `DM` whose labels are to be saved
2142 - viewer - The `PetscViewer` for saving
2143 
2144   Level: advanced
2145 
2146 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsLoad()`, `PetscViewer`
2147 @*/
2148 PetscErrorCode DMPlexLabelsView(DM dm, PetscViewer viewer)
2149 {
2150   PetscBool ishdf5;
2151 
2152   PetscFunctionBegin;
2153   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2154   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2155   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2156   PetscCall(PetscLogEventBegin(DMPLEX_LabelsView, viewer, 0, 0, 0));
2157   if (ishdf5) {
2158 #if defined(PETSC_HAVE_HDF5)
2159     IS                globalPointNumbering;
2160     PetscViewerFormat format;
2161 
2162     PetscCall(PetscViewerGetFormat(viewer, &format));
2163     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2164       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
2165       PetscCall(DMPlexLabelsView_HDF5_Internal(dm, globalPointNumbering, viewer));
2166       PetscCall(ISDestroy(&globalPointNumbering));
2167     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2168 #else
2169     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2170 #endif
2171   }
2172   PetscCall(PetscLogEventEnd(DMPLEX_LabelsView, viewer, 0, 0, 0));
2173   PetscFunctionReturn(PETSC_SUCCESS);
2174 }
2175 
2176 /*@
2177   DMPlexSectionView - Saves a section associated with a `DMPLEX`
2178 
2179   Collective
2180 
2181   Input Parameters:
2182 + dm        - The `DM` that contains the topology on which the section to be saved is defined
2183 . viewer    - The `PetscViewer` for saving
2184 - sectiondm - The `DM` that contains the section to be saved, can be `NULL`
2185 
2186   Level: advanced
2187 
2188   Notes:
2189   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.
2190 
2191   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.
2192 
2193 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`, `PetscSectionView()`, `DMPlexSectionLoad()`, `PetscViewer`
2194 @*/
2195 PetscErrorCode DMPlexSectionView(DM dm, PetscViewer viewer, DM sectiondm)
2196 {
2197   PetscBool ishdf5;
2198 
2199   PetscFunctionBegin;
2200   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2201   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2202   if (!sectiondm) sectiondm = dm;
2203   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2204   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2205   PetscCall(PetscLogEventBegin(DMPLEX_SectionView, viewer, 0, 0, 0));
2206   if (ishdf5) {
2207 #if defined(PETSC_HAVE_HDF5)
2208     PetscCall(DMPlexSectionView_HDF5_Internal(dm, viewer, sectiondm));
2209 #else
2210     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2211 #endif
2212   }
2213   PetscCall(PetscLogEventEnd(DMPLEX_SectionView, viewer, 0, 0, 0));
2214   PetscFunctionReturn(PETSC_SUCCESS);
2215 }
2216 
2217 /*@
2218   DMPlexGlobalVectorView - Saves a global vector
2219 
2220   Collective
2221 
2222   Input Parameters:
2223 + dm        - The `DM` that represents the topology
2224 . viewer    - The `PetscViewer` to save data with
2225 . sectiondm - The `DM` that contains the global section on which vec is defined, can be `NULL`
2226 - vec       - The global vector to be saved
2227 
2228   Level: advanced
2229 
2230   Notes:
2231   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.
2232 
2233   Calling sequence:
2234 .vb
2235        DMCreate(PETSC_COMM_WORLD, &dm);
2236        DMSetType(dm, DMPLEX);
2237        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2238        DMClone(dm, &sectiondm);
2239        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2240        PetscSectionCreate(PETSC_COMM_WORLD, &section);
2241        DMPlexGetChart(sectiondm, &pStart, &pEnd);
2242        PetscSectionSetChart(section, pStart, pEnd);
2243        PetscSectionSetUp(section);
2244        DMSetLocalSection(sectiondm, section);
2245        PetscSectionDestroy(&section);
2246        DMGetGlobalVector(sectiondm, &vec);
2247        PetscObjectSetName((PetscObject)vec, "vec_name");
2248        DMPlexTopologyView(dm, viewer);
2249        DMPlexSectionView(dm, viewer, sectiondm);
2250        DMPlexGlobalVectorView(dm, viewer, sectiondm, vec);
2251        DMRestoreGlobalVector(sectiondm, &vec);
2252        DMDestroy(&sectiondm);
2253        DMDestroy(&dm);
2254 .ve
2255 
2256 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexLocalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2257 @*/
2258 PetscErrorCode DMPlexGlobalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2259 {
2260   PetscBool ishdf5;
2261 
2262   PetscFunctionBegin;
2263   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2264   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2265   if (!sectiondm) sectiondm = dm;
2266   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2267   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2268   /* Check consistency */
2269   {
2270     PetscSection section;
2271     PetscBool    includesConstraints;
2272     PetscInt     m, m1;
2273 
2274     PetscCall(VecGetLocalSize(vec, &m1));
2275     PetscCall(DMGetGlobalSection(sectiondm, &section));
2276     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2277     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2278     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2279     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2280   }
2281   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2282   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorView, viewer, 0, 0, 0));
2283   if (ishdf5) {
2284 #if defined(PETSC_HAVE_HDF5)
2285     PetscCall(DMPlexGlobalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2286 #else
2287     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2288 #endif
2289   }
2290   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorView, viewer, 0, 0, 0));
2291   PetscFunctionReturn(PETSC_SUCCESS);
2292 }
2293 
2294 /*@
2295   DMPlexLocalVectorView - Saves a local vector
2296 
2297   Collective
2298 
2299   Input Parameters:
2300 + dm        - The `DM` that represents the topology
2301 . viewer    - The `PetscViewer` to save data with
2302 . sectiondm - The `DM` that contains the local section on which `vec` is defined, can be `NULL`
2303 - vec       - The local vector to be saved
2304 
2305   Level: advanced
2306 
2307   Note:
2308   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.
2309 
2310   Calling sequence:
2311 .vb
2312        DMCreate(PETSC_COMM_WORLD, &dm);
2313        DMSetType(dm, DMPLEX);
2314        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2315        DMClone(dm, &sectiondm);
2316        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2317        PetscSectionCreate(PETSC_COMM_WORLD, &section);
2318        DMPlexGetChart(sectiondm, &pStart, &pEnd);
2319        PetscSectionSetChart(section, pStart, pEnd);
2320        PetscSectionSetUp(section);
2321        DMSetLocalSection(sectiondm, section);
2322        DMGetLocalVector(sectiondm, &vec);
2323        PetscObjectSetName((PetscObject)vec, "vec_name");
2324        DMPlexTopologyView(dm, viewer);
2325        DMPlexSectionView(dm, viewer, sectiondm);
2326        DMPlexLocalVectorView(dm, viewer, sectiondm, vec);
2327        DMRestoreLocalVector(sectiondm, &vec);
2328        DMDestroy(&sectiondm);
2329        DMDestroy(&dm);
2330 .ve
2331 
2332 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexGlobalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2333 @*/
2334 PetscErrorCode DMPlexLocalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2335 {
2336   PetscBool ishdf5;
2337 
2338   PetscFunctionBegin;
2339   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2340   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2341   if (!sectiondm) sectiondm = dm;
2342   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2343   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2344   /* Check consistency */
2345   {
2346     PetscSection section;
2347     PetscBool    includesConstraints;
2348     PetscInt     m, m1;
2349 
2350     PetscCall(VecGetLocalSize(vec, &m1));
2351     PetscCall(DMGetLocalSection(sectiondm, &section));
2352     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2353     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2354     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2355     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2356   }
2357   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2358   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorView, viewer, 0, 0, 0));
2359   if (ishdf5) {
2360 #if defined(PETSC_HAVE_HDF5)
2361     PetscCall(DMPlexLocalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2362 #else
2363     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2364 #endif
2365   }
2366   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorView, viewer, 0, 0, 0));
2367   PetscFunctionReturn(PETSC_SUCCESS);
2368 }
2369 
2370 PetscErrorCode DMLoad_Plex(DM dm, PetscViewer viewer)
2371 {
2372   PetscBool ishdf5;
2373 
2374   PetscFunctionBegin;
2375   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2376   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2377   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2378   if (ishdf5) {
2379 #if defined(PETSC_HAVE_HDF5)
2380     PetscViewerFormat format;
2381     PetscCall(PetscViewerGetFormat(viewer, &format));
2382     if (format == PETSC_VIEWER_HDF5_XDMF || format == PETSC_VIEWER_HDF5_VIZ) {
2383       PetscCall(DMPlexLoad_HDF5_Xdmf_Internal(dm, viewer));
2384     } else if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2385       PetscCall(DMPlexLoad_HDF5_Internal(dm, viewer));
2386     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2387     PetscFunctionReturn(PETSC_SUCCESS);
2388 #else
2389     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2390 #endif
2391   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex loading", ((PetscObject)viewer)->type_name);
2392 }
2393 
2394 /*@
2395   DMPlexTopologyLoad - Loads a topology into a `DMPLEX`
2396 
2397   Collective
2398 
2399   Input Parameters:
2400 + dm     - The `DM` into which the topology is loaded
2401 - viewer - The `PetscViewer` for the saved topology
2402 
2403   Output Parameter:
2404 . 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;
2405   `NULL` if unneeded
2406 
2407   Level: advanced
2408 
2409 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2410           `PetscViewer`, `PetscSF`
2411 @*/
2412 PetscErrorCode DMPlexTopologyLoad(DM dm, PetscViewer viewer, PetscSF *globalToLocalPointSF)
2413 {
2414   PetscBool ishdf5;
2415 
2416   PetscFunctionBegin;
2417   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2418   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2419   if (globalToLocalPointSF) PetscAssertPointer(globalToLocalPointSF, 3);
2420   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2421   PetscCall(PetscLogEventBegin(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2422   if (ishdf5) {
2423 #if defined(PETSC_HAVE_HDF5)
2424     PetscViewerFormat format;
2425     PetscCall(PetscViewerGetFormat(viewer, &format));
2426     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2427       PetscCall(DMPlexTopologyLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2428     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2429 #else
2430     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2431 #endif
2432   }
2433   PetscCall(PetscLogEventEnd(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2434   PetscFunctionReturn(PETSC_SUCCESS);
2435 }
2436 
2437 /*@
2438   DMPlexCoordinatesLoad - Loads coordinates into a `DMPLEX`
2439 
2440   Collective
2441 
2442   Input Parameters:
2443 + dm                   - The `DM` into which the coordinates are loaded
2444 . viewer               - The `PetscViewer` for the saved coordinates
2445 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad()` when loading dm from viewer
2446 
2447   Level: advanced
2448 
2449 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2450           `PetscSF`, `PetscViewer`
2451 @*/
2452 PetscErrorCode DMPlexCoordinatesLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2453 {
2454   PetscBool ishdf5;
2455 
2456   PetscFunctionBegin;
2457   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2458   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2459   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2460   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2461   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesLoad, viewer, 0, 0, 0));
2462   if (ishdf5) {
2463 #if defined(PETSC_HAVE_HDF5)
2464     PetscViewerFormat format;
2465     PetscCall(PetscViewerGetFormat(viewer, &format));
2466     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2467       PetscCall(DMPlexCoordinatesLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2468     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2469 #else
2470     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2471 #endif
2472   }
2473   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesLoad, viewer, 0, 0, 0));
2474   PetscFunctionReturn(PETSC_SUCCESS);
2475 }
2476 
2477 /*@
2478   DMPlexLabelsLoad - Loads labels into a `DMPLEX`
2479 
2480   Collective
2481 
2482   Input Parameters:
2483 + dm                   - The `DM` into which the labels are loaded
2484 . viewer               - The `PetscViewer` for the saved labels
2485 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad()` when loading `dm` from viewer
2486 
2487   Level: advanced
2488 
2489   Note:
2490   The `PetscSF` argument must not be `NULL` if the `DM` is distributed, otherwise an error occurs.
2491 
2492 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2493           `PetscSF`, `PetscViewer`
2494 @*/
2495 PetscErrorCode DMPlexLabelsLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2496 {
2497   PetscBool ishdf5;
2498 
2499   PetscFunctionBegin;
2500   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2501   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2502   if (globalToLocalPointSF) PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2503   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2504   PetscCall(PetscLogEventBegin(DMPLEX_LabelsLoad, viewer, 0, 0, 0));
2505   if (ishdf5) {
2506 #if defined(PETSC_HAVE_HDF5)
2507     PetscViewerFormat format;
2508 
2509     PetscCall(PetscViewerGetFormat(viewer, &format));
2510     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2511       PetscCall(DMPlexLabelsLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2512     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2513 #else
2514     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2515 #endif
2516   }
2517   PetscCall(PetscLogEventEnd(DMPLEX_LabelsLoad, viewer, 0, 0, 0));
2518   PetscFunctionReturn(PETSC_SUCCESS);
2519 }
2520 
2521 /*@
2522   DMPlexSectionLoad - Loads section into a `DMPLEX`
2523 
2524   Collective
2525 
2526   Input Parameters:
2527 + dm                   - The `DM` that represents the topology
2528 . viewer               - The `PetscViewer` that represents the on-disk section (sectionA)
2529 . sectiondm            - The `DM` into which the on-disk section (sectionA) is migrated, can be `NULL`
2530 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad(`) when loading dm from viewer
2531 
2532   Output Parameters:
2533 + 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)
2534 - 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)
2535 
2536   Level: advanced
2537 
2538   Notes:
2539   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.
2540 
2541   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.
2542 
2543   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.
2544 
2545   Example using 2 processes:
2546 .vb
2547   NX (number of points on dm): 4
2548   sectionA                   : the on-disk section
2549   vecA                       : a vector associated with sectionA
2550   sectionB                   : sectiondm's local section constructed in this function
2551   vecB (local)               : a vector associated with sectiondm's local section
2552   vecB (global)              : a vector associated with sectiondm's global section
2553 
2554                                      rank 0    rank 1
2555   vecA (global)                  : [.0 .4 .1 | .2 .3]        <- to be loaded in DMPlexGlobalVectorLoad() or DMPlexLocalVectorLoad()
2556   sectionA->atlasOff             :       0 2 | 1             <- loaded in PetscSectionLoad()
2557   sectionA->atlasDof             :       1 3 | 1             <- loaded in PetscSectionLoad()
2558   sectionA's global point numbers:       0 2 | 3             <- loaded in DMPlexSectionLoad()
2559   [0, NX)                        :       0 1 | 2 3           <- conceptual partition used in globalToLocalPointSF
2560   sectionB's global point numbers:     0 1 3 | 3 2           <- associated with [0, NX) by globalToLocalPointSF
2561   sectionB->atlasDof             :     1 0 1 | 1 3
2562   sectionB->atlasOff (no perm)   :     0 1 1 | 0 1
2563   vecB (local)                   :   [.0 .4] | [.4 .1 .2 .3] <- to be constructed by calling DMPlexLocalVectorLoad() with localDofSF
2564   vecB (global)                  :    [.0 .4 | .1 .2 .3]     <- to be constructed by calling DMPlexGlobalVectorLoad() with globalDofSF
2565 .ve
2566   where "|" represents a partition of loaded data, and global point 3 is assumed to be owned by rank 0.
2567 
2568 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`, `PetscSectionLoad()`, `DMPlexSectionView()`, `PetscSF`, `PetscViewer`
2569 @*/
2570 PetscErrorCode DMPlexSectionLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF globalToLocalPointSF, PetscSF *globalDofSF, PetscSF *localDofSF)
2571 {
2572   PetscBool ishdf5;
2573 
2574   PetscFunctionBegin;
2575   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2576   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2577   if (!sectiondm) sectiondm = dm;
2578   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2579   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 4);
2580   if (globalDofSF) PetscAssertPointer(globalDofSF, 5);
2581   if (localDofSF) PetscAssertPointer(localDofSF, 6);
2582   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2583   PetscCall(PetscLogEventBegin(DMPLEX_SectionLoad, viewer, 0, 0, 0));
2584   if (ishdf5) {
2585 #if defined(PETSC_HAVE_HDF5)
2586     PetscCall(DMPlexSectionLoad_HDF5_Internal(dm, viewer, sectiondm, globalToLocalPointSF, globalDofSF, localDofSF));
2587 #else
2588     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2589 #endif
2590   }
2591   PetscCall(PetscLogEventEnd(DMPLEX_SectionLoad, viewer, 0, 0, 0));
2592   PetscFunctionReturn(PETSC_SUCCESS);
2593 }
2594 
2595 /*@
2596   DMPlexGlobalVectorLoad - Loads on-disk vector data into a global vector
2597 
2598   Collective
2599 
2600   Input Parameters:
2601 + dm        - The `DM` that represents the topology
2602 . viewer    - The `PetscViewer` that represents the on-disk vector data
2603 . sectiondm - The `DM` that contains the global section on which vec is defined, can be `NULL`
2604 . sf        - The `PetscSF` that migrates the on-disk vector data into vec
2605 - vec       - The global vector to set values of
2606 
2607   Level: advanced
2608 
2609   Notes:
2610   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.
2611 
2612   Calling sequence:
2613 .vb
2614        DMCreate(PETSC_COMM_WORLD, &dm);
2615        DMSetType(dm, DMPLEX);
2616        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2617        DMPlexTopologyLoad(dm, viewer, &sfX);
2618        DMClone(dm, &sectiondm);
2619        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2620        DMPlexSectionLoad(dm, viewer, sectiondm, sfX, &gsf, NULL);
2621        DMGetGlobalVector(sectiondm, &vec);
2622        PetscObjectSetName((PetscObject)vec, "vec_name");
2623        DMPlexGlobalVectorLoad(dm, viewer, sectiondm, gsf, vec);
2624        DMRestoreGlobalVector(sectiondm, &vec);
2625        PetscSFDestroy(&gsf);
2626        PetscSFDestroy(&sfX);
2627        DMDestroy(&sectiondm);
2628        DMDestroy(&dm);
2629 .ve
2630 
2631 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexLocalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`,
2632           `PetscSF`, `PetscViewer`
2633 @*/
2634 PetscErrorCode DMPlexGlobalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2635 {
2636   PetscBool ishdf5;
2637 
2638   PetscFunctionBegin;
2639   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2640   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2641   if (!sectiondm) sectiondm = dm;
2642   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2643   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2644   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2645   /* Check consistency */
2646   {
2647     PetscSection section;
2648     PetscBool    includesConstraints;
2649     PetscInt     m, m1;
2650 
2651     PetscCall(VecGetLocalSize(vec, &m1));
2652     PetscCall(DMGetGlobalSection(sectiondm, &section));
2653     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2654     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2655     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2656     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2657   }
2658   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2659   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorLoad, viewer, 0, 0, 0));
2660   if (ishdf5) {
2661 #if defined(PETSC_HAVE_HDF5)
2662     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2663 #else
2664     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2665 #endif
2666   }
2667   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorLoad, viewer, 0, 0, 0));
2668   PetscFunctionReturn(PETSC_SUCCESS);
2669 }
2670 
2671 /*@
2672   DMPlexLocalVectorLoad - Loads on-disk vector data into a local vector
2673 
2674   Collective
2675 
2676   Input Parameters:
2677 + dm        - The `DM` that represents the topology
2678 . viewer    - The `PetscViewer` that represents the on-disk vector data
2679 . sectiondm - The `DM` that contains the local section on which vec is defined, can be `NULL`
2680 . sf        - The `PetscSF` that migrates the on-disk vector data into vec
2681 - vec       - The local vector to set values of
2682 
2683   Level: advanced
2684 
2685   Notes:
2686   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.
2687 
2688   Calling sequence:
2689 .vb
2690        DMCreate(PETSC_COMM_WORLD, &dm);
2691        DMSetType(dm, DMPLEX);
2692        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2693        DMPlexTopologyLoad(dm, viewer, &sfX);
2694        DMClone(dm, &sectiondm);
2695        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2696        DMPlexSectionLoad(dm, viewer, sectiondm, sfX, NULL, &lsf);
2697        DMGetLocalVector(sectiondm, &vec);
2698        PetscObjectSetName((PetscObject)vec, "vec_name");
2699        DMPlexLocalVectorLoad(dm, viewer, sectiondm, lsf, vec);
2700        DMRestoreLocalVector(sectiondm, &vec);
2701        PetscSFDestroy(&lsf);
2702        PetscSFDestroy(&sfX);
2703        DMDestroy(&sectiondm);
2704        DMDestroy(&dm);
2705 .ve
2706 
2707 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`,
2708           `PetscSF`, `PetscViewer`
2709 @*/
2710 PetscErrorCode DMPlexLocalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2711 {
2712   PetscBool ishdf5;
2713 
2714   PetscFunctionBegin;
2715   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2716   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2717   if (!sectiondm) sectiondm = dm;
2718   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2719   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2720   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2721   /* Check consistency */
2722   {
2723     PetscSection section;
2724     PetscBool    includesConstraints;
2725     PetscInt     m, m1;
2726 
2727     PetscCall(VecGetLocalSize(vec, &m1));
2728     PetscCall(DMGetLocalSection(sectiondm, &section));
2729     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2730     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2731     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2732     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2733   }
2734   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2735   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorLoad, viewer, 0, 0, 0));
2736   if (ishdf5) {
2737 #if defined(PETSC_HAVE_HDF5)
2738     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2739 #else
2740     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2741 #endif
2742   }
2743   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorLoad, viewer, 0, 0, 0));
2744   PetscFunctionReturn(PETSC_SUCCESS);
2745 }
2746 
2747 PetscErrorCode DMDestroy_Plex(DM dm)
2748 {
2749   DM_Plex *mesh = (DM_Plex *)dm->data;
2750 
2751   PetscFunctionBegin;
2752   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMSetUpGLVisViewer_C", NULL));
2753   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertBoundaryValues_C", NULL));
2754   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMCreateNeumannOverlap_C", NULL));
2755   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMInterpolateSolution_C", NULL));
2756   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertTimeDerivativeBoundaryValues_C", NULL));
2757   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", NULL));
2758   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeGetDefault_C", NULL));
2759   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeSetDefault_C", NULL));
2760   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "MatComputeNeumannOverlap_C", NULL));
2761   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderGetDefault_C", NULL));
2762   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderSetDefault_C", NULL));
2763   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionGetDefault_C", NULL));
2764   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionSetDefault_C", NULL));
2765   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionGetType_C", NULL));
2766   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionSetType_C", NULL));
2767   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", NULL));
2768   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexSetOverlap_C", NULL));
2769   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetUseCeed_C", NULL));
2770   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexSetUseCeed_C", NULL));
2771   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMGetIsoperiodicPointSF_C", NULL));
2772   if (--mesh->refct > 0) PetscFunctionReturn(PETSC_SUCCESS);
2773   PetscCall(PetscSectionDestroy(&mesh->coneSection));
2774   PetscCall(PetscFree(mesh->cones));
2775   PetscCall(PetscFree(mesh->coneOrientations));
2776   PetscCall(PetscSectionDestroy(&mesh->supportSection));
2777   PetscCall(PetscSectionDestroy(&mesh->subdomainSection));
2778   PetscCall(PetscFree(mesh->supports));
2779   PetscCall(PetscFree(mesh->cellTypes));
2780   PetscCall(DMPlexTransformDestroy(&mesh->tr));
2781   PetscCall(PetscFree(mesh->tetgenOpts));
2782   PetscCall(PetscFree(mesh->triangleOpts));
2783   PetscCall(PetscFree(mesh->transformType));
2784   PetscCall(PetscFree(mesh->distributionName));
2785   PetscCall(PetscPartitionerDestroy(&mesh->partitioner));
2786   PetscCall(DMLabelDestroy(&mesh->subpointMap));
2787   PetscCall(ISDestroy(&mesh->subpointIS));
2788   PetscCall(ISDestroy(&mesh->globalVertexNumbers));
2789   PetscCall(ISDestroy(&mesh->globalCellNumbers));
2790   if (mesh->periodic.face_sfs) {
2791     for (PetscInt i = 0; i < mesh->periodic.num_face_sfs; i++) PetscCall(PetscSFDestroy(&mesh->periodic.face_sfs[i]));
2792     PetscCall(PetscFree(mesh->periodic.face_sfs));
2793   }
2794   PetscCall(PetscSFDestroy(&mesh->periodic.composed_sf));
2795   if (mesh->periodic.periodic_points) {
2796     for (PetscInt i = 0; i < mesh->periodic.num_face_sfs; i++) PetscCall(ISDestroy(&mesh->periodic.periodic_points[i]));
2797     PetscCall(PetscFree(mesh->periodic.periodic_points));
2798   }
2799   if (mesh->periodic.transform) PetscCall(PetscFree(mesh->periodic.transform));
2800   PetscCall(PetscSectionDestroy(&mesh->anchorSection));
2801   PetscCall(ISDestroy(&mesh->anchorIS));
2802   PetscCall(PetscSectionDestroy(&mesh->parentSection));
2803   PetscCall(PetscFree(mesh->parents));
2804   PetscCall(PetscFree(mesh->childIDs));
2805   PetscCall(PetscSectionDestroy(&mesh->childSection));
2806   PetscCall(PetscFree(mesh->children));
2807   PetscCall(DMDestroy(&mesh->referenceTree));
2808   PetscCall(PetscGridHashDestroy(&mesh->lbox));
2809   PetscCall(PetscFree(mesh->neighbors));
2810   if (mesh->metricCtx) PetscCall(PetscFree(mesh->metricCtx));
2811   if (mesh->nonempty_comm != MPI_COMM_NULL && mesh->nonempty_comm != MPI_COMM_SELF) PetscCallMPI(MPI_Comm_free(&mesh->nonempty_comm));
2812   /* This was originally freed in DMDestroy(), but that prevents reference counting of backend objects */
2813   PetscCall(PetscFree(mesh));
2814   PetscFunctionReturn(PETSC_SUCCESS);
2815 }
2816 
2817 PetscErrorCode DMCreateMatrix_Plex(DM dm, Mat *J)
2818 {
2819   PetscSection           sectionGlobal, sectionLocal;
2820   PetscInt               bs = -1, mbs;
2821   PetscInt               localSize, localStart = 0;
2822   PetscBool              isShell, isBlock, isSeqBlock, isMPIBlock, isSymBlock, isSymSeqBlock, isSymMPIBlock, isMatIS;
2823   MatType                mtype;
2824   ISLocalToGlobalMapping ltog;
2825 
2826   PetscFunctionBegin;
2827   PetscCall(MatInitializePackage());
2828   mtype = dm->mattype;
2829   PetscCall(DMGetLocalSection(dm, &sectionLocal));
2830   PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
2831   /* PetscCall(PetscSectionGetStorageSize(sectionGlobal, &localSize)); */
2832   PetscCall(PetscSectionGetConstrainedStorageSize(sectionGlobal, &localSize));
2833   PetscCallMPI(MPI_Exscan(&localSize, &localStart, 1, MPIU_INT, MPI_SUM, PetscObjectComm((PetscObject)dm)));
2834   PetscCall(MatCreate(PetscObjectComm((PetscObject)dm), J));
2835   PetscCall(MatSetSizes(*J, localSize, localSize, PETSC_DETERMINE, PETSC_DETERMINE));
2836   PetscCall(MatSetType(*J, mtype));
2837   PetscCall(MatSetFromOptions(*J));
2838   PetscCall(MatGetBlockSize(*J, &mbs));
2839   if (mbs > 1) bs = mbs;
2840   PetscCall(PetscStrcmp(mtype, MATSHELL, &isShell));
2841   PetscCall(PetscStrcmp(mtype, MATBAIJ, &isBlock));
2842   PetscCall(PetscStrcmp(mtype, MATSEQBAIJ, &isSeqBlock));
2843   PetscCall(PetscStrcmp(mtype, MATMPIBAIJ, &isMPIBlock));
2844   PetscCall(PetscStrcmp(mtype, MATSBAIJ, &isSymBlock));
2845   PetscCall(PetscStrcmp(mtype, MATSEQSBAIJ, &isSymSeqBlock));
2846   PetscCall(PetscStrcmp(mtype, MATMPISBAIJ, &isSymMPIBlock));
2847   PetscCall(PetscStrcmp(mtype, MATIS, &isMatIS));
2848   if (!isShell) {
2849     // There are three states with pblocks, since block starts can have no dofs:
2850     // UNKNOWN) New Block:   An open block has been signalled by pblocks[p] == 1
2851     // TRUE)    Block Start: The first entry in a block has been added
2852     // FALSE)   Block Add:   An additional block entry has been added, since pblocks[p] == 0
2853     PetscBT         blst;
2854     PetscBool3      bstate     = PETSC_BOOL3_UNKNOWN;
2855     PetscBool       fillMatrix = (PetscBool)(!dm->prealloc_only && !isMatIS);
2856     const PetscInt *perm       = NULL;
2857     PetscInt       *dnz, *onz, *dnzu, *onzu, bsLocal[2], bsMinMax[2], *pblocks;
2858     PetscInt        pStart, pEnd, dof, cdof, num_fields;
2859 
2860     PetscCall(DMGetLocalToGlobalMapping(dm, &ltog));
2861     PetscCall(PetscSectionGetBlockStarts(sectionLocal, &blst));
2862     if (sectionLocal->perm) PetscCall(ISGetIndices(sectionLocal->perm, &perm));
2863 
2864     PetscCall(PetscCalloc1(localSize, &pblocks));
2865     PetscCall(PetscSectionGetChart(sectionGlobal, &pStart, &pEnd));
2866     PetscCall(PetscSectionGetNumFields(sectionGlobal, &num_fields));
2867     // We need to process in the permuted order to get block sizes right
2868     for (PetscInt point = pStart; point < pEnd; ++point) {
2869       const PetscInt p = perm ? perm[point] : point;
2870 
2871       switch (dm->blocking_type) {
2872       case DM_BLOCKING_TOPOLOGICAL_POINT: { // One block per topological point
2873         PetscInt bdof, offset;
2874 
2875         PetscCall(PetscSectionGetDof(sectionGlobal, p, &dof));
2876         PetscCall(PetscSectionGetOffset(sectionGlobal, p, &offset));
2877         PetscCall(PetscSectionGetConstraintDof(sectionGlobal, p, &cdof));
2878         if (blst && PetscBTLookup(blst, p)) bstate = PETSC_BOOL3_UNKNOWN;
2879         if (dof > 0) {
2880           // State change
2881           if (bstate == PETSC_BOOL3_UNKNOWN) bstate = PETSC_BOOL3_TRUE;
2882           else if (bstate == PETSC_BOOL3_TRUE && blst && !PetscBTLookup(blst, p)) bstate = PETSC_BOOL3_FALSE;
2883 
2884           for (PetscInt i = 0; i < dof - cdof; ++i) pblocks[offset - localStart + i] = dof - cdof;
2885           // Signal block concatenation
2886           if (bstate == PETSC_BOOL3_FALSE && dof - cdof) pblocks[offset - localStart] = -(dof - cdof);
2887         }
2888         dof  = dof < 0 ? -(dof + 1) : dof;
2889         bdof = cdof && (dof - cdof) ? 1 : dof;
2890         if (dof) {
2891           if (bs < 0) {
2892             bs = bdof;
2893           } else if (bs != bdof) {
2894             bs = 1;
2895           }
2896         }
2897       } break;
2898       case DM_BLOCKING_FIELD_NODE: {
2899         for (PetscInt field = 0; field < num_fields; field++) {
2900           PetscInt num_comp, bdof, offset;
2901           PetscCall(PetscSectionGetFieldComponents(sectionGlobal, field, &num_comp));
2902           PetscCall(PetscSectionGetFieldDof(sectionGlobal, p, field, &dof));
2903           if (dof < 0) continue;
2904           PetscCall(PetscSectionGetFieldOffset(sectionGlobal, p, field, &offset));
2905           PetscCall(PetscSectionGetFieldConstraintDof(sectionGlobal, p, field, &cdof));
2906           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);
2907           PetscInt num_nodes = dof / num_comp;
2908           for (PetscInt i = 0; i < dof - cdof; i++) pblocks[offset - localStart + i] = (dof - cdof) / num_nodes;
2909           // Handle possibly constant block size (unlikely)
2910           bdof = cdof && (dof - cdof) ? 1 : dof;
2911           if (dof) {
2912             if (bs < 0) {
2913               bs = bdof;
2914             } else if (bs != bdof) {
2915               bs = 1;
2916             }
2917           }
2918         }
2919       } break;
2920       }
2921     }
2922     if (sectionLocal->perm) PetscCall(ISRestoreIndices(sectionLocal->perm, &perm));
2923     /* Must have same blocksize on all procs (some might have no points) */
2924     bsLocal[0] = bs < 0 ? PETSC_INT_MAX : bs;
2925     bsLocal[1] = bs;
2926     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
2927     if (bsMinMax[0] != bsMinMax[1]) bs = 1;
2928     else bs = bsMinMax[0];
2929     bs = PetscMax(1, bs);
2930     PetscCall(MatSetLocalToGlobalMapping(*J, ltog, ltog));
2931     if (dm->prealloc_skip) { // User will likely use MatSetPreallocationCOO(), but still set structural parameters
2932       PetscCall(MatSetBlockSize(*J, bs));
2933       PetscCall(MatSetUp(*J));
2934     } else {
2935       PetscCall(PetscCalloc4(localSize / bs, &dnz, localSize / bs, &onz, localSize / bs, &dnzu, localSize / bs, &onzu));
2936       PetscCall(DMPlexPreallocateOperator(dm, bs, dnz, onz, dnzu, onzu, *J, fillMatrix));
2937       PetscCall(PetscFree4(dnz, onz, dnzu, onzu));
2938     }
2939     if (pblocks) { // Consolidate blocks
2940       PetscInt nblocks = 0;
2941       pblocks[0]       = PetscAbs(pblocks[0]);
2942       for (PetscInt i = 0; i < localSize; i += PetscMax(1, pblocks[i])) {
2943         if (pblocks[i] == 0) continue;
2944         // Negative block size indicates the blocks should be concatenated
2945         if (pblocks[i] < 0) {
2946           pblocks[i] = -pblocks[i];
2947           pblocks[nblocks - 1] += pblocks[i];
2948         } else {
2949           pblocks[nblocks++] = pblocks[i]; // nblocks always <= i
2950         }
2951         for (PetscInt j = 1; j < pblocks[i]; j++)
2952           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);
2953       }
2954       PetscCall(MatSetVariableBlockSizes(*J, nblocks, pblocks));
2955     }
2956     PetscCall(PetscFree(pblocks));
2957   }
2958   PetscCall(MatSetDM(*J, dm));
2959   PetscFunctionReturn(PETSC_SUCCESS);
2960 }
2961 
2962 /*@
2963   DMPlexGetSubdomainSection - Returns the section associated with the subdomain
2964 
2965   Not Collective
2966 
2967   Input Parameter:
2968 . dm - The `DMPLEX`
2969 
2970   Output Parameter:
2971 . subsection - The subdomain section
2972 
2973   Level: developer
2974 
2975 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `PetscSection`
2976 @*/
2977 PetscErrorCode DMPlexGetSubdomainSection(DM dm, PetscSection *subsection)
2978 {
2979   DM_Plex *mesh = (DM_Plex *)dm->data;
2980 
2981   PetscFunctionBegin;
2982   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2983   if (!mesh->subdomainSection) {
2984     PetscSection section;
2985     PetscSF      sf;
2986 
2987     PetscCall(PetscSFCreate(PETSC_COMM_SELF, &sf));
2988     PetscCall(DMGetLocalSection(dm, &section));
2989     PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_TRUE, PETSC_FALSE, PETSC_TRUE, &mesh->subdomainSection));
2990     PetscCall(PetscSFDestroy(&sf));
2991   }
2992   *subsection = mesh->subdomainSection;
2993   PetscFunctionReturn(PETSC_SUCCESS);
2994 }
2995 
2996 /*@
2997   DMPlexGetChart - Return the interval for all mesh points [`pStart`, `pEnd`)
2998 
2999   Not Collective
3000 
3001   Input Parameter:
3002 . dm - The `DMPLEX`
3003 
3004   Output Parameters:
3005 + pStart - The first mesh point
3006 - pEnd   - The upper bound for mesh points
3007 
3008   Level: beginner
3009 
3010 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetChart()`
3011 @*/
3012 PetscErrorCode DMPlexGetChart(DM dm, PetscInt *pStart, PetscInt *pEnd)
3013 {
3014   DM_Plex *mesh = (DM_Plex *)dm->data;
3015 
3016   PetscFunctionBegin;
3017   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3018   if (mesh->tr) PetscCall(DMPlexTransformGetChart(mesh->tr, pStart, pEnd));
3019   else PetscCall(PetscSectionGetChart(mesh->coneSection, pStart, pEnd));
3020   PetscFunctionReturn(PETSC_SUCCESS);
3021 }
3022 
3023 /*@
3024   DMPlexSetChart - Set the interval for all mesh points [`pStart`, `pEnd`)
3025 
3026   Not Collective
3027 
3028   Input Parameters:
3029 + dm     - The `DMPLEX`
3030 . pStart - The first mesh point
3031 - pEnd   - The upper bound for mesh points
3032 
3033   Level: beginner
3034 
3035 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetChart()`
3036 @*/
3037 PetscErrorCode DMPlexSetChart(DM dm, PetscInt pStart, PetscInt pEnd)
3038 {
3039   DM_Plex *mesh = (DM_Plex *)dm->data;
3040 
3041   PetscFunctionBegin;
3042   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3043   PetscCall(PetscSectionSetChart(mesh->coneSection, pStart, pEnd));
3044   PetscCall(PetscSectionSetChart(mesh->supportSection, pStart, pEnd));
3045   PetscCall(PetscFree(mesh->cellTypes));
3046   PetscFunctionReturn(PETSC_SUCCESS);
3047 }
3048 
3049 /*@
3050   DMPlexGetConeSize - Return the number of in-edges for this point in the DAG
3051 
3052   Not Collective
3053 
3054   Input Parameters:
3055 + dm - The `DMPLEX`
3056 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3057 
3058   Output Parameter:
3059 . size - The cone size for point `p`
3060 
3061   Level: beginner
3062 
3063 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
3064 @*/
3065 PetscErrorCode DMPlexGetConeSize(DM dm, PetscInt p, PetscInt *size)
3066 {
3067   DM_Plex *mesh = (DM_Plex *)dm->data;
3068 
3069   PetscFunctionBegin;
3070   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3071   PetscAssertPointer(size, 3);
3072   if (mesh->tr) PetscCall(DMPlexTransformGetConeSize(mesh->tr, p, size));
3073   else PetscCall(PetscSectionGetDof(mesh->coneSection, p, size));
3074   PetscFunctionReturn(PETSC_SUCCESS);
3075 }
3076 
3077 /*@
3078   DMPlexSetConeSize - Set the number of in-edges for this point in the DAG
3079 
3080   Not Collective
3081 
3082   Input Parameters:
3083 + dm   - The `DMPLEX`
3084 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3085 - size - The cone size for point `p`
3086 
3087   Level: beginner
3088 
3089   Note:
3090   This should be called after `DMPlexSetChart()`.
3091 
3092 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetCone()`, `DMPlexCreate()`, `DMPlexGetConeSize()`, `DMPlexSetChart()`
3093 @*/
3094 PetscErrorCode DMPlexSetConeSize(DM dm, PetscInt p, PetscInt size)
3095 {
3096   DM_Plex *mesh = (DM_Plex *)dm->data;
3097 
3098   PetscFunctionBegin;
3099   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3100   PetscCheck(!mesh->tr, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Cannot call DMPlexSetConeSize() on a mesh with a transform defined.");
3101   PetscCall(PetscSectionSetDof(mesh->coneSection, p, size));
3102   PetscFunctionReturn(PETSC_SUCCESS);
3103 }
3104 
3105 /*@C
3106   DMPlexGetCone - Return the points on the in-edges for this point in the DAG
3107 
3108   Not Collective
3109 
3110   Input Parameters:
3111 + dm - The `DMPLEX`
3112 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3113 
3114   Output Parameter:
3115 . cone - An array of points which are on the in-edges for point `p`, the length of `cone` is the result of `DMPlexGetConeSize()`
3116 
3117   Level: beginner
3118 
3119   Fortran Notes:
3120   `cone` must be declared with
3121 .vb
3122   PetscInt, pointer :: cone(:)
3123 .ve
3124 
3125   You must also call `DMPlexRestoreCone()` after you finish using the array.
3126   `DMPlexRestoreCone()` is not needed/available in C.
3127 
3128 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSize()`, `DMPlexSetCone()`, `DMPlexGetConeTuple()`, `DMPlexSetChart()`, `DMPlexRestoreCone()`
3129 @*/
3130 PetscErrorCode DMPlexGetCone(DM dm, PetscInt p, const PetscInt *cone[])
3131 {
3132   DM_Plex *mesh = (DM_Plex *)dm->data;
3133   PetscInt off;
3134 
3135   PetscFunctionBegin;
3136   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3137   PetscAssertPointer(cone, 3);
3138   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3139   *cone = PetscSafePointerPlusOffset(mesh->cones, off);
3140   PetscFunctionReturn(PETSC_SUCCESS);
3141 }
3142 
3143 /*@
3144   DMPlexGetConeTuple - Return the points on the in-edges of several points in the DAG
3145 
3146   Not Collective
3147 
3148   Input Parameters:
3149 + dm - The `DMPLEX`
3150 - p  - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3151 
3152   Output Parameters:
3153 + pConesSection - `PetscSection` describing the layout of `pCones`
3154 - pCones        - An `IS` containing the points which are on the in-edges for the point set `p`
3155 
3156   Level: intermediate
3157 
3158 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeRecursive()`, `DMPlexSetChart()`, `PetscSection`, `IS`
3159 @*/
3160 PetscErrorCode DMPlexGetConeTuple(DM dm, IS p, PetscSection *pConesSection, IS *pCones)
3161 {
3162   PetscSection cs, newcs;
3163   PetscInt    *cones;
3164   PetscInt    *newarr = NULL;
3165   PetscInt     n;
3166 
3167   PetscFunctionBegin;
3168   PetscCall(DMPlexGetCones(dm, &cones));
3169   PetscCall(DMPlexGetConeSection(dm, &cs));
3170   PetscCall(PetscSectionExtractDofsFromArray(cs, MPIU_INT, cones, p, &newcs, pCones ? ((void **)&newarr) : NULL));
3171   if (pConesSection) *pConesSection = newcs;
3172   if (pCones) {
3173     PetscCall(PetscSectionGetStorageSize(newcs, &n));
3174     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)p), n, newarr, PETSC_OWN_POINTER, pCones));
3175   }
3176   PetscFunctionReturn(PETSC_SUCCESS);
3177 }
3178 
3179 /*@
3180   DMPlexGetConeRecursiveVertices - Expand each given point into its cone points and do that recursively until we end up just with vertices.
3181 
3182   Not Collective
3183 
3184   Input Parameters:
3185 + dm     - The `DMPLEX`
3186 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3187 
3188   Output Parameter:
3189 . expandedPoints - An `IS` containing the of vertices recursively expanded from input points
3190 
3191   Level: advanced
3192 
3193   Notes:
3194   Like `DMPlexGetConeRecursive()` but returns only the 0-depth `IS` (i.e. vertices only) and no sections.
3195 
3196   There is no corresponding Restore function, just call `ISDestroy()` on the returned `IS` to deallocate.
3197 
3198 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexRestoreConeRecursive()`,
3199           `DMPlexGetDepth()`, `IS`
3200 @*/
3201 PetscErrorCode DMPlexGetConeRecursiveVertices(DM dm, IS points, IS *expandedPoints)
3202 {
3203   IS      *expandedPointsAll;
3204   PetscInt depth;
3205 
3206   PetscFunctionBegin;
3207   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3208   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
3209   PetscAssertPointer(expandedPoints, 3);
3210   PetscCall(DMPlexGetConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
3211   *expandedPoints = expandedPointsAll[0];
3212   PetscCall(PetscObjectReference((PetscObject)expandedPointsAll[0]));
3213   PetscCall(DMPlexRestoreConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
3214   PetscFunctionReturn(PETSC_SUCCESS);
3215 }
3216 
3217 /*@
3218   DMPlexGetConeRecursive - Expand each given point into its cone points and do that recursively until we end up just with vertices
3219   (DAG points of depth 0, i.e., without cones).
3220 
3221   Not Collective
3222 
3223   Input Parameters:
3224 + dm     - The `DMPLEX`
3225 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3226 
3227   Output Parameters:
3228 + depth          - (optional) Size of the output arrays, equal to `DMPLEX` depth, returned by `DMPlexGetDepth()`
3229 . expandedPoints - (optional) An array of index sets with recursively expanded cones
3230 - sections       - (optional) An array of sections which describe mappings from points to their cone points
3231 
3232   Level: advanced
3233 
3234   Notes:
3235   Like `DMPlexGetConeTuple()` but recursive.
3236 
3237   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.
3238   For example, for d=0 it contains only vertices, for d=1 it can contain vertices and edges, etc.
3239 
3240   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\:
3241   (1) DAG points in `expandedPoints`[d+1] with `depth` d+1 to their cone points in `expandedPoints`[d];
3242   (2) DAG points in `expandedPoints`[d+1] with `depth` in [0,d] to the same points in `expandedPoints`[d].
3243 
3244 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexRestoreConeRecursive()`, `DMPlexGetConeRecursiveVertices()`,
3245           `DMPlexGetDepth()`, `PetscSection`, `IS`
3246 @*/
3247 PetscErrorCode DMPlexGetConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
3248 {
3249   const PetscInt *arr0 = NULL, *cone = NULL;
3250   PetscInt       *arr = NULL, *newarr = NULL;
3251   PetscInt        d, depth_, i, n, newn, cn, co, start, end;
3252   IS             *expandedPoints_;
3253   PetscSection   *sections_;
3254 
3255   PetscFunctionBegin;
3256   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3257   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
3258   if (depth) PetscAssertPointer(depth, 3);
3259   if (expandedPoints) PetscAssertPointer(expandedPoints, 4);
3260   if (sections) PetscAssertPointer(sections, 5);
3261   PetscCall(ISGetLocalSize(points, &n));
3262   PetscCall(ISGetIndices(points, &arr0));
3263   PetscCall(DMPlexGetDepth(dm, &depth_));
3264   PetscCall(PetscCalloc1(depth_, &expandedPoints_));
3265   PetscCall(PetscCalloc1(depth_, &sections_));
3266   arr = (PetscInt *)arr0; /* this is ok because first generation of arr is not modified */
3267   for (d = depth_ - 1; d >= 0; d--) {
3268     PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &sections_[d]));
3269     PetscCall(PetscSectionSetChart(sections_[d], 0, n));
3270     for (i = 0; i < n; i++) {
3271       PetscCall(DMPlexGetDepthStratum(dm, d + 1, &start, &end));
3272       if (arr[i] >= start && arr[i] < end) {
3273         PetscCall(DMPlexGetConeSize(dm, arr[i], &cn));
3274         PetscCall(PetscSectionSetDof(sections_[d], i, cn));
3275       } else {
3276         PetscCall(PetscSectionSetDof(sections_[d], i, 1));
3277       }
3278     }
3279     PetscCall(PetscSectionSetUp(sections_[d]));
3280     PetscCall(PetscSectionGetStorageSize(sections_[d], &newn));
3281     PetscCall(PetscMalloc1(newn, &newarr));
3282     for (i = 0; i < n; i++) {
3283       PetscCall(PetscSectionGetDof(sections_[d], i, &cn));
3284       PetscCall(PetscSectionGetOffset(sections_[d], i, &co));
3285       if (cn > 1) {
3286         PetscCall(DMPlexGetCone(dm, arr[i], &cone));
3287         PetscCall(PetscMemcpy(&newarr[co], cone, cn * sizeof(PetscInt)));
3288       } else {
3289         newarr[co] = arr[i];
3290       }
3291     }
3292     PetscCall(ISCreateGeneral(PETSC_COMM_SELF, newn, newarr, PETSC_OWN_POINTER, &expandedPoints_[d]));
3293     arr = newarr;
3294     n   = newn;
3295   }
3296   PetscCall(ISRestoreIndices(points, &arr0));
3297   *depth = depth_;
3298   if (expandedPoints) *expandedPoints = expandedPoints_;
3299   else {
3300     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&expandedPoints_[d]));
3301     PetscCall(PetscFree(expandedPoints_));
3302   }
3303   if (sections) *sections = sections_;
3304   else {
3305     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&sections_[d]));
3306     PetscCall(PetscFree(sections_));
3307   }
3308   PetscFunctionReturn(PETSC_SUCCESS);
3309 }
3310 
3311 /*@
3312   DMPlexRestoreConeRecursive - Deallocates arrays created by `DMPlexGetConeRecursive()`
3313 
3314   Not Collective
3315 
3316   Input Parameters:
3317 + dm     - The `DMPLEX`
3318 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3319 
3320   Output Parameters:
3321 + depth          - (optional) Size of the output arrays, equal to `DMPLEX` depth, returned by `DMPlexGetDepth()`
3322 . expandedPoints - (optional) An array of recursively expanded cones
3323 - sections       - (optional) An array of sections which describe mappings from points to their cone points
3324 
3325   Level: advanced
3326 
3327   Note:
3328   See `DMPlexGetConeRecursive()`
3329 
3330 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexGetConeRecursiveVertices()`,
3331           `DMPlexGetDepth()`, `IS`, `PetscSection`
3332 @*/
3333 PetscErrorCode DMPlexRestoreConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
3334 {
3335   PetscInt d, depth_;
3336 
3337   PetscFunctionBegin;
3338   PetscCall(DMPlexGetDepth(dm, &depth_));
3339   PetscCheck(!depth || *depth == depth_, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "depth changed since last call to DMPlexGetConeRecursive");
3340   if (depth) *depth = 0;
3341   if (expandedPoints) {
3342     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&(*expandedPoints)[d]));
3343     PetscCall(PetscFree(*expandedPoints));
3344   }
3345   if (sections) {
3346     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&(*sections)[d]));
3347     PetscCall(PetscFree(*sections));
3348   }
3349   PetscFunctionReturn(PETSC_SUCCESS);
3350 }
3351 
3352 /*@
3353   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
3354 
3355   Not Collective
3356 
3357   Input Parameters:
3358 + dm   - The `DMPLEX`
3359 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3360 - cone - An array of points which are on the in-edges for point `p`, its length must have been previously provided with `DMPlexSetConeSize()`
3361 
3362   Level: beginner
3363 
3364   Note:
3365   This should be called after all calls to `DMPlexSetConeSize()` and `DMSetUp()`.
3366 
3367 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`, `DMPlexSetSupport()`, `DMPlexSetSupportSize()`
3368 @*/
3369 PetscErrorCode DMPlexSetCone(DM dm, PetscInt p, const PetscInt cone[])
3370 {
3371   DM_Plex *mesh = (DM_Plex *)dm->data;
3372   PetscInt dof, off, c;
3373 
3374   PetscFunctionBegin;
3375   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3376   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3377   if (dof) PetscAssertPointer(cone, 3);
3378   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3379   if (PetscDefined(USE_DEBUG)) {
3380     PetscInt pStart, pEnd;
3381     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3382     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);
3383     for (c = 0; c < dof; ++c) {
3384       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);
3385       mesh->cones[off + c] = cone[c];
3386     }
3387   } else {
3388     for (c = 0; c < dof; ++c) mesh->cones[off + c] = cone[c];
3389   }
3390   PetscFunctionReturn(PETSC_SUCCESS);
3391 }
3392 
3393 /*@C
3394   DMPlexGetConeOrientation - Return the orientations on the in-edges for this point in the DAG
3395 
3396   Not Collective
3397 
3398   Input Parameters:
3399 + dm - The `DMPLEX`
3400 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3401 
3402   Output Parameter:
3403 . coneOrientation - An array of orientations which are on the in-edges for point `p`. An orientation is an
3404                     integer giving the prescription for cone traversal. Its length is given by the result of `DMPlexSetConeSize()`
3405 
3406   Level: beginner
3407 
3408   Note:
3409   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3410   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3411   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3412   with the identity.
3413 
3414   Fortran Notes:
3415   You must call `DMPlexRestoreConeOrientation()` after you finish using the returned array.
3416   `DMPlexRestoreConeOrientation()` is not needed/available in C.
3417 
3418 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetConeSize()`, `DMPolytopeTypeComposeOrientation()`, `DMPolytopeTypeComposeOrientationInv()`,
3419           `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetCone()`, `DMPlexSetChart()`
3420 @*/
3421 PetscErrorCode DMPlexGetConeOrientation(DM dm, PetscInt p, const PetscInt *coneOrientation[])
3422 {
3423   DM_Plex *mesh = (DM_Plex *)dm->data;
3424   PetscInt off;
3425 
3426   PetscFunctionBegin;
3427   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3428   if (PetscDefined(USE_DEBUG)) {
3429     PetscInt dof;
3430     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3431     if (dof) PetscAssertPointer(coneOrientation, 3);
3432   }
3433   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3434 
3435   *coneOrientation = &mesh->coneOrientations[off];
3436   PetscFunctionReturn(PETSC_SUCCESS);
3437 }
3438 
3439 /*@
3440   DMPlexSetConeOrientation - Set the orientations on the in-edges for this point in the DAG
3441 
3442   Not Collective
3443 
3444   Input Parameters:
3445 + dm              - The `DMPLEX`
3446 . p               - The point, which must lie in the chart set with `DMPlexSetChart()`
3447 - coneOrientation - An array of orientations. Its length is given by the result of `DMPlexSetConeSize()`
3448 
3449   Level: beginner
3450 
3451   Notes:
3452   This should be called after all calls to `DMPlexSetConeSize()` and `DMSetUp()`.
3453 
3454   The meaning of coneOrientation is detailed in `DMPlexGetConeOrientation()`.
3455 
3456 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetConeOrientation()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3457 @*/
3458 PetscErrorCode DMPlexSetConeOrientation(DM dm, PetscInt p, const PetscInt coneOrientation[])
3459 {
3460   DM_Plex *mesh = (DM_Plex *)dm->data;
3461   PetscInt pStart, pEnd;
3462   PetscInt dof, off, c;
3463 
3464   PetscFunctionBegin;
3465   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3466   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3467   if (dof) PetscAssertPointer(coneOrientation, 3);
3468   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3469   if (PetscDefined(USE_DEBUG)) {
3470     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3471     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);
3472     for (c = 0; c < dof; ++c) {
3473       PetscInt cdof, o = coneOrientation[c];
3474 
3475       PetscCall(PetscSectionGetDof(mesh->coneSection, mesh->cones[off + c], &cdof));
3476       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);
3477       mesh->coneOrientations[off + c] = o;
3478     }
3479   } else {
3480     for (c = 0; c < dof; ++c) mesh->coneOrientations[off + c] = coneOrientation[c];
3481   }
3482   PetscFunctionReturn(PETSC_SUCCESS);
3483 }
3484 
3485 /*@
3486   DMPlexInsertCone - Insert a point into the in-edges for the point p in the DAG
3487 
3488   Not Collective
3489 
3490   Input Parameters:
3491 + dm        - The `DMPLEX`
3492 . p         - The point, which must lie in the chart set with `DMPlexSetChart()`
3493 . conePos   - The local index in the cone where the point should be put
3494 - conePoint - The mesh point to insert
3495 
3496   Level: beginner
3497 
3498 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3499 @*/
3500 PetscErrorCode DMPlexInsertCone(DM dm, PetscInt p, PetscInt conePos, PetscInt conePoint)
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     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);
3512     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3513     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);
3514   }
3515   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3516   mesh->cones[off + conePos] = conePoint;
3517   PetscFunctionReturn(PETSC_SUCCESS);
3518 }
3519 
3520 /*@
3521   DMPlexInsertConeOrientation - Insert a point orientation for the in-edge for the point p in the DAG
3522 
3523   Not Collective
3524 
3525   Input Parameters:
3526 + dm              - The `DMPLEX`
3527 . p               - The point, which must lie in the chart set with `DMPlexSetChart()`
3528 . conePos         - The local index in the cone where the point should be put
3529 - coneOrientation - The point orientation to insert
3530 
3531   Level: beginner
3532 
3533   Note:
3534   The meaning of coneOrientation values is detailed in `DMPlexGetConeOrientation()`.
3535 
3536 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3537 @*/
3538 PetscErrorCode DMPlexInsertConeOrientation(DM dm, PetscInt p, PetscInt conePos, PetscInt coneOrientation)
3539 {
3540   DM_Plex *mesh = (DM_Plex *)dm->data;
3541   PetscInt pStart, pEnd;
3542   PetscInt dof, off;
3543 
3544   PetscFunctionBegin;
3545   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3546   if (PetscDefined(USE_DEBUG)) {
3547     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3548     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);
3549     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3550     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);
3551   }
3552   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3553   mesh->coneOrientations[off + conePos] = coneOrientation;
3554   PetscFunctionReturn(PETSC_SUCCESS);
3555 }
3556 
3557 /*@C
3558   DMPlexGetOrientedCone - Return the points and orientations on the in-edges for this point in the DAG
3559 
3560   Not collective
3561 
3562   Input Parameters:
3563 + dm - The DMPlex
3564 - p  - The point, which must lie in the chart set with DMPlexSetChart()
3565 
3566   Output Parameters:
3567 + cone - An array of points which are on the in-edges for point `p`
3568 - ornt - An array of orientations which are on the in-edges for point `p`. An orientation is an
3569          integer giving the prescription for cone traversal.
3570 
3571   Level: beginner
3572 
3573   Notes:
3574   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3575   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3576   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3577   with the identity.
3578 
3579   You must also call `DMPlexRestoreOrientedCone()` after you finish using the returned array.
3580 
3581   Fortran Notes:
3582   `cone` and `ornt` must be declared with
3583 .vb
3584   PetscInt, pointer :: cone(:)
3585   PetscInt, pointer :: ornt(:)
3586 .ve
3587 
3588 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreOrientedCone()`, `DMPlexGetConeSize()`, `DMPlexGetCone()`, `DMPlexGetChart()`
3589 @*/
3590 PetscErrorCode DMPlexGetOrientedCone(DM dm, PetscInt p, const PetscInt *cone[], const PetscInt *ornt[])
3591 {
3592   DM_Plex *mesh = (DM_Plex *)dm->data;
3593 
3594   PetscFunctionBegin;
3595   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3596   if (mesh->tr) {
3597     PetscCall(DMPlexTransformGetCone(mesh->tr, p, cone, ornt));
3598   } else {
3599     PetscInt off;
3600     if (PetscDefined(USE_DEBUG)) {
3601       PetscInt dof;
3602       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3603       if (dof) {
3604         if (cone) PetscAssertPointer(cone, 3);
3605         if (ornt) PetscAssertPointer(ornt, 4);
3606       }
3607     }
3608     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3609     if (cone) *cone = PetscSafePointerPlusOffset(mesh->cones, off);
3610     if (ornt) *ornt = PetscSafePointerPlusOffset(mesh->coneOrientations, off);
3611   }
3612   PetscFunctionReturn(PETSC_SUCCESS);
3613 }
3614 
3615 /*@C
3616   DMPlexRestoreOrientedCone - Restore the points and orientations on the in-edges for this point in the DAG obtained with `DMPlexGetOrientedCone()`
3617 
3618   Not Collective
3619 
3620   Input Parameters:
3621 + dm   - The DMPlex
3622 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3623 . cone - An array of points which are on the in-edges for point p
3624 - ornt - An array of orientations which are on the in-edges for point `p`. An orientation is an
3625          integer giving the prescription for cone traversal.
3626 
3627   Level: beginner
3628 
3629 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetOrientedCone()`, `DMPlexGetConeSize()`, `DMPlexGetCone()`, `DMPlexGetChart()`
3630 @*/
3631 PetscErrorCode DMPlexRestoreOrientedCone(DM dm, PetscInt p, const PetscInt *cone[], const PetscInt *ornt[])
3632 {
3633   DM_Plex *mesh = (DM_Plex *)dm->data;
3634 
3635   PetscFunctionBegin;
3636   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3637   if (mesh->tr) PetscCall(DMPlexTransformRestoreCone(mesh->tr, p, cone, ornt));
3638   PetscFunctionReturn(PETSC_SUCCESS);
3639 }
3640 
3641 /*@
3642   DMPlexGetSupportSize - Return the number of out-edges for this point in the DAG
3643 
3644   Not Collective
3645 
3646   Input Parameters:
3647 + dm - The `DMPLEX`
3648 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3649 
3650   Output Parameter:
3651 . size - The support size for point `p`
3652 
3653   Level: beginner
3654 
3655 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`, `DMPlexGetConeSize()`
3656 @*/
3657 PetscErrorCode DMPlexGetSupportSize(DM dm, PetscInt p, PetscInt *size)
3658 {
3659   DM_Plex *mesh = (DM_Plex *)dm->data;
3660 
3661   PetscFunctionBegin;
3662   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3663   PetscAssertPointer(size, 3);
3664   PetscCall(PetscSectionGetDof(mesh->supportSection, p, size));
3665   PetscFunctionReturn(PETSC_SUCCESS);
3666 }
3667 
3668 /*@
3669   DMPlexSetSupportSize - Set the number of out-edges for this point in the DAG
3670 
3671   Not Collective
3672 
3673   Input Parameters:
3674 + dm   - The `DMPLEX`
3675 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3676 - size - The support size for point `p`
3677 
3678   Level: beginner
3679 
3680   Note:
3681   This should be called after `DMPlexSetChart()`.
3682 
3683 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetSupportSize()`, `DMPlexSetChart()`
3684 @*/
3685 PetscErrorCode DMPlexSetSupportSize(DM dm, PetscInt p, PetscInt size)
3686 {
3687   DM_Plex *mesh = (DM_Plex *)dm->data;
3688 
3689   PetscFunctionBegin;
3690   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3691   PetscCall(PetscSectionSetDof(mesh->supportSection, p, size));
3692   PetscFunctionReturn(PETSC_SUCCESS);
3693 }
3694 
3695 /*@C
3696   DMPlexGetSupport - Return the points on the out-edges for this point in the DAG
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 
3704   Output Parameter:
3705 . support - An array of points which are on the out-edges for point `p`, its length is that obtained from `DMPlexGetSupportSize()`
3706 
3707   Level: beginner
3708 
3709   Fortran Notes:
3710   `support` must be declared with
3711 .vb
3712   PetscInt, pointer :: support(:)
3713 .ve
3714 
3715   You must also call `DMPlexRestoreSupport()` after you finish using the returned array.
3716   `DMPlexRestoreSupport()` is not needed/available in C.
3717 
3718 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSupportSize()`, `DMPlexSetSupport()`, `DMPlexGetCone()`, `DMPlexSetChart()`
3719 @*/
3720 PetscErrorCode DMPlexGetSupport(DM dm, PetscInt p, const PetscInt *support[])
3721 {
3722   DM_Plex *mesh = (DM_Plex *)dm->data;
3723   PetscInt off;
3724 
3725   PetscFunctionBegin;
3726   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3727   PetscAssertPointer(support, 3);
3728   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3729   *support = PetscSafePointerPlusOffset(mesh->supports, off);
3730   PetscFunctionReturn(PETSC_SUCCESS);
3731 }
3732 
3733 /*@
3734   DMPlexSetSupport - Set the points on the out-edges for this point in the DAG, that is the list of points that this point covers
3735 
3736   Not Collective
3737 
3738   Input Parameters:
3739 + dm      - The `DMPLEX`
3740 . p       - The point, which must lie in the chart set with `DMPlexSetChart()`
3741 - support - An array of points which are on the out-edges for point `p`, its length is that obtained from `DMPlexGetSupportSize()`
3742 
3743   Level: beginner
3744 
3745   Note:
3746   This should be called after all calls to `DMPlexSetSupportSize()` and `DMSetUp()`.
3747 
3748 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetCone()`, `DMPlexSetConeSize()`, `DMPlexCreate()`, `DMPlexGetSupport()`, `DMPlexSetChart()`, `DMPlexSetSupportSize()`, `DMSetUp()`
3749 @*/
3750 PetscErrorCode DMPlexSetSupport(DM dm, PetscInt p, const PetscInt support[])
3751 {
3752   DM_Plex *mesh = (DM_Plex *)dm->data;
3753   PetscInt pStart, pEnd;
3754   PetscInt dof, off, c;
3755 
3756   PetscFunctionBegin;
3757   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3758   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3759   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3760   if (dof) PetscAssertPointer(support, 3);
3761   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3762   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);
3763   for (c = 0; c < dof; ++c) {
3764     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);
3765     mesh->supports[off + c] = support[c];
3766   }
3767   PetscFunctionReturn(PETSC_SUCCESS);
3768 }
3769 
3770 /*@
3771   DMPlexInsertSupport - Insert a point into the out-edges for the point p in the DAG
3772 
3773   Not Collective
3774 
3775   Input Parameters:
3776 + dm           - The `DMPLEX`
3777 . p            - The point, which must lie in the chart set with `DMPlexSetChart()`
3778 . supportPos   - The local index in the cone where the point should be put
3779 - supportPoint - The mesh point to insert
3780 
3781   Level: beginner
3782 
3783 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3784 @*/
3785 PetscErrorCode DMPlexInsertSupport(DM dm, PetscInt p, PetscInt supportPos, PetscInt supportPoint)
3786 {
3787   DM_Plex *mesh = (DM_Plex *)dm->data;
3788   PetscInt pStart, pEnd;
3789   PetscInt dof, off;
3790 
3791   PetscFunctionBegin;
3792   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3793   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3794   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3795   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3796   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);
3797   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);
3798   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);
3799   mesh->supports[off + supportPos] = supportPoint;
3800   PetscFunctionReturn(PETSC_SUCCESS);
3801 }
3802 
3803 /* Converts an orientation o in the current numbering to the previous scheme used in Plex */
3804 PetscInt DMPolytopeConvertNewOrientation_Internal(DMPolytopeType ct, PetscInt o)
3805 {
3806   switch (ct) {
3807   case DM_POLYTOPE_SEGMENT:
3808     if (o == -1) return -2;
3809     break;
3810   case DM_POLYTOPE_TRIANGLE:
3811     if (o == -3) return -1;
3812     if (o == -2) return -3;
3813     if (o == -1) return -2;
3814     break;
3815   case DM_POLYTOPE_QUADRILATERAL:
3816     if (o == -4) return -2;
3817     if (o == -3) return -1;
3818     if (o == -2) return -4;
3819     if (o == -1) return -3;
3820     break;
3821   default:
3822     return o;
3823   }
3824   return o;
3825 }
3826 
3827 /* Converts an orientation o in the previous scheme used in Plex to the current numbering */
3828 PetscInt DMPolytopeConvertOldOrientation_Internal(DMPolytopeType ct, PetscInt o)
3829 {
3830   switch (ct) {
3831   case DM_POLYTOPE_SEGMENT:
3832     if ((o == -2) || (o == 1)) return -1;
3833     if (o == -1) return 0;
3834     break;
3835   case DM_POLYTOPE_TRIANGLE:
3836     if (o == -3) return -2;
3837     if (o == -2) return -1;
3838     if (o == -1) return -3;
3839     break;
3840   case DM_POLYTOPE_QUADRILATERAL:
3841     if (o == -4) return -2;
3842     if (o == -3) return -1;
3843     if (o == -2) return -4;
3844     if (o == -1) return -3;
3845     break;
3846   default:
3847     return o;
3848   }
3849   return o;
3850 }
3851 
3852 /* Takes in a mesh whose orientations are in the previous scheme and converts them all to the current numbering */
3853 PetscErrorCode DMPlexConvertOldOrientations_Internal(DM dm)
3854 {
3855   PetscInt pStart, pEnd, p;
3856 
3857   PetscFunctionBegin;
3858   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3859   for (p = pStart; p < pEnd; ++p) {
3860     const PetscInt *cone, *ornt;
3861     PetscInt        coneSize, c;
3862 
3863     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
3864     PetscCall(DMPlexGetCone(dm, p, &cone));
3865     PetscCall(DMPlexGetConeOrientation(dm, p, &ornt));
3866     for (c = 0; c < coneSize; ++c) {
3867       DMPolytopeType ct;
3868       const PetscInt o = ornt[c];
3869 
3870       PetscCall(DMPlexGetCellType(dm, cone[c], &ct));
3871       switch (ct) {
3872       case DM_POLYTOPE_SEGMENT:
3873         if ((o == -2) || (o == 1)) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3874         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, 0));
3875         break;
3876       case DM_POLYTOPE_TRIANGLE:
3877         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3878         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3879         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3880         break;
3881       case DM_POLYTOPE_QUADRILATERAL:
3882         if (o == -4) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3883         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3884         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -4));
3885         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3886         break;
3887       default:
3888         break;
3889       }
3890     }
3891   }
3892   PetscFunctionReturn(PETSC_SUCCESS);
3893 }
3894 
3895 static inline PetscErrorCode DMPlexGetTransitiveClosure_Hot_Private(DM dm, PetscInt p, PetscBool useCone, PetscInt *size, const PetscInt *arr[], const PetscInt *ornt[])
3896 {
3897   DM_Plex *mesh = (DM_Plex *)dm->data;
3898 
3899   PetscFunctionBeginHot;
3900   if (PetscDefined(USE_DEBUG) || mesh->tr) {
3901     if (useCone) {
3902       PetscCall(DMPlexGetConeSize(dm, p, size));
3903       PetscCall(DMPlexGetOrientedCone(dm, p, arr, ornt));
3904     } else {
3905       PetscCall(DMPlexGetSupportSize(dm, p, size));
3906       PetscCall(DMPlexGetSupport(dm, p, arr));
3907     }
3908   } else {
3909     if (useCone) {
3910       const PetscSection s   = mesh->coneSection;
3911       const PetscInt     ps  = p - s->pStart;
3912       const PetscInt     off = s->atlasOff[ps];
3913 
3914       *size = s->atlasDof[ps];
3915       *arr  = mesh->cones + off;
3916       *ornt = mesh->coneOrientations + off;
3917     } else {
3918       const PetscSection s   = mesh->supportSection;
3919       const PetscInt     ps  = p - s->pStart;
3920       const PetscInt     off = s->atlasOff[ps];
3921 
3922       *size = s->atlasDof[ps];
3923       *arr  = mesh->supports + off;
3924     }
3925   }
3926   PetscFunctionReturn(PETSC_SUCCESS);
3927 }
3928 
3929 static inline PetscErrorCode DMPlexRestoreTransitiveClosure_Hot_Private(DM dm, PetscInt p, PetscBool useCone, PetscInt *size, const PetscInt *arr[], const PetscInt *ornt[])
3930 {
3931   DM_Plex *mesh = (DM_Plex *)dm->data;
3932 
3933   PetscFunctionBeginHot;
3934   if (PetscDefined(USE_DEBUG) || mesh->tr) {
3935     if (useCone) PetscCall(DMPlexRestoreOrientedCone(dm, p, arr, ornt));
3936   }
3937   PetscFunctionReturn(PETSC_SUCCESS);
3938 }
3939 
3940 static PetscErrorCode DMPlexGetTransitiveClosure_Depth1_Private(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3941 {
3942   DMPolytopeType  ct = DM_POLYTOPE_UNKNOWN;
3943   PetscInt       *closure;
3944   const PetscInt *tmp = NULL, *tmpO = NULL;
3945   PetscInt        off = 0, tmpSize, t;
3946 
3947   PetscFunctionBeginHot;
3948   if (ornt) {
3949     PetscCall(DMPlexGetCellType(dm, p, &ct));
3950     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;
3951   }
3952   if (*points) {
3953     closure = *points;
3954   } else {
3955     PetscInt maxConeSize, maxSupportSize;
3956     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3957     PetscCall(DMGetWorkArray(dm, 2 * (PetscMax(maxConeSize, maxSupportSize) + 1), MPIU_INT, &closure));
3958   }
3959   PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, p, useCone, &tmpSize, &tmp, &tmpO));
3960   if (ct == DM_POLYTOPE_UNKNOWN) {
3961     closure[off++] = p;
3962     closure[off++] = 0;
3963     for (t = 0; t < tmpSize; ++t) {
3964       closure[off++] = tmp[t];
3965       closure[off++] = tmpO ? tmpO[t] : 0;
3966     }
3967   } else {
3968     const PetscInt *arr = DMPolytopeTypeGetArrangement(ct, ornt);
3969 
3970     /* We assume that cells with a valid type have faces with a valid type */
3971     closure[off++] = p;
3972     closure[off++] = ornt;
3973     for (t = 0; t < tmpSize; ++t) {
3974       DMPolytopeType ft;
3975 
3976       PetscCall(DMPlexGetCellType(dm, tmp[t], &ft));
3977       closure[off++] = tmp[arr[t]];
3978       closure[off++] = tmpO ? DMPolytopeTypeComposeOrientation(ft, ornt, tmpO[t]) : 0;
3979     }
3980   }
3981   PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, p, useCone, &tmpSize, &tmp, &tmpO));
3982   if (numPoints) *numPoints = tmpSize + 1;
3983   if (points) *points = closure;
3984   PetscFunctionReturn(PETSC_SUCCESS);
3985 }
3986 
3987 /* We need a special tensor version because we want to allow duplicate points in the endcaps for hybrid cells */
3988 static PetscErrorCode DMPlexTransitiveClosure_Tensor_Internal(DM dm, PetscInt point, DMPolytopeType ct, PetscInt o, PetscBool useCone, PetscInt *numPoints, PetscInt **points)
3989 {
3990   const PetscInt *arr = DMPolytopeTypeGetArrangement(ct, o);
3991   const PetscInt *cone, *ornt;
3992   PetscInt       *pts, *closure = NULL;
3993   DMPolytopeType  ft;
3994   PetscInt        maxConeSize, maxSupportSize, coneSeries, supportSeries, maxSize;
3995   PetscInt        dim, coneSize, c, d, clSize, cl;
3996 
3997   PetscFunctionBeginHot;
3998   PetscCall(DMGetDimension(dm, &dim));
3999   PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, point, PETSC_TRUE, &coneSize, &cone, &ornt));
4000   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
4001   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, dim + 1) - 1) / (maxConeSize - 1)) : dim + 1;
4002   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, dim + 1) - 1) / (maxSupportSize - 1)) : dim + 1;
4003   maxSize       = PetscMax(coneSeries, supportSeries);
4004   if (*points) {
4005     pts = *points;
4006   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &pts));
4007   c        = 0;
4008   pts[c++] = point;
4009   pts[c++] = o;
4010   PetscCall(DMPlexGetCellType(dm, cone[arr[0 * 2 + 0]], &ft));
4011   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[0 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[0 * 2 + 1], ornt[0]), useCone, &clSize, &closure));
4012   for (cl = 0; cl < clSize * 2; cl += 2) {
4013     pts[c++] = closure[cl];
4014     pts[c++] = closure[cl + 1];
4015   }
4016   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[1 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[1 * 2 + 1], ornt[1]), useCone, &clSize, &closure));
4017   for (cl = 0; cl < clSize * 2; cl += 2) {
4018     pts[c++] = closure[cl];
4019     pts[c++] = closure[cl + 1];
4020   }
4021   PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[0], useCone, &clSize, &closure));
4022   for (d = 2; d < coneSize; ++d) {
4023     PetscCall(DMPlexGetCellType(dm, cone[arr[d * 2 + 0]], &ft));
4024     pts[c++] = cone[arr[d * 2 + 0]];
4025     pts[c++] = DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]);
4026   }
4027   PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, point, PETSC_TRUE, &coneSize, &cone, &ornt));
4028   if (dim >= 3) {
4029     for (d = 2; d < coneSize; ++d) {
4030       const PetscInt  fpoint = cone[arr[d * 2 + 0]];
4031       const PetscInt *fcone, *fornt;
4032       PetscInt        fconeSize, fc, i;
4033 
4034       PetscCall(DMPlexGetCellType(dm, fpoint, &ft));
4035       const PetscInt *farr = DMPolytopeTypeGetArrangement(ft, DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]));
4036       PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, fpoint, PETSC_TRUE, &fconeSize, &fcone, &fornt));
4037       for (fc = 0; fc < fconeSize; ++fc) {
4038         const PetscInt cp = fcone[farr[fc * 2 + 0]];
4039         const PetscInt co = farr[fc * 2 + 1];
4040 
4041         for (i = 0; i < c; i += 2)
4042           if (pts[i] == cp) break;
4043         if (i == c) {
4044           PetscCall(DMPlexGetCellType(dm, cp, &ft));
4045           pts[c++] = cp;
4046           pts[c++] = DMPolytopeTypeComposeOrientation(ft, co, fornt[farr[fc * 2 + 0]]);
4047         }
4048       }
4049       PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, fpoint, PETSC_TRUE, &fconeSize, &fcone, &fornt));
4050     }
4051   }
4052   *numPoints = c / 2;
4053   *points    = pts;
4054   PetscFunctionReturn(PETSC_SUCCESS);
4055 }
4056 
4057 PetscErrorCode DMPlexGetTransitiveClosure_Internal(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
4058 {
4059   DMPolytopeType ct;
4060   PetscInt      *closure, *fifo;
4061   PetscInt       closureSize = 0, fifoStart = 0, fifoSize = 0;
4062   PetscInt       maxConeSize, maxSupportSize, coneSeries, supportSeries;
4063   PetscInt       depth, maxSize;
4064 
4065   PetscFunctionBeginHot;
4066   PetscCall(DMPlexGetDepth(dm, &depth));
4067   if (depth == 1) {
4068     PetscCall(DMPlexGetTransitiveClosure_Depth1_Private(dm, p, ornt, useCone, numPoints, points));
4069     PetscFunctionReturn(PETSC_SUCCESS);
4070   }
4071   PetscCall(DMPlexGetCellType(dm, p, &ct));
4072   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;
4073   if (DMPolytopeTypeIsHybrid(ct) && ct != DM_POLYTOPE_POINT_PRISM_TENSOR) {
4074     PetscCall(DMPlexTransitiveClosure_Tensor_Internal(dm, p, ct, ornt, useCone, numPoints, points));
4075     PetscFunctionReturn(PETSC_SUCCESS);
4076   }
4077   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
4078   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, depth + 1) - 1) / (maxConeSize - 1)) : depth + 1;
4079   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, depth + 1) - 1) / (maxSupportSize - 1)) : depth + 1;
4080   maxSize       = PetscMax(coneSeries, supportSeries);
4081   PetscCall(DMGetWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
4082   if (*points) {
4083     closure = *points;
4084   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &closure));
4085   closure[closureSize++] = p;
4086   closure[closureSize++] = ornt;
4087   fifo[fifoSize++]       = p;
4088   fifo[fifoSize++]       = ornt;
4089   fifo[fifoSize++]       = ct;
4090   /* Should kick out early when depth is reached, rather than checking all vertices for empty cones */
4091   while (fifoSize - fifoStart) {
4092     const PetscInt       q    = fifo[fifoStart++];
4093     const PetscInt       o    = fifo[fifoStart++];
4094     const DMPolytopeType qt   = (DMPolytopeType)fifo[fifoStart++];
4095     const PetscInt      *qarr = DMPolytopeTypeGetArrangement(qt, o);
4096     const PetscInt      *tmp, *tmpO = NULL;
4097     PetscInt             tmpSize, t;
4098 
4099     if (PetscDefined(USE_DEBUG)) {
4100       PetscInt nO = DMPolytopeTypeGetNumArrangements(qt) / 2;
4101       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);
4102     }
4103     PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, q, useCone, &tmpSize, &tmp, &tmpO));
4104     for (t = 0; t < tmpSize; ++t) {
4105       const PetscInt ip = useCone && qarr ? qarr[t * 2] : t;
4106       const PetscInt io = useCone && qarr ? qarr[t * 2 + 1] : 0;
4107       const PetscInt cp = tmp[ip];
4108       PetscCall(DMPlexGetCellType(dm, cp, &ct));
4109       const PetscInt co = tmpO ? DMPolytopeTypeComposeOrientation(ct, io, tmpO[ip]) : 0;
4110       PetscInt       c;
4111 
4112       /* Check for duplicate */
4113       for (c = 0; c < closureSize; c += 2) {
4114         if (closure[c] == cp) break;
4115       }
4116       if (c == closureSize) {
4117         closure[closureSize++] = cp;
4118         closure[closureSize++] = co;
4119         fifo[fifoSize++]       = cp;
4120         fifo[fifoSize++]       = co;
4121         fifo[fifoSize++]       = ct;
4122       }
4123     }
4124     PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, q, useCone, &tmpSize, &tmp, &tmpO));
4125   }
4126   PetscCall(DMRestoreWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
4127   if (numPoints) *numPoints = closureSize / 2;
4128   if (points) *points = closure;
4129   PetscFunctionReturn(PETSC_SUCCESS);
4130 }
4131 
4132 /*@C
4133   DMPlexGetTransitiveClosure - Return the points on the transitive closure of the in-edges or out-edges for this point in the DAG
4134 
4135   Not Collective
4136 
4137   Input Parameters:
4138 + dm      - The `DMPLEX`
4139 . p       - The mesh point
4140 - useCone - `PETSC_TRUE` for the closure, otherwise return the star
4141 
4142   Input/Output Parameter:
4143 . points - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...];
4144            if *points is `NULL` on input, internal storage will be returned, use `DMPlexRestoreTransitiveClosure()`,
4145            otherwise the provided array is used to hold the values
4146 
4147   Output Parameter:
4148 . numPoints - The number of points in the closure, so `points` is of size 2*`numPoints`
4149 
4150   Level: beginner
4151 
4152   Note:
4153   If using internal storage (points is `NULL` on input), each call overwrites the last output.
4154 
4155   Fortran Notes:
4156   `points` must be declared with
4157 .vb
4158   PetscInt, pointer :: points(:)
4159 .ve
4160   and is always allocated by the function.
4161 
4162   The `numPoints` argument is not present in the Fortran binding.
4163 
4164 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
4165 @*/
4166 PetscErrorCode DMPlexGetTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
4167 {
4168   PetscFunctionBeginHot;
4169   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4170   if (numPoints) PetscAssertPointer(numPoints, 4);
4171   if (points) PetscAssertPointer(points, 5);
4172   if (PetscDefined(USE_DEBUG)) {
4173     PetscInt pStart, pEnd;
4174     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4175     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);
4176   }
4177   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, p, 0, useCone, numPoints, points));
4178   PetscFunctionReturn(PETSC_SUCCESS);
4179 }
4180 
4181 /*@C
4182   DMPlexRestoreTransitiveClosure - Restore the array of points on the transitive closure of the in-edges or out-edges for this point in the DAG
4183 
4184   Not Collective
4185 
4186   Input Parameters:
4187 + dm        - The `DMPLEX`
4188 . p         - The mesh point
4189 . useCone   - `PETSC_TRUE` for the closure, otherwise return the star
4190 . numPoints - The number of points in the closure, so points[] is of size 2*`numPoints`
4191 - points    - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...]
4192 
4193   Level: beginner
4194 
4195   Note:
4196   If not using internal storage (points is not `NULL` on input), this call is unnecessary
4197 
4198 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
4199 @*/
4200 PetscErrorCode DMPlexRestoreTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
4201 {
4202   PetscFunctionBeginHot;
4203   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4204   if (numPoints) *numPoints = 0;
4205   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, points));
4206   PetscFunctionReturn(PETSC_SUCCESS);
4207 }
4208 
4209 /*@
4210   DMPlexGetMaxSizes - Return the maximum number of in-edges (cone) and out-edges (support) for any point in the DAG
4211 
4212   Not Collective
4213 
4214   Input Parameter:
4215 . dm - The `DMPLEX`
4216 
4217   Output Parameters:
4218 + maxConeSize    - The maximum number of in-edges
4219 - maxSupportSize - The maximum number of out-edges
4220 
4221   Level: beginner
4222 
4223 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
4224 @*/
4225 PetscErrorCode DMPlexGetMaxSizes(DM dm, PetscInt *maxConeSize, PetscInt *maxSupportSize)
4226 {
4227   DM_Plex *mesh = (DM_Plex *)dm->data;
4228 
4229   PetscFunctionBegin;
4230   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4231   if (maxConeSize) PetscCall(PetscSectionGetMaxDof(mesh->coneSection, maxConeSize));
4232   if (maxSupportSize) PetscCall(PetscSectionGetMaxDof(mesh->supportSection, maxSupportSize));
4233   PetscFunctionReturn(PETSC_SUCCESS);
4234 }
4235 
4236 PetscErrorCode DMSetUp_Plex(DM dm)
4237 {
4238   DM_Plex *mesh = (DM_Plex *)dm->data;
4239   PetscInt size, maxSupportSize;
4240 
4241   PetscFunctionBegin;
4242   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4243   PetscCall(PetscSectionSetUp(mesh->coneSection));
4244   PetscCall(PetscSectionGetStorageSize(mesh->coneSection, &size));
4245   PetscCall(PetscMalloc1(size, &mesh->cones));
4246   PetscCall(PetscCalloc1(size, &mesh->coneOrientations));
4247   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
4248   if (maxSupportSize) {
4249     PetscCall(PetscSectionSetUp(mesh->supportSection));
4250     PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &size));
4251     PetscCall(PetscMalloc1(size, &mesh->supports));
4252   }
4253   PetscFunctionReturn(PETSC_SUCCESS);
4254 }
4255 
4256 PetscErrorCode DMCreateSubDM_Plex(DM dm, PetscInt numFields, const PetscInt fields[], IS *is, DM *subdm)
4257 {
4258   PetscFunctionBegin;
4259   if (subdm) PetscCall(DMClone(dm, subdm));
4260   PetscCall(DMCreateSectionSubDM(dm, numFields, fields, NULL, NULL, is, subdm));
4261   if (subdm) (*subdm)->useNatural = dm->useNatural;
4262   if (dm->useNatural && dm->sfMigration) {
4263     PetscSF sfNatural;
4264 
4265     (*subdm)->sfMigration = dm->sfMigration;
4266     PetscCall(PetscObjectReference((PetscObject)dm->sfMigration));
4267     PetscCall(DMPlexCreateGlobalToNaturalSF(*subdm, NULL, (*subdm)->sfMigration, &sfNatural));
4268     (*subdm)->sfNatural = sfNatural;
4269   }
4270   PetscFunctionReturn(PETSC_SUCCESS);
4271 }
4272 
4273 PetscErrorCode DMCreateSuperDM_Plex(DM dms[], PetscInt len, IS **is, DM *superdm)
4274 {
4275   PetscInt i = 0;
4276 
4277   PetscFunctionBegin;
4278   PetscCall(DMClone(dms[0], superdm));
4279   PetscCall(DMCreateSectionSuperDM(dms, len, is, superdm));
4280   (*superdm)->useNatural = PETSC_FALSE;
4281   for (i = 0; i < len; i++) {
4282     if (dms[i]->useNatural && dms[i]->sfMigration) {
4283       PetscSF sfNatural;
4284 
4285       (*superdm)->sfMigration = dms[i]->sfMigration;
4286       PetscCall(PetscObjectReference((PetscObject)dms[i]->sfMigration));
4287       (*superdm)->useNatural = PETSC_TRUE;
4288       PetscCall(DMPlexCreateGlobalToNaturalSF(*superdm, NULL, (*superdm)->sfMigration, &sfNatural));
4289       (*superdm)->sfNatural = sfNatural;
4290       break;
4291     }
4292   }
4293   PetscFunctionReturn(PETSC_SUCCESS);
4294 }
4295 
4296 /*@
4297   DMPlexSymmetrize - Create support (out-edge) information from cone (in-edge) information
4298 
4299   Not Collective
4300 
4301   Input Parameter:
4302 . dm - The `DMPLEX`
4303 
4304   Level: beginner
4305 
4306   Note:
4307   This should be called after all calls to `DMPlexSetCone()`
4308 
4309 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMPlexSetCone()`
4310 @*/
4311 PetscErrorCode DMPlexSymmetrize(DM dm)
4312 {
4313   DM_Plex  *mesh = (DM_Plex *)dm->data;
4314   PetscInt *offsets;
4315   PetscInt  supportSize;
4316   PetscInt  pStart, pEnd, p;
4317 
4318   PetscFunctionBegin;
4319   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4320   PetscCheck(!mesh->supports, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONGSTATE, "Supports were already setup in this DMPlex");
4321   PetscCall(PetscLogEventBegin(DMPLEX_Symmetrize, dm, 0, 0, 0));
4322   /* Calculate support sizes */
4323   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4324   for (p = pStart; p < pEnd; ++p) {
4325     PetscInt dof, off, c;
4326 
4327     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
4328     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
4329     for (c = off; c < off + dof; ++c) PetscCall(PetscSectionAddDof(mesh->supportSection, mesh->cones[c], 1));
4330   }
4331   PetscCall(PetscSectionSetUp(mesh->supportSection));
4332   /* Calculate supports */
4333   PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &supportSize));
4334   PetscCall(PetscMalloc1(supportSize, &mesh->supports));
4335   PetscCall(PetscCalloc1(pEnd - pStart, &offsets));
4336   for (p = pStart; p < pEnd; ++p) {
4337     PetscInt dof, off, c;
4338 
4339     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
4340     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
4341     for (c = off; c < off + dof; ++c) {
4342       const PetscInt q = mesh->cones[c];
4343       PetscInt       offS;
4344 
4345       PetscCall(PetscSectionGetOffset(mesh->supportSection, q, &offS));
4346 
4347       mesh->supports[offS + offsets[q]] = p;
4348       ++offsets[q];
4349     }
4350   }
4351   PetscCall(PetscFree(offsets));
4352   PetscCall(PetscLogEventEnd(DMPLEX_Symmetrize, dm, 0, 0, 0));
4353   PetscFunctionReturn(PETSC_SUCCESS);
4354 }
4355 
4356 static PetscErrorCode DMPlexCreateDepthStratum(DM dm, DMLabel label, PetscInt depth, PetscInt pStart, PetscInt pEnd)
4357 {
4358   IS stratumIS;
4359 
4360   PetscFunctionBegin;
4361   if (pStart >= pEnd) PetscFunctionReturn(PETSC_SUCCESS);
4362   if (PetscDefined(USE_DEBUG)) {
4363     PetscInt  qStart, qEnd, numLevels, level;
4364     PetscBool overlap = PETSC_FALSE;
4365     PetscCall(DMLabelGetNumValues(label, &numLevels));
4366     for (level = 0; level < numLevels; level++) {
4367       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4368       if ((pStart >= qStart && pStart < qEnd) || (pEnd > qStart && pEnd <= qEnd)) {
4369         overlap = PETSC_TRUE;
4370         break;
4371       }
4372     }
4373     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);
4374   }
4375   PetscCall(ISCreateStride(PETSC_COMM_SELF, pEnd - pStart, pStart, 1, &stratumIS));
4376   PetscCall(DMLabelSetStratumIS(label, depth, stratumIS));
4377   PetscCall(ISDestroy(&stratumIS));
4378   PetscFunctionReturn(PETSC_SUCCESS);
4379 }
4380 
4381 static PetscErrorCode DMPlexStratify_CellType_Private(DM dm, DMLabel label)
4382 {
4383   PetscInt *pMin, *pMax;
4384   PetscInt  pStart, pEnd;
4385   PetscInt  dmin = PETSC_INT_MAX, dmax = PETSC_INT_MIN;
4386 
4387   PetscFunctionBegin;
4388   {
4389     DMLabel label2;
4390 
4391     PetscCall(DMPlexGetCellTypeLabel(dm, &label2));
4392     PetscCall(PetscObjectViewFromOptions((PetscObject)label2, NULL, "-ct_view"));
4393   }
4394   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4395   for (PetscInt p = pStart; p < pEnd; ++p) {
4396     DMPolytopeType ct;
4397 
4398     PetscCall(DMPlexGetCellType(dm, p, &ct));
4399     dmin = PetscMin(DMPolytopeTypeGetDim(ct), dmin);
4400     dmax = PetscMax(DMPolytopeTypeGetDim(ct), dmax);
4401   }
4402   PetscCall(PetscMalloc2(dmax + 1, &pMin, dmax + 1, &pMax));
4403   for (PetscInt d = dmin; d <= dmax; ++d) {
4404     pMin[d] = PETSC_INT_MAX;
4405     pMax[d] = PETSC_INT_MIN;
4406   }
4407   for (PetscInt p = pStart; p < pEnd; ++p) {
4408     DMPolytopeType ct;
4409     PetscInt       d;
4410 
4411     PetscCall(DMPlexGetCellType(dm, p, &ct));
4412     d       = DMPolytopeTypeGetDim(ct);
4413     pMin[d] = PetscMin(p, pMin[d]);
4414     pMax[d] = PetscMax(p, pMax[d]);
4415   }
4416   for (PetscInt d = dmin; d <= dmax; ++d) {
4417     if (pMin[d] > pMax[d]) continue;
4418     PetscCall(DMPlexCreateDepthStratum(dm, label, d, pMin[d], pMax[d] + 1));
4419   }
4420   PetscCall(PetscFree2(pMin, pMax));
4421   PetscFunctionReturn(PETSC_SUCCESS);
4422 }
4423 
4424 static PetscErrorCode DMPlexStratify_Topological_Private(DM dm, DMLabel label)
4425 {
4426   PetscInt pStart, pEnd;
4427   PetscInt numRoots = 0, numLeaves = 0;
4428 
4429   PetscFunctionBegin;
4430   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4431   {
4432     /* Initialize roots and count leaves */
4433     PetscInt sMin = PETSC_INT_MAX;
4434     PetscInt sMax = PETSC_INT_MIN;
4435     PetscInt coneSize, supportSize;
4436 
4437     for (PetscInt p = pStart; p < pEnd; ++p) {
4438       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4439       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
4440       if (!coneSize && supportSize) {
4441         sMin = PetscMin(p, sMin);
4442         sMax = PetscMax(p, sMax);
4443         ++numRoots;
4444       } else if (!supportSize && coneSize) {
4445         ++numLeaves;
4446       } else if (!supportSize && !coneSize) {
4447         /* Isolated points */
4448         sMin = PetscMin(p, sMin);
4449         sMax = PetscMax(p, sMax);
4450       }
4451     }
4452     PetscCall(DMPlexCreateDepthStratum(dm, label, 0, sMin, sMax + 1));
4453   }
4454 
4455   if (numRoots + numLeaves == (pEnd - pStart)) {
4456     PetscInt sMin = PETSC_INT_MAX;
4457     PetscInt sMax = PETSC_INT_MIN;
4458     PetscInt coneSize, supportSize;
4459 
4460     for (PetscInt p = pStart; p < pEnd; ++p) {
4461       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4462       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
4463       if (!supportSize && coneSize) {
4464         sMin = PetscMin(p, sMin);
4465         sMax = PetscMax(p, sMax);
4466       }
4467     }
4468     PetscCall(DMPlexCreateDepthStratum(dm, label, 1, sMin, sMax + 1));
4469   } else {
4470     PetscInt level = 0;
4471     PetscInt qStart, qEnd;
4472 
4473     PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4474     while (qEnd > qStart) {
4475       PetscInt sMin = PETSC_INT_MAX;
4476       PetscInt sMax = PETSC_INT_MIN;
4477 
4478       for (PetscInt q = qStart; q < qEnd; ++q) {
4479         const PetscInt *support;
4480         PetscInt        supportSize;
4481 
4482         PetscCall(DMPlexGetSupportSize(dm, q, &supportSize));
4483         PetscCall(DMPlexGetSupport(dm, q, &support));
4484         for (PetscInt s = 0; s < supportSize; ++s) {
4485           sMin = PetscMin(support[s], sMin);
4486           sMax = PetscMax(support[s], sMax);
4487         }
4488       }
4489       PetscCall(DMLabelGetNumValues(label, &level));
4490       PetscCall(DMPlexCreateDepthStratum(dm, label, level, sMin, sMax + 1));
4491       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4492     }
4493   }
4494   PetscFunctionReturn(PETSC_SUCCESS);
4495 }
4496 
4497 /*@
4498   DMPlexStratify - Computes the strata for all points in the `DMPLEX`
4499 
4500   Collective
4501 
4502   Input Parameter:
4503 . dm - The `DMPLEX`
4504 
4505   Level: beginner
4506 
4507   Notes:
4508   The strata group all points of the same grade, and this function calculates the strata. This
4509   grade can be seen as the height (or depth) of the point in the DAG.
4510 
4511   The DAG for most topologies is a graded poset (https://en.wikipedia.org/wiki/Graded_poset), and
4512   can be illustrated by a Hasse Diagram (https://en.wikipedia.org/wiki/Hasse_diagram).
4513   Concretely, `DMPlexStratify()` creates a new label named "depth" containing the depth in the DAG of each point. For cell-vertex
4514   meshes, vertices are depth 0 and cells are depth 1. For fully interpolated meshes, depth 0 for vertices, 1 for edges, and so on
4515   until cells have depth equal to the dimension of the mesh. The depth label can be accessed through `DMPlexGetDepthLabel()` or `DMPlexGetDepthStratum()`, or
4516   manually via `DMGetLabel()`.  The height is defined implicitly by height = maxDimension - depth, and can be accessed
4517   via `DMPlexGetHeightStratum()`.  For example, cells have height 0 and faces have height 1.
4518 
4519   The depth of a point is calculated by executing a breadth-first search (BFS) on the DAG. This could produce surprising results
4520   if run on a partially interpolated mesh, meaning one that had some edges and faces, but not others. For example, suppose that
4521   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
4522   to interpolate only that one (e0), so that
4523 .vb
4524   cone(c0) = {e0, v2}
4525   cone(e0) = {v0, v1}
4526 .ve
4527   If `DMPlexStratify()` is run on this mesh, it will give depths
4528 .vb
4529    depth 0 = {v0, v1, v2}
4530    depth 1 = {e0, c0}
4531 .ve
4532   where the triangle has been given depth 1, instead of 2, because it is reachable from vertex v2.
4533 
4534   `DMPlexStratify()` should be called after all calls to `DMPlexSymmetrize()`
4535 
4536 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexComputeCellTypes()`
4537 @*/
4538 PetscErrorCode DMPlexStratify(DM dm)
4539 {
4540   DM_Plex  *mesh = (DM_Plex *)dm->data;
4541   DMLabel   label;
4542   PetscBool flg = PETSC_FALSE;
4543 
4544   PetscFunctionBegin;
4545   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4546   PetscCall(PetscLogEventBegin(DMPLEX_Stratify, dm, 0, 0, 0));
4547 
4548   // Create depth label
4549   PetscCall(DMRemoveLabel(dm, "depth", NULL));
4550   PetscCall(DMCreateLabel(dm, "depth"));
4551   PetscCall(DMPlexGetDepthLabel(dm, &label));
4552 
4553   PetscCall(PetscOptionsGetBool(NULL, dm->hdr.prefix, "-dm_plex_stratify_celltype", &flg, NULL));
4554   if (flg) PetscCall(DMPlexStratify_CellType_Private(dm, label));
4555   else PetscCall(DMPlexStratify_Topological_Private(dm, label));
4556 
4557   { /* just in case there is an empty process */
4558     PetscInt numValues, maxValues = 0, v;
4559 
4560     PetscCall(DMLabelGetNumValues(label, &numValues));
4561     PetscCallMPI(MPIU_Allreduce(&numValues, &maxValues, 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
4562     for (v = numValues; v < maxValues; v++) PetscCall(DMLabelAddStratum(label, v));
4563   }
4564   PetscCall(PetscObjectStateGet((PetscObject)label, &mesh->depthState));
4565   PetscCall(PetscLogEventEnd(DMPLEX_Stratify, dm, 0, 0, 0));
4566   PetscFunctionReturn(PETSC_SUCCESS);
4567 }
4568 
4569 PetscErrorCode DMPlexComputeCellType_Internal(DM dm, PetscInt p, PetscInt pdepth, DMPolytopeType *pt)
4570 {
4571   DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4572   PetscInt       dim, depth, pheight, coneSize;
4573 
4574   PetscFunctionBeginHot;
4575   PetscCall(DMGetDimension(dm, &dim));
4576   PetscCall(DMPlexGetDepth(dm, &depth));
4577   PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4578   pheight = depth - pdepth;
4579   if (depth <= 1) {
4580     switch (pdepth) {
4581     case 0:
4582       ct = DM_POLYTOPE_POINT;
4583       break;
4584     case 1:
4585       switch (coneSize) {
4586       case 2:
4587         ct = DM_POLYTOPE_SEGMENT;
4588         break;
4589       case 3:
4590         ct = DM_POLYTOPE_TRIANGLE;
4591         break;
4592       case 4:
4593         switch (dim) {
4594         case 2:
4595           ct = DM_POLYTOPE_QUADRILATERAL;
4596           break;
4597         case 3:
4598           ct = DM_POLYTOPE_TETRAHEDRON;
4599           break;
4600         default:
4601           break;
4602         }
4603         break;
4604       case 5:
4605         ct = DM_POLYTOPE_PYRAMID;
4606         break;
4607       case 6:
4608         ct = DM_POLYTOPE_TRI_PRISM_TENSOR;
4609         break;
4610       case 8:
4611         ct = DM_POLYTOPE_HEXAHEDRON;
4612         break;
4613       default:
4614         break;
4615       }
4616     }
4617   } else {
4618     if (pdepth == 0) {
4619       ct = DM_POLYTOPE_POINT;
4620     } else if (pheight == 0) {
4621       switch (dim) {
4622       case 1:
4623         switch (coneSize) {
4624         case 2:
4625           ct = DM_POLYTOPE_SEGMENT;
4626           break;
4627         default:
4628           break;
4629         }
4630         break;
4631       case 2:
4632         switch (coneSize) {
4633         case 3:
4634           ct = DM_POLYTOPE_TRIANGLE;
4635           break;
4636         case 4:
4637           ct = DM_POLYTOPE_QUADRILATERAL;
4638           break;
4639         default:
4640           break;
4641         }
4642         break;
4643       case 3:
4644         switch (coneSize) {
4645         case 4:
4646           ct = DM_POLYTOPE_TETRAHEDRON;
4647           break;
4648         case 5: {
4649           const PetscInt *cone;
4650           PetscInt        faceConeSize;
4651 
4652           PetscCall(DMPlexGetCone(dm, p, &cone));
4653           PetscCall(DMPlexGetConeSize(dm, cone[0], &faceConeSize));
4654           switch (faceConeSize) {
4655           case 3:
4656             ct = DM_POLYTOPE_TRI_PRISM_TENSOR;
4657             break;
4658           case 4:
4659             ct = DM_POLYTOPE_PYRAMID;
4660             break;
4661           }
4662         } break;
4663         case 6:
4664           ct = DM_POLYTOPE_HEXAHEDRON;
4665           break;
4666         default:
4667           break;
4668         }
4669         break;
4670       default:
4671         break;
4672       }
4673     } else if (pheight > 0) {
4674       switch (coneSize) {
4675       case 2:
4676         ct = DM_POLYTOPE_SEGMENT;
4677         break;
4678       case 3:
4679         ct = DM_POLYTOPE_TRIANGLE;
4680         break;
4681       case 4:
4682         ct = DM_POLYTOPE_QUADRILATERAL;
4683         break;
4684       default:
4685         break;
4686       }
4687     }
4688   }
4689   *pt = ct;
4690   PetscFunctionReturn(PETSC_SUCCESS);
4691 }
4692 
4693 /*@
4694   DMPlexComputeCellTypes - Infer the polytope type of every cell using its dimension and cone size.
4695 
4696   Collective
4697 
4698   Input Parameter:
4699 . dm - The `DMPLEX`
4700 
4701   Level: developer
4702 
4703   Note:
4704   This function is normally called automatically when a cell type is requested. It creates an
4705   internal `DMLabel` named "celltype" which can be directly accessed using `DMGetLabel()`. A user may disable
4706   automatic creation by creating the label manually, using `DMCreateLabel`(dm, "celltype").
4707 
4708   `DMPlexComputeCellTypes()` should be called after all calls to `DMPlexSymmetrize()` and `DMPlexStratify()`
4709 
4710 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexStratify()`, `DMGetLabel()`, `DMCreateLabel()`
4711 @*/
4712 PetscErrorCode DMPlexComputeCellTypes(DM dm)
4713 {
4714   DM_Plex *mesh;
4715   DMLabel  ctLabel;
4716   PetscInt pStart, pEnd, p;
4717 
4718   PetscFunctionBegin;
4719   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4720   mesh = (DM_Plex *)dm->data;
4721   PetscCall(DMCreateLabel(dm, "celltype"));
4722   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
4723   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4724   PetscCall(PetscFree(mesh->cellTypes));
4725   PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
4726   for (p = pStart; p < pEnd; ++p) {
4727     DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4728     PetscInt       pdepth;
4729 
4730     PetscCall(DMPlexGetPointDepth(dm, p, &pdepth));
4731     PetscCall(DMPlexComputeCellType_Internal(dm, p, pdepth, &ct));
4732     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]);
4733     PetscCall(DMLabelSetValue(ctLabel, p, ct));
4734     mesh->cellTypes[p - pStart].value_as_uint8 = (uint8_t)ct;
4735   }
4736   PetscCall(PetscObjectStateGet((PetscObject)ctLabel, &mesh->celltypeState));
4737   PetscCall(PetscObjectViewFromOptions((PetscObject)ctLabel, NULL, "-dm_plex_celltypes_view"));
4738   PetscFunctionReturn(PETSC_SUCCESS);
4739 }
4740 
4741 /*@C
4742   DMPlexGetJoin - Get an array for the join of the set of points
4743 
4744   Not Collective
4745 
4746   Input Parameters:
4747 + dm        - The `DMPLEX` object
4748 . numPoints - The number of input points for the join
4749 - points    - The input points
4750 
4751   Output Parameters:
4752 + numCoveredPoints - The number of points in the join
4753 - coveredPoints    - The points in the join
4754 
4755   Level: intermediate
4756 
4757   Note:
4758   Currently, this is restricted to a single level join
4759 
4760   Fortran Notes:
4761   `converedPoints` must be declared with
4762 .vb
4763   PetscInt, pointer :: coveredPints(:)
4764 .ve
4765 
4766   The `numCoveredPoints` argument is not present in the Fortran binding.
4767 
4768 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4769 @*/
4770 PetscErrorCode DMPlexGetJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt *coveredPoints[])
4771 {
4772   DM_Plex  *mesh = (DM_Plex *)dm->data;
4773   PetscInt *join[2];
4774   PetscInt  joinSize, i = 0;
4775   PetscInt  dof, off, p, c, m;
4776   PetscInt  maxSupportSize;
4777 
4778   PetscFunctionBegin;
4779   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4780   PetscAssertPointer(points, 3);
4781   PetscAssertPointer(numCoveredPoints, 4);
4782   PetscAssertPointer(coveredPoints, 5);
4783   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
4784   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[0]));
4785   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[1]));
4786   /* Copy in support of first point */
4787   PetscCall(PetscSectionGetDof(mesh->supportSection, points[0], &dof));
4788   PetscCall(PetscSectionGetOffset(mesh->supportSection, points[0], &off));
4789   for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = mesh->supports[off + joinSize];
4790   /* Check each successive support */
4791   for (p = 1; p < numPoints; ++p) {
4792     PetscInt newJoinSize = 0;
4793 
4794     PetscCall(PetscSectionGetDof(mesh->supportSection, points[p], &dof));
4795     PetscCall(PetscSectionGetOffset(mesh->supportSection, points[p], &off));
4796     for (c = 0; c < dof; ++c) {
4797       const PetscInt point = mesh->supports[off + c];
4798 
4799       for (m = 0; m < joinSize; ++m) {
4800         if (point == join[i][m]) {
4801           join[1 - i][newJoinSize++] = point;
4802           break;
4803         }
4804       }
4805     }
4806     joinSize = newJoinSize;
4807     i        = 1 - i;
4808   }
4809   *numCoveredPoints = joinSize;
4810   *coveredPoints    = join[i];
4811   PetscCall(DMRestoreWorkArray(dm, maxSupportSize, MPIU_INT, &join[1 - i]));
4812   PetscFunctionReturn(PETSC_SUCCESS);
4813 }
4814 
4815 /*@C
4816   DMPlexRestoreJoin - Restore an array for the join of the set of points obtained with `DMPlexGetJoin()`
4817 
4818   Not Collective
4819 
4820   Input Parameters:
4821 + dm        - The `DMPLEX` object
4822 . numPoints - The number of input points for the join
4823 - points    - The input points
4824 
4825   Output Parameters:
4826 + numCoveredPoints - The number of points in the join
4827 - coveredPoints    - The points in the join
4828 
4829   Level: intermediate
4830 
4831   Fortran Notes:
4832   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4833 
4834 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetJoin()`, `DMPlexGetFullJoin()`, `DMPlexGetMeet()`
4835 @*/
4836 PetscErrorCode DMPlexRestoreJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt *coveredPoints[])
4837 {
4838   PetscFunctionBegin;
4839   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4840   if (points) PetscAssertPointer(points, 3);
4841   if (numCoveredPoints) PetscAssertPointer(numCoveredPoints, 4);
4842   PetscAssertPointer(coveredPoints, 5);
4843   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
4844   if (numCoveredPoints) *numCoveredPoints = 0;
4845   PetscFunctionReturn(PETSC_SUCCESS);
4846 }
4847 
4848 /*@C
4849   DMPlexGetFullJoin - Get an array for the join of the set of points
4850 
4851   Not Collective
4852 
4853   Input Parameters:
4854 + dm        - The `DMPLEX` object
4855 . numPoints - The number of input points for the join
4856 - points    - The input points, its length is `numPoints`
4857 
4858   Output Parameters:
4859 + numCoveredPoints - The number of points in the join
4860 - coveredPoints    - The points in the join, its length is `numCoveredPoints`
4861 
4862   Level: intermediate
4863 
4864   Fortran Notes:
4865   `points` and `converedPoints` must be declared with
4866 .vb
4867   PetscInt, pointer :: points(:)
4868   PetscInt, pointer :: coveredPints(:)
4869 .ve
4870 
4871   The `numCoveredPoints` argument is not present in the Fortran binding.
4872 
4873 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetJoin()`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4874 @*/
4875 PetscErrorCode DMPlexGetFullJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt *coveredPoints[])
4876 {
4877   PetscInt *offsets, **closures;
4878   PetscInt *join[2];
4879   PetscInt  depth = 0, maxSize, joinSize = 0, i = 0;
4880   PetscInt  p, d, c, m, ms;
4881 
4882   PetscFunctionBegin;
4883   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4884   PetscAssertPointer(points, 3);
4885   PetscAssertPointer(numCoveredPoints, 4);
4886   PetscAssertPointer(coveredPoints, 5);
4887 
4888   PetscCall(DMPlexGetDepth(dm, &depth));
4889   PetscCall(PetscCalloc1(numPoints, &closures));
4890   PetscCall(DMGetWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
4891   PetscCall(DMPlexGetMaxSizes(dm, NULL, &ms));
4892   maxSize = (ms > 1) ? ((PetscPowInt(ms, depth + 1) - 1) / (ms - 1)) : depth + 1;
4893   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[0]));
4894   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[1]));
4895 
4896   for (p = 0; p < numPoints; ++p) {
4897     PetscInt closureSize;
4898 
4899     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_FALSE, &closureSize, &closures[p]));
4900 
4901     offsets[p * (depth + 2) + 0] = 0;
4902     for (d = 0; d < depth + 1; ++d) {
4903       PetscInt pStart, pEnd, i;
4904 
4905       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
4906       for (i = offsets[p * (depth + 2) + d]; i < closureSize; ++i) {
4907         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
4908           offsets[p * (depth + 2) + d + 1] = i;
4909           break;
4910         }
4911       }
4912       if (i == closureSize) offsets[p * (depth + 2) + d + 1] = i;
4913     }
4914     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);
4915   }
4916   for (d = 0; d < depth + 1; ++d) {
4917     PetscInt dof;
4918 
4919     /* Copy in support of first point */
4920     dof = offsets[d + 1] - offsets[d];
4921     for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = closures[0][(offsets[d] + joinSize) * 2];
4922     /* Check each successive cone */
4923     for (p = 1; p < numPoints && joinSize; ++p) {
4924       PetscInt newJoinSize = 0;
4925 
4926       dof = offsets[p * (depth + 2) + d + 1] - offsets[p * (depth + 2) + d];
4927       for (c = 0; c < dof; ++c) {
4928         const PetscInt point = closures[p][(offsets[p * (depth + 2) + d] + c) * 2];
4929 
4930         for (m = 0; m < joinSize; ++m) {
4931           if (point == join[i][m]) {
4932             join[1 - i][newJoinSize++] = point;
4933             break;
4934           }
4935         }
4936       }
4937       joinSize = newJoinSize;
4938       i        = 1 - i;
4939     }
4940     if (joinSize) break;
4941   }
4942   *numCoveredPoints = joinSize;
4943   *coveredPoints    = join[i];
4944   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_FALSE, NULL, &closures[p]));
4945   PetscCall(PetscFree(closures));
4946   PetscCall(DMRestoreWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
4947   PetscCall(DMRestoreWorkArray(dm, ms, MPIU_INT, &join[1 - i]));
4948   PetscFunctionReturn(PETSC_SUCCESS);
4949 }
4950 
4951 /*@C
4952   DMPlexGetMeet - Get an array for the meet of the set of points
4953 
4954   Not Collective
4955 
4956   Input Parameters:
4957 + dm        - The `DMPLEX` object
4958 . numPoints - The number of input points for the meet
4959 - points    - The input points, of length `numPoints`
4960 
4961   Output Parameters:
4962 + numCoveringPoints - The number of points in the meet
4963 - coveringPoints    - The points in the meet, of length `numCoveringPoints`
4964 
4965   Level: intermediate
4966 
4967   Note:
4968   Currently, this is restricted to a single level meet
4969 
4970   Fortran Notes:
4971   `coveringPoints` must be declared with
4972 .vb
4973   PetscInt, pointer :: coveringPoints(:)
4974 .ve
4975 
4976   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4977 
4978 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
4979 @*/
4980 PetscErrorCode DMPlexGetMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveringPoints, const PetscInt *coveringPoints[])
4981 {
4982   DM_Plex  *mesh = (DM_Plex *)dm->data;
4983   PetscInt *meet[2];
4984   PetscInt  meetSize, i = 0;
4985   PetscInt  dof, off, p, c, m;
4986   PetscInt  maxConeSize;
4987 
4988   PetscFunctionBegin;
4989   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4990   PetscAssertPointer(points, 3);
4991   PetscAssertPointer(numCoveringPoints, 4);
4992   PetscAssertPointer(coveringPoints, 5);
4993   PetscCall(PetscSectionGetMaxDof(mesh->coneSection, &maxConeSize));
4994   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[0]));
4995   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[1]));
4996   /* Copy in cone of first point */
4997   PetscCall(PetscSectionGetDof(mesh->coneSection, points[0], &dof));
4998   PetscCall(PetscSectionGetOffset(mesh->coneSection, points[0], &off));
4999   for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = mesh->cones[off + meetSize];
5000   /* Check each successive cone */
5001   for (p = 1; p < numPoints; ++p) {
5002     PetscInt newMeetSize = 0;
5003 
5004     PetscCall(PetscSectionGetDof(mesh->coneSection, points[p], &dof));
5005     PetscCall(PetscSectionGetOffset(mesh->coneSection, points[p], &off));
5006     for (c = 0; c < dof; ++c) {
5007       const PetscInt point = mesh->cones[off + c];
5008 
5009       for (m = 0; m < meetSize; ++m) {
5010         if (point == meet[i][m]) {
5011           meet[1 - i][newMeetSize++] = point;
5012           break;
5013         }
5014       }
5015     }
5016     meetSize = newMeetSize;
5017     i        = 1 - i;
5018   }
5019   *numCoveringPoints = meetSize;
5020   *coveringPoints    = meet[i];
5021   PetscCall(DMRestoreWorkArray(dm, maxConeSize, MPIU_INT, &meet[1 - i]));
5022   PetscFunctionReturn(PETSC_SUCCESS);
5023 }
5024 
5025 /*@C
5026   DMPlexRestoreMeet - Restore an array for the meet of the set of points obtained with `DMPlexGetMeet()`
5027 
5028   Not Collective
5029 
5030   Input Parameters:
5031 + dm        - The `DMPLEX` object
5032 . numPoints - The number of input points for the meet
5033 - points    - The input points
5034 
5035   Output Parameters:
5036 + numCoveredPoints - The number of points in the meet
5037 - coveredPoints    - The points in the meet
5038 
5039   Level: intermediate
5040 
5041   Fortran Notes:
5042   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
5043 
5044 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetMeet()`, `DMPlexGetFullMeet()`, `DMPlexGetJoin()`
5045 @*/
5046 PetscErrorCode DMPlexRestoreMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt *coveredPoints[])
5047 {
5048   PetscFunctionBegin;
5049   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5050   if (points) PetscAssertPointer(points, 3);
5051   if (numCoveredPoints) PetscAssertPointer(numCoveredPoints, 4);
5052   PetscAssertPointer(coveredPoints, 5);
5053   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
5054   if (numCoveredPoints) *numCoveredPoints = 0;
5055   PetscFunctionReturn(PETSC_SUCCESS);
5056 }
5057 
5058 /*@C
5059   DMPlexGetFullMeet - Get an array for the meet of the set of points
5060 
5061   Not Collective
5062 
5063   Input Parameters:
5064 + dm        - The `DMPLEX` object
5065 . numPoints - The number of input points for the meet
5066 - points    - The input points, of length  `numPoints`
5067 
5068   Output Parameters:
5069 + numCoveredPoints - The number of points in the meet
5070 - coveredPoints    - The points in the meet, of length  `numCoveredPoints`
5071 
5072   Level: intermediate
5073 
5074   Fortran Notes:
5075   `points` and `coveredPoints` must be declared with
5076 .vb
5077   PetscInt, pointer :: points(:)
5078   PetscInt, pointer :: coveredPoints(:)
5079 .ve
5080 
5081   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
5082 
5083 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetMeet()`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
5084 @*/
5085 PetscErrorCode DMPlexGetFullMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt *coveredPoints[])
5086 {
5087   PetscInt *offsets, **closures;
5088   PetscInt *meet[2];
5089   PetscInt  height = 0, maxSize, meetSize = 0, i = 0;
5090   PetscInt  p, h, c, m, mc;
5091 
5092   PetscFunctionBegin;
5093   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5094   PetscAssertPointer(points, 3);
5095   PetscAssertPointer(numCoveredPoints, 4);
5096   PetscAssertPointer(coveredPoints, 5);
5097 
5098   PetscCall(DMPlexGetDepth(dm, &height));
5099   PetscCall(PetscMalloc1(numPoints, &closures));
5100   PetscCall(DMGetWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
5101   PetscCall(DMPlexGetMaxSizes(dm, &mc, NULL));
5102   maxSize = (mc > 1) ? ((PetscPowInt(mc, height + 1) - 1) / (mc - 1)) : height + 1;
5103   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[0]));
5104   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[1]));
5105 
5106   for (p = 0; p < numPoints; ++p) {
5107     PetscInt closureSize;
5108 
5109     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_TRUE, &closureSize, &closures[p]));
5110 
5111     offsets[p * (height + 2) + 0] = 0;
5112     for (h = 0; h < height + 1; ++h) {
5113       PetscInt pStart, pEnd, i;
5114 
5115       PetscCall(DMPlexGetHeightStratum(dm, h, &pStart, &pEnd));
5116       for (i = offsets[p * (height + 2) + h]; i < closureSize; ++i) {
5117         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
5118           offsets[p * (height + 2) + h + 1] = i;
5119           break;
5120         }
5121       }
5122       if (i == closureSize) offsets[p * (height + 2) + h + 1] = i;
5123     }
5124     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);
5125   }
5126   for (h = 0; h < height + 1; ++h) {
5127     PetscInt dof;
5128 
5129     /* Copy in cone of first point */
5130     dof = offsets[h + 1] - offsets[h];
5131     for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = closures[0][(offsets[h] + meetSize) * 2];
5132     /* Check each successive cone */
5133     for (p = 1; p < numPoints && meetSize; ++p) {
5134       PetscInt newMeetSize = 0;
5135 
5136       dof = offsets[p * (height + 2) + h + 1] - offsets[p * (height + 2) + h];
5137       for (c = 0; c < dof; ++c) {
5138         const PetscInt point = closures[p][(offsets[p * (height + 2) + h] + c) * 2];
5139 
5140         for (m = 0; m < meetSize; ++m) {
5141           if (point == meet[i][m]) {
5142             meet[1 - i][newMeetSize++] = point;
5143             break;
5144           }
5145         }
5146       }
5147       meetSize = newMeetSize;
5148       i        = 1 - i;
5149     }
5150     if (meetSize) break;
5151   }
5152   *numCoveredPoints = meetSize;
5153   *coveredPoints    = meet[i];
5154   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_TRUE, NULL, &closures[p]));
5155   PetscCall(PetscFree(closures));
5156   PetscCall(DMRestoreWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
5157   PetscCall(DMRestoreWorkArray(dm, mc, MPIU_INT, &meet[1 - i]));
5158   PetscFunctionReturn(PETSC_SUCCESS);
5159 }
5160 
5161 /*@
5162   DMPlexEqual - Determine if two `DM` have the same topology
5163 
5164   Not Collective
5165 
5166   Input Parameters:
5167 + dmA - A `DMPLEX` object
5168 - dmB - A `DMPLEX` object
5169 
5170   Output Parameter:
5171 . equal - `PETSC_TRUE` if the topologies are identical
5172 
5173   Level: intermediate
5174 
5175   Note:
5176   We are not solving graph isomorphism, so we do not permute.
5177 
5178 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCone()`
5179 @*/
5180 PetscErrorCode DMPlexEqual(DM dmA, DM dmB, PetscBool *equal)
5181 {
5182   PetscInt depth, depthB, pStart, pEnd, pStartB, pEndB, p;
5183 
5184   PetscFunctionBegin;
5185   PetscValidHeaderSpecific(dmA, DM_CLASSID, 1);
5186   PetscValidHeaderSpecific(dmB, DM_CLASSID, 2);
5187   PetscAssertPointer(equal, 3);
5188 
5189   *equal = PETSC_FALSE;
5190   PetscCall(DMPlexGetDepth(dmA, &depth));
5191   PetscCall(DMPlexGetDepth(dmB, &depthB));
5192   if (depth != depthB) PetscFunctionReturn(PETSC_SUCCESS);
5193   PetscCall(DMPlexGetChart(dmA, &pStart, &pEnd));
5194   PetscCall(DMPlexGetChart(dmB, &pStartB, &pEndB));
5195   if ((pStart != pStartB) || (pEnd != pEndB)) PetscFunctionReturn(PETSC_SUCCESS);
5196   for (p = pStart; p < pEnd; ++p) {
5197     const PetscInt *cone, *coneB, *ornt, *orntB, *support, *supportB;
5198     PetscInt        coneSize, coneSizeB, c, supportSize, supportSizeB, s;
5199 
5200     PetscCall(DMPlexGetConeSize(dmA, p, &coneSize));
5201     PetscCall(DMPlexGetCone(dmA, p, &cone));
5202     PetscCall(DMPlexGetConeOrientation(dmA, p, &ornt));
5203     PetscCall(DMPlexGetConeSize(dmB, p, &coneSizeB));
5204     PetscCall(DMPlexGetCone(dmB, p, &coneB));
5205     PetscCall(DMPlexGetConeOrientation(dmB, p, &orntB));
5206     if (coneSize != coneSizeB) PetscFunctionReturn(PETSC_SUCCESS);
5207     for (c = 0; c < coneSize; ++c) {
5208       if (cone[c] != coneB[c]) PetscFunctionReturn(PETSC_SUCCESS);
5209       if (ornt[c] != orntB[c]) PetscFunctionReturn(PETSC_SUCCESS);
5210     }
5211     PetscCall(DMPlexGetSupportSize(dmA, p, &supportSize));
5212     PetscCall(DMPlexGetSupport(dmA, p, &support));
5213     PetscCall(DMPlexGetSupportSize(dmB, p, &supportSizeB));
5214     PetscCall(DMPlexGetSupport(dmB, p, &supportB));
5215     if (supportSize != supportSizeB) PetscFunctionReturn(PETSC_SUCCESS);
5216     for (s = 0; s < supportSize; ++s) {
5217       if (support[s] != supportB[s]) PetscFunctionReturn(PETSC_SUCCESS);
5218     }
5219   }
5220   *equal = PETSC_TRUE;
5221   PetscFunctionReturn(PETSC_SUCCESS);
5222 }
5223 
5224 /*@
5225   DMPlexGetNumFaceVertices - Returns the number of vertices on a face
5226 
5227   Not Collective
5228 
5229   Input Parameters:
5230 + dm         - The `DMPLEX`
5231 . cellDim    - The cell dimension
5232 - numCorners - The number of vertices on a cell
5233 
5234   Output Parameter:
5235 . numFaceVertices - The number of vertices on a face
5236 
5237   Level: developer
5238 
5239   Note:
5240   Of course this can only work for a restricted set of symmetric shapes
5241 
5242 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCone()`
5243 @*/
5244 PetscErrorCode DMPlexGetNumFaceVertices(DM dm, PetscInt cellDim, PetscInt numCorners, PetscInt *numFaceVertices)
5245 {
5246   MPI_Comm comm;
5247 
5248   PetscFunctionBegin;
5249   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
5250   PetscAssertPointer(numFaceVertices, 4);
5251   switch (cellDim) {
5252   case 0:
5253     *numFaceVertices = 0;
5254     break;
5255   case 1:
5256     *numFaceVertices = 1;
5257     break;
5258   case 2:
5259     switch (numCorners) {
5260     case 3:                 /* triangle */
5261       *numFaceVertices = 2; /* Edge has 2 vertices */
5262       break;
5263     case 4:                 /* quadrilateral */
5264       *numFaceVertices = 2; /* Edge has 2 vertices */
5265       break;
5266     case 6:                 /* quadratic triangle, tri and quad cohesive Lagrange cells */
5267       *numFaceVertices = 3; /* Edge has 3 vertices */
5268       break;
5269     case 9:                 /* quadratic quadrilateral, quadratic quad cohesive Lagrange cells */
5270       *numFaceVertices = 3; /* Edge has 3 vertices */
5271       break;
5272     default:
5273       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
5274     }
5275     break;
5276   case 3:
5277     switch (numCorners) {
5278     case 4:                 /* tetradehdron */
5279       *numFaceVertices = 3; /* Face has 3 vertices */
5280       break;
5281     case 6:                 /* tet cohesive cells */
5282       *numFaceVertices = 4; /* Face has 4 vertices */
5283       break;
5284     case 8:                 /* hexahedron */
5285       *numFaceVertices = 4; /* Face has 4 vertices */
5286       break;
5287     case 9:                 /* tet cohesive Lagrange cells */
5288       *numFaceVertices = 6; /* Face has 6 vertices */
5289       break;
5290     case 10:                /* quadratic tetrahedron */
5291       *numFaceVertices = 6; /* Face has 6 vertices */
5292       break;
5293     case 12:                /* hex cohesive Lagrange cells */
5294       *numFaceVertices = 6; /* Face has 6 vertices */
5295       break;
5296     case 18:                /* quadratic tet cohesive Lagrange cells */
5297       *numFaceVertices = 6; /* Face has 6 vertices */
5298       break;
5299     case 27:                /* quadratic hexahedron, quadratic hex cohesive Lagrange cells */
5300       *numFaceVertices = 9; /* Face has 9 vertices */
5301       break;
5302     default:
5303       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
5304     }
5305     break;
5306   default:
5307     SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid cell dimension %" PetscInt_FMT, cellDim);
5308   }
5309   PetscFunctionReturn(PETSC_SUCCESS);
5310 }
5311 
5312 /*@
5313   DMPlexGetDepthLabel - Get the `DMLabel` recording the depth of each point
5314 
5315   Not Collective
5316 
5317   Input Parameter:
5318 . dm - The `DMPLEX` object
5319 
5320   Output Parameter:
5321 . depthLabel - The `DMLabel` recording point depth
5322 
5323   Level: developer
5324 
5325 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepth()`, `DMPlexGetHeightStratum()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`,
5326 @*/
5327 PetscErrorCode DMPlexGetDepthLabel(DM dm, DMLabel *depthLabel)
5328 {
5329   PetscFunctionBegin;
5330   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5331   PetscAssertPointer(depthLabel, 2);
5332   *depthLabel = dm->depthLabel;
5333   PetscFunctionReturn(PETSC_SUCCESS);
5334 }
5335 
5336 /*@
5337   DMPlexGetDepth - Get the depth of the DAG representing this mesh
5338 
5339   Not Collective
5340 
5341   Input Parameter:
5342 . dm - The `DMPLEX` object
5343 
5344   Output Parameter:
5345 . depth - The number of strata (breadth first levels) in the DAG
5346 
5347   Level: developer
5348 
5349   Notes:
5350   This returns maximum of point depths over all points, i.e. maximum value of the label returned by `DMPlexGetDepthLabel()`.
5351 
5352   The point depth is described more in detail in `DMPlexGetDepthStratum()`.
5353 
5354   An empty mesh gives -1.
5355 
5356 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepthLabel()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`
5357 @*/
5358 PetscErrorCode DMPlexGetDepth(DM dm, PetscInt *depth)
5359 {
5360   DM_Plex *mesh = (DM_Plex *)dm->data;
5361   DMLabel  label;
5362   PetscInt d = -1;
5363 
5364   PetscFunctionBegin;
5365   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5366   PetscAssertPointer(depth, 2);
5367   if (mesh->tr) {
5368     PetscCall(DMPlexTransformGetDepth(mesh->tr, depth));
5369   } else {
5370     PetscCall(DMPlexGetDepthLabel(dm, &label));
5371     // Allow missing depths
5372     if (label) PetscCall(DMLabelGetValueBounds(label, NULL, &d));
5373     *depth = d;
5374   }
5375   PetscFunctionReturn(PETSC_SUCCESS);
5376 }
5377 
5378 /*@
5379   DMPlexGetDepthStratum - Get the bounds [`start`, `end`) for all points at a certain depth.
5380 
5381   Not Collective
5382 
5383   Input Parameters:
5384 + dm    - The `DMPLEX` object
5385 - depth - The requested depth
5386 
5387   Output Parameters:
5388 + start - The first point at this `depth`
5389 - end   - One beyond the last point at this `depth`
5390 
5391   Level: developer
5392 
5393   Notes:
5394   Depth indexing is related to topological dimension.  Depth stratum 0 contains the lowest topological dimension points,
5395   often "vertices".  If the mesh is "interpolated" (see `DMPlexInterpolate()`), then depth stratum 1 contains the next
5396   higher dimension, e.g., "edges".
5397 
5398 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetHeightStratum()`, `DMPlexGetCellTypeStratum()`, `DMPlexGetDepth()`, `DMPlexGetDepthLabel()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`, `DMPlexInterpolate()`
5399 @*/
5400 PetscErrorCode DMPlexGetDepthStratum(DM dm, PetscInt depth, PetscInt *start, PetscInt *end)
5401 {
5402   DM_Plex *mesh = (DM_Plex *)dm->data;
5403   DMLabel  label;
5404   PetscInt pStart, pEnd;
5405 
5406   PetscFunctionBegin;
5407   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5408   if (start) {
5409     PetscAssertPointer(start, 3);
5410     *start = 0;
5411   }
5412   if (end) {
5413     PetscAssertPointer(end, 4);
5414     *end = 0;
5415   }
5416   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
5417   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
5418   if (depth < 0) {
5419     if (start) *start = pStart;
5420     if (end) *end = pEnd;
5421     PetscFunctionReturn(PETSC_SUCCESS);
5422   }
5423   if (mesh->tr) {
5424     PetscCall(DMPlexTransformGetDepthStratum(mesh->tr, depth, start, end));
5425   } else {
5426     PetscCall(DMPlexGetDepthLabel(dm, &label));
5427     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named depth was found");
5428     PetscCall(DMLabelGetStratumBounds(label, depth, start, end));
5429   }
5430   PetscFunctionReturn(PETSC_SUCCESS);
5431 }
5432 
5433 /*@
5434   DMPlexGetHeightStratum - Get the bounds [`start`, `end`) for all points at a certain height.
5435 
5436   Not Collective
5437 
5438   Input Parameters:
5439 + dm     - The `DMPLEX` object
5440 - height - The requested height
5441 
5442   Output Parameters:
5443 + start - The first point at this `height`
5444 - end   - One beyond the last point at this `height`
5445 
5446   Level: developer
5447 
5448   Notes:
5449   Height indexing is related to topological codimension.  Height stratum 0 contains the highest topological dimension
5450   points, often called "cells" or "elements".  If the mesh is "interpolated" (see `DMPlexInterpolate()`), then height
5451   stratum 1 contains the boundary of these "cells", often called "faces" or "facets".
5452 
5453 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepthStratum()`, `DMPlexGetCellTypeStratum()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
5454 @*/
5455 PetscErrorCode DMPlexGetHeightStratum(DM dm, PetscInt height, PetscInt *start, PetscInt *end)
5456 {
5457   DMLabel  label;
5458   PetscInt depth, pStart, pEnd;
5459 
5460   PetscFunctionBegin;
5461   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5462   if (start) {
5463     PetscAssertPointer(start, 3);
5464     *start = 0;
5465   }
5466   if (end) {
5467     PetscAssertPointer(end, 4);
5468     *end = 0;
5469   }
5470   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
5471   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
5472   if (height < 0) {
5473     if (start) *start = pStart;
5474     if (end) *end = pEnd;
5475     PetscFunctionReturn(PETSC_SUCCESS);
5476   }
5477   PetscCall(DMPlexGetDepthLabel(dm, &label));
5478   if (label) PetscCall(DMLabelGetNumValues(label, &depth));
5479   else PetscCall(DMGetDimension(dm, &depth));
5480   PetscCheck(depth >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Depth not yet computed");
5481   PetscCall(DMPlexGetDepthStratum(dm, depth - 1 - height, start, end));
5482   PetscFunctionReturn(PETSC_SUCCESS);
5483 }
5484 
5485 /*@
5486   DMPlexGetPointDepth - Get the `depth` of a given point
5487 
5488   Not Collective
5489 
5490   Input Parameters:
5491 + dm    - The `DMPLEX` object
5492 - point - The point
5493 
5494   Output Parameter:
5495 . depth - The depth of the `point`
5496 
5497   Level: intermediate
5498 
5499 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
5500 @*/
5501 PetscErrorCode DMPlexGetPointDepth(DM dm, PetscInt point, PetscInt *depth)
5502 {
5503   PetscFunctionBegin;
5504   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5505   PetscAssertPointer(depth, 3);
5506   PetscCall(DMLabelGetValue(dm->depthLabel, point, depth));
5507   PetscFunctionReturn(PETSC_SUCCESS);
5508 }
5509 
5510 /*@
5511   DMPlexGetPointHeight - Get the `height` of a given point
5512 
5513   Not Collective
5514 
5515   Input Parameters:
5516 + dm    - The `DMPLEX` object
5517 - point - The point
5518 
5519   Output Parameter:
5520 . height - The height of the `point`
5521 
5522   Level: intermediate
5523 
5524 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointDepth()`
5525 @*/
5526 PetscErrorCode DMPlexGetPointHeight(DM dm, PetscInt point, PetscInt *height)
5527 {
5528   PetscInt n, pDepth;
5529 
5530   PetscFunctionBegin;
5531   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5532   PetscAssertPointer(height, 3);
5533   PetscCall(DMLabelGetNumValues(dm->depthLabel, &n));
5534   PetscCall(DMLabelGetValue(dm->depthLabel, point, &pDepth));
5535   *height = n - 1 - pDepth; /* DAG depth is n-1 */
5536   PetscFunctionReturn(PETSC_SUCCESS);
5537 }
5538 
5539 /*@
5540   DMPlexGetCellTypeLabel - Get the `DMLabel` recording the polytope type of each cell
5541 
5542   Not Collective
5543 
5544   Input Parameter:
5545 . dm - The `DMPLEX` object
5546 
5547   Output Parameter:
5548 . celltypeLabel - The `DMLabel` recording cell polytope type
5549 
5550   Level: developer
5551 
5552   Note:
5553   This function will trigger automatica computation of cell types. This can be disabled by calling
5554   `DMCreateLabel`(dm, "celltype") beforehand.
5555 
5556 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMCreateLabel()`
5557 @*/
5558 PetscErrorCode DMPlexGetCellTypeLabel(DM dm, DMLabel *celltypeLabel)
5559 {
5560   PetscFunctionBegin;
5561   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5562   PetscAssertPointer(celltypeLabel, 2);
5563   if (!dm->celltypeLabel) PetscCall(DMPlexComputeCellTypes(dm));
5564   *celltypeLabel = dm->celltypeLabel;
5565   PetscFunctionReturn(PETSC_SUCCESS);
5566 }
5567 
5568 /*@
5569   DMPlexGetCellType - Get the polytope type of a given cell
5570 
5571   Not Collective
5572 
5573   Input Parameters:
5574 + dm   - The `DMPLEX` object
5575 - cell - The cell
5576 
5577   Output Parameter:
5578 . celltype - The polytope type of the cell
5579 
5580   Level: intermediate
5581 
5582 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPolytopeType`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`
5583 @*/
5584 PetscErrorCode DMPlexGetCellType(DM dm, PetscInt cell, DMPolytopeType *celltype)
5585 {
5586   DM_Plex *mesh = (DM_Plex *)dm->data;
5587   DMLabel  label;
5588   PetscInt ct;
5589 
5590   PetscFunctionBegin;
5591   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5592   PetscAssertPointer(celltype, 3);
5593   if (mesh->tr) {
5594     PetscCall(DMPlexTransformGetCellType(mesh->tr, cell, celltype));
5595   } else {
5596     PetscInt pStart, pEnd;
5597 
5598     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, NULL));
5599     if (!mesh->cellTypes) { /* XXX remove? optimize? */
5600       PetscCall(PetscSectionGetChart(mesh->coneSection, NULL, &pEnd));
5601       PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
5602       PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5603       for (PetscInt p = pStart; p < pEnd; p++) {
5604         PetscCall(DMLabelGetValue(label, p, &ct));
5605         mesh->cellTypes[p - pStart].value_as_uint8 = (uint8_t)ct;
5606       }
5607     }
5608     *celltype = (DMPolytopeType)mesh->cellTypes[cell - pStart].value_as_uint8;
5609     if (PetscDefined(USE_DEBUG)) {
5610       PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5611       PetscCall(DMLabelGetValue(label, cell, &ct));
5612       PetscCheck(ct >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Cell %" PetscInt_FMT " has not been assigned a cell type", cell);
5613       PetscCheck(ct == (PetscInt)*celltype, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid cellType for %" PetscInt_FMT ": %d != %" PetscInt_FMT, cell, (int)*celltype, ct);
5614     }
5615   }
5616   PetscFunctionReturn(PETSC_SUCCESS);
5617 }
5618 
5619 /*@
5620   DMPlexSetCellType - Set the polytope type of a given cell
5621 
5622   Not Collective
5623 
5624   Input Parameters:
5625 + dm       - The `DMPLEX` object
5626 . cell     - The cell
5627 - celltype - The polytope type of the cell
5628 
5629   Level: advanced
5630 
5631   Note:
5632   By default, cell types will be automatically computed using `DMPlexComputeCellTypes()` before this function
5633   is executed. This function will override the computed type. However, if automatic classification will not succeed
5634   and a user wants to manually specify all types, the classification must be disabled by calling
5635   DMCreateLabel(dm, "celltype") before getting or setting any cell types.
5636 
5637 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexComputeCellTypes()`, `DMCreateLabel()`
5638 @*/
5639 PetscErrorCode DMPlexSetCellType(DM dm, PetscInt cell, DMPolytopeType celltype)
5640 {
5641   DM_Plex *mesh = (DM_Plex *)dm->data;
5642   DMLabel  label;
5643   PetscInt pStart, pEnd;
5644 
5645   PetscFunctionBegin;
5646   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5647   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
5648   PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5649   PetscCall(DMLabelSetValue(label, cell, celltype));
5650   if (!mesh->cellTypes) PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
5651   mesh->cellTypes[cell - pStart].value_as_uint8 = (uint8_t)celltype;
5652   PetscFunctionReturn(PETSC_SUCCESS);
5653 }
5654 
5655 PetscErrorCode DMCreateCoordinateDM_Plex(DM dm, DM *cdm)
5656 {
5657   PetscSection section;
5658   PetscInt     maxHeight;
5659   const char  *prefix;
5660 
5661   PetscFunctionBegin;
5662   PetscCall(DMClone(dm, cdm));
5663   PetscCall(PetscObjectGetOptionsPrefix((PetscObject)dm, &prefix));
5664   PetscCall(PetscObjectSetOptionsPrefix((PetscObject)*cdm, prefix));
5665   PetscCall(PetscObjectAppendOptionsPrefix((PetscObject)*cdm, "cdm_"));
5666   PetscCall(DMPlexGetMaxProjectionHeight(dm, &maxHeight));
5667   PetscCall(DMPlexSetMaxProjectionHeight(*cdm, maxHeight));
5668   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
5669   PetscCall(DMSetLocalSection(*cdm, section));
5670   PetscCall(PetscSectionDestroy(&section));
5671 
5672   PetscCall(DMSetNumFields(*cdm, 1));
5673   PetscCall(DMCreateDS(*cdm));
5674   (*cdm)->cloneOpts = PETSC_TRUE;
5675   if (dm->setfromoptionscalled) PetscCall(DMSetFromOptions(*cdm));
5676   PetscFunctionReturn(PETSC_SUCCESS);
5677 }
5678 
5679 PetscErrorCode DMCreateCoordinateField_Plex(DM dm, DMField *field)
5680 {
5681   Vec coordsLocal, cellCoordsLocal;
5682   DM  coordsDM, cellCoordsDM;
5683 
5684   PetscFunctionBegin;
5685   *field = NULL;
5686   PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
5687   PetscCall(DMGetCoordinateDM(dm, &coordsDM));
5688   PetscCall(DMGetCellCoordinatesLocal(dm, &cellCoordsLocal));
5689   PetscCall(DMGetCellCoordinateDM(dm, &cellCoordsDM));
5690   if (coordsLocal && coordsDM) {
5691     if (cellCoordsLocal && cellCoordsDM) PetscCall(DMFieldCreateDSWithDG(coordsDM, cellCoordsDM, 0, coordsLocal, cellCoordsLocal, field));
5692     else PetscCall(DMFieldCreateDS(coordsDM, 0, coordsLocal, field));
5693   }
5694   PetscFunctionReturn(PETSC_SUCCESS);
5695 }
5696 
5697 /*@
5698   DMPlexGetConeSection - Return a section which describes the layout of cone data
5699 
5700   Not Collective
5701 
5702   Input Parameter:
5703 . dm - The `DMPLEX` object
5704 
5705   Output Parameter:
5706 . section - The `PetscSection` object
5707 
5708   Level: developer
5709 
5710 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSupportSection()`, `DMPlexGetCones()`, `DMPlexGetConeOrientations()`, `PetscSection`
5711 @*/
5712 PetscErrorCode DMPlexGetConeSection(DM dm, PetscSection *section)
5713 {
5714   DM_Plex *mesh = (DM_Plex *)dm->data;
5715 
5716   PetscFunctionBegin;
5717   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5718   if (section) *section = mesh->coneSection;
5719   PetscFunctionReturn(PETSC_SUCCESS);
5720 }
5721 
5722 /*@
5723   DMPlexGetSupportSection - Return a section which describes the layout of support data
5724 
5725   Not Collective
5726 
5727   Input Parameter:
5728 . dm - The `DMPLEX` object
5729 
5730   Output Parameter:
5731 . section - The `PetscSection` object
5732 
5733   Level: developer
5734 
5735 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `PetscSection`
5736 @*/
5737 PetscErrorCode DMPlexGetSupportSection(DM dm, PetscSection *section)
5738 {
5739   DM_Plex *mesh = (DM_Plex *)dm->data;
5740 
5741   PetscFunctionBegin;
5742   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5743   if (section) *section = mesh->supportSection;
5744   PetscFunctionReturn(PETSC_SUCCESS);
5745 }
5746 
5747 /*@C
5748   DMPlexGetCones - Return cone data
5749 
5750   Not Collective
5751 
5752   Input Parameter:
5753 . dm - The `DMPLEX` object
5754 
5755   Output Parameter:
5756 . cones - The cone for each point
5757 
5758   Level: developer
5759 
5760 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`
5761 @*/
5762 PetscErrorCode DMPlexGetCones(DM dm, PetscInt *cones[])
5763 {
5764   DM_Plex *mesh = (DM_Plex *)dm->data;
5765 
5766   PetscFunctionBegin;
5767   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5768   if (cones) *cones = mesh->cones;
5769   PetscFunctionReturn(PETSC_SUCCESS);
5770 }
5771 
5772 /*@C
5773   DMPlexGetConeOrientations - Return cone orientation data
5774 
5775   Not Collective
5776 
5777   Input Parameter:
5778 . dm - The `DMPLEX` object
5779 
5780   Output Parameter:
5781 . coneOrientations - The array of cone orientations for all points
5782 
5783   Level: developer
5784 
5785   Notes:
5786   The `PetscSection` returned by `DMPlexGetConeSection()` partitions coneOrientations into cone orientations of particular points
5787   as returned by `DMPlexGetConeOrientation()`.
5788 
5789   The meaning of coneOrientations values is detailed in `DMPlexGetConeOrientation()`.
5790 
5791 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `DMPlexGetConeOrientation()`, `PetscSection`
5792 @*/
5793 PetscErrorCode DMPlexGetConeOrientations(DM dm, PetscInt *coneOrientations[])
5794 {
5795   DM_Plex *mesh = (DM_Plex *)dm->data;
5796 
5797   PetscFunctionBegin;
5798   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5799   if (coneOrientations) *coneOrientations = mesh->coneOrientations;
5800   PetscFunctionReturn(PETSC_SUCCESS);
5801 }
5802 
5803 /* FEM Support */
5804 
5805 PetscErrorCode DMPlexGetAllCells_Internal(DM plex, IS *cellIS)
5806 {
5807   PetscInt depth;
5808 
5809   PetscFunctionBegin;
5810   PetscCall(DMPlexGetDepth(plex, &depth));
5811   PetscCall(DMGetStratumIS(plex, "dim", depth, cellIS));
5812   if (!*cellIS) PetscCall(DMGetStratumIS(plex, "depth", depth, cellIS));
5813   PetscFunctionReturn(PETSC_SUCCESS);
5814 }
5815 
5816 PetscErrorCode DMPlexGetAllFaces_Internal(DM plex, IS *faceIS)
5817 {
5818   PetscInt depth;
5819 
5820   PetscFunctionBegin;
5821   PetscCall(DMPlexGetDepth(plex, &depth));
5822   PetscCall(DMGetStratumIS(plex, "dim", depth - 1, faceIS));
5823   if (!*faceIS) PetscCall(DMGetStratumIS(plex, "depth", depth - 1, faceIS));
5824   PetscFunctionReturn(PETSC_SUCCESS);
5825 }
5826 
5827 /*
5828  Returns number of components and tensor degree for the field.  For interpolated meshes, line should be a point
5829  representing a line in the section.
5830 */
5831 static PetscErrorCode PetscSectionFieldGetTensorDegree_Private(DM dm, PetscSection section, PetscInt field, PetscInt line, PetscInt *Nc, PetscInt *k, PetscBool *continuous, PetscBool *tensor)
5832 {
5833   PetscObject  obj;
5834   PetscClassId id;
5835   PetscFE      fe = NULL;
5836 
5837   PetscFunctionBeginHot;
5838   PetscCall(PetscSectionGetFieldComponents(section, field, Nc));
5839   PetscCall(DMGetField(dm, field, NULL, &obj));
5840   PetscCall(PetscObjectGetClassId(obj, &id));
5841   if (id == PETSCFE_CLASSID) fe = (PetscFE)obj;
5842 
5843   if (!fe) {
5844     /* Assume the full interpolated mesh is in the chart; lines in particular */
5845     /* An order k SEM disc has k-1 dofs on an edge */
5846     PetscCall(PetscSectionGetFieldDof(section, line, field, k));
5847     *k = *k / *Nc + 1;
5848   } else {
5849     PetscInt       dual_space_size, dim;
5850     PetscDualSpace dsp;
5851 
5852     PetscCall(DMGetDimension(dm, &dim));
5853     PetscCall(PetscFEGetDualSpace(fe, &dsp));
5854     PetscCall(PetscDualSpaceGetDimension(dsp, &dual_space_size));
5855     *k = (PetscInt)PetscCeilReal(PetscPowReal(dual_space_size / *Nc, 1.0 / dim)) - 1;
5856     PetscCall(PetscDualSpaceLagrangeGetContinuity(dsp, continuous));
5857     PetscCall(PetscDualSpaceLagrangeGetTensor(dsp, tensor));
5858   }
5859   PetscFunctionReturn(PETSC_SUCCESS);
5860 }
5861 
5862 static PetscErrorCode GetFieldSize_Private(PetscInt dim, PetscInt k, PetscBool tensor, PetscInt *dof)
5863 {
5864   PetscFunctionBeginHot;
5865   if (tensor) {
5866     *dof = PetscPowInt(k + 1, dim);
5867   } else {
5868     switch (dim) {
5869     case 1:
5870       *dof = k + 1;
5871       break;
5872     case 2:
5873       *dof = ((k + 1) * (k + 2)) / 2;
5874       break;
5875     case 3:
5876       *dof = ((k + 1) * (k + 2) * (k + 3)) / 6;
5877       break;
5878     default:
5879       *dof = 0;
5880     }
5881   }
5882   PetscFunctionReturn(PETSC_SUCCESS);
5883 }
5884 
5885 /*@
5886   DMPlexSetClosurePermutationTensor - Create a permutation from the default (BFS) point ordering in the closure, to a
5887   lexicographic ordering over the tensor product cell (i.e., line, quad, hex, etc.), and set this permutation in the
5888   section provided (or the section of the `DM`).
5889 
5890   Input Parameters:
5891 + dm      - The `DM`
5892 . point   - Either a cell (highest dim point) or an edge (dim 1 point), or `PETSC_DETERMINE`
5893 - section - The `PetscSection` to reorder, or `NULL` for the default section
5894 
5895   Example:
5896   A typical interpolated single-quad mesh might order points as
5897 .vb
5898   [c0, v1, v2, v3, v4, e5, e6, e7, e8]
5899 
5900   v4 -- e6 -- v3
5901   |           |
5902   e7    c0    e8
5903   |           |
5904   v1 -- e5 -- v2
5905 .ve
5906 
5907   (There is no significance to the ordering described here.)  The default section for a Q3 quad might typically assign
5908   dofs in the order of points, e.g.,
5909 .vb
5910     c0 -> [0,1,2,3]
5911     v1 -> [4]
5912     ...
5913     e5 -> [8, 9]
5914 .ve
5915 
5916   which corresponds to the dofs
5917 .vb
5918     6   10  11  7
5919     13  2   3   15
5920     12  0   1   14
5921     4   8   9   5
5922 .ve
5923 
5924   The closure in BFS ordering works through height strata (cells, edges, vertices) to produce the ordering
5925 .vb
5926   0 1 2 3 8 9 14 15 11 10 13 12 4 5 7 6
5927 .ve
5928 
5929   After calling DMPlexSetClosurePermutationTensor(), the closure will be ordered lexicographically,
5930 .vb
5931    4 8 9 5 12 0 1 14 13 2 3 15 6 10 11 7
5932 .ve
5933 
5934   Level: developer
5935 
5936   Notes:
5937   The point is used to determine the number of dofs/field on an edge. For SEM, this is related to the polynomial
5938   degree of the basis.
5939 
5940   This is required to run with libCEED.
5941 
5942 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMGetLocalSection()`, `PetscSectionSetClosurePermutation()`, `DMSetGlobalSection()`
5943 @*/
5944 PetscErrorCode DMPlexSetClosurePermutationTensor(DM dm, PetscInt point, PetscSection section)
5945 {
5946   DMLabel   label;
5947   PetscInt  dim, depth = -1, eStart = -1, Nf;
5948   PetscBool continuous = PETSC_TRUE, tensor = PETSC_TRUE;
5949 
5950   PetscFunctionBegin;
5951   PetscCall(DMGetDimension(dm, &dim));
5952   if (dim < 1) PetscFunctionReturn(PETSC_SUCCESS);
5953   if (point < 0) {
5954     PetscInt sStart, sEnd;
5955 
5956     PetscCall(DMPlexGetDepthStratum(dm, 1, &sStart, &sEnd));
5957     point = sEnd - sStart ? sStart : point;
5958   }
5959   PetscCall(DMPlexGetDepthLabel(dm, &label));
5960   if (point >= 0) PetscCall(DMLabelGetValue(label, point, &depth));
5961   if (!section) PetscCall(DMGetLocalSection(dm, &section));
5962   if (depth == 1) {
5963     eStart = point;
5964   } else if (depth == dim) {
5965     const PetscInt *cone;
5966 
5967     PetscCall(DMPlexGetCone(dm, point, &cone));
5968     if (dim == 2) eStart = cone[0];
5969     else if (dim == 3) {
5970       const PetscInt *cone2;
5971       PetscCall(DMPlexGetCone(dm, cone[0], &cone2));
5972       eStart = cone2[0];
5973     } 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);
5974   } 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);
5975 
5976   PetscCall(PetscSectionGetNumFields(section, &Nf));
5977   for (PetscInt d = 1; d <= dim; d++) {
5978     PetscInt  k, f, Nc, c, i, j, size = 0, offset = 0, foffset = 0;
5979     PetscInt *perm;
5980 
5981     for (f = 0; f < Nf; ++f) {
5982       PetscInt dof;
5983 
5984       PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
5985       PetscCheck(dim == 1 || tensor || !continuous, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Continuous field %" PetscInt_FMT " must have a tensor product discretization", f);
5986       if (!continuous && d < dim) continue;
5987       PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
5988       size += dof * Nc;
5989     }
5990     PetscCall(PetscMalloc1(size, &perm));
5991     for (f = 0; f < Nf; ++f) {
5992       switch (d) {
5993       case 1:
5994         PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
5995         if (!continuous && d < dim) continue;
5996         /*
5997          Original ordering is [ edge of length k-1; vtx0; vtx1 ]
5998          We want              [ vtx0; edge of length k-1; vtx1 ]
5999          */
6000         if (continuous) {
6001           for (c = 0; c < Nc; c++, offset++) perm[offset] = (k - 1) * Nc + c + foffset;
6002           for (i = 0; i < k - 1; i++)
6003             for (c = 0; c < Nc; c++, offset++) perm[offset] = i * Nc + c + foffset;
6004           for (c = 0; c < Nc; c++, offset++) perm[offset] = k * Nc + c + foffset;
6005           foffset = offset;
6006         } else {
6007           PetscInt dof;
6008 
6009           PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
6010           for (i = 0; i < dof * Nc; ++i, ++offset) perm[offset] = i + foffset;
6011           foffset = offset;
6012         }
6013         break;
6014       case 2:
6015         /* The original quad closure is oriented clockwise, {f, e_b, e_r, e_t, e_l, v_lb, v_rb, v_tr, v_tl} */
6016         PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
6017         if (!continuous && d < dim) continue;
6018         /* The SEM order is
6019 
6020          v_lb, {e_b}, v_rb,
6021          e^{(k-1)-i}_l, {f^{i*(k-1)}}, e^i_r,
6022          v_lt, reverse {e_t}, v_rt
6023          */
6024         if (continuous) {
6025           const PetscInt of   = 0;
6026           const PetscInt oeb  = of + PetscSqr(k - 1);
6027           const PetscInt oer  = oeb + (k - 1);
6028           const PetscInt oet  = oer + (k - 1);
6029           const PetscInt oel  = oet + (k - 1);
6030           const PetscInt ovlb = oel + (k - 1);
6031           const PetscInt ovrb = ovlb + 1;
6032           const PetscInt ovrt = ovrb + 1;
6033           const PetscInt ovlt = ovrt + 1;
6034           PetscInt       o;
6035 
6036           /* bottom */
6037           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlb * Nc + c + foffset;
6038           for (o = oeb; o < oer; ++o)
6039             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6040           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrb * Nc + c + foffset;
6041           /* middle */
6042           for (i = 0; i < k - 1; ++i) {
6043             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oel + (k - 2) - i) * Nc + c + foffset;
6044             for (o = of + (k - 1) * i; o < of + (k - 1) * (i + 1); ++o)
6045               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6046             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oer + i) * Nc + c + foffset;
6047           }
6048           /* top */
6049           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlt * Nc + c + foffset;
6050           for (o = oel - 1; o >= oet; --o)
6051             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6052           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrt * Nc + c + foffset;
6053           foffset = offset;
6054         } else {
6055           PetscInt dof;
6056 
6057           PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
6058           for (i = 0; i < dof * Nc; ++i, ++offset) perm[offset] = i + foffset;
6059           foffset = offset;
6060         }
6061         break;
6062       case 3:
6063         /* The original hex closure is
6064 
6065          {c,
6066          f_b, f_t, f_f, f_b, f_r, f_l,
6067          e_bl, e_bb, e_br, e_bf,  e_tf, e_tr, e_tb, e_tl,  e_rf, e_lf, e_lb, e_rb,
6068          v_blf, v_blb, v_brb, v_brf, v_tlf, v_trf, v_trb, v_tlb}
6069          */
6070         PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
6071         if (!continuous && d < dim) continue;
6072         /* The SEM order is
6073          Bottom Slice
6074          v_blf, {e^{(k-1)-n}_bf}, v_brf,
6075          e^{i}_bl, f^{n*(k-1)+(k-1)-i}_b, e^{(k-1)-i}_br,
6076          v_blb, {e_bb}, v_brb,
6077 
6078          Middle Slice (j)
6079          {e^{(k-1)-j}_lf}, {f^{j*(k-1)+n}_f}, e^j_rf,
6080          f^{i*(k-1)+j}_l, {c^{(j*(k-1) + i)*(k-1)+n}_t}, f^{j*(k-1)+i}_r,
6081          e^j_lb, {f^{j*(k-1)+(k-1)-n}_b}, e^{(k-1)-j}_rb,
6082 
6083          Top Slice
6084          v_tlf, {e_tf}, v_trf,
6085          e^{(k-1)-i}_tl, {f^{i*(k-1)}_t}, e^{i}_tr,
6086          v_tlb, {e^{(k-1)-n}_tb}, v_trb,
6087          */
6088         if (continuous) {
6089           const PetscInt oc    = 0;
6090           const PetscInt ofb   = oc + PetscSqr(k - 1) * (k - 1);
6091           const PetscInt oft   = ofb + PetscSqr(k - 1);
6092           const PetscInt off   = oft + PetscSqr(k - 1);
6093           const PetscInt ofk   = off + PetscSqr(k - 1);
6094           const PetscInt ofr   = ofk + PetscSqr(k - 1);
6095           const PetscInt ofl   = ofr + PetscSqr(k - 1);
6096           const PetscInt oebl  = ofl + PetscSqr(k - 1);
6097           const PetscInt oebb  = oebl + (k - 1);
6098           const PetscInt oebr  = oebb + (k - 1);
6099           const PetscInt oebf  = oebr + (k - 1);
6100           const PetscInt oetf  = oebf + (k - 1);
6101           const PetscInt oetr  = oetf + (k - 1);
6102           const PetscInt oetb  = oetr + (k - 1);
6103           const PetscInt oetl  = oetb + (k - 1);
6104           const PetscInt oerf  = oetl + (k - 1);
6105           const PetscInt oelf  = oerf + (k - 1);
6106           const PetscInt oelb  = oelf + (k - 1);
6107           const PetscInt oerb  = oelb + (k - 1);
6108           const PetscInt ovblf = oerb + (k - 1);
6109           const PetscInt ovblb = ovblf + 1;
6110           const PetscInt ovbrb = ovblb + 1;
6111           const PetscInt ovbrf = ovbrb + 1;
6112           const PetscInt ovtlf = ovbrf + 1;
6113           const PetscInt ovtrf = ovtlf + 1;
6114           const PetscInt ovtrb = ovtrf + 1;
6115           const PetscInt ovtlb = ovtrb + 1;
6116           PetscInt       o, n;
6117 
6118           /* Bottom Slice */
6119           /*   bottom */
6120           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblf * Nc + c + foffset;
6121           for (o = oetf - 1; o >= oebf; --o)
6122             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6123           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrf * Nc + c + foffset;
6124           /*   middle */
6125           for (i = 0; i < k - 1; ++i) {
6126             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebl + i) * Nc + c + foffset;
6127             for (n = 0; n < k - 1; ++n) {
6128               o = ofb + n * (k - 1) + i;
6129               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6130             }
6131             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebr + (k - 2) - i) * Nc + c + foffset;
6132           }
6133           /*   top */
6134           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblb * Nc + c + foffset;
6135           for (o = oebb; o < oebr; ++o)
6136             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6137           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrb * Nc + c + foffset;
6138 
6139           /* Middle Slice */
6140           for (j = 0; j < k - 1; ++j) {
6141             /*   bottom */
6142             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelf + (k - 2) - j) * Nc + c + foffset;
6143             for (o = off + j * (k - 1); o < off + (j + 1) * (k - 1); ++o)
6144               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6145             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerf + j) * Nc + c + foffset;
6146             /*   middle */
6147             for (i = 0; i < k - 1; ++i) {
6148               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofl + i * (k - 1) + j) * Nc + c + foffset;
6149               for (n = 0; n < k - 1; ++n)
6150                 for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oc + (j * (k - 1) + i) * (k - 1) + n) * Nc + c + foffset;
6151               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofr + j * (k - 1) + i) * Nc + c + foffset;
6152             }
6153             /*   top */
6154             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelb + j) * Nc + c + foffset;
6155             for (o = ofk + j * (k - 1) + (k - 2); o >= ofk + j * (k - 1); --o)
6156               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6157             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerb + (k - 2) - j) * Nc + c + foffset;
6158           }
6159 
6160           /* Top Slice */
6161           /*   bottom */
6162           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlf * Nc + c + foffset;
6163           for (o = oetf; o < oetr; ++o)
6164             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6165           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrf * Nc + c + foffset;
6166           /*   middle */
6167           for (i = 0; i < k - 1; ++i) {
6168             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetl + (k - 2) - i) * Nc + c + foffset;
6169             for (n = 0; n < k - 1; ++n)
6170               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oft + i * (k - 1) + n) * Nc + c + foffset;
6171             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetr + i) * Nc + c + foffset;
6172           }
6173           /*   top */
6174           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlb * Nc + c + foffset;
6175           for (o = oetl - 1; o >= oetb; --o)
6176             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6177           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrb * Nc + c + foffset;
6178 
6179           foffset = offset;
6180         } else {
6181           PetscInt dof;
6182 
6183           PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
6184           for (i = 0; i < dof * Nc; ++i, ++offset) perm[offset] = i + foffset;
6185           foffset = offset;
6186         }
6187         break;
6188       default:
6189         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "No spectral ordering for dimension %" PetscInt_FMT, d);
6190       }
6191     }
6192     PetscCheck(offset == size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Number of permutation entries %" PetscInt_FMT " != %" PetscInt_FMT, offset, size);
6193     /* Check permutation */
6194     {
6195       PetscInt *check;
6196 
6197       PetscCall(PetscMalloc1(size, &check));
6198       for (i = 0; i < size; ++i) {
6199         check[i] = -1;
6200         PetscCheck(perm[i] >= 0 && perm[i] < size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid permutation index p[%" PetscInt_FMT "] = %" PetscInt_FMT, i, perm[i]);
6201       }
6202       for (i = 0; i < size; ++i) check[perm[i]] = i;
6203       for (i = 0; i < size; ++i) PetscCheck(check[i] >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Missing permutation index %" PetscInt_FMT, i);
6204       PetscCall(PetscFree(check));
6205     }
6206     PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size, PETSC_OWN_POINTER, perm));
6207     if (d == dim) { // Add permutation for localized (in case this is a coordinate DM)
6208       PetscInt *loc_perm;
6209       PetscCall(PetscMalloc1(size * 2, &loc_perm));
6210       for (PetscInt i = 0; i < size; i++) {
6211         loc_perm[i]        = perm[i];
6212         loc_perm[size + i] = size + perm[i];
6213       }
6214       PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size * 2, PETSC_OWN_POINTER, loc_perm));
6215     }
6216   }
6217   PetscFunctionReturn(PETSC_SUCCESS);
6218 }
6219 
6220 PetscErrorCode DMPlexGetPointDualSpaceFEM(DM dm, PetscInt point, PetscInt field, PetscDualSpace *dspace)
6221 {
6222   PetscDS  prob;
6223   PetscInt depth, Nf, h;
6224   DMLabel  label;
6225 
6226   PetscFunctionBeginHot;
6227   PetscCall(DMGetDS(dm, &prob));
6228   Nf      = prob->Nf;
6229   label   = dm->depthLabel;
6230   *dspace = NULL;
6231   if (field < Nf) {
6232     PetscObject disc = prob->disc[field];
6233 
6234     if (disc->classid == PETSCFE_CLASSID) {
6235       PetscDualSpace dsp;
6236 
6237       PetscCall(PetscFEGetDualSpace((PetscFE)disc, &dsp));
6238       PetscCall(DMLabelGetNumValues(label, &depth));
6239       PetscCall(DMLabelGetValue(label, point, &h));
6240       h = depth - 1 - h;
6241       if (h) {
6242         PetscCall(PetscDualSpaceGetHeightSubspace(dsp, h, dspace));
6243       } else {
6244         *dspace = dsp;
6245       }
6246     }
6247   }
6248   PetscFunctionReturn(PETSC_SUCCESS);
6249 }
6250 
6251 static inline PetscErrorCode DMPlexVecGetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6252 {
6253   PetscScalar       *array;
6254   const PetscScalar *vArray;
6255   const PetscInt    *cone, *coneO;
6256   PetscInt           pStart, pEnd, p, numPoints, size = 0, offset = 0;
6257 
6258   PetscFunctionBeginHot;
6259   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6260   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
6261   PetscCall(DMPlexGetCone(dm, point, &cone));
6262   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
6263   if (!values || !*values) {
6264     if ((point >= pStart) && (point < pEnd)) {
6265       PetscInt dof;
6266 
6267       PetscCall(PetscSectionGetDof(section, point, &dof));
6268       size += dof;
6269     }
6270     for (p = 0; p < numPoints; ++p) {
6271       const PetscInt cp = cone[p];
6272       PetscInt       dof;
6273 
6274       if ((cp < pStart) || (cp >= pEnd)) continue;
6275       PetscCall(PetscSectionGetDof(section, cp, &dof));
6276       size += dof;
6277     }
6278     if (!values) {
6279       if (csize) *csize = size;
6280       PetscFunctionReturn(PETSC_SUCCESS);
6281     }
6282     PetscCall(DMGetWorkArray(dm, size, MPIU_SCALAR, &array));
6283   } else {
6284     array = *values;
6285   }
6286   size = 0;
6287   PetscCall(VecGetArrayRead(v, &vArray));
6288   if ((point >= pStart) && (point < pEnd)) {
6289     PetscInt           dof, off, d;
6290     const PetscScalar *varr;
6291 
6292     PetscCall(PetscSectionGetDof(section, point, &dof));
6293     PetscCall(PetscSectionGetOffset(section, point, &off));
6294     varr = PetscSafePointerPlusOffset(vArray, off);
6295     for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
6296     size += dof;
6297   }
6298   for (p = 0; p < numPoints; ++p) {
6299     const PetscInt     cp = cone[p];
6300     PetscInt           o  = coneO[p];
6301     PetscInt           dof, off, d;
6302     const PetscScalar *varr;
6303 
6304     if ((cp < pStart) || (cp >= pEnd)) continue;
6305     PetscCall(PetscSectionGetDof(section, cp, &dof));
6306     PetscCall(PetscSectionGetOffset(section, cp, &off));
6307     varr = PetscSafePointerPlusOffset(vArray, off);
6308     if (o >= 0) {
6309       for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
6310     } else {
6311       for (d = dof - 1; d >= 0; --d, ++offset) array[offset] = varr[d];
6312     }
6313     size += dof;
6314   }
6315   PetscCall(VecRestoreArrayRead(v, &vArray));
6316   if (!*values) {
6317     if (csize) *csize = size;
6318     *values = array;
6319   } else {
6320     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
6321     *csize = size;
6322   }
6323   PetscFunctionReturn(PETSC_SUCCESS);
6324 }
6325 
6326 /* Compress out points not in the section */
6327 static inline PetscErrorCode CompressPoints_Private(PetscSection section, PetscInt *numPoints, PetscInt points[])
6328 {
6329   const PetscInt np = *numPoints;
6330   PetscInt       pStart, pEnd, p, q;
6331 
6332   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6333   for (p = 0, q = 0; p < np; ++p) {
6334     const PetscInt r = points[p * 2];
6335     if ((r >= pStart) && (r < pEnd)) {
6336       points[q * 2]     = r;
6337       points[q * 2 + 1] = points[p * 2 + 1];
6338       ++q;
6339     }
6340   }
6341   *numPoints = q;
6342   return PETSC_SUCCESS;
6343 }
6344 
6345 /* Compressed closure does not apply closure permutation */
6346 PetscErrorCode DMPlexGetCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt ornt, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
6347 {
6348   const PetscInt *cla = NULL;
6349   PetscInt        np, *pts = NULL;
6350 
6351   PetscFunctionBeginHot;
6352   PetscCall(PetscSectionGetClosureIndex(section, (PetscObject)dm, clSec, clPoints));
6353   if (!ornt && *clPoints) {
6354     PetscInt dof, off;
6355 
6356     PetscCall(PetscSectionGetDof(*clSec, point, &dof));
6357     PetscCall(PetscSectionGetOffset(*clSec, point, &off));
6358     PetscCall(ISGetIndices(*clPoints, &cla));
6359     np  = dof / 2;
6360     pts = PetscSafePointerPlusOffset((PetscInt *)cla, off);
6361   } else {
6362     PetscCall(DMPlexGetTransitiveClosure_Internal(dm, point, ornt, PETSC_TRUE, &np, &pts));
6363     PetscCall(CompressPoints_Private(section, &np, pts));
6364   }
6365   *numPoints = np;
6366   *points    = pts;
6367   *clp       = cla;
6368   PetscFunctionReturn(PETSC_SUCCESS);
6369 }
6370 
6371 PetscErrorCode DMPlexRestoreCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
6372 {
6373   PetscFunctionBeginHot;
6374   if (!*clPoints) {
6375     PetscCall(DMPlexRestoreTransitiveClosure(dm, point, PETSC_TRUE, numPoints, points));
6376   } else {
6377     PetscCall(ISRestoreIndices(*clPoints, clp));
6378   }
6379   *numPoints = 0;
6380   *points    = NULL;
6381   *clSec     = NULL;
6382   *clPoints  = NULL;
6383   *clp       = NULL;
6384   PetscFunctionReturn(PETSC_SUCCESS);
6385 }
6386 
6387 static inline PetscErrorCode DMPlexVecGetClosure_Static(DM dm, PetscSection section, PetscInt numPoints, const PetscInt points[], const PetscInt clperm[], const PetscScalar vArray[], PetscInt *size, PetscScalar array[])
6388 {
6389   PetscInt            offset = 0, p;
6390   const PetscInt    **perms  = NULL;
6391   const PetscScalar **flips  = NULL;
6392 
6393   PetscFunctionBeginHot;
6394   *size = 0;
6395   PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
6396   for (p = 0; p < numPoints; p++) {
6397     const PetscInt     point = points[2 * p];
6398     const PetscInt    *perm  = perms ? perms[p] : NULL;
6399     const PetscScalar *flip  = flips ? flips[p] : NULL;
6400     PetscInt           dof, off, d;
6401     const PetscScalar *varr;
6402 
6403     PetscCall(PetscSectionGetDof(section, point, &dof));
6404     PetscCall(PetscSectionGetOffset(section, point, &off));
6405     varr = PetscSafePointerPlusOffset(vArray, off);
6406     if (clperm) {
6407       if (perm) {
6408         for (d = 0; d < dof; d++) array[clperm[offset + perm[d]]] = varr[d];
6409       } else {
6410         for (d = 0; d < dof; d++) array[clperm[offset + d]] = varr[d];
6411       }
6412       if (flip) {
6413         for (d = 0; d < dof; d++) array[clperm[offset + d]] *= flip[d];
6414       }
6415     } else {
6416       if (perm) {
6417         for (d = 0; d < dof; d++) array[offset + perm[d]] = varr[d];
6418       } else {
6419         for (d = 0; d < dof; d++) array[offset + d] = varr[d];
6420       }
6421       if (flip) {
6422         for (d = 0; d < dof; d++) array[offset + d] *= flip[d];
6423       }
6424     }
6425     offset += dof;
6426   }
6427   PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
6428   *size = offset;
6429   PetscFunctionReturn(PETSC_SUCCESS);
6430 }
6431 
6432 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[])
6433 {
6434   PetscInt offset = 0, f;
6435 
6436   PetscFunctionBeginHot;
6437   *size = 0;
6438   for (f = 0; f < numFields; ++f) {
6439     PetscInt            p;
6440     const PetscInt    **perms = NULL;
6441     const PetscScalar **flips = NULL;
6442 
6443     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6444     for (p = 0; p < numPoints; p++) {
6445       const PetscInt     point = points[2 * p];
6446       PetscInt           fdof, foff, b;
6447       const PetscScalar *varr;
6448       const PetscInt    *perm = perms ? perms[p] : NULL;
6449       const PetscScalar *flip = flips ? flips[p] : NULL;
6450 
6451       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6452       PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6453       varr = &vArray[foff];
6454       if (clperm) {
6455         if (perm) {
6456           for (b = 0; b < fdof; b++) array[clperm[offset + perm[b]]] = varr[b];
6457         } else {
6458           for (b = 0; b < fdof; b++) array[clperm[offset + b]] = varr[b];
6459         }
6460         if (flip) {
6461           for (b = 0; b < fdof; b++) array[clperm[offset + b]] *= flip[b];
6462         }
6463       } else {
6464         if (perm) {
6465           for (b = 0; b < fdof; b++) array[offset + perm[b]] = varr[b];
6466         } else {
6467           for (b = 0; b < fdof; b++) array[offset + b] = varr[b];
6468         }
6469         if (flip) {
6470           for (b = 0; b < fdof; b++) array[offset + b] *= flip[b];
6471         }
6472       }
6473       offset += fdof;
6474     }
6475     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6476   }
6477   *size = offset;
6478   PetscFunctionReturn(PETSC_SUCCESS);
6479 }
6480 
6481 PetscErrorCode DMPlexVecGetOrientedClosure_Internal(DM dm, PetscSection section, PetscBool useClPerm, Vec v, PetscInt point, PetscInt ornt, PetscInt *csize, PetscScalar *values[])
6482 {
6483   PetscSection    clSection;
6484   IS              clPoints;
6485   PetscInt       *points = NULL;
6486   const PetscInt *clp, *perm = NULL;
6487   PetscInt        depth, numFields, numPoints, asize;
6488 
6489   PetscFunctionBeginHot;
6490   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6491   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6492   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6493   PetscValidHeaderSpecific(v, VEC_CLASSID, 4);
6494   PetscCall(DMPlexGetDepth(dm, &depth));
6495   PetscCall(PetscSectionGetNumFields(section, &numFields));
6496   if (depth == 1 && numFields < 2) {
6497     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6498     PetscFunctionReturn(PETSC_SUCCESS);
6499   }
6500   /* Get points */
6501   PetscCall(DMPlexGetCompressedClosure(dm, section, point, ornt, &numPoints, &points, &clSection, &clPoints, &clp));
6502   /* Get sizes */
6503   asize = 0;
6504   for (PetscInt p = 0; p < numPoints * 2; p += 2) {
6505     PetscInt dof;
6506     PetscCall(PetscSectionGetDof(section, points[p], &dof));
6507     asize += dof;
6508   }
6509   if (values) {
6510     const PetscScalar *vArray;
6511     PetscInt           size;
6512 
6513     if (*values) {
6514       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);
6515     } else PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, values));
6516     if (useClPerm) PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, asize, &perm));
6517     PetscCall(VecGetArrayRead(v, &vArray));
6518     /* Get values */
6519     if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, numPoints, points, numFields, perm, vArray, &size, *values));
6520     else PetscCall(DMPlexVecGetClosure_Static(dm, section, numPoints, points, perm, vArray, &size, *values));
6521     PetscCheck(asize == size, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Section size %" PetscInt_FMT " does not match Vec closure size %" PetscInt_FMT, asize, size);
6522     /* Cleanup array */
6523     PetscCall(VecRestoreArrayRead(v, &vArray));
6524   }
6525   if (csize) *csize = asize;
6526   /* Cleanup points */
6527   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6528   PetscFunctionReturn(PETSC_SUCCESS);
6529 }
6530 
6531 /*@C
6532   DMPlexVecGetClosure - Get an array of the values on the closure of 'point'
6533 
6534   Not collective
6535 
6536   Input Parameters:
6537 + dm      - The `DM`
6538 . section - The section describing the layout in `v`, or `NULL` to use the default section
6539 . v       - The local vector
6540 - point   - The point in the `DM`
6541 
6542   Input/Output Parameters:
6543 + csize  - The size of the input values array, or `NULL`; on output the number of values in the closure
6544 - values - An array to use for the values, or *values = `NULL` to have it allocated automatically;
6545            if the user provided `NULL`, it is a borrowed array and should not be freed, use  `DMPlexVecRestoreClosure()` to return it
6546 
6547   Level: intermediate
6548 
6549   Notes:
6550   `DMPlexVecGetClosure()`/`DMPlexVecRestoreClosure()` only allocates the values array if it set to `NULL` in the
6551   calling function. This is because `DMPlexVecGetClosure()` is typically called in the inner loop of a `Vec` or `Mat`
6552   assembly function, and a user may already have allocated storage for this operation.
6553 
6554   A typical use could be
6555 .vb
6556    values = NULL;
6557    PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
6558    for (cl = 0; cl < clSize; ++cl) {
6559      <Compute on closure>
6560    }
6561    PetscCall(DMPlexVecRestoreClosure(dm, NULL, v, p, &clSize, &values));
6562 .ve
6563   or
6564 .vb
6565    PetscMalloc1(clMaxSize, &values);
6566    for (p = pStart; p < pEnd; ++p) {
6567      clSize = clMaxSize;
6568      PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
6569      for (cl = 0; cl < clSize; ++cl) {
6570        <Compute on closure>
6571      }
6572    }
6573    PetscFree(values);
6574 .ve
6575 
6576   Fortran Notes:
6577   The `csize` argument is not present in the Fortran binding.
6578 
6579   `values` must be declared with
6580 .vb
6581   PetscScalar,dimension(:),pointer   :: values
6582 .ve
6583   and it will be allocated internally by PETSc to hold the values returned
6584 
6585 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecRestoreClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6586 @*/
6587 PetscErrorCode DMPlexVecGetClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6588 {
6589   PetscFunctionBeginHot;
6590   PetscCall(DMPlexVecGetOrientedClosure_Internal(dm, section, PETSC_TRUE, v, point, 0, csize, values));
6591   PetscFunctionReturn(PETSC_SUCCESS);
6592 }
6593 
6594 PetscErrorCode DMPlexVecGetClosureAtDepth_Internal(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt depth, PetscInt *csize, PetscScalar *values[])
6595 {
6596   DMLabel            depthLabel;
6597   PetscSection       clSection;
6598   IS                 clPoints;
6599   PetscScalar       *array;
6600   const PetscScalar *vArray;
6601   PetscInt          *points = NULL;
6602   const PetscInt    *clp, *perm = NULL;
6603   PetscInt           mdepth, numFields, numPoints, Np = 0, p, clsize, size;
6604 
6605   PetscFunctionBeginHot;
6606   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6607   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6608   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6609   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6610   PetscCall(DMPlexGetDepth(dm, &mdepth));
6611   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
6612   PetscCall(PetscSectionGetNumFields(section, &numFields));
6613   if (mdepth == 1 && numFields < 2) {
6614     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6615     PetscFunctionReturn(PETSC_SUCCESS);
6616   }
6617   /* Get points */
6618   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
6619   for (clsize = 0, p = 0; p < Np; p++) {
6620     PetscInt dof;
6621     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
6622     clsize += dof;
6623   }
6624   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &perm));
6625   /* Filter points */
6626   for (p = 0; p < numPoints * 2; p += 2) {
6627     PetscInt dep;
6628 
6629     PetscCall(DMLabelGetValue(depthLabel, points[p], &dep));
6630     if (dep != depth) continue;
6631     points[Np * 2 + 0] = points[p];
6632     points[Np * 2 + 1] = points[p + 1];
6633     ++Np;
6634   }
6635   /* Get array */
6636   if (!values || !*values) {
6637     PetscInt asize = 0, dof;
6638 
6639     for (p = 0; p < Np * 2; p += 2) {
6640       PetscCall(PetscSectionGetDof(section, points[p], &dof));
6641       asize += dof;
6642     }
6643     if (!values) {
6644       PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6645       if (csize) *csize = asize;
6646       PetscFunctionReturn(PETSC_SUCCESS);
6647     }
6648     PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, &array));
6649   } else {
6650     array = *values;
6651   }
6652   PetscCall(VecGetArrayRead(v, &vArray));
6653   /* Get values */
6654   if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, Np, points, numFields, perm, vArray, &size, array));
6655   else PetscCall(DMPlexVecGetClosure_Static(dm, section, Np, points, perm, vArray, &size, array));
6656   /* Cleanup points */
6657   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6658   /* Cleanup array */
6659   PetscCall(VecRestoreArrayRead(v, &vArray));
6660   if (!*values) {
6661     if (csize) *csize = size;
6662     *values = array;
6663   } else {
6664     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
6665     *csize = size;
6666   }
6667   PetscFunctionReturn(PETSC_SUCCESS);
6668 }
6669 
6670 /*@C
6671   DMPlexVecRestoreClosure - Restore the array of the values on the closure of 'point' obtained with `DMPlexVecGetClosure()`
6672 
6673   Not collective
6674 
6675   Input Parameters:
6676 + dm      - The `DM`
6677 . section - The section describing the layout in `v`, or `NULL` to use the default section
6678 . v       - The local vector
6679 . point   - The point in the `DM`
6680 . csize   - The number of values in the closure, or `NULL`
6681 - values  - The array of values
6682 
6683   Level: intermediate
6684 
6685   Note:
6686   The array values are discarded and not copied back into `v`. In order to copy values back to `v`, use `DMPlexVecSetClosure()`
6687 
6688   Fortran Note:
6689   The `csize` argument is not present in the Fortran binding since it is internal to the array.
6690 
6691 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6692 @*/
6693 PetscErrorCode DMPlexVecRestoreClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6694 {
6695   PetscInt size = 0;
6696 
6697   PetscFunctionBegin;
6698   /* Should work without recalculating size */
6699   PetscCall(DMRestoreWorkArray(dm, size, MPIU_SCALAR, (void *)values));
6700   *values = NULL;
6701   PetscFunctionReturn(PETSC_SUCCESS);
6702 }
6703 
6704 static inline void add(PetscScalar *x, PetscScalar y)
6705 {
6706   *x += y;
6707 }
6708 static inline void insert(PetscScalar *x, PetscScalar y)
6709 {
6710   *x = y;
6711 }
6712 
6713 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[])
6714 {
6715   PetscInt        cdof;  /* The number of constraints on this point */
6716   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6717   PetscScalar    *a;
6718   PetscInt        off, cind = 0, k;
6719 
6720   PetscFunctionBegin;
6721   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6722   PetscCall(PetscSectionGetOffset(section, point, &off));
6723   a = &array[off];
6724   if (!cdof || setBC) {
6725     if (clperm) {
6726       if (perm) {
6727         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6728       } else {
6729         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6730       }
6731     } else {
6732       if (perm) {
6733         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6734       } else {
6735         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6736       }
6737     }
6738   } else {
6739     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6740     if (clperm) {
6741       if (perm) {
6742         for (k = 0; k < dof; ++k) {
6743           if ((cind < cdof) && (k == cdofs[cind])) {
6744             ++cind;
6745             continue;
6746           }
6747           fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6748         }
6749       } else {
6750         for (k = 0; k < dof; ++k) {
6751           if ((cind < cdof) && (k == cdofs[cind])) {
6752             ++cind;
6753             continue;
6754           }
6755           fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6756         }
6757       }
6758     } else {
6759       if (perm) {
6760         for (k = 0; k < dof; ++k) {
6761           if ((cind < cdof) && (k == cdofs[cind])) {
6762             ++cind;
6763             continue;
6764           }
6765           fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6766         }
6767       } else {
6768         for (k = 0; k < dof; ++k) {
6769           if ((cind < cdof) && (k == cdofs[cind])) {
6770             ++cind;
6771             continue;
6772           }
6773           fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6774         }
6775       }
6776     }
6777   }
6778   PetscFunctionReturn(PETSC_SUCCESS);
6779 }
6780 
6781 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[])
6782 {
6783   PetscInt        cdof;  /* The number of constraints on this point */
6784   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6785   PetscScalar    *a;
6786   PetscInt        off, cind = 0, k;
6787 
6788   PetscFunctionBegin;
6789   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6790   PetscCall(PetscSectionGetOffset(section, point, &off));
6791   a = &array[off];
6792   if (cdof) {
6793     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6794     if (clperm) {
6795       if (perm) {
6796         for (k = 0; k < dof; ++k) {
6797           if ((cind < cdof) && (k == cdofs[cind])) {
6798             fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6799             cind++;
6800           }
6801         }
6802       } else {
6803         for (k = 0; k < dof; ++k) {
6804           if ((cind < cdof) && (k == cdofs[cind])) {
6805             fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6806             cind++;
6807           }
6808         }
6809       }
6810     } else {
6811       if (perm) {
6812         for (k = 0; k < dof; ++k) {
6813           if ((cind < cdof) && (k == cdofs[cind])) {
6814             fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6815             cind++;
6816           }
6817         }
6818       } else {
6819         for (k = 0; k < dof; ++k) {
6820           if ((cind < cdof) && (k == cdofs[cind])) {
6821             fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6822             cind++;
6823           }
6824         }
6825       }
6826     }
6827   }
6828   PetscFunctionReturn(PETSC_SUCCESS);
6829 }
6830 
6831 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[])
6832 {
6833   PetscScalar    *a;
6834   PetscInt        fdof, foff, fcdof, foffset = *offset;
6835   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6836   PetscInt        cind = 0, b;
6837 
6838   PetscFunctionBegin;
6839   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6840   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6841   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6842   a = &array[foff];
6843   if (!fcdof || setBC) {
6844     if (clperm) {
6845       if (perm) {
6846         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6847       } else {
6848         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6849       }
6850     } else {
6851       if (perm) {
6852         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6853       } else {
6854         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6855       }
6856     }
6857   } else {
6858     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6859     if (clperm) {
6860       if (perm) {
6861         for (b = 0; b < fdof; b++) {
6862           if ((cind < fcdof) && (b == fcdofs[cind])) {
6863             ++cind;
6864             continue;
6865           }
6866           fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6867         }
6868       } else {
6869         for (b = 0; b < fdof; b++) {
6870           if ((cind < fcdof) && (b == fcdofs[cind])) {
6871             ++cind;
6872             continue;
6873           }
6874           fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6875         }
6876       }
6877     } else {
6878       if (perm) {
6879         for (b = 0; b < fdof; b++) {
6880           if ((cind < fcdof) && (b == fcdofs[cind])) {
6881             ++cind;
6882             continue;
6883           }
6884           fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6885         }
6886       } else {
6887         for (b = 0; b < fdof; b++) {
6888           if ((cind < fcdof) && (b == fcdofs[cind])) {
6889             ++cind;
6890             continue;
6891           }
6892           fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6893         }
6894       }
6895     }
6896   }
6897   *offset += fdof;
6898   PetscFunctionReturn(PETSC_SUCCESS);
6899 }
6900 
6901 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[])
6902 {
6903   PetscScalar    *a;
6904   PetscInt        fdof, foff, fcdof, foffset = *offset;
6905   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6906   PetscInt        Nc, cind = 0, ncind = 0, b;
6907   PetscBool       ncSet, fcSet;
6908 
6909   PetscFunctionBegin;
6910   PetscCall(PetscSectionGetFieldComponents(section, f, &Nc));
6911   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6912   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6913   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6914   a = &array[foff];
6915   if (fcdof) {
6916     /* We just override fcdof and fcdofs with Ncc and comps */
6917     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6918     if (clperm) {
6919       if (perm) {
6920         if (comps) {
6921           for (b = 0; b < fdof; b++) {
6922             ncSet = fcSet = PETSC_FALSE;
6923             if (b % Nc == comps[ncind]) {
6924               ncind = (ncind + 1) % Ncc;
6925               ncSet = PETSC_TRUE;
6926             }
6927             if ((cind < fcdof) && (b == fcdofs[cind])) {
6928               ++cind;
6929               fcSet = PETSC_TRUE;
6930             }
6931             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6932           }
6933         } else {
6934           for (b = 0; b < fdof; b++) {
6935             if ((cind < fcdof) && (b == fcdofs[cind])) {
6936               fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6937               ++cind;
6938             }
6939           }
6940         }
6941       } else {
6942         if (comps) {
6943           for (b = 0; b < fdof; b++) {
6944             ncSet = fcSet = PETSC_FALSE;
6945             if (b % Nc == comps[ncind]) {
6946               ncind = (ncind + 1) % Ncc;
6947               ncSet = PETSC_TRUE;
6948             }
6949             if ((cind < fcdof) && (b == fcdofs[cind])) {
6950               ++cind;
6951               fcSet = PETSC_TRUE;
6952             }
6953             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6954           }
6955         } else {
6956           for (b = 0; b < fdof; b++) {
6957             if ((cind < fcdof) && (b == fcdofs[cind])) {
6958               fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6959               ++cind;
6960             }
6961           }
6962         }
6963       }
6964     } else {
6965       if (perm) {
6966         if (comps) {
6967           for (b = 0; b < fdof; b++) {
6968             ncSet = fcSet = PETSC_FALSE;
6969             if (b % Nc == comps[ncind]) {
6970               ncind = (ncind + 1) % Ncc;
6971               ncSet = PETSC_TRUE;
6972             }
6973             if ((cind < fcdof) && (b == fcdofs[cind])) {
6974               ++cind;
6975               fcSet = PETSC_TRUE;
6976             }
6977             if (ncSet && fcSet) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6978           }
6979         } else {
6980           for (b = 0; b < fdof; b++) {
6981             if ((cind < fcdof) && (b == fcdofs[cind])) {
6982               fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6983               ++cind;
6984             }
6985           }
6986         }
6987       } else {
6988         if (comps) {
6989           for (b = 0; b < fdof; b++) {
6990             ncSet = fcSet = PETSC_FALSE;
6991             if (b % Nc == comps[ncind]) {
6992               ncind = (ncind + 1) % Ncc;
6993               ncSet = PETSC_TRUE;
6994             }
6995             if ((cind < fcdof) && (b == fcdofs[cind])) {
6996               ++cind;
6997               fcSet = PETSC_TRUE;
6998             }
6999             if (ncSet && fcSet) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
7000           }
7001         } else {
7002           for (b = 0; b < fdof; b++) {
7003             if ((cind < fcdof) && (b == fcdofs[cind])) {
7004               fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
7005               ++cind;
7006             }
7007           }
7008         }
7009       }
7010     }
7011   }
7012   *offset += fdof;
7013   PetscFunctionReturn(PETSC_SUCCESS);
7014 }
7015 
7016 static inline PetscErrorCode DMPlexVecSetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
7017 {
7018   PetscScalar    *array;
7019   const PetscInt *cone, *coneO;
7020   PetscInt        pStart, pEnd, p, numPoints, off, dof;
7021 
7022   PetscFunctionBeginHot;
7023   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
7024   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
7025   PetscCall(DMPlexGetCone(dm, point, &cone));
7026   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
7027   PetscCall(VecGetArray(v, &array));
7028   for (p = 0, off = 0; p <= numPoints; ++p, off += dof) {
7029     const PetscInt cp = !p ? point : cone[p - 1];
7030     const PetscInt o  = !p ? 0 : coneO[p - 1];
7031 
7032     if ((cp < pStart) || (cp >= pEnd)) {
7033       dof = 0;
7034       continue;
7035     }
7036     PetscCall(PetscSectionGetDof(section, cp, &dof));
7037     /* ADD_VALUES */
7038     {
7039       const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
7040       PetscScalar    *a;
7041       PetscInt        cdof, coff, cind = 0, k;
7042 
7043       PetscCall(PetscSectionGetConstraintDof(section, cp, &cdof));
7044       PetscCall(PetscSectionGetOffset(section, cp, &coff));
7045       a = &array[coff];
7046       if (!cdof) {
7047         if (o >= 0) {
7048           for (k = 0; k < dof; ++k) a[k] += values[off + k];
7049         } else {
7050           for (k = 0; k < dof; ++k) a[k] += values[off + dof - k - 1];
7051         }
7052       } else {
7053         PetscCall(PetscSectionGetConstraintIndices(section, cp, &cdofs));
7054         if (o >= 0) {
7055           for (k = 0; k < dof; ++k) {
7056             if ((cind < cdof) && (k == cdofs[cind])) {
7057               ++cind;
7058               continue;
7059             }
7060             a[k] += values[off + k];
7061           }
7062         } else {
7063           for (k = 0; k < dof; ++k) {
7064             if ((cind < cdof) && (k == cdofs[cind])) {
7065               ++cind;
7066               continue;
7067             }
7068             a[k] += values[off + dof - k - 1];
7069           }
7070         }
7071       }
7072     }
7073   }
7074   PetscCall(VecRestoreArray(v, &array));
7075   PetscFunctionReturn(PETSC_SUCCESS);
7076 }
7077 
7078 /*@C
7079   DMPlexVecSetClosure - Set an array of the values on the closure of `point`
7080 
7081   Not collective
7082 
7083   Input Parameters:
7084 + dm      - The `DM`
7085 . section - The section describing the layout in `v`, or `NULL` to use the default section
7086 . v       - The local vector
7087 . point   - The point in the `DM`
7088 . values  - The array of values
7089 - mode    - The insert mode. One of `INSERT_ALL_VALUES`, `ADD_ALL_VALUES`, `INSERT_VALUES`, `ADD_VALUES`, `INSERT_BC_VALUES`, and `ADD_BC_VALUES`,
7090             where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions.
7091 
7092   Level: intermediate
7093 
7094   Note:
7095   Usually the input arrays were obtained with `DMPlexVecGetClosure()`
7096 
7097   Fortran Note:
7098   `values` must be declared with
7099 .vb
7100   PetscScalar,dimension(:),pointer   :: values
7101 .ve
7102 
7103 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`
7104 @*/
7105 PetscErrorCode DMPlexVecSetClosure(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
7106 {
7107   PetscSection    clSection;
7108   IS              clPoints;
7109   PetscScalar    *array;
7110   PetscInt       *points = NULL;
7111   const PetscInt *clp, *clperm = NULL;
7112   PetscInt        depth, numFields, numPoints, p, clsize;
7113 
7114   PetscFunctionBeginHot;
7115   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7116   if (!section) PetscCall(DMGetLocalSection(dm, &section));
7117   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7118   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
7119   PetscCall(DMPlexGetDepth(dm, &depth));
7120   PetscCall(PetscSectionGetNumFields(section, &numFields));
7121   if (depth == 1 && numFields < 2 && mode == ADD_VALUES) {
7122     PetscCall(DMPlexVecSetClosure_Depth1_Static(dm, section, v, point, values, mode));
7123     PetscFunctionReturn(PETSC_SUCCESS);
7124   }
7125   /* Get points */
7126   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
7127   for (clsize = 0, p = 0; p < numPoints; p++) {
7128     PetscInt dof;
7129     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
7130     clsize += dof;
7131   }
7132   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
7133   /* Get array */
7134   PetscCall(VecGetArray(v, &array));
7135   /* Get values */
7136   if (numFields > 0) {
7137     PetscInt offset = 0, f;
7138     for (f = 0; f < numFields; ++f) {
7139       const PetscInt    **perms = NULL;
7140       const PetscScalar **flips = NULL;
7141 
7142       PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7143       switch (mode) {
7144       case INSERT_VALUES:
7145         for (p = 0; p < numPoints; p++) {
7146           const PetscInt     point = points[2 * p];
7147           const PetscInt    *perm  = perms ? perms[p] : NULL;
7148           const PetscScalar *flip  = flips ? flips[p] : NULL;
7149           PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, clperm, values, &offset, array));
7150         }
7151         break;
7152       case INSERT_ALL_VALUES:
7153         for (p = 0; p < numPoints; p++) {
7154           const PetscInt     point = points[2 * p];
7155           const PetscInt    *perm  = perms ? perms[p] : NULL;
7156           const PetscScalar *flip  = flips ? flips[p] : NULL;
7157           PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, clperm, values, &offset, array));
7158         }
7159         break;
7160       case INSERT_BC_VALUES:
7161         for (p = 0; p < numPoints; p++) {
7162           const PetscInt     point = points[2 * p];
7163           const PetscInt    *perm  = perms ? perms[p] : NULL;
7164           const PetscScalar *flip  = flips ? flips[p] : NULL;
7165           PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, insert, clperm, values, &offset, array));
7166         }
7167         break;
7168       case ADD_VALUES:
7169         for (p = 0; p < numPoints; p++) {
7170           const PetscInt     point = points[2 * p];
7171           const PetscInt    *perm  = perms ? perms[p] : NULL;
7172           const PetscScalar *flip  = flips ? flips[p] : NULL;
7173           PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, clperm, values, &offset, array));
7174         }
7175         break;
7176       case ADD_ALL_VALUES:
7177         for (p = 0; p < numPoints; p++) {
7178           const PetscInt     point = points[2 * p];
7179           const PetscInt    *perm  = perms ? perms[p] : NULL;
7180           const PetscScalar *flip  = flips ? flips[p] : NULL;
7181           PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, clperm, values, &offset, array));
7182         }
7183         break;
7184       case ADD_BC_VALUES:
7185         for (p = 0; p < numPoints; p++) {
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(updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, add, clperm, values, &offset, array));
7190         }
7191         break;
7192       default:
7193         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
7194       }
7195       PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7196     }
7197   } else {
7198     PetscInt            dof, off;
7199     const PetscInt    **perms = NULL;
7200     const PetscScalar **flips = NULL;
7201 
7202     PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
7203     switch (mode) {
7204     case INSERT_VALUES:
7205       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7206         const PetscInt     point = points[2 * p];
7207         const PetscInt    *perm  = perms ? perms[p] : NULL;
7208         const PetscScalar *flip  = flips ? flips[p] : NULL;
7209         PetscCall(PetscSectionGetDof(section, point, &dof));
7210         PetscCall(updatePoint_private(section, point, dof, insert, PETSC_FALSE, perm, flip, clperm, values, off, array));
7211       }
7212       break;
7213     case INSERT_ALL_VALUES:
7214       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7215         const PetscInt     point = points[2 * p];
7216         const PetscInt    *perm  = perms ? perms[p] : NULL;
7217         const PetscScalar *flip  = flips ? flips[p] : NULL;
7218         PetscCall(PetscSectionGetDof(section, point, &dof));
7219         PetscCall(updatePoint_private(section, point, dof, insert, PETSC_TRUE, perm, flip, clperm, values, off, array));
7220       }
7221       break;
7222     case INSERT_BC_VALUES:
7223       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7224         const PetscInt     point = points[2 * p];
7225         const PetscInt    *perm  = perms ? perms[p] : NULL;
7226         const PetscScalar *flip  = flips ? flips[p] : NULL;
7227         PetscCall(PetscSectionGetDof(section, point, &dof));
7228         PetscCall(updatePointBC_private(section, point, dof, insert, perm, flip, clperm, values, off, array));
7229       }
7230       break;
7231     case ADD_VALUES:
7232       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7233         const PetscInt     point = points[2 * p];
7234         const PetscInt    *perm  = perms ? perms[p] : NULL;
7235         const PetscScalar *flip  = flips ? flips[p] : NULL;
7236         PetscCall(PetscSectionGetDof(section, point, &dof));
7237         PetscCall(updatePoint_private(section, point, dof, add, PETSC_FALSE, perm, flip, clperm, values, off, array));
7238       }
7239       break;
7240     case ADD_ALL_VALUES:
7241       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7242         const PetscInt     point = points[2 * p];
7243         const PetscInt    *perm  = perms ? perms[p] : NULL;
7244         const PetscScalar *flip  = flips ? flips[p] : NULL;
7245         PetscCall(PetscSectionGetDof(section, point, &dof));
7246         PetscCall(updatePoint_private(section, point, dof, add, PETSC_TRUE, perm, flip, clperm, values, off, array));
7247       }
7248       break;
7249     case ADD_BC_VALUES:
7250       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7251         const PetscInt     point = points[2 * p];
7252         const PetscInt    *perm  = perms ? perms[p] : NULL;
7253         const PetscScalar *flip  = flips ? flips[p] : NULL;
7254         PetscCall(PetscSectionGetDof(section, point, &dof));
7255         PetscCall(updatePointBC_private(section, point, dof, add, perm, flip, clperm, values, off, array));
7256       }
7257       break;
7258     default:
7259       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
7260     }
7261     PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
7262   }
7263   /* Cleanup points */
7264   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
7265   /* Cleanup array */
7266   PetscCall(VecRestoreArray(v, &array));
7267   PetscFunctionReturn(PETSC_SUCCESS);
7268 }
7269 
7270 /* Check whether the given point is in the label. If not, update the offset to skip this point */
7271 static inline PetscErrorCode CheckPoint_Private(DMLabel label, PetscInt labelId, PetscSection section, PetscInt point, PetscInt f, PetscInt *offset, PetscBool *contains)
7272 {
7273   PetscFunctionBegin;
7274   *contains = PETSC_TRUE;
7275   if (label) {
7276     PetscInt fdof;
7277 
7278     PetscCall(DMLabelStratumHasPoint(label, labelId, point, contains));
7279     if (!*contains) {
7280       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7281       *offset += fdof;
7282       PetscFunctionReturn(PETSC_SUCCESS);
7283     }
7284   }
7285   PetscFunctionReturn(PETSC_SUCCESS);
7286 }
7287 
7288 /* Unlike DMPlexVecSetClosure(), this uses plex-native closure permutation, not a user-specified permutation such as DMPlexSetClosurePermutationTensor(). */
7289 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)
7290 {
7291   PetscSection    clSection;
7292   IS              clPoints;
7293   PetscScalar    *array;
7294   PetscInt       *points = NULL;
7295   const PetscInt *clp;
7296   PetscInt        numFields, numPoints, p;
7297   PetscInt        offset = 0, f;
7298 
7299   PetscFunctionBeginHot;
7300   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7301   if (!section) PetscCall(DMGetLocalSection(dm, &section));
7302   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7303   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
7304   PetscCall(PetscSectionGetNumFields(section, &numFields));
7305   /* Get points */
7306   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
7307   /* Get array */
7308   PetscCall(VecGetArray(v, &array));
7309   /* Get values */
7310   for (f = 0; f < numFields; ++f) {
7311     const PetscInt    **perms = NULL;
7312     const PetscScalar **flips = NULL;
7313     PetscBool           contains;
7314 
7315     if (!fieldActive[f]) {
7316       for (p = 0; p < numPoints * 2; p += 2) {
7317         PetscInt fdof;
7318         PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
7319         offset += fdof;
7320       }
7321       continue;
7322     }
7323     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7324     switch (mode) {
7325     case INSERT_VALUES:
7326       for (p = 0; p < numPoints; p++) {
7327         const PetscInt     point = points[2 * p];
7328         const PetscInt    *perm  = perms ? perms[p] : NULL;
7329         const PetscScalar *flip  = flips ? flips[p] : NULL;
7330         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7331         if (!contains) continue;
7332         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, NULL, values, &offset, array));
7333       }
7334       break;
7335     case INSERT_ALL_VALUES:
7336       for (p = 0; p < numPoints; p++) {
7337         const PetscInt     point = points[2 * p];
7338         const PetscInt    *perm  = perms ? perms[p] : NULL;
7339         const PetscScalar *flip  = flips ? flips[p] : NULL;
7340         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7341         if (!contains) continue;
7342         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, NULL, values, &offset, array));
7343       }
7344       break;
7345     case INSERT_BC_VALUES:
7346       for (p = 0; p < numPoints; p++) {
7347         const PetscInt     point = points[2 * p];
7348         const PetscInt    *perm  = perms ? perms[p] : NULL;
7349         const PetscScalar *flip  = flips ? flips[p] : NULL;
7350         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7351         if (!contains) continue;
7352         PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, Ncc, comps, insert, NULL, values, &offset, array));
7353       }
7354       break;
7355     case ADD_VALUES:
7356       for (p = 0; p < numPoints; p++) {
7357         const PetscInt     point = points[2 * p];
7358         const PetscInt    *perm  = perms ? perms[p] : NULL;
7359         const PetscScalar *flip  = flips ? flips[p] : NULL;
7360         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7361         if (!contains) continue;
7362         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, NULL, values, &offset, array));
7363       }
7364       break;
7365     case ADD_ALL_VALUES:
7366       for (p = 0; p < numPoints; p++) {
7367         const PetscInt     point = points[2 * p];
7368         const PetscInt    *perm  = perms ? perms[p] : NULL;
7369         const PetscScalar *flip  = flips ? flips[p] : NULL;
7370         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7371         if (!contains) continue;
7372         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, NULL, values, &offset, array));
7373       }
7374       break;
7375     default:
7376       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
7377     }
7378     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7379   }
7380   /* Cleanup points */
7381   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
7382   /* Cleanup array */
7383   PetscCall(VecRestoreArray(v, &array));
7384   PetscFunctionReturn(PETSC_SUCCESS);
7385 }
7386 
7387 static PetscErrorCode DMPlexPrintMatSetValues(PetscViewer viewer, Mat A, PetscInt point, PetscInt numRIndices, const PetscInt rindices[], PetscInt numCIndices, const PetscInt cindices[], const PetscScalar values[])
7388 {
7389   PetscMPIInt rank;
7390   PetscInt    i, j;
7391 
7392   PetscFunctionBegin;
7393   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7394   PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat for point %" PetscInt_FMT "\n", rank, point));
7395   for (i = 0; i < numRIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat row indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, rindices[i]));
7396   for (i = 0; i < numCIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat col indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, cindices[i]));
7397   numCIndices = numCIndices ? numCIndices : numRIndices;
7398   if (!values) PetscFunctionReturn(PETSC_SUCCESS);
7399   for (i = 0; i < numRIndices; i++) {
7400     PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]", rank));
7401     for (j = 0; j < numCIndices; j++) {
7402 #if defined(PETSC_USE_COMPLEX)
7403       PetscCall(PetscViewerASCIIPrintf(viewer, " (%g,%g)", (double)PetscRealPart(values[i * numCIndices + j]), (double)PetscImaginaryPart(values[i * numCIndices + j])));
7404 #else
7405       PetscCall(PetscViewerASCIIPrintf(viewer, " %g", (double)values[i * numCIndices + j]));
7406 #endif
7407     }
7408     PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
7409   }
7410   PetscFunctionReturn(PETSC_SUCCESS);
7411 }
7412 
7413 /*
7414   DMPlexGetIndicesPoint_Internal - Add the indices for dofs on a point to an index array
7415 
7416   Input Parameters:
7417 + section - The section for this data layout
7418 . islocal - Is the section (and thus indices being requested) local or global?
7419 . point   - The point contributing dofs with these indices
7420 . off     - The global offset of this point
7421 . loff    - The local offset of each field
7422 . setBC   - The flag determining whether to include indices of boundary values
7423 . perm    - A permutation of the dofs on this point, or NULL
7424 - indperm - A permutation of the entire indices array, or NULL
7425 
7426   Output Parameter:
7427 . indices - Indices for dofs on this point
7428 
7429   Level: developer
7430 
7431   Note: The indices could be local or global, depending on the value of 'off'.
7432 */
7433 PetscErrorCode DMPlexGetIndicesPoint_Internal(PetscSection section, PetscBool islocal, PetscInt point, PetscInt off, PetscInt *loff, PetscBool setBC, const PetscInt perm[], const PetscInt indperm[], PetscInt indices[])
7434 {
7435   PetscInt        dof;   /* The number of unknowns on this point */
7436   PetscInt        cdof;  /* The number of constraints on this point */
7437   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
7438   PetscInt        cind = 0, k;
7439 
7440   PetscFunctionBegin;
7441   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
7442   PetscCall(PetscSectionGetDof(section, point, &dof));
7443   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
7444   if (!cdof || setBC) {
7445     for (k = 0; k < dof; ++k) {
7446       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
7447       const PetscInt ind    = indperm ? indperm[preind] : preind;
7448 
7449       indices[ind] = off + k;
7450     }
7451   } else {
7452     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
7453     for (k = 0; k < dof; ++k) {
7454       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
7455       const PetscInt ind    = indperm ? indperm[preind] : preind;
7456 
7457       if ((cind < cdof) && (k == cdofs[cind])) {
7458         /* Insert check for returning constrained indices */
7459         indices[ind] = -(off + k + 1);
7460         ++cind;
7461       } else {
7462         indices[ind] = off + k - (islocal ? 0 : cind);
7463       }
7464     }
7465   }
7466   *loff += dof;
7467   PetscFunctionReturn(PETSC_SUCCESS);
7468 }
7469 
7470 /*
7471  DMPlexGetIndicesPointFields_Internal - gets section indices for a point in its canonical ordering.
7472 
7473  Input Parameters:
7474 + section - a section (global or local)
7475 - islocal - `PETSC_TRUE` if requesting local indices (i.e., section is local); `PETSC_FALSE` for global
7476 . point - point within section
7477 . off - The offset of this point in the (local or global) indexed space - should match islocal and (usually) the section
7478 . foffs - array of length numFields containing the offset in canonical point ordering (the location in indices) of each field
7479 . setBC - identify constrained (boundary condition) points via involution.
7480 . perms - perms[f][permsoff][:] is a permutation of dofs within each field
7481 . permsoff - offset
7482 - indperm - index permutation
7483 
7484  Output Parameter:
7485 . foffs - each entry is incremented by the number of (unconstrained if setBC=FALSE) dofs in that field
7486 . indices - array to hold indices (as defined by section) of each dof associated with point
7487 
7488  Notes:
7489  If section is local and setBC=true, there is no distinction between constrained and unconstrained dofs.
7490  If section is local and setBC=false, the indices for constrained points are the involution -(i+1) of their position
7491  in the local vector.
7492 
7493  If section is global and setBC=false, the indices for constrained points are negative (and their value is not
7494  significant).  It is invalid to call with a global section and setBC=true.
7495 
7496  Developer Note:
7497  The section is only used for field layout, so islocal is technically a statement about the offset (off).  At some point
7498  in the future, global sections may have fields set, in which case we could pass the global section and obtain the
7499  offset could be obtained from the section instead of passing it explicitly as we do now.
7500 
7501  Example:
7502  Suppose a point contains one field with three components, and for which the unconstrained indices are {10, 11, 12}.
7503  When the middle component is constrained, we get the array {10, -12, 12} for (islocal=TRUE, setBC=FALSE).
7504  Note that -12 is the involution of 11, so the user can involute negative indices to recover local indices.
7505  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.
7506 
7507  Level: developer
7508 */
7509 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[])
7510 {
7511   PetscInt numFields, foff, f;
7512 
7513   PetscFunctionBegin;
7514   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
7515   PetscCall(PetscSectionGetNumFields(section, &numFields));
7516   for (f = 0, foff = 0; f < numFields; ++f) {
7517     PetscInt        fdof, cfdof;
7518     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7519     PetscInt        cind = 0, b;
7520     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7521 
7522     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7523     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7524     if (!cfdof || setBC) {
7525       for (b = 0; b < fdof; ++b) {
7526         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7527         const PetscInt ind    = indperm ? indperm[preind] : preind;
7528 
7529         indices[ind] = off + foff + b;
7530       }
7531     } else {
7532       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7533       for (b = 0; b < fdof; ++b) {
7534         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7535         const PetscInt ind    = indperm ? indperm[preind] : preind;
7536 
7537         if ((cind < cfdof) && (b == fcdofs[cind])) {
7538           indices[ind] = -(off + foff + b + 1);
7539           ++cind;
7540         } else {
7541           indices[ind] = off + foff + b - (islocal ? 0 : cind);
7542         }
7543       }
7544     }
7545     foff += (setBC || islocal ? fdof : (fdof - cfdof));
7546     foffs[f] += fdof;
7547   }
7548   PetscFunctionReturn(PETSC_SUCCESS);
7549 }
7550 
7551 /*
7552   This version believes the globalSection offsets for each field, rather than just the point offset
7553 
7554  . foffs - The offset into 'indices' for each field, since it is segregated by field
7555 
7556  Notes:
7557  The semantics of this function relate to that of setBC=FALSE in DMPlexGetIndicesPointFields_Internal.
7558  Since this function uses global indices, setBC=TRUE would be invalid, so no such argument exists.
7559 */
7560 static PetscErrorCode DMPlexGetIndicesPointFieldsSplit_Internal(PetscSection section, PetscSection globalSection, PetscInt point, PetscInt foffs[], const PetscInt ***perms, PetscInt permsoff, const PetscInt indperm[], PetscInt indices[])
7561 {
7562   PetscInt numFields, foff, f;
7563 
7564   PetscFunctionBegin;
7565   PetscCall(PetscSectionGetNumFields(section, &numFields));
7566   for (f = 0; f < numFields; ++f) {
7567     PetscInt        fdof, cfdof;
7568     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7569     PetscInt        cind = 0, b;
7570     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7571 
7572     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7573     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7574     PetscCall(PetscSectionGetFieldOffset(globalSection, point, f, &foff));
7575     if (!cfdof) {
7576       for (b = 0; b < fdof; ++b) {
7577         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7578         const PetscInt ind    = indperm ? indperm[preind] : preind;
7579 
7580         indices[ind] = foff + b;
7581       }
7582     } else {
7583       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7584       for (b = 0; b < fdof; ++b) {
7585         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7586         const PetscInt ind    = indperm ? indperm[preind] : preind;
7587 
7588         if ((cind < cfdof) && (b == fcdofs[cind])) {
7589           indices[ind] = -(foff + b + 1);
7590           ++cind;
7591         } else {
7592           indices[ind] = foff + b - cind;
7593         }
7594       }
7595     }
7596     foffs[f] += fdof;
7597   }
7598   PetscFunctionReturn(PETSC_SUCCESS);
7599 }
7600 
7601 static PetscErrorCode DMPlexAnchorsGetSubMatIndices(PetscInt nPoints, const PetscInt pnts[], PetscSection section, PetscSection cSec, PetscInt tmpIndices[], PetscInt fieldOffsets[], PetscInt indices[], const PetscInt ***perms)
7602 {
7603   PetscInt numFields, sStart, sEnd, cStart, cEnd;
7604 
7605   PetscFunctionBegin;
7606   PetscCall(PetscSectionGetNumFields(section, &numFields));
7607   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
7608   PetscCall(PetscSectionGetChart(cSec, &cStart, &cEnd));
7609   for (PetscInt p = 0; p < nPoints; p++) {
7610     PetscInt     b       = pnts[2 * p];
7611     PetscInt     bSecDof = 0, bOff;
7612     PetscInt     cSecDof = 0;
7613     PetscSection indices_section;
7614 
7615     if (b >= sStart && b < sEnd) PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7616     if (!bSecDof) continue;
7617     if (b >= cStart && b < cEnd) PetscCall(PetscSectionGetDof(cSec, b, &cSecDof));
7618     indices_section = cSecDof > 0 ? cSec : section;
7619     if (numFields) {
7620       PetscInt fStart[32], fEnd[32];
7621 
7622       fStart[0] = 0;
7623       fEnd[0]   = 0;
7624       for (PetscInt f = 0; f < numFields; f++) {
7625         PetscInt fDof = 0;
7626 
7627         PetscCall(PetscSectionGetFieldDof(indices_section, b, f, &fDof));
7628         fStart[f + 1] = fStart[f] + fDof;
7629         fEnd[f + 1]   = fStart[f + 1];
7630       }
7631       PetscCall(PetscSectionGetOffset(indices_section, b, &bOff));
7632       // only apply permutations on one side
7633       PetscCall(DMPlexGetIndicesPointFields_Internal(indices_section, PETSC_TRUE, b, bOff, fEnd, PETSC_TRUE, perms, perms ? p : -1, NULL, tmpIndices));
7634       for (PetscInt f = 0; f < numFields; f++) {
7635         for (PetscInt i = fStart[f]; i < fEnd[f]; i++) { indices[fieldOffsets[f]++] = (cSecDof > 0) ? tmpIndices[i] : -(tmpIndices[i] + 1); }
7636       }
7637     } else {
7638       PetscInt bEnd = 0;
7639 
7640       PetscCall(PetscSectionGetOffset(indices_section, b, &bOff));
7641       PetscCall(DMPlexGetIndicesPoint_Internal(indices_section, PETSC_TRUE, b, bOff, &bEnd, PETSC_TRUE, (perms && perms[0]) ? perms[0][p] : NULL, NULL, tmpIndices));
7642 
7643       for (PetscInt i = 0; i < bEnd; i++) indices[fieldOffsets[0]++] = (cSecDof > 0) ? tmpIndices[i] : -(tmpIndices[i] + 1);
7644     }
7645   }
7646   PetscFunctionReturn(PETSC_SUCCESS);
7647 }
7648 
7649 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[])
7650 {
7651   Mat             cMat;
7652   PetscSection    aSec, cSec;
7653   IS              aIS;
7654   PetscInt        aStart = -1, aEnd = -1;
7655   PetscInt        sStart = -1, sEnd = -1;
7656   PetscInt        cStart = -1, cEnd = -1;
7657   const PetscInt *anchors;
7658   PetscInt        numFields, p;
7659   PetscInt        newNumPoints = 0, newNumIndices = 0;
7660   PetscInt       *newPoints, *indices, *newIndices, *tmpIndices, *tmpNewIndices;
7661   PetscInt        oldOffsets[32];
7662   PetscInt        newOffsets[32];
7663   PetscInt        oldOffsetsCopy[32];
7664   PetscInt        newOffsetsCopy[32];
7665   PetscScalar    *modMat         = NULL;
7666   PetscBool       anyConstrained = PETSC_FALSE;
7667 
7668   PetscFunctionBegin;
7669   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7670   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7671   PetscCall(PetscSectionGetNumFields(section, &numFields));
7672 
7673   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
7674   /* if there are point-to-point constraints */
7675   if (aSec) {
7676     PetscCall(PetscArrayzero(newOffsets, 32));
7677     PetscCall(PetscArrayzero(oldOffsets, 32));
7678     PetscCall(ISGetIndices(aIS, &anchors));
7679     PetscCall(PetscSectionGetChart(aSec, &aStart, &aEnd));
7680     PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
7681     /* figure out how many points are going to be in the new element matrix
7682      * (we allow double counting, because it's all just going to be summed
7683      * into the global matrix anyway) */
7684     for (p = 0; p < 2 * numPoints; p += 2) {
7685       PetscInt b    = points[p];
7686       PetscInt bDof = 0, bSecDof = 0;
7687 
7688       if (b >= sStart && b < sEnd) PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7689       if (!bSecDof) continue;
7690 
7691       for (PetscInt f = 0; f < numFields; f++) {
7692         PetscInt fDof = 0;
7693 
7694         PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7695         oldOffsets[f + 1] += fDof;
7696       }
7697       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7698       if (bDof) {
7699         /* this point is constrained */
7700         /* it is going to be replaced by its anchors */
7701         PetscInt bOff, q;
7702 
7703         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7704         for (q = 0; q < bDof; q++) {
7705           PetscInt a    = anchors[bOff + q];
7706           PetscInt aDof = 0;
7707 
7708           if (a >= sStart && a < sEnd) PetscCall(PetscSectionGetDof(section, a, &aDof));
7709           if (aDof) {
7710             anyConstrained = PETSC_TRUE;
7711             newNumPoints += 1;
7712           }
7713           newNumIndices += aDof;
7714           for (PetscInt f = 0; f < numFields; ++f) {
7715             PetscInt fDof = 0;
7716 
7717             if (a >= sStart && a < sEnd) PetscCall(PetscSectionGetFieldDof(section, a, f, &fDof));
7718             newOffsets[f + 1] += fDof;
7719           }
7720         }
7721       } else {
7722         /* this point is not constrained */
7723         newNumPoints++;
7724         newNumIndices += bSecDof;
7725         for (PetscInt f = 0; f < numFields; ++f) {
7726           PetscInt fDof;
7727 
7728           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7729           newOffsets[f + 1] += fDof;
7730         }
7731       }
7732     }
7733   }
7734   if (!anyConstrained) {
7735     if (outNumPoints) *outNumPoints = 0;
7736     if (outNumIndices) *outNumIndices = 0;
7737     if (outPoints) *outPoints = NULL;
7738     if (outMat) *outMat = NULL;
7739     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7740     PetscFunctionReturn(PETSC_SUCCESS);
7741   }
7742 
7743   if (outNumPoints) *outNumPoints = newNumPoints;
7744   if (outNumIndices) *outNumIndices = newNumIndices;
7745 
7746   for (PetscInt f = 0; f < numFields; ++f) newOffsets[f + 1] += newOffsets[f];
7747   for (PetscInt f = 0; f < numFields; ++f) oldOffsets[f + 1] += oldOffsets[f];
7748 
7749   if (!outPoints && !outMat) {
7750     if (offsets) {
7751       for (PetscInt f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7752     }
7753     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7754     PetscFunctionReturn(PETSC_SUCCESS);
7755   }
7756 
7757   PetscCheck(!numFields || newOffsets[numFields] == newNumIndices, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, newOffsets[numFields], newNumIndices);
7758   PetscCheck(!numFields || oldOffsets[numFields] == numIndices, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, oldOffsets[numFields], numIndices);
7759 
7760   PetscCall(DMGetDefaultConstraints(dm, &cSec, &cMat, NULL));
7761   PetscCall(PetscSectionGetChart(cSec, &cStart, &cEnd));
7762 
7763   /* output arrays */
7764   PetscCall(DMGetWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7765   PetscCall(PetscArrayzero(newPoints, 2 * newNumPoints));
7766 
7767   // get the new Points
7768   for (PetscInt p = 0, newP = 0; p < numPoints; p++) {
7769     PetscInt b    = points[2 * p];
7770     PetscInt bDof = 0, bSecDof = 0, bOff;
7771 
7772     if (b >= sStart && b < sEnd) PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7773     if (!bSecDof) continue;
7774     if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7775     if (bDof) {
7776       PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7777       for (PetscInt q = 0; q < bDof; q++) {
7778         PetscInt a = anchors[bOff + q], aDof = 0;
7779 
7780         if (a >= sStart && a < sEnd) PetscCall(PetscSectionGetDof(section, a, &aDof));
7781         if (aDof) {
7782           newPoints[2 * newP]     = a;
7783           newPoints[2 * newP + 1] = 0; // orientations are accounted for in constructing the matrix, newly added points are in default orientation
7784           newP++;
7785         }
7786       }
7787     } else {
7788       newPoints[2 * newP]     = b;
7789       newPoints[2 * newP + 1] = points[2 * p + 1];
7790       newP++;
7791     }
7792   }
7793 
7794   if (outMat) {
7795     PetscScalar *tmpMat;
7796     PetscCall(PetscArraycpy(oldOffsetsCopy, oldOffsets, 32));
7797     PetscCall(PetscArraycpy(newOffsetsCopy, newOffsets, 32));
7798 
7799     PetscCall(DMGetWorkArray(dm, numIndices, MPIU_INT, &indices));
7800     PetscCall(DMGetWorkArray(dm, numIndices, MPIU_INT, &tmpIndices));
7801     PetscCall(DMGetWorkArray(dm, newNumIndices, MPIU_INT, &newIndices));
7802     PetscCall(DMGetWorkArray(dm, newNumIndices, MPIU_INT, &tmpNewIndices));
7803 
7804     for (PetscInt i = 0; i < numIndices; i++) indices[i] = -1;
7805     for (PetscInt i = 0; i < newNumIndices; i++) newIndices[i] = -1;
7806 
7807     PetscCall(DMPlexAnchorsGetSubMatIndices(numPoints, points, section, cSec, tmpIndices, oldOffsetsCopy, indices, perms));
7808     PetscCall(DMPlexAnchorsGetSubMatIndices(newNumPoints, newPoints, section, section, tmpNewIndices, newOffsetsCopy, newIndices, NULL));
7809 
7810     PetscCall(DMGetWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &modMat));
7811     PetscCall(DMGetWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &tmpMat));
7812     PetscCall(PetscArrayzero(modMat, newNumIndices * numIndices));
7813     // for each field, insert the anchor modification into modMat
7814     for (PetscInt f = 0; f < PetscMax(1, numFields); f++) {
7815       PetscInt fStart    = oldOffsets[f];
7816       PetscInt fNewStart = newOffsets[f];
7817       for (PetscInt p = 0, newP = 0, o = fStart, oNew = fNewStart; p < numPoints; p++) {
7818         PetscInt b    = points[2 * p];
7819         PetscInt bDof = 0, bSecDof = 0, bOff;
7820 
7821         if (b >= sStart && b < sEnd) {
7822           if (numFields) {
7823             PetscCall(PetscSectionGetFieldDof(section, b, f, &bSecDof));
7824           } else {
7825             PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7826           }
7827         }
7828         if (!bSecDof) continue;
7829         if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7830         if (bDof) {
7831           PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7832           for (PetscInt q = 0; q < bDof; q++, newP++) {
7833             PetscInt a = anchors[bOff + q], aDof = 0;
7834 
7835             if (a >= sStart && a < sEnd) {
7836               if (numFields) {
7837                 PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
7838               } else {
7839                 PetscCall(PetscSectionGetDof(section, a, &aDof));
7840               }
7841             }
7842             if (aDof) {
7843               PetscCall(MatGetValues(cMat, bSecDof, &indices[o], aDof, &newIndices[oNew], tmpMat));
7844               for (PetscInt d = 0; d < bSecDof; d++) {
7845                 for (PetscInt e = 0; e < aDof; e++) modMat[(o + d) * newNumIndices + oNew + e] = tmpMat[d * aDof + e];
7846               }
7847             }
7848             oNew += aDof;
7849           }
7850         } else {
7851           // Insert the identity matrix in this block
7852           for (PetscInt d = 0; d < bSecDof; d++) modMat[(o + d) * newNumIndices + oNew + d] = 1;
7853           oNew += bSecDof;
7854           newP++;
7855         }
7856         o += bSecDof;
7857       }
7858     }
7859 
7860     *outMat = modMat;
7861 
7862     PetscCall(DMRestoreWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &tmpMat));
7863     PetscCall(DMRestoreWorkArray(dm, newNumIndices, MPIU_INT, &tmpNewIndices));
7864     PetscCall(DMRestoreWorkArray(dm, newNumIndices, MPIU_INT, &newIndices));
7865     PetscCall(DMRestoreWorkArray(dm, numIndices, MPIU_INT, &tmpIndices));
7866     PetscCall(DMRestoreWorkArray(dm, numIndices, MPIU_INT, &indices));
7867   }
7868   PetscCall(ISRestoreIndices(aIS, &anchors));
7869 
7870   /* output */
7871   if (outPoints) {
7872     *outPoints = newPoints;
7873   } else {
7874     PetscCall(DMRestoreWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7875   }
7876   for (PetscInt f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7877   PetscFunctionReturn(PETSC_SUCCESS);
7878 }
7879 
7880 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)
7881 {
7882   PetscScalar *modMat        = NULL;
7883   PetscInt     newNumIndices = -1;
7884 
7885   PetscFunctionBegin;
7886   /* 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.
7887      modMat is that matrix C */
7888   PetscCall(DMPlexAnchorsGetSubMatModification(dm, section, numPoints, numIndices, points, perms, outNumPoints, &newNumIndices, outPoints, offsets, outValues ? &modMat : NULL));
7889   if (outNumIndices) *outNumIndices = newNumIndices;
7890   if (modMat) {
7891     const PetscScalar *newValues = values;
7892 
7893     if (multiplyRight) {
7894       PetscScalar *newNewValues = NULL;
7895       PetscBLASInt M, N, K;
7896       PetscScalar  a = 1.0, b = 0.0;
7897 
7898       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);
7899 
7900       PetscCall(PetscBLASIntCast(newNumIndices, &M));
7901       PetscCall(PetscBLASIntCast(numRows, &N));
7902       PetscCall(PetscBLASIntCast(numIndices, &K));
7903       PetscCall(DMGetWorkArray(dm, numRows * newNumIndices, MPIU_SCALAR, &newNewValues));
7904       // row-major to column-major conversion, right multiplication becomes left multiplication
7905       PetscCallBLAS("BLASgemm", BLASgemm_("N", "N", &M, &N, &K, &a, modMat, &M, newValues, &K, &b, newNewValues, &M));
7906       numCols   = newNumIndices;
7907       newValues = newNewValues;
7908     }
7909 
7910     if (multiplyLeft) {
7911       PetscScalar *newNewValues = NULL;
7912       PetscBLASInt M, N, K;
7913       PetscScalar  a = 1.0, b = 0.0;
7914 
7915       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);
7916 
7917       PetscCall(PetscBLASIntCast(numCols, &M));
7918       PetscCall(PetscBLASIntCast(newNumIndices, &N));
7919       PetscCall(PetscBLASIntCast(numIndices, &K));
7920       PetscCall(DMGetWorkArray(dm, newNumIndices * numCols, MPIU_SCALAR, &newNewValues));
7921       // row-major to column-major conversion, left multiplication becomes right multiplication
7922       PetscCallBLAS("BLASgemm", BLASgemm_("N", "T", &M, &N, &K, &a, newValues, &M, modMat, &N, &b, newNewValues, &M));
7923       if (newValues != values) PetscCall(DMRestoreWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &newValues));
7924       newValues = newNewValues;
7925     }
7926     *outValues = (PetscScalar *)newValues;
7927     PetscCall(DMRestoreWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &modMat));
7928   }
7929   PetscFunctionReturn(PETSC_SUCCESS);
7930 }
7931 
7932 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)
7933 {
7934   PetscFunctionBegin;
7935   PetscCall(DMPlexAnchorsModifyMat_Internal(dm, section, numPoints, numIndices, points, perms, numIndices, numIndices, values, outNumPoints, outNumIndices, outPoints, outValues, offsets, PETSC_TRUE, multiplyLeft));
7936   PetscFunctionReturn(PETSC_SUCCESS);
7937 }
7938 
7939 static PetscErrorCode DMPlexGetClosureIndicesSize_Internal(DM dm, PetscSection section, PetscInt point, PetscInt *closureSize)
7940 {
7941   /* Closure ordering */
7942   PetscSection    clSection;
7943   IS              clPoints;
7944   const PetscInt *clp;
7945   PetscInt       *points;
7946   PetscInt        Ncl, Ni = 0;
7947 
7948   PetscFunctionBeginHot;
7949   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &Ncl, &points, &clSection, &clPoints, &clp));
7950   for (PetscInt p = 0; p < Ncl * 2; p += 2) {
7951     PetscInt dof;
7952 
7953     PetscCall(PetscSectionGetDof(section, points[p], &dof));
7954     Ni += dof;
7955   }
7956   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7957   *closureSize = Ni;
7958   PetscFunctionReturn(PETSC_SUCCESS);
7959 }
7960 
7961 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)
7962 {
7963   /* Closure ordering */
7964   PetscSection    clSection;
7965   IS              clPoints;
7966   const PetscInt *clp;
7967   PetscInt       *points;
7968   const PetscInt *clperm = NULL;
7969   /* Dof permutation and sign flips */
7970   const PetscInt    **perms[32] = {NULL};
7971   const PetscScalar **flips[32] = {NULL};
7972   PetscScalar        *valCopy   = NULL;
7973   /* Hanging node constraints */
7974   PetscInt    *pointsC = NULL;
7975   PetscScalar *valuesC = NULL;
7976   PetscInt     NclC, NiC;
7977 
7978   PetscInt *idx;
7979   PetscInt  Nf, Ncl, Ni = 0, offsets[32], p, f;
7980   PetscBool isLocal = (section == idxSection) ? PETSC_TRUE : PETSC_FALSE;
7981   PetscInt  idxStart, idxEnd;
7982   PetscInt  nRows, nCols;
7983 
7984   PetscFunctionBeginHot;
7985   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7986   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7987   PetscValidHeaderSpecific(idxSection, PETSC_SECTION_CLASSID, 3);
7988   PetscAssertPointer(numRows, 6);
7989   PetscAssertPointer(numCols, 7);
7990   if (indices) PetscAssertPointer(indices, 8);
7991   if (outOffsets) PetscAssertPointer(outOffsets, 9);
7992   if (values) PetscAssertPointer(values, 10);
7993   PetscCall(PetscSectionGetNumFields(section, &Nf));
7994   PetscCheck(Nf <= 31, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", Nf);
7995   PetscCall(PetscArrayzero(offsets, 32));
7996   /* 1) Get points in closure */
7997   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &Ncl, &points, &clSection, &clPoints, &clp));
7998   if (useClPerm) {
7999     PetscInt depth, clsize;
8000     PetscCall(DMPlexGetPointDepth(dm, point, &depth));
8001     for (clsize = 0, p = 0; p < Ncl; p++) {
8002       PetscInt dof;
8003       PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
8004       clsize += dof;
8005     }
8006     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
8007   }
8008   /* 2) Get number of indices on these points and field offsets from section */
8009   for (p = 0; p < Ncl * 2; p += 2) {
8010     PetscInt dof, fdof;
8011 
8012     PetscCall(PetscSectionGetDof(section, points[p], &dof));
8013     for (f = 0; f < Nf; ++f) {
8014       PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
8015       offsets[f + 1] += fdof;
8016     }
8017     Ni += dof;
8018   }
8019   if (*numRows == -1) *numRows = Ni;
8020   if (*numCols == -1) *numCols = Ni;
8021   nRows = *numRows;
8022   nCols = *numCols;
8023   for (f = 1; f < Nf; ++f) offsets[f + 1] += offsets[f];
8024   PetscCheck(!Nf || offsets[Nf] == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, offsets[Nf], Ni);
8025   /* 3) Get symmetries and sign flips. Apply sign flips to values if passed in (only works for square values matrix) */
8026   if (multiplyRight) PetscCheck(nCols == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Expected %" PetscInt_FMT " columns, got %" PetscInt_FMT, Ni, nCols);
8027   if (multiplyLeft) PetscCheck(nRows == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Expected %" PetscInt_FMT " rows, got %" PetscInt_FMT, Ni, nRows);
8028   for (f = 0; f < PetscMax(1, Nf); ++f) {
8029     if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
8030     else PetscCall(PetscSectionGetPointSyms(section, Ncl, points, &perms[f], &flips[f]));
8031     /* may need to apply sign changes to the element matrix */
8032     if (values && flips[f]) {
8033       PetscInt foffset = offsets[f];
8034 
8035       for (p = 0; p < Ncl; ++p) {
8036         PetscInt           pnt  = points[2 * p], fdof;
8037         const PetscScalar *flip = flips[f] ? flips[f][p] : NULL;
8038 
8039         if (!Nf) PetscCall(PetscSectionGetDof(section, pnt, &fdof));
8040         else PetscCall(PetscSectionGetFieldDof(section, pnt, f, &fdof));
8041         if (flip) {
8042           PetscInt i, j, k;
8043 
8044           if (!valCopy) {
8045             PetscCall(DMGetWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
8046             for (j = 0; j < Ni * Ni; ++j) valCopy[j] = (*values)[j];
8047             *values = valCopy;
8048           }
8049           for (i = 0; i < fdof; ++i) {
8050             PetscScalar fval = flip[i];
8051 
8052             if (multiplyRight) {
8053               for (k = 0; k < nRows; ++k) { valCopy[Ni * k + (foffset + i)] *= fval; }
8054             }
8055             if (multiplyLeft) {
8056               for (k = 0; k < nCols; ++k) { valCopy[nCols * (foffset + i) + k] *= fval; }
8057             }
8058           }
8059         }
8060         foffset += fdof;
8061       }
8062     }
8063   }
8064   /* 4) Apply hanging node constraints. Get new symmetries and replace all storage with constrained storage */
8065   PetscCall(DMPlexAnchorsModifyMat_Internal(dm, section, Ncl, Ni, points, perms, nRows, nCols, values ? *values : NULL, &NclC, &NiC, &pointsC, values ? &valuesC : NULL, offsets, multiplyRight, multiplyLeft));
8066   if (NclC) {
8067     if (multiplyRight) *numCols = NiC;
8068     if (multiplyLeft) *numRows = NiC;
8069     if (valCopy) PetscCall(DMRestoreWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
8070     for (f = 0; f < PetscMax(1, Nf); ++f) {
8071       if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
8072       else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
8073     }
8074     for (f = 0; f < PetscMax(1, Nf); ++f) {
8075       if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, NclC, pointsC, &perms[f], &flips[f]));
8076       else PetscCall(PetscSectionGetPointSyms(section, NclC, pointsC, &perms[f], &flips[f]));
8077     }
8078     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
8079     Ncl    = NclC;
8080     Ni     = NiC;
8081     points = pointsC;
8082     if (values) *values = valuesC;
8083   }
8084   /* 5) Calculate indices */
8085   PetscCall(DMGetWorkArray(dm, Ni, MPIU_INT, &idx));
8086   PetscCall(PetscSectionGetChart(idxSection, &idxStart, &idxEnd));
8087   if (Nf) {
8088     PetscInt  idxOff;
8089     PetscBool useFieldOffsets;
8090 
8091     if (outOffsets) {
8092       for (f = 0; f <= Nf; f++) outOffsets[f] = offsets[f];
8093     }
8094     PetscCall(PetscSectionGetUseFieldOffsets(idxSection, &useFieldOffsets));
8095     if (useFieldOffsets) {
8096       for (p = 0; p < Ncl; ++p) {
8097         const PetscInt pnt = points[p * 2];
8098 
8099         PetscCall(DMPlexGetIndicesPointFieldsSplit_Internal(section, idxSection, pnt, offsets, perms, p, clperm, idx));
8100       }
8101     } else {
8102       for (p = 0; p < Ncl; ++p) {
8103         const PetscInt pnt = points[p * 2];
8104 
8105         if (pnt < idxStart || pnt >= idxEnd) continue;
8106         PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
8107         /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
8108          * not (at the time of this writing) have fields set. They probably should, in which case we would pass the
8109          * global section. */
8110         PetscCall(DMPlexGetIndicesPointFields_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, offsets, PETSC_FALSE, perms, p, clperm, idx));
8111       }
8112     }
8113   } else {
8114     PetscInt off = 0, idxOff;
8115 
8116     for (p = 0; p < Ncl; ++p) {
8117       const PetscInt  pnt  = points[p * 2];
8118       const PetscInt *perm = perms[0] ? perms[0][p] : NULL;
8119 
8120       if (pnt < idxStart || pnt >= idxEnd) continue;
8121       PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
8122       /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
8123        * not (at the time of this writing) have fields set. They probably should, in which case we would pass the global section. */
8124       PetscCall(DMPlexGetIndicesPoint_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, &off, PETSC_FALSE, perm, clperm, idx));
8125     }
8126   }
8127   /* 6) Cleanup */
8128   for (f = 0; f < PetscMax(1, Nf); ++f) {
8129     if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
8130     else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
8131   }
8132   if (NclC) {
8133     PetscCall(DMRestoreWorkArray(dm, NclC * 2, MPIU_INT, &pointsC));
8134   } else {
8135     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
8136   }
8137 
8138   if (indices) *indices = idx;
8139   PetscFunctionReturn(PETSC_SUCCESS);
8140 }
8141 
8142 /*@C
8143   DMPlexGetClosureIndices - Gets the global dof indices associated with the closure of the given point within the provided sections.
8144 
8145   Not collective
8146 
8147   Input Parameters:
8148 + dm         - The `DM`
8149 . section    - The `PetscSection` describing the points (a local section)
8150 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
8151 . point      - The point defining the closure
8152 - useClPerm  - Use the closure point permutation if available
8153 
8154   Output Parameters:
8155 + numIndices - The number of dof indices in the closure of point with the input sections
8156 . indices    - The dof indices
8157 . outOffsets - Array to write the field offsets into, or `NULL`
8158 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or `NULL`
8159 
8160   Level: advanced
8161 
8162   Notes:
8163   Call `DMPlexRestoreClosureIndices()` to free allocated memory
8164 
8165   If `idxSection` is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
8166   of those indices is not significant.  If `idxSection` is local, the constrained dofs will yield the involution -(idx+1)
8167   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
8168   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when `idxSection` == section, otherwise global
8169   indices (with the above semantics) are implied.
8170 
8171 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`,
8172           `PetscSection`, `DMGetGlobalSection()`
8173 @*/
8174 PetscErrorCode DMPlexGetClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
8175 {
8176   PetscInt numRows = -1, numCols = -1;
8177 
8178   PetscFunctionBeginHot;
8179   PetscCall(DMPlexGetClosureIndices_Internal(dm, section, idxSection, point, useClPerm, &numRows, &numCols, indices, outOffsets, values, PETSC_TRUE, PETSC_TRUE));
8180   PetscCheck(numRows == numCols, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Symmetric matrix transformation produces rectangular dimensions (%" PetscInt_FMT ", %" PetscInt_FMT ")", numRows, numCols);
8181   *numIndices = numRows;
8182   PetscFunctionReturn(PETSC_SUCCESS);
8183 }
8184 
8185 /*@C
8186   DMPlexRestoreClosureIndices - Restores the global dof indices associated with the closure of the given point within the provided sections.
8187 
8188   Not collective
8189 
8190   Input Parameters:
8191 + dm         - The `DM`
8192 . section    - The `PetscSection` describing the points (a local section)
8193 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
8194 . point      - The point defining the closure
8195 - useClPerm  - Use the closure point permutation if available
8196 
8197   Output Parameters:
8198 + numIndices - The number of dof indices in the closure of point with the input sections
8199 . indices    - The dof indices
8200 . outOffsets - Array to write the field offsets into, or `NULL`
8201 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or `NULL`
8202 
8203   Level: advanced
8204 
8205   Notes:
8206   If values were modified, the user is responsible for calling `DMRestoreWorkArray`(dm, 0, `MPIU_SCALAR`, &values).
8207 
8208   If idxSection is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
8209   of those indices is not significant.  If idxSection is local, the constrained dofs will yield the involution -(idx+1)
8210   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
8211   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when idxSection == section, otherwise global
8212   indices (with the above semantics) are implied.
8213 
8214 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`, `DMGetGlobalSection()`
8215 @*/
8216 PetscErrorCode DMPlexRestoreClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
8217 {
8218   PetscFunctionBegin;
8219   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8220   PetscAssertPointer(indices, 7);
8221   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, indices));
8222   PetscFunctionReturn(PETSC_SUCCESS);
8223 }
8224 
8225 PetscErrorCode DMPlexMatSetClosure_Internal(DM dm, PetscSection section, PetscSection globalSection, PetscBool useClPerm, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8226 {
8227   DM_Plex           *mesh = (DM_Plex *)dm->data;
8228   PetscInt          *indices;
8229   PetscInt           numIndices;
8230   const PetscScalar *valuesOrig = values;
8231   PetscErrorCode     ierr;
8232 
8233   PetscFunctionBegin;
8234   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8235   if (!section) PetscCall(DMGetLocalSection(dm, &section));
8236   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
8237   if (!globalSection) PetscCall(DMGetGlobalSection(dm, &globalSection));
8238   PetscValidHeaderSpecific(globalSection, PETSC_SECTION_CLASSID, 3);
8239   PetscValidHeaderSpecific(A, MAT_CLASSID, 5);
8240 
8241   PetscCall(DMPlexGetClosureIndices(dm, section, globalSection, point, useClPerm, &numIndices, &indices, NULL, (PetscScalar **)&values));
8242 
8243   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndices, indices, 0, NULL, values));
8244   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8245   ierr = MatSetValues(A, numIndices, indices, numIndices, indices, values, mode);
8246   if (ierr) {
8247     PetscMPIInt rank;
8248 
8249     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8250     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8251     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndices, indices, 0, NULL, values));
8252     PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
8253     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
8254     SETERRQ(PetscObjectComm((PetscObject)dm), ierr, "Not possible to set matrix values");
8255   }
8256   if (mesh->printFEM > 1) {
8257     PetscInt i;
8258     PetscCall(PetscPrintf(PETSC_COMM_SELF, "  Indices:"));
8259     for (i = 0; i < numIndices; ++i) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, indices[i]));
8260     PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8261   }
8262 
8263   PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
8264   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
8265   PetscFunctionReturn(PETSC_SUCCESS);
8266 }
8267 
8268 /*@C
8269   DMPlexMatSetClosure - Set an array of the values on the closure of 'point'
8270 
8271   Not collective
8272 
8273   Input Parameters:
8274 + dm            - The `DM`
8275 . section       - The section describing the layout in `v`, or `NULL` to use the default section
8276 . globalSection - The section describing the layout in `v`, or `NULL` to use the default global section
8277 . A             - The matrix
8278 . point         - The point in the `DM`
8279 . values        - The array of values
8280 - mode          - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
8281 
8282   Level: intermediate
8283 
8284 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosureGeneral()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
8285 @*/
8286 PetscErrorCode DMPlexMatSetClosure(DM dm, PetscSection section, PetscSection globalSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8287 {
8288   PetscFunctionBegin;
8289   PetscCall(DMPlexMatSetClosure_Internal(dm, section, globalSection, PETSC_TRUE, A, point, values, mode));
8290   PetscFunctionReturn(PETSC_SUCCESS);
8291 }
8292 
8293 /*@C
8294   DMPlexMatSetClosureGeneral - Set an array of the values on the closure of 'point' using a different row and column section
8295 
8296   Not collective
8297 
8298   Input Parameters:
8299 + dmRow            - The `DM` for the row fields
8300 . sectionRow       - The section describing the layout, or `NULL` to use the default section in `dmRow`
8301 . useRowPerm       - The flag to use the closure permutation of the `dmRow` if available
8302 . globalSectionRow - The section describing the layout, or `NULL` to use the default global section in `dmRow`
8303 . dmCol            - The `DM` for the column fields
8304 . sectionCol       - The section describing the layout, or `NULL` to use the default section in `dmCol`
8305 . useColPerm       - The flag to use the closure permutation of the `dmCol` if available
8306 . globalSectionCol - The section describing the layout, or `NULL` to use the default global section in `dmCol`
8307 . A                - The matrix
8308 . point            - The point in the `DM`
8309 . values           - The array of values
8310 - mode             - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
8311 
8312   Level: intermediate
8313 
8314 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosure()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
8315 @*/
8316 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)
8317 {
8318   DM_Plex           *mesh = (DM_Plex *)dmRow->data;
8319   PetscInt          *indicesRow, *indicesCol;
8320   PetscInt           numIndicesRow = -1, numIndicesCol = -1;
8321   const PetscScalar *valuesV0 = values, *valuesV1, *valuesV2;
8322 
8323   PetscErrorCode ierr;
8324 
8325   PetscFunctionBegin;
8326   PetscValidHeaderSpecific(dmRow, DM_CLASSID, 1);
8327   if (!sectionRow) PetscCall(DMGetLocalSection(dmRow, &sectionRow));
8328   PetscValidHeaderSpecific(sectionRow, PETSC_SECTION_CLASSID, 2);
8329   if (!globalSectionRow) PetscCall(DMGetGlobalSection(dmRow, &globalSectionRow));
8330   PetscValidHeaderSpecific(globalSectionRow, PETSC_SECTION_CLASSID, 3);
8331   PetscValidHeaderSpecific(dmCol, DM_CLASSID, 5);
8332   if (!sectionCol) PetscCall(DMGetLocalSection(dmCol, &sectionCol));
8333   PetscValidHeaderSpecific(sectionCol, PETSC_SECTION_CLASSID, 6);
8334   if (!globalSectionCol) PetscCall(DMGetGlobalSection(dmCol, &globalSectionCol));
8335   PetscValidHeaderSpecific(globalSectionCol, PETSC_SECTION_CLASSID, 7);
8336   PetscValidHeaderSpecific(A, MAT_CLASSID, 9);
8337 
8338   PetscCall(DMPlexGetClosureIndicesSize_Internal(dmRow, sectionRow, point, &numIndicesRow));
8339   PetscCall(DMPlexGetClosureIndicesSize_Internal(dmCol, sectionCol, point, &numIndicesCol));
8340   valuesV1 = valuesV0;
8341   PetscCall(DMPlexGetClosureIndices_Internal(dmRow, sectionRow, globalSectionRow, point, useRowPerm, &numIndicesRow, &numIndicesCol, &indicesRow, NULL, (PetscScalar **)&valuesV1, PETSC_FALSE, PETSC_TRUE));
8342   valuesV2 = valuesV1;
8343   PetscCall(DMPlexGetClosureIndices_Internal(dmCol, sectionCol, globalSectionCol, point, useColPerm, &numIndicesRow, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&valuesV2, PETSC_TRUE, PETSC_FALSE));
8344 
8345   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, valuesV2));
8346   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8347   ierr = MatSetValues(A, numIndicesRow, indicesRow, numIndicesCol, indicesCol, valuesV2, mode);
8348   if (ierr) {
8349     PetscMPIInt rank;
8350 
8351     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8352     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8353     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
8354     PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesRow, NULL, (PetscScalar **)&valuesV2));
8355     PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&valuesV1));
8356     if (valuesV2 != valuesV1) PetscCall(DMRestoreWorkArray(dmCol, 0, MPIU_SCALAR, &valuesV2));
8357     if (valuesV1 != valuesV0) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &valuesV1));
8358   }
8359 
8360   PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, useColPerm, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&valuesV2));
8361   PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, useRowPerm, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&valuesV1));
8362   if (valuesV2 != valuesV1) PetscCall(DMRestoreWorkArray(dmCol, 0, MPIU_SCALAR, &valuesV2));
8363   if (valuesV1 != valuesV0) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &valuesV1));
8364   PetscFunctionReturn(PETSC_SUCCESS);
8365 }
8366 
8367 PetscErrorCode DMPlexMatSetClosureRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8368 {
8369   DM_Plex        *mesh    = (DM_Plex *)dmf->data;
8370   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
8371   PetscInt       *cpoints = NULL;
8372   PetscInt       *findices, *cindices;
8373   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
8374   PetscInt        foffsets[32], coffsets[32];
8375   DMPolytopeType  ct;
8376   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
8377   PetscErrorCode  ierr;
8378 
8379   PetscFunctionBegin;
8380   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
8381   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
8382   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
8383   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
8384   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
8385   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
8386   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
8387   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
8388   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
8389   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
8390   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
8391   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
8392   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
8393   PetscCall(PetscArrayzero(foffsets, 32));
8394   PetscCall(PetscArrayzero(coffsets, 32));
8395   /* Column indices */
8396   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8397   maxFPoints = numCPoints;
8398   /* Compress out points not in the section */
8399   /*   TODO: Squeeze out points with 0 dof as well */
8400   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
8401   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
8402     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
8403       cpoints[q * 2]     = cpoints[p];
8404       cpoints[q * 2 + 1] = cpoints[p + 1];
8405       ++q;
8406     }
8407   }
8408   numCPoints = q;
8409   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
8410     PetscInt fdof;
8411 
8412     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
8413     if (!dof) continue;
8414     for (f = 0; f < numFields; ++f) {
8415       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
8416       coffsets[f + 1] += fdof;
8417     }
8418     numCIndices += dof;
8419   }
8420   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
8421   /* Row indices */
8422   PetscCall(DMPlexGetCellType(dmc, point, &ct));
8423   {
8424     DMPlexTransform tr;
8425     DMPolytopeType *rct;
8426     PetscInt       *rsize, *rcone, *rornt, Nt;
8427 
8428     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8429     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8430     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8431     numSubcells = rsize[Nt - 1];
8432     PetscCall(DMPlexTransformDestroy(&tr));
8433   }
8434   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8435   for (r = 0, q = 0; r < numSubcells; ++r) {
8436     /* TODO Map from coarse to fine cells */
8437     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8438     /* Compress out points not in the section */
8439     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8440     for (p = 0; p < numFPoints * 2; p += 2) {
8441       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8442         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8443         if (!dof) continue;
8444         for (s = 0; s < q; ++s)
8445           if (fpoints[p] == ftotpoints[s * 2]) break;
8446         if (s < q) continue;
8447         ftotpoints[q * 2]     = fpoints[p];
8448         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8449         ++q;
8450       }
8451     }
8452     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8453   }
8454   numFPoints = q;
8455   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8456     PetscInt fdof;
8457 
8458     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8459     if (!dof) continue;
8460     for (f = 0; f < numFields; ++f) {
8461       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8462       foffsets[f + 1] += fdof;
8463     }
8464     numFIndices += dof;
8465   }
8466   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8467 
8468   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8469   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8470   PetscCall(DMGetWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8471   PetscCall(DMGetWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8472   if (numFields) {
8473     const PetscInt **permsF[32] = {NULL};
8474     const PetscInt **permsC[32] = {NULL};
8475 
8476     for (f = 0; f < numFields; f++) {
8477       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8478       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8479     }
8480     for (p = 0; p < numFPoints; p++) {
8481       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8482       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8483     }
8484     for (p = 0; p < numCPoints; p++) {
8485       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8486       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8487     }
8488     for (f = 0; f < numFields; f++) {
8489       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8490       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8491     }
8492   } else {
8493     const PetscInt **permsF = NULL;
8494     const PetscInt **permsC = NULL;
8495 
8496     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8497     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8498     for (p = 0, off = 0; p < numFPoints; p++) {
8499       const PetscInt *perm = permsF ? permsF[p] : NULL;
8500 
8501       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8502       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8503     }
8504     for (p = 0, off = 0; p < numCPoints; p++) {
8505       const PetscInt *perm = permsC ? permsC[p] : NULL;
8506 
8507       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8508       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8509     }
8510     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8511     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8512   }
8513   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8514   /* TODO: flips */
8515   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8516   ierr = MatSetValues(A, numFIndices, findices, numCIndices, cindices, values, mode);
8517   if (ierr) {
8518     PetscMPIInt rank;
8519 
8520     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8521     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8522     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8523     PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8524     PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8525   }
8526   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8527   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8528   PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8529   PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8530   PetscFunctionReturn(PETSC_SUCCESS);
8531 }
8532 
8533 PetscErrorCode DMPlexMatGetClosureIndicesRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, PetscInt point, PetscInt cindices[], PetscInt findices[])
8534 {
8535   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
8536   PetscInt       *cpoints      = NULL;
8537   PetscInt        foffsets[32] = {0}, coffsets[32] = {0};
8538   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
8539   DMPolytopeType  ct;
8540   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
8541 
8542   PetscFunctionBegin;
8543   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
8544   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
8545   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
8546   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
8547   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
8548   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
8549   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
8550   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
8551   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
8552   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
8553   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
8554   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
8555   /* Column indices */
8556   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8557   maxFPoints = numCPoints;
8558   /* Compress out points not in the section */
8559   /*   TODO: Squeeze out points with 0 dof as well */
8560   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
8561   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
8562     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
8563       cpoints[q * 2]     = cpoints[p];
8564       cpoints[q * 2 + 1] = cpoints[p + 1];
8565       ++q;
8566     }
8567   }
8568   numCPoints = q;
8569   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
8570     PetscInt fdof;
8571 
8572     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
8573     if (!dof) continue;
8574     for (f = 0; f < numFields; ++f) {
8575       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
8576       coffsets[f + 1] += fdof;
8577     }
8578     numCIndices += dof;
8579   }
8580   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
8581   /* Row indices */
8582   PetscCall(DMPlexGetCellType(dmc, point, &ct));
8583   {
8584     DMPlexTransform tr;
8585     DMPolytopeType *rct;
8586     PetscInt       *rsize, *rcone, *rornt, Nt;
8587 
8588     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8589     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8590     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8591     numSubcells = rsize[Nt - 1];
8592     PetscCall(DMPlexTransformDestroy(&tr));
8593   }
8594   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8595   for (r = 0, q = 0; r < numSubcells; ++r) {
8596     /* TODO Map from coarse to fine cells */
8597     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8598     /* Compress out points not in the section */
8599     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8600     for (p = 0; p < numFPoints * 2; p += 2) {
8601       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8602         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8603         if (!dof) continue;
8604         for (s = 0; s < q; ++s)
8605           if (fpoints[p] == ftotpoints[s * 2]) break;
8606         if (s < q) continue;
8607         ftotpoints[q * 2]     = fpoints[p];
8608         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8609         ++q;
8610       }
8611     }
8612     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8613   }
8614   numFPoints = q;
8615   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8616     PetscInt fdof;
8617 
8618     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8619     if (!dof) continue;
8620     for (f = 0; f < numFields; ++f) {
8621       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8622       foffsets[f + 1] += fdof;
8623     }
8624     numFIndices += dof;
8625   }
8626   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8627 
8628   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8629   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8630   if (numFields) {
8631     const PetscInt **permsF[32] = {NULL};
8632     const PetscInt **permsC[32] = {NULL};
8633 
8634     for (f = 0; f < numFields; f++) {
8635       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8636       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8637     }
8638     for (p = 0; p < numFPoints; p++) {
8639       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8640       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8641     }
8642     for (p = 0; p < numCPoints; p++) {
8643       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8644       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8645     }
8646     for (f = 0; f < numFields; f++) {
8647       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8648       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8649     }
8650   } else {
8651     const PetscInt **permsF = NULL;
8652     const PetscInt **permsC = NULL;
8653 
8654     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8655     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8656     for (p = 0, off = 0; p < numFPoints; p++) {
8657       const PetscInt *perm = permsF ? permsF[p] : NULL;
8658 
8659       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8660       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8661     }
8662     for (p = 0, off = 0; p < numCPoints; p++) {
8663       const PetscInt *perm = permsC ? permsC[p] : NULL;
8664 
8665       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8666       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8667     }
8668     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8669     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8670   }
8671   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8672   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8673   PetscFunctionReturn(PETSC_SUCCESS);
8674 }
8675 
8676 /*@
8677   DMPlexGetVTKCellHeight - Returns the height in the DAG used to determine which points are cells (normally 0)
8678 
8679   Input Parameter:
8680 . dm - The `DMPLEX` object
8681 
8682   Output Parameter:
8683 . cellHeight - The height of a cell
8684 
8685   Level: developer
8686 
8687 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetVTKCellHeight()`
8688 @*/
8689 PetscErrorCode DMPlexGetVTKCellHeight(DM dm, PetscInt *cellHeight)
8690 {
8691   DM_Plex *mesh = (DM_Plex *)dm->data;
8692 
8693   PetscFunctionBegin;
8694   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8695   PetscAssertPointer(cellHeight, 2);
8696   *cellHeight = mesh->vtkCellHeight;
8697   PetscFunctionReturn(PETSC_SUCCESS);
8698 }
8699 
8700 /*@
8701   DMPlexSetVTKCellHeight - Sets the height in the DAG used to determine which points are cells (normally 0)
8702 
8703   Input Parameters:
8704 + dm         - The `DMPLEX` object
8705 - cellHeight - The height of a cell
8706 
8707   Level: developer
8708 
8709 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetVTKCellHeight()`
8710 @*/
8711 PetscErrorCode DMPlexSetVTKCellHeight(DM dm, PetscInt cellHeight)
8712 {
8713   DM_Plex *mesh = (DM_Plex *)dm->data;
8714 
8715   PetscFunctionBegin;
8716   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8717   mesh->vtkCellHeight = cellHeight;
8718   PetscFunctionReturn(PETSC_SUCCESS);
8719 }
8720 
8721 /*@
8722   DMPlexGetCellTypeStratum - Get the range of cells of a given celltype
8723 
8724   Input Parameters:
8725 + dm - The `DMPLEX` object
8726 - ct - The `DMPolytopeType` of the cell
8727 
8728   Output Parameters:
8729 + start - The first cell of this type, or `NULL`
8730 - end   - The upper bound on this celltype, or `NULL`
8731 
8732   Level: advanced
8733 
8734 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexConstructGhostCells()`, `DMPlexGetDepthStratum()`, `DMPlexGetHeightStratum()`
8735 @*/
8736 PetscErrorCode DMPlexGetCellTypeStratum(DM dm, DMPolytopeType ct, PetscInt *start, PetscInt *end)
8737 {
8738   DM_Plex *mesh = (DM_Plex *)dm->data;
8739   DMLabel  label;
8740   PetscInt pStart, pEnd;
8741 
8742   PetscFunctionBegin;
8743   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8744   if (start) {
8745     PetscAssertPointer(start, 3);
8746     *start = 0;
8747   }
8748   if (end) {
8749     PetscAssertPointer(end, 4);
8750     *end = 0;
8751   }
8752   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8753   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
8754   if (mesh->tr) {
8755     PetscCall(DMPlexTransformGetCellTypeStratum(mesh->tr, ct, start, end));
8756   } else {
8757     PetscCall(DMPlexGetCellTypeLabel(dm, &label));
8758     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named celltype was found");
8759     PetscCall(DMLabelGetStratumBounds(label, ct, start, end));
8760   }
8761   PetscFunctionReturn(PETSC_SUCCESS);
8762 }
8763 
8764 /*@
8765   DMPlexGetDepthStratumGlobalSize - Get the global size for a given depth stratum
8766 
8767   Input Parameters:
8768 + dm    - The `DMPLEX` object
8769 - depth - The depth for the given point stratum
8770 
8771   Output Parameter:
8772 . gsize - The global number of points in the stratum
8773 
8774   Level: advanced
8775 
8776 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`, `DMPlexGetVertexNumbering()`, `DMPlexGetDepthStratum()`, `DMPlexGetHeightStratum()`
8777 @*/
8778 PetscErrorCode DMPlexGetDepthStratumGlobalSize(DM dm, PetscInt depth, PetscInt *gsize)
8779 {
8780   PetscSF         sf;
8781   const PetscInt *leaves;
8782   PetscInt        Nl, loc, start, end, lsize = 0;
8783 
8784   PetscFunctionBegin;
8785   PetscCall(DMGetPointSF(dm, &sf));
8786   PetscCall(PetscSFGetGraph(sf, NULL, &Nl, &leaves, NULL));
8787   PetscCall(DMPlexGetDepthStratum(dm, depth, &start, &end));
8788   for (PetscInt p = start; p < end; ++p) {
8789     PetscCall(PetscFindInt(p, Nl, leaves, &loc));
8790     if (loc < 0) ++lsize;
8791   }
8792   PetscCallMPI(MPI_Allreduce(&lsize, gsize, 1, MPIU_INT, MPI_SUM, PetscObjectComm((PetscObject)dm)));
8793   PetscFunctionReturn(PETSC_SUCCESS);
8794 }
8795 
8796 PetscErrorCode DMPlexCreateNumbering_Plex(DM dm, PetscInt pStart, PetscInt pEnd, PetscInt shift, PetscInt *globalSize, PetscSF sf, IS *numbering)
8797 {
8798   PetscSection section, globalSection;
8799   PetscInt    *numbers, p;
8800 
8801   PetscFunctionBegin;
8802   if (PetscDefined(USE_DEBUG)) PetscCall(DMPlexCheckPointSF(dm, sf, PETSC_TRUE));
8803   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
8804   PetscCall(PetscSectionSetChart(section, pStart, pEnd));
8805   for (p = pStart; p < pEnd; ++p) PetscCall(PetscSectionSetDof(section, p, 1));
8806   PetscCall(PetscSectionSetUp(section));
8807   PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &globalSection));
8808   PetscCall(PetscMalloc1(pEnd - pStart, &numbers));
8809   for (p = pStart; p < pEnd; ++p) {
8810     PetscCall(PetscSectionGetOffset(globalSection, p, &numbers[p - pStart]));
8811     if (numbers[p - pStart] < 0) numbers[p - pStart] -= shift;
8812     else numbers[p - pStart] += shift;
8813   }
8814   PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), pEnd - pStart, numbers, PETSC_OWN_POINTER, numbering));
8815   if (globalSize) {
8816     PetscLayout layout;
8817     PetscCall(PetscSectionGetPointLayout(PetscObjectComm((PetscObject)dm), globalSection, &layout));
8818     PetscCall(PetscLayoutGetSize(layout, globalSize));
8819     PetscCall(PetscLayoutDestroy(&layout));
8820   }
8821   PetscCall(PetscSectionDestroy(&section));
8822   PetscCall(PetscSectionDestroy(&globalSection));
8823   PetscFunctionReturn(PETSC_SUCCESS);
8824 }
8825 
8826 /*@
8827   DMPlexCreateCellNumbering - Get a global cell numbering for all cells on this process
8828 
8829   Input Parameters:
8830 + dm         - The `DMPLEX` object
8831 - includeAll - Whether to include all cells, or just the simplex and box cells
8832 
8833   Output Parameter:
8834 . globalCellNumbers - Global cell numbers for all cells on this process
8835 
8836   Level: developer
8837 
8838 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`, `DMPlexGetVertexNumbering()`
8839 @*/
8840 PetscErrorCode DMPlexCreateCellNumbering(DM dm, PetscBool includeAll, IS *globalCellNumbers)
8841 {
8842   PetscInt cellHeight, cStart, cEnd;
8843 
8844   PetscFunctionBegin;
8845   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8846   if (includeAll) PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8847   else PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd));
8848   PetscCall(DMPlexCreateNumbering_Plex(dm, cStart, cEnd, 0, NULL, dm->sf, globalCellNumbers));
8849   PetscFunctionReturn(PETSC_SUCCESS);
8850 }
8851 
8852 /*@
8853   DMPlexGetCellNumbering - Get a global cell numbering for all cells on this process
8854 
8855   Input Parameter:
8856 . dm - The `DMPLEX` object
8857 
8858   Output Parameter:
8859 . globalCellNumbers - Global cell numbers for all cells on this process
8860 
8861   Level: developer
8862 
8863 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreateCellNumbering()`, `DMPlexGetVertexNumbering()`
8864 @*/
8865 PetscErrorCode DMPlexGetCellNumbering(DM dm, IS *globalCellNumbers)
8866 {
8867   DM_Plex *mesh = (DM_Plex *)dm->data;
8868 
8869   PetscFunctionBegin;
8870   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8871   if (!mesh->globalCellNumbers) PetscCall(DMPlexCreateCellNumbering(dm, PETSC_FALSE, &mesh->globalCellNumbers));
8872   *globalCellNumbers = mesh->globalCellNumbers;
8873   PetscFunctionReturn(PETSC_SUCCESS);
8874 }
8875 
8876 PetscErrorCode DMPlexCreateVertexNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalVertexNumbers)
8877 {
8878   PetscInt vStart, vEnd;
8879 
8880   PetscFunctionBegin;
8881   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8882   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8883   PetscCall(DMPlexCreateNumbering_Plex(dm, vStart, vEnd, 0, NULL, dm->sf, globalVertexNumbers));
8884   PetscFunctionReturn(PETSC_SUCCESS);
8885 }
8886 
8887 /*@
8888   DMPlexGetVertexNumbering - Get a global vertex numbering for all vertices on this process
8889 
8890   Input Parameter:
8891 . dm - The `DMPLEX` object
8892 
8893   Output Parameter:
8894 . globalVertexNumbers - Global vertex numbers for all vertices on this process
8895 
8896   Level: developer
8897 
8898 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
8899 @*/
8900 PetscErrorCode DMPlexGetVertexNumbering(DM dm, IS *globalVertexNumbers)
8901 {
8902   DM_Plex *mesh = (DM_Plex *)dm->data;
8903 
8904   PetscFunctionBegin;
8905   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8906   if (!mesh->globalVertexNumbers) PetscCall(DMPlexCreateVertexNumbering_Internal(dm, PETSC_FALSE, &mesh->globalVertexNumbers));
8907   *globalVertexNumbers = mesh->globalVertexNumbers;
8908   PetscFunctionReturn(PETSC_SUCCESS);
8909 }
8910 
8911 /*@
8912   DMPlexCreatePointNumbering - Create a global numbering for all points.
8913 
8914   Collective
8915 
8916   Input Parameter:
8917 . dm - The `DMPLEX` object
8918 
8919   Output Parameter:
8920 . globalPointNumbers - Global numbers for all points on this process
8921 
8922   Level: developer
8923 
8924   Notes:
8925   The point numbering `IS` is parallel, with local portion indexed by local points (see `DMGetLocalSection()`). The global
8926   points are taken as stratified, with each MPI rank owning a contiguous subset of each stratum. In the IS, owned points
8927   will have their non-negative value while points owned by different ranks will be involuted -(idx+1). As an example,
8928   consider a parallel mesh in which the first two elements and first two vertices are owned by rank 0.
8929 
8930   The partitioned mesh is
8931   ```
8932   (2)--0--(3)--1--(4)    (1)--0--(2)
8933   ```
8934   and its global numbering is
8935   ```
8936   (3)--0--(4)--1--(5)--2--(6)
8937   ```
8938   Then the global numbering is provided as
8939   ```
8940   [0] Number of indices in set 5
8941   [0] 0 0
8942   [0] 1 1
8943   [0] 2 3
8944   [0] 3 4
8945   [0] 4 -6
8946   [1] Number of indices in set 3
8947   [1] 0 2
8948   [1] 1 5
8949   [1] 2 6
8950   ```
8951 
8952 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
8953 @*/
8954 PetscErrorCode DMPlexCreatePointNumbering(DM dm, IS *globalPointNumbers)
8955 {
8956   IS          nums[4];
8957   PetscInt    depths[4], gdepths[4], starts[4];
8958   PetscInt    depth, d, shift = 0;
8959   PetscBool   empty = PETSC_FALSE;
8960   PetscMPIInt idepth;
8961 
8962   PetscFunctionBegin;
8963   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8964   PetscCall(DMPlexGetDepth(dm, &depth));
8965   // For unstratified meshes use dim instead of depth
8966   if (depth < 0) PetscCall(DMGetDimension(dm, &depth));
8967   // If any stratum is empty, we must mark all empty
8968   for (d = 0; d <= depth; ++d) {
8969     PetscInt end;
8970 
8971     depths[d] = depth - d;
8972     PetscCall(DMPlexGetDepthStratum(dm, depths[d], &starts[d], &end));
8973     if (!(starts[d] - end)) empty = PETSC_TRUE;
8974   }
8975   if (empty)
8976     for (d = 0; d <= depth; ++d) {
8977       depths[d] = -1;
8978       starts[d] = -1;
8979     }
8980   else PetscCall(PetscSortIntWithArray(depth + 1, starts, depths));
8981   PetscCall(PetscMPIIntCast(depth + 1, &idepth));
8982   PetscCallMPI(MPIU_Allreduce(depths, gdepths, idepth, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
8983   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]);
8984   // Note here that 'shift' is collective, so that the numbering is stratified by depth
8985   for (d = 0; d <= depth; ++d) {
8986     PetscInt pStart, pEnd, gsize;
8987 
8988     PetscCall(DMPlexGetDepthStratum(dm, gdepths[d], &pStart, &pEnd));
8989     PetscCall(DMPlexCreateNumbering_Plex(dm, pStart, pEnd, shift, &gsize, dm->sf, &nums[d]));
8990     shift += gsize;
8991   }
8992   PetscCall(ISConcatenate(PETSC_COMM_SELF, depth + 1, nums, globalPointNumbers));
8993   for (d = 0; d <= depth; ++d) PetscCall(ISDestroy(&nums[d]));
8994   PetscFunctionReturn(PETSC_SUCCESS);
8995 }
8996 
8997 /*@
8998   DMPlexCreateEdgeNumbering - Create a global numbering for edges.
8999 
9000   Collective
9001 
9002   Input Parameter:
9003 . dm - The `DMPLEX` object
9004 
9005   Output Parameter:
9006 . globalEdgeNumbers - Global numbers for all edges on this process
9007 
9008   Level: developer
9009 
9010   Notes:
9011   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).
9012 
9013 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`, `DMPlexGetVertexNumbering()`, `DMPlexCreatePointNumbering()`
9014 @*/
9015 PetscErrorCode DMPlexCreateEdgeNumbering(DM dm, IS *globalEdgeNumbers)
9016 {
9017   PetscSF  sf;
9018   PetscInt eStart, eEnd;
9019 
9020   PetscFunctionBegin;
9021   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9022   PetscCall(DMGetPointSF(dm, &sf));
9023   PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
9024   PetscCall(DMPlexCreateNumbering_Plex(dm, eStart, eEnd, 0, NULL, sf, globalEdgeNumbers));
9025   PetscFunctionReturn(PETSC_SUCCESS);
9026 }
9027 
9028 /*@
9029   DMPlexCreateRankField - Create a cell field whose value is the rank of the owner
9030 
9031   Input Parameter:
9032 . dm - The `DMPLEX` object
9033 
9034   Output Parameter:
9035 . ranks - The rank field
9036 
9037   Options Database Key:
9038 . -dm_partition_view - Adds the rank field into the `DM` output from `-dm_view` using the same viewer
9039 
9040   Level: intermediate
9041 
9042 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`
9043 @*/
9044 PetscErrorCode DMPlexCreateRankField(DM dm, Vec *ranks)
9045 {
9046   DM             rdm;
9047   PetscFE        fe;
9048   PetscScalar   *r;
9049   PetscMPIInt    rank;
9050   DMPolytopeType ct;
9051   PetscInt       dim, cStart, cEnd, c;
9052   PetscBool      simplex;
9053 
9054   PetscFunctionBeginUser;
9055   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9056   PetscAssertPointer(ranks, 2);
9057   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
9058   PetscCall(DMClone(dm, &rdm));
9059   PetscCall(DMGetDimension(rdm, &dim));
9060   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
9061   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
9062   simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct) + 1 ? PETSC_TRUE : PETSC_FALSE;
9063   PetscCall(PetscFECreateDefault(PETSC_COMM_SELF, dim, 1, simplex, "PETSc___rank_", -1, &fe));
9064   PetscCall(PetscObjectSetName((PetscObject)fe, "rank"));
9065   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
9066   PetscCall(PetscFEDestroy(&fe));
9067   PetscCall(DMCreateDS(rdm));
9068   PetscCall(DMCreateGlobalVector(rdm, ranks));
9069   PetscCall(PetscObjectSetName((PetscObject)*ranks, "partition"));
9070   PetscCall(VecGetArray(*ranks, &r));
9071   for (c = cStart; c < cEnd; ++c) {
9072     PetscScalar *lr;
9073 
9074     PetscCall(DMPlexPointGlobalRef(rdm, c, r, &lr));
9075     if (lr) *lr = rank;
9076   }
9077   PetscCall(VecRestoreArray(*ranks, &r));
9078   PetscCall(DMDestroy(&rdm));
9079   PetscFunctionReturn(PETSC_SUCCESS);
9080 }
9081 
9082 /*@
9083   DMPlexCreateLabelField - Create a field whose value is the label value for that point
9084 
9085   Input Parameters:
9086 + dm    - The `DMPLEX`
9087 - label - The `DMLabel`
9088 
9089   Output Parameter:
9090 . val - The label value field
9091 
9092   Options Database Key:
9093 . -dm_label_view - Adds the label value field into the `DM` output from `-dm_view` using the same viewer
9094 
9095   Level: intermediate
9096 
9097 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`
9098 @*/
9099 PetscErrorCode DMPlexCreateLabelField(DM dm, DMLabel label, Vec *val)
9100 {
9101   DM             rdm, plex;
9102   Vec            lval;
9103   PetscSection   section;
9104   PetscFE        fe;
9105   PetscScalar   *v;
9106   PetscInt       dim, pStart, pEnd, p, cStart;
9107   DMPolytopeType ct;
9108   char           name[PETSC_MAX_PATH_LEN];
9109   const char    *lname, *prefix;
9110 
9111   PetscFunctionBeginUser;
9112   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9113   PetscAssertPointer(label, 2);
9114   PetscAssertPointer(val, 3);
9115   PetscCall(DMClone(dm, &rdm));
9116   PetscCall(DMConvert(rdm, DMPLEX, &plex));
9117   PetscCall(DMPlexGetHeightStratum(plex, 0, &cStart, NULL));
9118   PetscCall(DMPlexGetCellType(plex, cStart, &ct));
9119   PetscCall(DMDestroy(&plex));
9120   PetscCall(DMGetDimension(rdm, &dim));
9121   PetscCall(DMGetOptionsPrefix(dm, &prefix));
9122   PetscCall(PetscObjectGetName((PetscObject)label, &lname));
9123   PetscCall(PetscSNPrintf(name, sizeof(name), "%s%s_", prefix ? prefix : "", lname));
9124   PetscCall(PetscFECreateByCell(PETSC_COMM_SELF, dim, 1, ct, name, -1, &fe));
9125   PetscCall(PetscObjectSetName((PetscObject)fe, ""));
9126   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
9127   PetscCall(PetscFEDestroy(&fe));
9128   PetscCall(DMCreateDS(rdm));
9129   PetscCall(DMCreateGlobalVector(rdm, val));
9130   PetscCall(DMCreateLocalVector(rdm, &lval));
9131   PetscCall(PetscObjectSetName((PetscObject)*val, lname));
9132   PetscCall(DMGetLocalSection(rdm, &section));
9133   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
9134   PetscCall(VecGetArray(lval, &v));
9135   for (p = pStart; p < pEnd; ++p) {
9136     PetscInt cval, dof, off;
9137 
9138     PetscCall(PetscSectionGetDof(section, p, &dof));
9139     if (!dof) continue;
9140     PetscCall(DMLabelGetValue(label, p, &cval));
9141     PetscCall(PetscSectionGetOffset(section, p, &off));
9142     for (PetscInt d = 0; d < dof; d++) v[off + d] = cval;
9143   }
9144   PetscCall(VecRestoreArray(lval, &v));
9145   PetscCall(DMLocalToGlobal(rdm, lval, INSERT_VALUES, *val));
9146   PetscCall(VecDestroy(&lval));
9147   PetscCall(DMDestroy(&rdm));
9148   PetscFunctionReturn(PETSC_SUCCESS);
9149 }
9150 
9151 /*@
9152   DMPlexCheckSymmetry - Check that the adjacency information in the mesh is symmetric.
9153 
9154   Input Parameter:
9155 . dm - The `DMPLEX` object
9156 
9157   Level: developer
9158 
9159   Notes:
9160   This is a useful diagnostic when creating meshes programmatically.
9161 
9162   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9163 
9164 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9165 @*/
9166 PetscErrorCode DMPlexCheckSymmetry(DM dm)
9167 {
9168   PetscSection    coneSection, supportSection;
9169   const PetscInt *cone, *support;
9170   PetscInt        coneSize, c, supportSize, s;
9171   PetscInt        pStart, pEnd, p, pp, csize, ssize;
9172   PetscBool       storagecheck = PETSC_TRUE;
9173 
9174   PetscFunctionBegin;
9175   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9176   PetscCall(DMViewFromOptions(dm, NULL, "-sym_dm_view"));
9177   PetscCall(DMPlexGetConeSection(dm, &coneSection));
9178   PetscCall(DMPlexGetSupportSection(dm, &supportSection));
9179   /* Check that point p is found in the support of its cone points, and vice versa */
9180   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
9181   for (p = pStart; p < pEnd; ++p) {
9182     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
9183     PetscCall(DMPlexGetCone(dm, p, &cone));
9184     for (c = 0; c < coneSize; ++c) {
9185       PetscBool dup = PETSC_FALSE;
9186       PetscInt  d;
9187       for (d = c - 1; d >= 0; --d) {
9188         if (cone[c] == cone[d]) {
9189           dup = PETSC_TRUE;
9190           break;
9191         }
9192       }
9193       PetscCall(DMPlexGetSupportSize(dm, cone[c], &supportSize));
9194       PetscCall(DMPlexGetSupport(dm, cone[c], &support));
9195       for (s = 0; s < supportSize; ++s) {
9196         if (support[s] == p) break;
9197       }
9198       if ((s >= supportSize) || (dup && (support[s + 1] != p))) {
9199         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", p));
9200         for (s = 0; s < coneSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[s]));
9201         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9202         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", cone[c]));
9203         for (s = 0; s < supportSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[s]));
9204         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9205         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]);
9206         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in support of cone point %" PetscInt_FMT, p, cone[c]);
9207       }
9208     }
9209     PetscCall(DMPlexGetTreeParent(dm, p, &pp, NULL));
9210     if (p != pp) {
9211       storagecheck = PETSC_FALSE;
9212       continue;
9213     }
9214     PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
9215     PetscCall(DMPlexGetSupport(dm, p, &support));
9216     for (s = 0; s < supportSize; ++s) {
9217       PetscCall(DMPlexGetConeSize(dm, support[s], &coneSize));
9218       PetscCall(DMPlexGetCone(dm, support[s], &cone));
9219       for (c = 0; c < coneSize; ++c) {
9220         PetscCall(DMPlexGetTreeParent(dm, cone[c], &pp, NULL));
9221         if (cone[c] != pp) {
9222           c = 0;
9223           break;
9224         }
9225         if (cone[c] == p) break;
9226       }
9227       if (c >= coneSize) {
9228         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", p));
9229         for (c = 0; c < supportSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[c]));
9230         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9231         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", support[s]));
9232         for (c = 0; c < coneSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[c]));
9233         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9234         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in cone of support point %" PetscInt_FMT, p, support[s]);
9235       }
9236     }
9237   }
9238   if (storagecheck) {
9239     PetscCall(PetscSectionGetStorageSize(coneSection, &csize));
9240     PetscCall(PetscSectionGetStorageSize(supportSection, &ssize));
9241     PetscCheck(csize == ssize, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Total cone size %" PetscInt_FMT " != Total support size %" PetscInt_FMT, csize, ssize);
9242   }
9243   PetscFunctionReturn(PETSC_SUCCESS);
9244 }
9245 
9246 /*
9247   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.
9248 */
9249 static PetscErrorCode DMPlexCellUnsplitVertices_Private(DM dm, PetscInt c, DMPolytopeType ct, PetscInt *unsplit)
9250 {
9251   DMPolytopeType  cct;
9252   PetscInt        ptpoints[4];
9253   const PetscInt *cone, *ccone, *ptcone;
9254   PetscInt        coneSize, cp, cconeSize, ccp, npt = 0, pt;
9255 
9256   PetscFunctionBegin;
9257   *unsplit = 0;
9258   switch (ct) {
9259   case DM_POLYTOPE_POINT_PRISM_TENSOR:
9260     ptpoints[npt++] = c;
9261     break;
9262   case DM_POLYTOPE_SEG_PRISM_TENSOR:
9263     PetscCall(DMPlexGetCone(dm, c, &cone));
9264     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9265     for (cp = 0; cp < coneSize; ++cp) {
9266       PetscCall(DMPlexGetCellType(dm, cone[cp], &cct));
9267       if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) ptpoints[npt++] = cone[cp];
9268     }
9269     break;
9270   case DM_POLYTOPE_TRI_PRISM_TENSOR:
9271   case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9272     PetscCall(DMPlexGetCone(dm, c, &cone));
9273     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9274     for (cp = 0; cp < coneSize; ++cp) {
9275       PetscCall(DMPlexGetCone(dm, cone[cp], &ccone));
9276       PetscCall(DMPlexGetConeSize(dm, cone[cp], &cconeSize));
9277       for (ccp = 0; ccp < cconeSize; ++ccp) {
9278         PetscCall(DMPlexGetCellType(dm, ccone[ccp], &cct));
9279         if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) {
9280           PetscInt p;
9281           for (p = 0; p < npt; ++p)
9282             if (ptpoints[p] == ccone[ccp]) break;
9283           if (p == npt) ptpoints[npt++] = ccone[ccp];
9284         }
9285       }
9286     }
9287     break;
9288   default:
9289     break;
9290   }
9291   for (pt = 0; pt < npt; ++pt) {
9292     PetscCall(DMPlexGetCone(dm, ptpoints[pt], &ptcone));
9293     if (ptcone[0] == ptcone[1]) ++(*unsplit);
9294   }
9295   PetscFunctionReturn(PETSC_SUCCESS);
9296 }
9297 
9298 /*@
9299   DMPlexCheckSkeleton - Check that each cell has the correct number of vertices
9300 
9301   Input Parameters:
9302 + dm         - The `DMPLEX` object
9303 - cellHeight - Normally 0
9304 
9305   Level: developer
9306 
9307   Notes:
9308   This is a useful diagnostic when creating meshes programmatically.
9309   Currently applicable only to homogeneous simplex or tensor meshes.
9310 
9311   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9312 
9313 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9314 @*/
9315 PetscErrorCode DMPlexCheckSkeleton(DM dm, PetscInt cellHeight)
9316 {
9317   DMPlexInterpolatedFlag interp;
9318   DMPolytopeType         ct;
9319   PetscInt               vStart, vEnd, cStart, cEnd, c;
9320 
9321   PetscFunctionBegin;
9322   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9323   PetscCall(DMPlexIsInterpolated(dm, &interp));
9324   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9325   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
9326   for (c = cStart; c < cEnd; ++c) {
9327     PetscInt *closure = NULL;
9328     PetscInt  coneSize, closureSize, cl, Nv = 0;
9329 
9330     PetscCall(DMPlexGetCellType(dm, c, &ct));
9331     if (ct == DM_POLYTOPE_UNKNOWN) continue;
9332     if (interp == DMPLEX_INTERPOLATED_FULL) {
9333       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9334       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));
9335     }
9336     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9337     for (cl = 0; cl < closureSize * 2; cl += 2) {
9338       const PetscInt p = closure[cl];
9339       if ((p >= vStart) && (p < vEnd)) ++Nv;
9340     }
9341     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9342     /* Special Case: Tensor faces with identified vertices */
9343     if (Nv < DMPolytopeTypeGetNumVertices(ct)) {
9344       PetscInt unsplit;
9345 
9346       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9347       if (Nv + unsplit == DMPolytopeTypeGetNumVertices(ct)) continue;
9348     }
9349     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));
9350   }
9351   PetscFunctionReturn(PETSC_SUCCESS);
9352 }
9353 
9354 /*@
9355   DMPlexCheckFaces - Check that the faces of each cell give a vertex order this is consistent with what we expect from the cell type
9356 
9357   Collective
9358 
9359   Input Parameters:
9360 + dm         - The `DMPLEX` object
9361 - cellHeight - Normally 0
9362 
9363   Level: developer
9364 
9365   Notes:
9366   This is a useful diagnostic when creating meshes programmatically.
9367   This routine is only relevant for meshes that are fully interpolated across all ranks.
9368   It will error out if a partially interpolated mesh is given on some rank.
9369   It will do nothing for locally uninterpolated mesh (as there is nothing to check).
9370 
9371   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9372 
9373 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMPlexGetVTKCellHeight()`, `DMSetFromOptions()`
9374 @*/
9375 PetscErrorCode DMPlexCheckFaces(DM dm, PetscInt cellHeight)
9376 {
9377   PetscInt               dim, depth, vStart, vEnd, cStart, cEnd, c, h;
9378   DMPlexInterpolatedFlag interpEnum;
9379 
9380   PetscFunctionBegin;
9381   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9382   PetscCall(DMPlexIsInterpolatedCollective(dm, &interpEnum));
9383   if (interpEnum == DMPLEX_INTERPOLATED_NONE) PetscFunctionReturn(PETSC_SUCCESS);
9384   if (interpEnum != DMPLEX_INTERPOLATED_FULL) {
9385     PetscCall(PetscPrintf(PetscObjectComm((PetscObject)dm), "DMPlexCheckFaces() warning: Mesh is only partially interpolated, this is currently not supported"));
9386     PetscFunctionReturn(PETSC_SUCCESS);
9387   }
9388 
9389   PetscCall(DMGetDimension(dm, &dim));
9390   PetscCall(DMPlexGetDepth(dm, &depth));
9391   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
9392   for (h = cellHeight; h < PetscMin(depth, dim); ++h) {
9393     PetscCall(DMPlexGetHeightStratum(dm, h, &cStart, &cEnd));
9394     for (c = cStart; c < cEnd; ++c) {
9395       const PetscInt       *cone, *ornt, *faceSizes, *faces;
9396       const DMPolytopeType *faceTypes;
9397       DMPolytopeType        ct;
9398       PetscInt              numFaces, coneSize, f;
9399       PetscInt             *closure = NULL, closureSize, cl, numCorners = 0, fOff = 0, unsplit;
9400 
9401       PetscCall(DMPlexGetCellType(dm, c, &ct));
9402       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9403       if (unsplit) continue;
9404       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9405       PetscCall(DMPlexGetCone(dm, c, &cone));
9406       PetscCall(DMPlexGetConeOrientation(dm, c, &ornt));
9407       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9408       for (cl = 0; cl < closureSize * 2; cl += 2) {
9409         const PetscInt p = closure[cl];
9410         if ((p >= vStart) && (p < vEnd)) closure[numCorners++] = p;
9411       }
9412       PetscCall(DMPlexGetRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
9413       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);
9414       for (f = 0; f < numFaces; ++f) {
9415         DMPolytopeType fct;
9416         PetscInt      *fclosure = NULL, fclosureSize, cl, fnumCorners = 0, v;
9417 
9418         PetscCall(DMPlexGetCellType(dm, cone[f], &fct));
9419         PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[f], ornt[f], PETSC_TRUE, &fclosureSize, &fclosure));
9420         for (cl = 0; cl < fclosureSize * 2; cl += 2) {
9421           const PetscInt p = fclosure[cl];
9422           if ((p >= vStart) && (p < vEnd)) fclosure[fnumCorners++] = p;
9423         }
9424         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]);
9425         for (v = 0; v < fnumCorners; ++v) {
9426           if (fclosure[v] != faces[fOff + v]) {
9427             PetscInt v1;
9428 
9429             PetscCall(PetscPrintf(PETSC_COMM_SELF, "face closure:"));
9430             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, fclosure[v1]));
9431             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\ncell face:"));
9432             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, faces[fOff + v1]));
9433             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9434             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]);
9435           }
9436         }
9437         PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[f], PETSC_TRUE, &fclosureSize, &fclosure));
9438         fOff += faceSizes[f];
9439       }
9440       PetscCall(DMPlexRestoreRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
9441       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9442     }
9443   }
9444   PetscFunctionReturn(PETSC_SUCCESS);
9445 }
9446 
9447 /*@
9448   DMPlexCheckGeometry - Check the geometry of mesh cells
9449 
9450   Input Parameter:
9451 . dm - The `DMPLEX` object
9452 
9453   Level: developer
9454 
9455   Notes:
9456   This is a useful diagnostic when creating meshes programmatically.
9457 
9458   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9459 
9460 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9461 @*/
9462 PetscErrorCode DMPlexCheckGeometry(DM dm)
9463 {
9464   Vec       coordinates;
9465   PetscReal detJ, J[9], refVol = 1.0;
9466   PetscReal vol;
9467   PetscInt  dim, depth, dE, d, cStart, cEnd, c;
9468 
9469   PetscFunctionBegin;
9470   PetscCall(DMGetDimension(dm, &dim));
9471   PetscCall(DMGetCoordinateDim(dm, &dE));
9472   if (dim != dE) PetscFunctionReturn(PETSC_SUCCESS);
9473   PetscCall(DMPlexGetDepth(dm, &depth));
9474   for (d = 0; d < dim; ++d) refVol *= 2.0;
9475   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
9476   /* Make sure local coordinates are created, because that step is collective */
9477   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
9478   if (!coordinates) PetscFunctionReturn(PETSC_SUCCESS);
9479   for (c = cStart; c < cEnd; ++c) {
9480     DMPolytopeType ct;
9481     PetscInt       unsplit;
9482     PetscBool      ignoreZeroVol = PETSC_FALSE;
9483 
9484     PetscCall(DMPlexGetCellType(dm, c, &ct));
9485     switch (ct) {
9486     case DM_POLYTOPE_SEG_PRISM_TENSOR:
9487     case DM_POLYTOPE_TRI_PRISM_TENSOR:
9488     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9489       ignoreZeroVol = PETSC_TRUE;
9490       break;
9491     default:
9492       break;
9493     }
9494     switch (ct) {
9495     case DM_POLYTOPE_TRI_PRISM:
9496     case DM_POLYTOPE_TRI_PRISM_TENSOR:
9497     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9498     case DM_POLYTOPE_PYRAMID:
9499       continue;
9500     default:
9501       break;
9502     }
9503     PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9504     if (unsplit) continue;
9505     PetscCall(DMPlexComputeCellGeometryFEM(dm, c, NULL, NULL, J, NULL, &detJ));
9506     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);
9507     PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FEM Volume %g\n", c, (double)(detJ * refVol)));
9508     /* This should work with periodicity since DG coordinates should be used */
9509     if (depth > 1) {
9510       PetscCall(DMPlexComputeCellGeometryFVM(dm, c, &vol, NULL, NULL));
9511       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);
9512       PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FVM Volume %g\n", c, (double)vol));
9513     }
9514   }
9515   PetscFunctionReturn(PETSC_SUCCESS);
9516 }
9517 
9518 /*@
9519   DMPlexCheckPointSF - Check that several necessary conditions are met for the point `PetscSF` of this plex.
9520 
9521   Collective
9522 
9523   Input Parameters:
9524 + dm              - The `DMPLEX` object
9525 . pointSF         - The `PetscSF`, or `NULL` for `PointSF` attached to `DM`
9526 - allowExtraRoots - Flag to allow extra points not present in the `DM`
9527 
9528   Level: developer
9529 
9530   Notes:
9531   This is mainly intended for debugging/testing purposes.
9532 
9533   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9534 
9535   Extra roots can come from periodic cuts, where additional points appear on the boundary
9536 
9537 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMGetPointSF()`, `DMSetFromOptions()`
9538 @*/
9539 PetscErrorCode DMPlexCheckPointSF(DM dm, PetscSF pointSF, PetscBool allowExtraRoots)
9540 {
9541   PetscInt           l, nleaves, nroots, overlap;
9542   const PetscInt    *locals;
9543   const PetscSFNode *remotes;
9544   PetscBool          distributed;
9545   MPI_Comm           comm;
9546   PetscMPIInt        rank;
9547 
9548   PetscFunctionBegin;
9549   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9550   if (pointSF) PetscValidHeaderSpecific(pointSF, PETSCSF_CLASSID, 2);
9551   else pointSF = dm->sf;
9552   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9553   PetscCheck(pointSF, comm, PETSC_ERR_ARG_WRONGSTATE, "DMPlex must have Point SF attached");
9554   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9555   {
9556     PetscMPIInt mpiFlag;
9557 
9558     PetscCallMPI(MPI_Comm_compare(comm, PetscObjectComm((PetscObject)pointSF), &mpiFlag));
9559     PetscCheck(mpiFlag == MPI_CONGRUENT || mpiFlag == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "DM and Point SF have different communicators (flag %d)", mpiFlag);
9560   }
9561   PetscCall(PetscSFGetGraph(pointSF, &nroots, &nleaves, &locals, &remotes));
9562   PetscCall(DMPlexIsDistributed(dm, &distributed));
9563   if (!distributed) {
9564     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);
9565     PetscFunctionReturn(PETSC_SUCCESS);
9566   }
9567   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);
9568   PetscCall(DMPlexGetOverlap(dm, &overlap));
9569 
9570   /* Check SF graph is compatible with DMPlex chart */
9571   {
9572     PetscInt pStart, pEnd, maxLeaf;
9573 
9574     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
9575     PetscCall(PetscSFGetLeafRange(pointSF, NULL, &maxLeaf));
9576     PetscCheck(allowExtraRoots || pEnd - pStart == nroots, PETSC_COMM_SELF, PETSC_ERR_PLIB, "pEnd - pStart = %" PetscInt_FMT " != nroots = %" PetscInt_FMT, pEnd - pStart, nroots);
9577     PetscCheck(maxLeaf < pEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "maxLeaf = %" PetscInt_FMT " >= pEnd = %" PetscInt_FMT, maxLeaf, pEnd);
9578   }
9579 
9580   /* Check Point SF has no local points referenced */
9581   for (l = 0; l < nleaves; l++) {
9582     PetscMPIInt irank;
9583 
9584     PetscCall(PetscMPIIntCast(remotes[l].rank, &irank));
9585     PetscAssert(irank != rank, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains local point %" PetscInt_FMT " <- (%d,%" PetscInt_FMT ")", locals ? locals[l] : l, irank, remotes[l].index);
9586   }
9587 
9588   /* Check there are no cells in interface */
9589   if (!overlap) {
9590     PetscInt cellHeight, cStart, cEnd;
9591 
9592     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9593     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9594     for (l = 0; l < nleaves; ++l) {
9595       const PetscInt point = locals ? locals[l] : l;
9596 
9597       PetscCheck(point < cStart || point >= cEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " which is a cell", point);
9598     }
9599   }
9600 
9601   /* If some point is in interface, then all its cone points must be also in interface (either as leaves or roots) */
9602   {
9603     const PetscInt *rootdegree;
9604 
9605     PetscCall(PetscSFComputeDegreeBegin(pointSF, &rootdegree));
9606     PetscCall(PetscSFComputeDegreeEnd(pointSF, &rootdegree));
9607     for (l = 0; l < nleaves; ++l) {
9608       const PetscInt  point = locals ? locals[l] : l;
9609       const PetscInt *cone;
9610       PetscInt        coneSize, c, idx;
9611 
9612       PetscCall(DMPlexGetConeSize(dm, point, &coneSize));
9613       PetscCall(DMPlexGetCone(dm, point, &cone));
9614       for (c = 0; c < coneSize; ++c) {
9615         if (!rootdegree[cone[c]]) {
9616           if (locals) {
9617             PetscCall(PetscFindInt(cone[c], nleaves, locals, &idx));
9618           } else {
9619             idx = (cone[c] < nleaves) ? cone[c] : -1;
9620           }
9621           PetscCheck(idx >= 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " but not %" PetscInt_FMT " from its cone", point, cone[c]);
9622         }
9623       }
9624     }
9625   }
9626   PetscFunctionReturn(PETSC_SUCCESS);
9627 }
9628 
9629 /*@
9630   DMPlexCheckOrphanVertices - Check that no vertices are disconnected from the mesh, unless the mesh only consists of disconnected vertices.
9631 
9632   Collective
9633 
9634   Input Parameter:
9635 . dm - The `DMPLEX` object
9636 
9637   Level: developer
9638 
9639   Notes:
9640   This is mainly intended for debugging/testing purposes.
9641 
9642   Other cell types which are disconnected would be caught by the symmetry and face checks.
9643 
9644   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9645 
9646 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCheck()`, `DMSetFromOptions()`
9647 @*/
9648 PetscErrorCode DMPlexCheckOrphanVertices(DM dm)
9649 {
9650   PetscInt pStart, pEnd, vStart, vEnd;
9651 
9652   PetscFunctionBegin;
9653   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
9654   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
9655   if (pStart == vStart && pEnd == vEnd) PetscFunctionReturn(PETSC_SUCCESS);
9656   for (PetscInt v = vStart; v < vEnd; ++v) {
9657     PetscInt suppSize;
9658 
9659     PetscCall(DMPlexGetSupportSize(dm, v, &suppSize));
9660     PetscCheck(suppSize, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Vertex %" PetscInt_FMT " is disconnected from the mesh", v);
9661   }
9662   PetscFunctionReturn(PETSC_SUCCESS);
9663 }
9664 
9665 /*@
9666   DMPlexCheck - Perform various checks of `DMPLEX` sanity
9667 
9668   Input Parameter:
9669 . dm - The `DMPLEX` object
9670 
9671   Level: developer
9672 
9673   Notes:
9674   This is a useful diagnostic when creating meshes programmatically.
9675 
9676   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9677 
9678   Currently does not include `DMPlexCheckCellShape()`.
9679 
9680 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9681 @*/
9682 PetscErrorCode DMPlexCheck(DM dm)
9683 {
9684   PetscInt cellHeight;
9685 
9686   PetscFunctionBegin;
9687   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9688   PetscCall(DMPlexCheckSymmetry(dm));
9689   PetscCall(DMPlexCheckSkeleton(dm, cellHeight));
9690   PetscCall(DMPlexCheckFaces(dm, cellHeight));
9691   PetscCall(DMPlexCheckGeometry(dm));
9692   PetscCall(DMPlexCheckPointSF(dm, NULL, PETSC_FALSE));
9693   PetscCall(DMPlexCheckInterfaceCones(dm));
9694   PetscCall(DMPlexCheckOrphanVertices(dm));
9695   PetscFunctionReturn(PETSC_SUCCESS);
9696 }
9697 
9698 typedef struct cell_stats {
9699   PetscReal min, max, sum, squaresum;
9700   PetscInt  count;
9701 } cell_stats_t;
9702 
9703 static void MPIAPI cell_stats_reduce(void *a, void *b, int *len, MPI_Datatype *datatype)
9704 {
9705   PetscInt i, N = *len;
9706 
9707   for (i = 0; i < N; i++) {
9708     cell_stats_t *A = (cell_stats_t *)a;
9709     cell_stats_t *B = (cell_stats_t *)b;
9710 
9711     B->min = PetscMin(A->min, B->min);
9712     B->max = PetscMax(A->max, B->max);
9713     B->sum += A->sum;
9714     B->squaresum += A->squaresum;
9715     B->count += A->count;
9716   }
9717 }
9718 
9719 /*@
9720   DMPlexCheckCellShape - Checks the Jacobian of the mapping from reference to real cells and computes some minimal statistics.
9721 
9722   Collective
9723 
9724   Input Parameters:
9725 + dm        - The `DMPLEX` object
9726 . output    - If true, statistics will be displayed on `stdout`
9727 - condLimit - Display all cells above this condition number, or `PETSC_DETERMINE` for no cell output
9728 
9729   Level: developer
9730 
9731   Notes:
9732   This is mainly intended for debugging/testing purposes.
9733 
9734   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9735 
9736 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexComputeOrthogonalQuality()`
9737 @*/
9738 PetscErrorCode DMPlexCheckCellShape(DM dm, PetscBool output, PetscReal condLimit)
9739 {
9740   DM           dmCoarse;
9741   cell_stats_t stats, globalStats;
9742   MPI_Comm     comm = PetscObjectComm((PetscObject)dm);
9743   PetscReal   *J, *invJ, min = 0, max = 0, mean = 0, stdev = 0;
9744   PetscReal    limit = condLimit > 0 ? condLimit : PETSC_MAX_REAL;
9745   PetscInt     cdim, cStart, cEnd, c, eStart, eEnd, count = 0;
9746   PetscMPIInt  rank, size;
9747 
9748   PetscFunctionBegin;
9749   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9750   stats.min = PETSC_MAX_REAL;
9751   stats.max = PETSC_MIN_REAL;
9752   stats.sum = stats.squaresum = 0.;
9753   stats.count                 = 0;
9754 
9755   PetscCallMPI(MPI_Comm_size(comm, &size));
9756   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9757   PetscCall(DMGetCoordinateDim(dm, &cdim));
9758   PetscCall(PetscMalloc2(PetscSqr(cdim), &J, PetscSqr(cdim), &invJ));
9759   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
9760   PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
9761   for (c = cStart; c < cEnd; c++) {
9762     PetscInt  i;
9763     PetscReal frobJ = 0., frobInvJ = 0., cond2, cond, detJ;
9764 
9765     PetscCall(DMPlexComputeCellGeometryAffineFEM(dm, c, NULL, J, invJ, &detJ));
9766     PetscCheck(detJ >= 0.0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Mesh cell %" PetscInt_FMT " is inverted", c);
9767     for (i = 0; i < PetscSqr(cdim); ++i) {
9768       frobJ += J[i] * J[i];
9769       frobInvJ += invJ[i] * invJ[i];
9770     }
9771     cond2 = frobJ * frobInvJ;
9772     cond  = PetscSqrtReal(cond2);
9773 
9774     stats.min = PetscMin(stats.min, cond);
9775     stats.max = PetscMax(stats.max, cond);
9776     stats.sum += cond;
9777     stats.squaresum += cond2;
9778     stats.count++;
9779     if (output && cond > limit) {
9780       PetscSection coordSection;
9781       Vec          coordsLocal;
9782       PetscScalar *coords = NULL;
9783       PetscInt     Nv, d, clSize, cl, *closure = NULL;
9784 
9785       PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
9786       PetscCall(DMGetCoordinateSection(dm, &coordSection));
9787       PetscCall(DMPlexVecGetClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9788       PetscCall(PetscSynchronizedPrintf(comm, "[%d] Cell %" PetscInt_FMT " cond %g\n", rank, c, (double)cond));
9789       for (i = 0; i < Nv / cdim; ++i) {
9790         PetscCall(PetscSynchronizedPrintf(comm, "  Vertex %" PetscInt_FMT ": (", i));
9791         for (d = 0; d < cdim; ++d) {
9792           if (d > 0) PetscCall(PetscSynchronizedPrintf(comm, ", "));
9793           PetscCall(PetscSynchronizedPrintf(comm, "%g", (double)PetscRealPart(coords[i * cdim + d])));
9794         }
9795         PetscCall(PetscSynchronizedPrintf(comm, ")\n"));
9796       }
9797       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9798       for (cl = 0; cl < clSize * 2; cl += 2) {
9799         const PetscInt edge = closure[cl];
9800 
9801         if ((edge >= eStart) && (edge < eEnd)) {
9802           PetscReal len;
9803 
9804           PetscCall(DMPlexComputeCellGeometryFVM(dm, edge, &len, NULL, NULL));
9805           PetscCall(PetscSynchronizedPrintf(comm, "  Edge %" PetscInt_FMT ": length %g\n", edge, (double)len));
9806         }
9807       }
9808       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9809       PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9810     }
9811   }
9812   if (output) PetscCall(PetscSynchronizedFlush(comm, NULL));
9813 
9814   if (size > 1) {
9815     PetscMPIInt  blockLengths[2] = {4, 1};
9816     MPI_Aint     blockOffsets[2] = {offsetof(cell_stats_t, min), offsetof(cell_stats_t, count)};
9817     MPI_Datatype blockTypes[2]   = {MPIU_REAL, MPIU_INT}, statType;
9818     MPI_Op       statReduce;
9819 
9820     PetscCallMPI(MPI_Type_create_struct(2, blockLengths, blockOffsets, blockTypes, &statType));
9821     PetscCallMPI(MPI_Type_commit(&statType));
9822     PetscCallMPI(MPI_Op_create(cell_stats_reduce, PETSC_TRUE, &statReduce));
9823     PetscCallMPI(MPI_Reduce(&stats, &globalStats, 1, statType, statReduce, 0, comm));
9824     PetscCallMPI(MPI_Op_free(&statReduce));
9825     PetscCallMPI(MPI_Type_free(&statType));
9826   } else {
9827     PetscCall(PetscArraycpy(&globalStats, &stats, 1));
9828   }
9829   if (rank == 0) {
9830     count = globalStats.count;
9831     min   = globalStats.min;
9832     max   = globalStats.max;
9833     mean  = globalStats.sum / globalStats.count;
9834     stdev = globalStats.count > 1 ? PetscSqrtReal(PetscMax((globalStats.squaresum - globalStats.count * mean * mean) / (globalStats.count - 1), 0)) : 0.0;
9835   }
9836 
9837   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));
9838   PetscCall(PetscFree2(J, invJ));
9839 
9840   PetscCall(DMGetCoarseDM(dm, &dmCoarse));
9841   if (dmCoarse) {
9842     PetscBool isplex;
9843 
9844     PetscCall(PetscObjectTypeCompare((PetscObject)dmCoarse, DMPLEX, &isplex));
9845     if (isplex) PetscCall(DMPlexCheckCellShape(dmCoarse, output, condLimit));
9846   }
9847   PetscFunctionReturn(PETSC_SUCCESS);
9848 }
9849 
9850 /*@
9851   DMPlexComputeOrthogonalQuality - Compute cell-wise orthogonal quality mesh statistic. Optionally tags all cells with
9852   orthogonal quality below given tolerance.
9853 
9854   Collective
9855 
9856   Input Parameters:
9857 + dm   - The `DMPLEX` object
9858 . fv   - Optional `PetscFV` object for pre-computed cell/face centroid information
9859 - atol - [0, 1] Absolute tolerance for tagging cells.
9860 
9861   Output Parameters:
9862 + OrthQual      - `Vec` containing orthogonal quality per cell
9863 - OrthQualLabel - `DMLabel` tagging cells below atol with `DM_ADAPT_REFINE`
9864 
9865   Options Database Keys:
9866 + -dm_plex_orthogonal_quality_label_view - view OrthQualLabel if label is requested. Currently only `PETSCVIEWERASCII` is supported.
9867 - -dm_plex_orthogonal_quality_vec_view   - view OrthQual vector.
9868 
9869   Level: intermediate
9870 
9871   Notes:
9872   Orthogonal quality is given by the following formula\:
9873 
9874   $ \min \left[ \frac{A_i \cdot f_i}{\|A_i\| \|f_i\|} , \frac{A_i \cdot c_i}{\|A_i\| \|c_i\|} \right]$
9875 
9876   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
9877   is the vector from the current cells centroid to the centroid of its i'th neighbor (which shares a face with the
9878   current cell). This computes the vector similarity between each cell face and its corresponding neighbor centroid by
9879   calculating the cosine of the angle between these vectors.
9880 
9881   Orthogonal quality ranges from 1 (best) to 0 (worst).
9882 
9883   This routine is mainly useful for FVM, however is not restricted to only FVM. The `PetscFV` object is optionally used to check for
9884   pre-computed FVM cell data, but if it is not passed in then this data will be computed.
9885 
9886   Cells are tagged if they have an orthogonal quality less than or equal to the absolute tolerance.
9887 
9888 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCheckCellShape()`, `DMCreateLabel()`, `PetscFV`, `DMLabel`, `Vec`
9889 @*/
9890 PetscErrorCode DMPlexComputeOrthogonalQuality(DM dm, PetscFV fv, PetscReal atol, Vec *OrthQual, DMLabel *OrthQualLabel)
9891 {
9892   PetscInt               nc, cellHeight, cStart, cEnd, cell, cellIter = 0;
9893   PetscInt              *idx;
9894   PetscScalar           *oqVals;
9895   const PetscScalar     *cellGeomArr, *faceGeomArr;
9896   PetscReal             *ci, *fi, *Ai;
9897   MPI_Comm               comm;
9898   Vec                    cellgeom, facegeom;
9899   DM                     dmFace, dmCell;
9900   IS                     glob;
9901   ISLocalToGlobalMapping ltog;
9902   PetscViewer            vwr;
9903 
9904   PetscFunctionBegin;
9905   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9906   if (fv) PetscValidHeaderSpecific(fv, PETSCFV_CLASSID, 2);
9907   PetscAssertPointer(OrthQual, 4);
9908   PetscCheck(atol >= 0.0 && atol <= 1.0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Absolute tolerance %g not in [0,1]", (double)atol);
9909   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9910   PetscCall(DMGetDimension(dm, &nc));
9911   PetscCheck(nc >= 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must have dimension >= 2 (current %" PetscInt_FMT ")", nc);
9912   {
9913     DMPlexInterpolatedFlag interpFlag;
9914 
9915     PetscCall(DMPlexIsInterpolated(dm, &interpFlag));
9916     if (interpFlag != DMPLEX_INTERPOLATED_FULL) {
9917       PetscMPIInt rank;
9918 
9919       PetscCallMPI(MPI_Comm_rank(comm, &rank));
9920       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must be fully interpolated, DM on rank %d is not fully interpolated", rank);
9921     }
9922   }
9923   if (OrthQualLabel) {
9924     PetscAssertPointer(OrthQualLabel, 5);
9925     PetscCall(DMCreateLabel(dm, "Orthogonal_Quality"));
9926     PetscCall(DMGetLabel(dm, "Orthogonal_Quality", OrthQualLabel));
9927   } else {
9928     *OrthQualLabel = NULL;
9929   }
9930   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9931   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9932   PetscCall(DMPlexCreateCellNumbering(dm, PETSC_TRUE, &glob));
9933   PetscCall(ISLocalToGlobalMappingCreateIS(glob, &ltog));
9934   PetscCall(ISLocalToGlobalMappingSetType(ltog, ISLOCALTOGLOBALMAPPINGHASH));
9935   PetscCall(VecCreate(comm, OrthQual));
9936   PetscCall(VecSetType(*OrthQual, VECSTANDARD));
9937   PetscCall(VecSetSizes(*OrthQual, cEnd - cStart, PETSC_DETERMINE));
9938   PetscCall(VecSetLocalToGlobalMapping(*OrthQual, ltog));
9939   PetscCall(VecSetUp(*OrthQual));
9940   PetscCall(ISDestroy(&glob));
9941   PetscCall(ISLocalToGlobalMappingDestroy(&ltog));
9942   PetscCall(DMPlexGetDataFVM(dm, fv, &cellgeom, &facegeom, NULL));
9943   PetscCall(VecGetArrayRead(cellgeom, &cellGeomArr));
9944   PetscCall(VecGetArrayRead(facegeom, &faceGeomArr));
9945   PetscCall(VecGetDM(cellgeom, &dmCell));
9946   PetscCall(VecGetDM(facegeom, &dmFace));
9947   PetscCall(PetscMalloc5(cEnd - cStart, &idx, cEnd - cStart, &oqVals, nc, &ci, nc, &fi, nc, &Ai));
9948   for (cell = cStart; cell < cEnd; cellIter++, cell++) {
9949     PetscInt         cellneigh, cellneighiter = 0, adjSize = PETSC_DETERMINE;
9950     PetscInt         cellarr[2], *adj = NULL;
9951     PetscScalar     *cArr, *fArr;
9952     PetscReal        minvalc = 1.0, minvalf = 1.0;
9953     PetscFVCellGeom *cg;
9954 
9955     idx[cellIter] = cell - cStart;
9956     cellarr[0]    = cell;
9957     /* Make indexing into cellGeom easier */
9958     PetscCall(DMPlexPointLocalRead(dmCell, cell, cellGeomArr, &cg));
9959     PetscCall(DMPlexGetAdjacency_Internal(dm, cell, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &adjSize, &adj));
9960     /* Technically 1 too big, but easier than fiddling with empty adjacency array */
9961     PetscCall(PetscCalloc2(adjSize, &cArr, adjSize, &fArr));
9962     for (cellneigh = 0; cellneigh < adjSize; cellneighiter++, cellneigh++) {
9963       PetscInt         i;
9964       const PetscInt   neigh  = adj[cellneigh];
9965       PetscReal        normci = 0, normfi = 0, normai = 0;
9966       PetscFVCellGeom *cgneigh;
9967       PetscFVFaceGeom *fg;
9968 
9969       /* Don't count ourselves in the neighbor list */
9970       if (neigh == cell) continue;
9971       PetscCall(DMPlexPointLocalRead(dmCell, neigh, cellGeomArr, &cgneigh));
9972       cellarr[1] = neigh;
9973       {
9974         PetscInt        numcovpts;
9975         const PetscInt *covpts;
9976 
9977         PetscCall(DMPlexGetMeet(dm, 2, cellarr, &numcovpts, &covpts));
9978         PetscCall(DMPlexPointLocalRead(dmFace, covpts[0], faceGeomArr, &fg));
9979         PetscCall(DMPlexRestoreMeet(dm, 2, cellarr, &numcovpts, &covpts));
9980       }
9981 
9982       /* Compute c_i, f_i and their norms */
9983       for (i = 0; i < nc; i++) {
9984         ci[i] = cgneigh->centroid[i] - cg->centroid[i];
9985         fi[i] = fg->centroid[i] - cg->centroid[i];
9986         Ai[i] = fg->normal[i];
9987         normci += PetscPowReal(ci[i], 2);
9988         normfi += PetscPowReal(fi[i], 2);
9989         normai += PetscPowReal(Ai[i], 2);
9990       }
9991       normci = PetscSqrtReal(normci);
9992       normfi = PetscSqrtReal(normfi);
9993       normai = PetscSqrtReal(normai);
9994 
9995       /* Normalize and compute for each face-cell-normal pair */
9996       for (i = 0; i < nc; i++) {
9997         ci[i] = ci[i] / normci;
9998         fi[i] = fi[i] / normfi;
9999         Ai[i] = Ai[i] / normai;
10000         /* PetscAbs because I don't know if normals are guaranteed to point out */
10001         cArr[cellneighiter] += PetscAbs(Ai[i] * ci[i]);
10002         fArr[cellneighiter] += PetscAbs(Ai[i] * fi[i]);
10003       }
10004       if (PetscRealPart(cArr[cellneighiter]) < minvalc) minvalc = PetscRealPart(cArr[cellneighiter]);
10005       if (PetscRealPart(fArr[cellneighiter]) < minvalf) minvalf = PetscRealPart(fArr[cellneighiter]);
10006     }
10007     PetscCall(PetscFree(adj));
10008     PetscCall(PetscFree2(cArr, fArr));
10009     /* Defer to cell if they're equal */
10010     oqVals[cellIter] = PetscMin(minvalf, minvalc);
10011     if (OrthQualLabel) {
10012       if (PetscRealPart(oqVals[cellIter]) <= atol) PetscCall(DMLabelSetValue(*OrthQualLabel, cell, DM_ADAPT_REFINE));
10013     }
10014   }
10015   PetscCall(VecSetValuesLocal(*OrthQual, cEnd - cStart, idx, oqVals, INSERT_VALUES));
10016   PetscCall(VecAssemblyBegin(*OrthQual));
10017   PetscCall(VecAssemblyEnd(*OrthQual));
10018   PetscCall(VecRestoreArrayRead(cellgeom, &cellGeomArr));
10019   PetscCall(VecRestoreArrayRead(facegeom, &faceGeomArr));
10020   PetscCall(PetscOptionsCreateViewer(comm, NULL, NULL, "-dm_plex_orthogonal_quality_label_view", &vwr, NULL, NULL));
10021   if (OrthQualLabel) {
10022     if (vwr) PetscCall(DMLabelView(*OrthQualLabel, vwr));
10023   }
10024   PetscCall(PetscFree5(idx, oqVals, ci, fi, Ai));
10025   PetscCall(PetscViewerDestroy(&vwr));
10026   PetscCall(VecViewFromOptions(*OrthQual, NULL, "-dm_plex_orthogonal_quality_vec_view"));
10027   PetscFunctionReturn(PETSC_SUCCESS);
10028 }
10029 
10030 /* this is here instead of DMGetOutputDM because output DM still has constraints in the local indices that affect
10031  * interpolator construction */
10032 static PetscErrorCode DMGetFullDM(DM dm, DM *odm)
10033 {
10034   PetscSection section, newSection, gsection;
10035   PetscSF      sf;
10036   PetscBool    hasConstraints, ghasConstraints;
10037 
10038   PetscFunctionBegin;
10039   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10040   PetscAssertPointer(odm, 2);
10041   PetscCall(DMGetLocalSection(dm, &section));
10042   PetscCall(PetscSectionHasConstraints(section, &hasConstraints));
10043   PetscCallMPI(MPIU_Allreduce(&hasConstraints, &ghasConstraints, 1, MPIU_BOOL, MPI_LOR, PetscObjectComm((PetscObject)dm)));
10044   if (!ghasConstraints) {
10045     PetscCall(PetscObjectReference((PetscObject)dm));
10046     *odm = dm;
10047     PetscFunctionReturn(PETSC_SUCCESS);
10048   }
10049   PetscCall(DMClone(dm, odm));
10050   PetscCall(DMCopyFields(dm, PETSC_DETERMINE, PETSC_DETERMINE, *odm));
10051   PetscCall(DMGetLocalSection(*odm, &newSection));
10052   PetscCall(DMGetPointSF(*odm, &sf));
10053   PetscCall(PetscSectionCreateGlobalSection(newSection, sf, PETSC_TRUE, PETSC_TRUE, PETSC_FALSE, &gsection));
10054   PetscCall(DMSetGlobalSection(*odm, gsection));
10055   PetscCall(PetscSectionDestroy(&gsection));
10056   PetscFunctionReturn(PETSC_SUCCESS);
10057 }
10058 
10059 static PetscErrorCode DMCreateAffineInterpolationCorrection_Plex(DM dmc, DM dmf, Vec *shift)
10060 {
10061   DM        dmco, dmfo;
10062   Mat       interpo;
10063   Vec       rscale;
10064   Vec       cglobalo, clocal;
10065   Vec       fglobal, fglobalo, flocal;
10066   PetscBool regular;
10067 
10068   PetscFunctionBegin;
10069   PetscCall(DMGetFullDM(dmc, &dmco));
10070   PetscCall(DMGetFullDM(dmf, &dmfo));
10071   PetscCall(DMSetCoarseDM(dmfo, dmco));
10072   PetscCall(DMPlexGetRegularRefinement(dmf, &regular));
10073   PetscCall(DMPlexSetRegularRefinement(dmfo, regular));
10074   PetscCall(DMCreateInterpolation(dmco, dmfo, &interpo, &rscale));
10075   PetscCall(DMCreateGlobalVector(dmco, &cglobalo));
10076   PetscCall(DMCreateLocalVector(dmc, &clocal));
10077   PetscCall(VecSet(cglobalo, 0.));
10078   PetscCall(VecSet(clocal, 0.));
10079   PetscCall(DMCreateGlobalVector(dmf, &fglobal));
10080   PetscCall(DMCreateGlobalVector(dmfo, &fglobalo));
10081   PetscCall(DMCreateLocalVector(dmf, &flocal));
10082   PetscCall(VecSet(fglobal, 0.));
10083   PetscCall(VecSet(fglobalo, 0.));
10084   PetscCall(VecSet(flocal, 0.));
10085   PetscCall(DMPlexInsertBoundaryValues(dmc, PETSC_TRUE, clocal, 0., NULL, NULL, NULL));
10086   PetscCall(DMLocalToGlobalBegin(dmco, clocal, INSERT_VALUES, cglobalo));
10087   PetscCall(DMLocalToGlobalEnd(dmco, clocal, INSERT_VALUES, cglobalo));
10088   PetscCall(MatMult(interpo, cglobalo, fglobalo));
10089   PetscCall(DMGlobalToLocalBegin(dmfo, fglobalo, INSERT_VALUES, flocal));
10090   PetscCall(DMGlobalToLocalEnd(dmfo, fglobalo, INSERT_VALUES, flocal));
10091   PetscCall(DMLocalToGlobalBegin(dmf, flocal, INSERT_VALUES, fglobal));
10092   PetscCall(DMLocalToGlobalEnd(dmf, flocal, INSERT_VALUES, fglobal));
10093   *shift = fglobal;
10094   PetscCall(VecDestroy(&flocal));
10095   PetscCall(VecDestroy(&fglobalo));
10096   PetscCall(VecDestroy(&clocal));
10097   PetscCall(VecDestroy(&cglobalo));
10098   PetscCall(VecDestroy(&rscale));
10099   PetscCall(MatDestroy(&interpo));
10100   PetscCall(DMDestroy(&dmfo));
10101   PetscCall(DMDestroy(&dmco));
10102   PetscFunctionReturn(PETSC_SUCCESS);
10103 }
10104 
10105 PETSC_INTERN PetscErrorCode DMInterpolateSolution_Plex(DM coarse, DM fine, Mat interp, Vec coarseSol, Vec fineSol)
10106 {
10107   PetscObject shifto;
10108   Vec         shift;
10109 
10110   PetscFunctionBegin;
10111   if (!interp) {
10112     Vec rscale;
10113 
10114     PetscCall(DMCreateInterpolation(coarse, fine, &interp, &rscale));
10115     PetscCall(VecDestroy(&rscale));
10116   } else {
10117     PetscCall(PetscObjectReference((PetscObject)interp));
10118   }
10119   PetscCall(PetscObjectQuery((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", &shifto));
10120   if (!shifto) {
10121     PetscCall(DMCreateAffineInterpolationCorrection_Plex(coarse, fine, &shift));
10122     PetscCall(PetscObjectCompose((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", (PetscObject)shift));
10123     shifto = (PetscObject)shift;
10124     PetscCall(VecDestroy(&shift));
10125   }
10126   shift = (Vec)shifto;
10127   PetscCall(MatInterpolate(interp, coarseSol, fineSol));
10128   PetscCall(VecAXPY(fineSol, 1.0, shift));
10129   PetscCall(MatDestroy(&interp));
10130   PetscFunctionReturn(PETSC_SUCCESS);
10131 }
10132 
10133 /* Pointwise interpolation
10134      Just code FEM for now
10135      u^f = I u^c
10136      sum_k u^f_k phi^f_k = I sum_j u^c_j phi^c_j
10137      u^f_i = sum_j psi^f_i I phi^c_j u^c_j
10138      I_{ij} = psi^f_i phi^c_j
10139 */
10140 PetscErrorCode DMCreateInterpolation_Plex(DM dmCoarse, DM dmFine, Mat *interpolation, Vec *scaling)
10141 {
10142   PetscSection gsc, gsf;
10143   PetscInt     m, n;
10144   void        *ctx;
10145   DM           cdm;
10146   PetscBool    regular, ismatis, isRefined = dmCoarse->data == dmFine->data ? PETSC_FALSE : PETSC_TRUE;
10147 
10148   PetscFunctionBegin;
10149   PetscCall(DMGetGlobalSection(dmFine, &gsf));
10150   PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
10151   PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
10152   PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
10153 
10154   PetscCall(PetscStrcmp(dmCoarse->mattype, MATIS, &ismatis));
10155   PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), interpolation));
10156   PetscCall(MatSetSizes(*interpolation, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
10157   PetscCall(MatSetType(*interpolation, ismatis ? MATAIJ : dmCoarse->mattype));
10158   PetscCall(DMGetApplicationContext(dmFine, &ctx));
10159 
10160   PetscCall(DMGetCoarseDM(dmFine, &cdm));
10161   PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
10162   if (!isRefined || (regular && cdm == dmCoarse)) PetscCall(DMPlexComputeInterpolatorNested(dmCoarse, dmFine, isRefined, *interpolation, ctx));
10163   else PetscCall(DMPlexComputeInterpolatorGeneral(dmCoarse, dmFine, *interpolation, ctx));
10164   PetscCall(MatViewFromOptions(*interpolation, NULL, "-interp_mat_view"));
10165   if (scaling) {
10166     /* Use naive scaling */
10167     PetscCall(DMCreateInterpolationScale(dmCoarse, dmFine, *interpolation, scaling));
10168   }
10169   PetscFunctionReturn(PETSC_SUCCESS);
10170 }
10171 
10172 PetscErrorCode DMCreateInjection_Plex(DM dmCoarse, DM dmFine, Mat *mat)
10173 {
10174   VecScatter ctx;
10175 
10176   PetscFunctionBegin;
10177   PetscCall(DMPlexComputeInjectorFEM(dmCoarse, dmFine, &ctx, NULL));
10178   PetscCall(MatCreateScatter(PetscObjectComm((PetscObject)ctx), ctx, mat));
10179   PetscCall(VecScatterDestroy(&ctx));
10180   PetscFunctionReturn(PETSC_SUCCESS);
10181 }
10182 
10183 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[])
10184 {
10185   const PetscInt f  = (PetscInt)PetscRealPart(constants[numConstants]);
10186   const PetscInt Nc = uOff[f + 1] - uOff[f];
10187   for (PetscInt c = 0; c < Nc; ++c) g0[c * Nc + c] = 1.0;
10188 }
10189 
10190 PetscErrorCode DMCreateMassMatrixLumped_Plex(DM dm, Vec *lmass, Vec *mass)
10191 {
10192   DM           dmc;
10193   PetscDS      ds;
10194   Vec          ones, locmass;
10195   IS           cellIS;
10196   PetscFormKey key;
10197   PetscInt     depth;
10198 
10199   PetscFunctionBegin;
10200   PetscCall(DMClone(dm, &dmc));
10201   PetscCall(DMCopyDisc(dm, dmc));
10202   PetscCall(DMGetDS(dmc, &ds));
10203   for (PetscInt f = 0; f < dmc->Nf; ++f) PetscCall(PetscDSSetJacobian(ds, f, f, g0_identity_private, NULL, NULL, NULL));
10204   if (mass) PetscCall(DMCreateGlobalVector(dm, mass));
10205   if (lmass) PetscCall(DMCreateLocalVector(dm, &locmass));
10206   else PetscCall(DMGetLocalVector(dm, &locmass));
10207   PetscCall(DMGetLocalVector(dm, &ones));
10208   PetscCall(DMPlexGetDepth(dm, &depth));
10209   PetscCall(DMGetStratumIS(dm, "depth", depth, &cellIS));
10210   PetscCall(VecSet(locmass, 0.0));
10211   PetscCall(VecSet(ones, 1.0));
10212   key.label = NULL;
10213   key.value = 0;
10214   key.field = 0;
10215   key.part  = 0;
10216   PetscCall(DMPlexComputeJacobian_Action_Internal(dmc, key, cellIS, 0.0, 0.0, ones, NULL, ones, locmass, NULL));
10217   PetscCall(ISDestroy(&cellIS));
10218   if (mass) {
10219     PetscCall(DMLocalToGlobalBegin(dm, locmass, ADD_VALUES, *mass));
10220     PetscCall(DMLocalToGlobalEnd(dm, locmass, ADD_VALUES, *mass));
10221   }
10222   PetscCall(DMRestoreLocalVector(dm, &ones));
10223   if (lmass) *lmass = locmass;
10224   else PetscCall(DMRestoreLocalVector(dm, &locmass));
10225   PetscCall(DMDestroy(&dmc));
10226   PetscFunctionReturn(PETSC_SUCCESS);
10227 }
10228 
10229 PetscErrorCode DMCreateMassMatrix_Plex(DM dmCoarse, DM dmFine, Mat *mass)
10230 {
10231   PetscSection gsc, gsf;
10232   PetscInt     m, n;
10233   void        *ctx;
10234   DM           cdm;
10235   PetscBool    regular;
10236 
10237   PetscFunctionBegin;
10238   if (dmFine == dmCoarse) {
10239     DM            dmc;
10240     PetscDS       ds;
10241     PetscWeakForm wf;
10242     Vec           u;
10243     IS            cellIS;
10244     PetscFormKey  key;
10245     PetscInt      depth;
10246 
10247     PetscCall(DMClone(dmFine, &dmc));
10248     PetscCall(DMCopyDisc(dmFine, dmc));
10249     PetscCall(DMGetDS(dmc, &ds));
10250     PetscCall(PetscDSGetWeakForm(ds, &wf));
10251     PetscCall(PetscWeakFormClear(wf));
10252     for (PetscInt f = 0; f < dmc->Nf; ++f) PetscCall(PetscDSSetJacobian(ds, f, f, g0_identity_private, NULL, NULL, NULL));
10253     PetscCall(DMCreateMatrix(dmc, mass));
10254     PetscCall(DMGetLocalVector(dmc, &u));
10255     PetscCall(DMPlexGetDepth(dmc, &depth));
10256     PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
10257     PetscCall(MatZeroEntries(*mass));
10258     key.label = NULL;
10259     key.value = 0;
10260     key.field = 0;
10261     key.part  = 0;
10262     PetscCall(DMPlexComputeJacobian_Internal(dmc, key, cellIS, 0.0, 0.0, u, NULL, *mass, *mass, NULL));
10263     PetscCall(ISDestroy(&cellIS));
10264     PetscCall(DMRestoreLocalVector(dmc, &u));
10265     PetscCall(DMDestroy(&dmc));
10266   } else {
10267     PetscCall(DMGetGlobalSection(dmFine, &gsf));
10268     PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
10269     PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
10270     PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
10271 
10272     PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), mass));
10273     PetscCall(MatSetSizes(*mass, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
10274     PetscCall(MatSetType(*mass, dmCoarse->mattype));
10275     PetscCall(DMGetApplicationContext(dmFine, &ctx));
10276 
10277     PetscCall(DMGetCoarseDM(dmFine, &cdm));
10278     PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
10279     if (regular && cdm == dmCoarse) PetscCall(DMPlexComputeMassMatrixNested(dmCoarse, dmFine, *mass, ctx));
10280     else PetscCall(DMPlexComputeMassMatrixGeneral(dmCoarse, dmFine, *mass, ctx));
10281   }
10282   PetscCall(MatViewFromOptions(*mass, NULL, "-mass_mat_view"));
10283   PetscFunctionReturn(PETSC_SUCCESS);
10284 }
10285 
10286 /*@
10287   DMPlexGetRegularRefinement - Get the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
10288 
10289   Input Parameter:
10290 . dm - The `DMPLEX` object
10291 
10292   Output Parameter:
10293 . regular - The flag
10294 
10295   Level: intermediate
10296 
10297 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetRegularRefinement()`
10298 @*/
10299 PetscErrorCode DMPlexGetRegularRefinement(DM dm, PetscBool *regular)
10300 {
10301   PetscFunctionBegin;
10302   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10303   PetscAssertPointer(regular, 2);
10304   *regular = ((DM_Plex *)dm->data)->regularRefinement;
10305   PetscFunctionReturn(PETSC_SUCCESS);
10306 }
10307 
10308 /*@
10309   DMPlexSetRegularRefinement - Set the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
10310 
10311   Input Parameters:
10312 + dm      - The `DMPLEX` object
10313 - regular - The flag
10314 
10315   Level: intermediate
10316 
10317 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetRegularRefinement()`
10318 @*/
10319 PetscErrorCode DMPlexSetRegularRefinement(DM dm, PetscBool regular)
10320 {
10321   PetscFunctionBegin;
10322   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10323   ((DM_Plex *)dm->data)->regularRefinement = regular;
10324   PetscFunctionReturn(PETSC_SUCCESS);
10325 }
10326 
10327 /*@
10328   DMPlexGetAnchors - Get the layout of the anchor (point-to-point) constraints.  Typically, the user will not have to
10329   call DMPlexGetAnchors() directly: if there are anchors, then `DMPlexGetAnchors()` is called during `DMGetDefaultConstraints()`.
10330 
10331   Not Collective
10332 
10333   Input Parameter:
10334 . dm - The `DMPLEX` object
10335 
10336   Output Parameters:
10337 + anchorSection - If not `NULL`, set to the section describing which points anchor the constrained points.
10338 - anchorIS      - If not `NULL`, set to the list of anchors indexed by `anchorSection`
10339 
10340   Level: intermediate
10341 
10342 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`, `IS`, `PetscSection`
10343 @*/
10344 PetscErrorCode DMPlexGetAnchors(DM dm, PetscSection *anchorSection, IS *anchorIS)
10345 {
10346   DM_Plex *plex = (DM_Plex *)dm->data;
10347 
10348   PetscFunctionBegin;
10349   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10350   if (!plex->anchorSection && !plex->anchorIS && plex->createanchors) PetscCall((*plex->createanchors)(dm));
10351   if (anchorSection) *anchorSection = plex->anchorSection;
10352   if (anchorIS) *anchorIS = plex->anchorIS;
10353   PetscFunctionReturn(PETSC_SUCCESS);
10354 }
10355 
10356 /*@
10357   DMPlexSetAnchors - Set the layout of the local anchor (point-to-point) constraints.
10358 
10359   Collective
10360 
10361   Input Parameters:
10362 + dm            - The `DMPLEX` object
10363 . anchorSection - The section that describes the mapping from constrained points to the anchor points listed in anchorIS.
10364                   Must have a local communicator (`PETSC_COMM_SELF` or derivative).
10365 - anchorIS      - The list of all anchor points.  Must have a local communicator (`PETSC_COMM_SELF` or derivative).
10366 
10367   Level: intermediate
10368 
10369   Notes:
10370   Unlike boundary conditions, when a point's degrees of freedom in a section are constrained to
10371   an outside value, the anchor constraints set a point's degrees of freedom to be a linear
10372   combination of other points' degrees of freedom.
10373 
10374   After specifying the layout of constraints with `DMPlexSetAnchors()`, one specifies the constraints by calling
10375   `DMGetDefaultConstraints()` and filling in the entries in the constraint matrix.
10376 
10377   The reference counts of `anchorSection` and `anchorIS` are incremented.
10378 
10379 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`
10380 @*/
10381 PetscErrorCode DMPlexSetAnchors(DM dm, PetscSection anchorSection, IS anchorIS)
10382 {
10383   DM_Plex    *plex = (DM_Plex *)dm->data;
10384   PetscMPIInt result;
10385 
10386   PetscFunctionBegin;
10387   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10388   if (anchorSection) {
10389     PetscValidHeaderSpecific(anchorSection, PETSC_SECTION_CLASSID, 2);
10390     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorSection), &result));
10391     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor section must have local communicator");
10392   }
10393   if (anchorIS) {
10394     PetscValidHeaderSpecific(anchorIS, IS_CLASSID, 3);
10395     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorIS), &result));
10396     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor IS must have local communicator");
10397   }
10398 
10399   PetscCall(PetscObjectReference((PetscObject)anchorSection));
10400   PetscCall(PetscSectionDestroy(&plex->anchorSection));
10401   plex->anchorSection = anchorSection;
10402 
10403   PetscCall(PetscObjectReference((PetscObject)anchorIS));
10404   PetscCall(ISDestroy(&plex->anchorIS));
10405   plex->anchorIS = anchorIS;
10406 
10407   if (PetscUnlikelyDebug(anchorIS && anchorSection)) {
10408     PetscInt        size, a, pStart, pEnd;
10409     const PetscInt *anchors;
10410 
10411     PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
10412     PetscCall(ISGetLocalSize(anchorIS, &size));
10413     PetscCall(ISGetIndices(anchorIS, &anchors));
10414     for (a = 0; a < size; a++) {
10415       PetscInt p;
10416 
10417       p = anchors[a];
10418       if (p >= pStart && p < pEnd) {
10419         PetscInt dof;
10420 
10421         PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
10422         if (dof) {
10423           PetscCall(ISRestoreIndices(anchorIS, &anchors));
10424           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Point %" PetscInt_FMT " cannot be constrained and an anchor", p);
10425         }
10426       }
10427     }
10428     PetscCall(ISRestoreIndices(anchorIS, &anchors));
10429   }
10430   /* reset the generic constraints */
10431   PetscCall(DMSetDefaultConstraints(dm, NULL, NULL, NULL));
10432   PetscFunctionReturn(PETSC_SUCCESS);
10433 }
10434 
10435 static PetscErrorCode DMPlexCreateConstraintSection_Anchors(DM dm, PetscSection section, PetscSection *cSec)
10436 {
10437   PetscSection anchorSection;
10438   PetscInt     pStart, pEnd, sStart, sEnd, p, dof, numFields, f;
10439 
10440   PetscFunctionBegin;
10441   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10442   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
10443   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, cSec));
10444   PetscCall(PetscSectionGetNumFields(section, &numFields));
10445   if (numFields) {
10446     PetscInt f;
10447     PetscCall(PetscSectionSetNumFields(*cSec, numFields));
10448 
10449     for (f = 0; f < numFields; f++) {
10450       PetscInt numComp;
10451 
10452       PetscCall(PetscSectionGetFieldComponents(section, f, &numComp));
10453       PetscCall(PetscSectionSetFieldComponents(*cSec, f, numComp));
10454     }
10455   }
10456   PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
10457   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
10458   pStart = PetscMax(pStart, sStart);
10459   pEnd   = PetscMin(pEnd, sEnd);
10460   pEnd   = PetscMax(pStart, pEnd);
10461   PetscCall(PetscSectionSetChart(*cSec, pStart, pEnd));
10462   for (p = pStart; p < pEnd; p++) {
10463     PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
10464     if (dof) {
10465       PetscCall(PetscSectionGetDof(section, p, &dof));
10466       PetscCall(PetscSectionSetDof(*cSec, p, dof));
10467       for (f = 0; f < numFields; f++) {
10468         PetscCall(PetscSectionGetFieldDof(section, p, f, &dof));
10469         PetscCall(PetscSectionSetFieldDof(*cSec, p, f, dof));
10470       }
10471     }
10472   }
10473   PetscCall(PetscSectionSetUp(*cSec));
10474   PetscCall(PetscObjectSetName((PetscObject)*cSec, "Constraint Section"));
10475   PetscFunctionReturn(PETSC_SUCCESS);
10476 }
10477 
10478 static PetscErrorCode DMPlexCreateConstraintMatrix_Anchors(DM dm, PetscSection section, PetscSection cSec, Mat *cMat)
10479 {
10480   PetscSection    aSec;
10481   PetscInt        pStart, pEnd, p, sStart, sEnd, dof, aDof, aOff, off, nnz, annz, m, n, q, a, offset, *i, *j;
10482   const PetscInt *anchors;
10483   PetscInt        numFields, f;
10484   IS              aIS;
10485   MatType         mtype;
10486   PetscBool       iscuda, iskokkos;
10487 
10488   PetscFunctionBegin;
10489   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10490   PetscCall(PetscSectionGetStorageSize(cSec, &m));
10491   PetscCall(PetscSectionGetStorageSize(section, &n));
10492   PetscCall(MatCreate(PETSC_COMM_SELF, cMat));
10493   PetscCall(MatSetSizes(*cMat, m, n, m, n));
10494   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJCUSPARSE, &iscuda));
10495   if (!iscuda) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJCUSPARSE, &iscuda));
10496   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJKOKKOS, &iskokkos));
10497   if (!iskokkos) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJKOKKOS, &iskokkos));
10498   if (iscuda) mtype = MATSEQAIJCUSPARSE;
10499   else if (iskokkos) mtype = MATSEQAIJKOKKOS;
10500   else mtype = MATSEQAIJ;
10501   PetscCall(MatSetType(*cMat, mtype));
10502   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
10503   PetscCall(ISGetIndices(aIS, &anchors));
10504   /* cSec will be a subset of aSec and section */
10505   PetscCall(PetscSectionGetChart(cSec, &pStart, &pEnd));
10506   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
10507   PetscCall(PetscMalloc1(m + 1, &i));
10508   i[0] = 0;
10509   PetscCall(PetscSectionGetNumFields(section, &numFields));
10510   for (p = pStart; p < pEnd; p++) {
10511     PetscInt rDof, rOff, r;
10512 
10513     PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10514     if (!rDof) continue;
10515     PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10516     if (numFields) {
10517       for (f = 0; f < numFields; f++) {
10518         annz = 0;
10519         for (r = 0; r < rDof; r++) {
10520           a = anchors[rOff + r];
10521           if (a < sStart || a >= sEnd) continue;
10522           PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
10523           annz += aDof;
10524         }
10525         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
10526         PetscCall(PetscSectionGetFieldOffset(cSec, p, f, &off));
10527         for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
10528       }
10529     } else {
10530       annz = 0;
10531       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10532       for (q = 0; q < dof; q++) {
10533         a = anchors[rOff + q];
10534         if (a < sStart || a >= sEnd) continue;
10535         PetscCall(PetscSectionGetDof(section, a, &aDof));
10536         annz += aDof;
10537       }
10538       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10539       PetscCall(PetscSectionGetOffset(cSec, p, &off));
10540       for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
10541     }
10542   }
10543   nnz = i[m];
10544   PetscCall(PetscMalloc1(nnz, &j));
10545   offset = 0;
10546   for (p = pStart; p < pEnd; p++) {
10547     if (numFields) {
10548       for (f = 0; f < numFields; f++) {
10549         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
10550         for (q = 0; q < dof; q++) {
10551           PetscInt rDof, rOff, r;
10552           PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10553           PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10554           for (r = 0; r < rDof; r++) {
10555             PetscInt s;
10556 
10557             a = anchors[rOff + r];
10558             if (a < sStart || a >= sEnd) continue;
10559             PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
10560             PetscCall(PetscSectionGetFieldOffset(section, a, f, &aOff));
10561             for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
10562           }
10563         }
10564       }
10565     } else {
10566       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10567       for (q = 0; q < dof; q++) {
10568         PetscInt rDof, rOff, r;
10569         PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10570         PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10571         for (r = 0; r < rDof; r++) {
10572           PetscInt s;
10573 
10574           a = anchors[rOff + r];
10575           if (a < sStart || a >= sEnd) continue;
10576           PetscCall(PetscSectionGetDof(section, a, &aDof));
10577           PetscCall(PetscSectionGetOffset(section, a, &aOff));
10578           for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
10579         }
10580       }
10581     }
10582   }
10583   PetscCall(MatSeqAIJSetPreallocationCSR(*cMat, i, j, NULL));
10584   PetscCall(PetscFree(i));
10585   PetscCall(PetscFree(j));
10586   PetscCall(ISRestoreIndices(aIS, &anchors));
10587   PetscFunctionReturn(PETSC_SUCCESS);
10588 }
10589 
10590 PetscErrorCode DMCreateDefaultConstraints_Plex(DM dm)
10591 {
10592   DM_Plex     *plex = (DM_Plex *)dm->data;
10593   PetscSection anchorSection, section, cSec;
10594   Mat          cMat;
10595 
10596   PetscFunctionBegin;
10597   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10598   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
10599   if (anchorSection) {
10600     PetscInt Nf;
10601 
10602     PetscCall(DMGetLocalSection(dm, &section));
10603     PetscCall(DMPlexCreateConstraintSection_Anchors(dm, section, &cSec));
10604     PetscCall(DMPlexCreateConstraintMatrix_Anchors(dm, section, cSec, &cMat));
10605     PetscCall(DMGetNumFields(dm, &Nf));
10606     if (Nf && plex->computeanchormatrix) PetscCall((*plex->computeanchormatrix)(dm, section, cSec, cMat));
10607     PetscCall(DMSetDefaultConstraints(dm, cSec, cMat, NULL));
10608     PetscCall(PetscSectionDestroy(&cSec));
10609     PetscCall(MatDestroy(&cMat));
10610   }
10611   PetscFunctionReturn(PETSC_SUCCESS);
10612 }
10613 
10614 PetscErrorCode DMCreateSubDomainDM_Plex(DM dm, DMLabel label, PetscInt value, IS *is, DM *subdm)
10615 {
10616   IS           subis;
10617   PetscSection section, subsection;
10618 
10619   PetscFunctionBegin;
10620   PetscCall(DMGetLocalSection(dm, &section));
10621   PetscCheck(section, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set default section for DM before splitting subdomain");
10622   PetscCheck(subdm, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set output subDM for splitting subdomain");
10623   /* Create subdomain */
10624   PetscCall(DMPlexFilter(dm, label, value, PETSC_FALSE, PETSC_FALSE, NULL, subdm));
10625   /* Create submodel */
10626   PetscCall(DMPlexGetSubpointIS(*subdm, &subis));
10627   PetscCall(PetscSectionCreateSubmeshSection(section, subis, &subsection));
10628   PetscCall(DMSetLocalSection(*subdm, subsection));
10629   PetscCall(PetscSectionDestroy(&subsection));
10630   PetscCall(DMCopyDisc(dm, *subdm));
10631   /* Create map from submodel to global model */
10632   if (is) {
10633     PetscSection    sectionGlobal, subsectionGlobal;
10634     IS              spIS;
10635     const PetscInt *spmap;
10636     PetscInt       *subIndices;
10637     PetscInt        subSize = 0, subOff = 0, pStart, pEnd, p;
10638     PetscInt        Nf, f, bs = -1, bsLocal[2], bsMinMax[2];
10639 
10640     PetscCall(DMPlexGetSubpointIS(*subdm, &spIS));
10641     PetscCall(ISGetIndices(spIS, &spmap));
10642     PetscCall(PetscSectionGetNumFields(section, &Nf));
10643     PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
10644     PetscCall(DMGetGlobalSection(*subdm, &subsectionGlobal));
10645     PetscCall(PetscSectionGetChart(subsection, &pStart, &pEnd));
10646     for (p = pStart; p < pEnd; ++p) {
10647       PetscInt gdof, pSubSize = 0;
10648 
10649       PetscCall(PetscSectionGetDof(sectionGlobal, p, &gdof));
10650       if (gdof > 0) {
10651         for (f = 0; f < Nf; ++f) {
10652           PetscInt fdof, fcdof;
10653 
10654           PetscCall(PetscSectionGetFieldDof(subsection, p, f, &fdof));
10655           PetscCall(PetscSectionGetFieldConstraintDof(subsection, p, f, &fcdof));
10656           pSubSize += fdof - fcdof;
10657         }
10658         subSize += pSubSize;
10659         if (pSubSize) {
10660           if (bs < 0) {
10661             bs = pSubSize;
10662           } else if (bs != pSubSize) {
10663             /* Layout does not admit a pointwise block size */
10664             bs = 1;
10665           }
10666         }
10667       }
10668     }
10669     /* Must have same blocksize on all procs (some might have no points) */
10670     bsLocal[0] = bs < 0 ? PETSC_INT_MAX : bs;
10671     bsLocal[1] = bs;
10672     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
10673     if (bsMinMax[0] != bsMinMax[1]) {
10674       bs = 1;
10675     } else {
10676       bs = bsMinMax[0];
10677     }
10678     PetscCall(PetscMalloc1(subSize, &subIndices));
10679     for (p = pStart; p < pEnd; ++p) {
10680       PetscInt gdof, goff;
10681 
10682       PetscCall(PetscSectionGetDof(subsectionGlobal, p, &gdof));
10683       if (gdof > 0) {
10684         const PetscInt point = spmap[p];
10685 
10686         PetscCall(PetscSectionGetOffset(sectionGlobal, point, &goff));
10687         for (f = 0; f < Nf; ++f) {
10688           PetscInt fdof, fcdof, fc, f2, poff = 0;
10689 
10690           /* Can get rid of this loop by storing field information in the global section */
10691           for (f2 = 0; f2 < f; ++f2) {
10692             PetscCall(PetscSectionGetFieldDof(section, p, f2, &fdof));
10693             PetscCall(PetscSectionGetFieldConstraintDof(section, p, f2, &fcdof));
10694             poff += fdof - fcdof;
10695           }
10696           PetscCall(PetscSectionGetFieldDof(section, p, f, &fdof));
10697           PetscCall(PetscSectionGetFieldConstraintDof(section, p, f, &fcdof));
10698           for (fc = 0; fc < fdof - fcdof; ++fc, ++subOff) subIndices[subOff] = goff + poff + fc;
10699         }
10700       }
10701     }
10702     PetscCall(ISRestoreIndices(spIS, &spmap));
10703     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), subSize, subIndices, PETSC_OWN_POINTER, is));
10704     if (bs > 1) {
10705       /* We need to check that the block size does not come from non-contiguous fields */
10706       PetscInt i, j, set = 1;
10707       for (i = 0; i < subSize; i += bs) {
10708         for (j = 0; j < bs; ++j) {
10709           if (subIndices[i + j] != subIndices[i] + j) {
10710             set = 0;
10711             break;
10712           }
10713         }
10714       }
10715       if (set) PetscCall(ISSetBlockSize(*is, bs));
10716     }
10717     /* Attach nullspace */
10718     for (f = 0; f < Nf; ++f) {
10719       (*subdm)->nullspaceConstructors[f] = dm->nullspaceConstructors[f];
10720       if ((*subdm)->nullspaceConstructors[f]) break;
10721     }
10722     if (f < Nf) {
10723       MatNullSpace nullSpace;
10724       PetscCall((*(*subdm)->nullspaceConstructors[f])(*subdm, f, f, &nullSpace));
10725 
10726       PetscCall(PetscObjectCompose((PetscObject)*is, "nullspace", (PetscObject)nullSpace));
10727       PetscCall(MatNullSpaceDestroy(&nullSpace));
10728     }
10729   }
10730   PetscFunctionReturn(PETSC_SUCCESS);
10731 }
10732 
10733 /*@
10734   DMPlexMonitorThroughput - Report the cell throughput of FE integration
10735 
10736   Input Parameters:
10737 + dm    - The `DM`
10738 - dummy - unused argument
10739 
10740   Options Database Key:
10741 . -dm_plex_monitor_throughput - Activate the monitor
10742 
10743   Level: developer
10744 
10745 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexCreate()`
10746 @*/
10747 PetscErrorCode DMPlexMonitorThroughput(DM dm, void *dummy)
10748 {
10749   PetscLogHandler default_handler;
10750 
10751   PetscFunctionBegin;
10752   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10753   PetscCall(PetscLogGetDefaultHandler(&default_handler));
10754   if (default_handler) {
10755     PetscLogEvent      event;
10756     PetscEventPerfInfo eventInfo;
10757     PetscLogDouble     cellRate, flopRate;
10758     PetscInt           cStart, cEnd, Nf, N;
10759     const char        *name;
10760 
10761     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
10762     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
10763     PetscCall(DMGetNumFields(dm, &Nf));
10764     PetscCall(PetscLogEventGetId("DMPlexResidualFE", &event));
10765     PetscCall(PetscLogEventGetPerfInfo(PETSC_DEFAULT, event, &eventInfo));
10766     N        = (cEnd - cStart) * Nf * eventInfo.count;
10767     flopRate = eventInfo.flops / eventInfo.time;
10768     cellRate = N / eventInfo.time;
10769     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, cellRate, flopRate / 1.e6));
10770   } else {
10771     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.");
10772   }
10773   PetscFunctionReturn(PETSC_SUCCESS);
10774 }
10775