xref: /petsc/src/dm/impls/plex/plex.c (revision 882cb04d3cffde17d09a8aa45d5f5a7ba5f0e0ee)
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 
12 /* Logging support */
13 PetscLogEvent DMPLEX_Interpolate, DMPLEX_Partition, DMPLEX_Distribute, DMPLEX_DistributeCones, DMPLEX_DistributeLabels, DMPLEX_DistributeSF, DMPLEX_DistributeOverlap, DMPLEX_DistributeField, DMPLEX_DistributeData, DMPLEX_Migrate, DMPLEX_InterpolateSF, DMPLEX_GlobalToNaturalBegin, DMPLEX_GlobalToNaturalEnd, DMPLEX_NaturalToGlobalBegin, DMPLEX_NaturalToGlobalEnd, DMPLEX_Stratify, DMPLEX_Symmetrize, DMPLEX_Preallocate, DMPLEX_ResidualFEM, DMPLEX_JacobianFEM, DMPLEX_InterpolatorFEM, DMPLEX_InjectorFEM, DMPLEX_IntegralFEM, DMPLEX_CreateGmsh, DMPLEX_RebalanceSharedPoints, DMPLEX_PartSelf, DMPLEX_PartLabelInvert, DMPLEX_PartLabelCreateSF, DMPLEX_PartStratSF, DMPLEX_CreatePointSF, DMPLEX_LocatePoints, DMPLEX_TopologyView, DMPLEX_LabelsView, DMPLEX_CoordinatesView, DMPLEX_SectionView, DMPLEX_GlobalVectorView, DMPLEX_LocalVectorView, DMPLEX_TopologyLoad, DMPLEX_LabelsLoad, DMPLEX_CoordinatesLoad, DMPLEX_SectionLoad, DMPLEX_GlobalVectorLoad, DMPLEX_LocalVectorLoad;
14 PetscLogEvent DMPLEX_RebalBuildGraph, DMPLEX_RebalRewriteSF, DMPLEX_RebalGatherGraph, DMPLEX_RebalPartition, DMPLEX_RebalScatterPart, DMPLEX_Generate, DMPLEX_Transform, DMPLEX_GetLocalOffsets, DMPLEX_Uninterpolate;
15 
16 PetscBool  Plexcite       = PETSC_FALSE;
17 const char PlexCitation[] = "@article{LangeMitchellKnepleyGorman2015,\n"
18                             "title     = {Efficient mesh management in {Firedrake} using {PETSc-DMPlex}},\n"
19                             "author    = {Michael Lange and Lawrence Mitchell and Matthew G. Knepley and Gerard J. Gorman},\n"
20                             "journal   = {SIAM Journal on Scientific Computing},\n"
21                             "volume    = {38},\n"
22                             "number    = {5},\n"
23                             "pages     = {S143--S155},\n"
24                             "eprint    = {http://arxiv.org/abs/1506.07749},\n"
25                             "doi       = {10.1137/15M1026092},\n"
26                             "year      = {2016},\n"
27                             "petsc_uses={DMPlex},\n}\n";
28 
29 PETSC_EXTERN PetscErrorCode VecView_MPI(Vec, PetscViewer);
30 
31 /*@
32   DMPlexIsSimplex - Is the first cell in this mesh a simplex?
33 
34   Input Parameter:
35 . dm - The `DMPLEX` object
36 
37   Output Parameter:
38 . simplex - Flag checking for a simplex
39 
40   Level: intermediate
41 
42   Note:
43   This just gives the first range of cells found. If the mesh has several cell types, it will only give the first.
44   If the mesh has no cells, this returns `PETSC_FALSE`.
45 
46 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSimplexOrBoxCells()`, `DMPlexGetCellType()`, `DMPlexGetHeightStratum()`, `DMPolytopeTypeGetNumVertices()`
47 @*/
48 PetscErrorCode DMPlexIsSimplex(DM dm, PetscBool *simplex)
49 {
50   DMPolytopeType ct;
51   PetscInt       cStart, cEnd;
52 
53   PetscFunctionBegin;
54   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
55   if (cEnd <= cStart) {
56     *simplex = PETSC_FALSE;
57     PetscFunctionReturn(PETSC_SUCCESS);
58   }
59   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
60   *simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct) + 1 ? PETSC_TRUE : PETSC_FALSE;
61   PetscFunctionReturn(PETSC_SUCCESS);
62 }
63 
64 /*@
65   DMPlexGetSimplexOrBoxCells - Get the range of cells which are neither prisms nor ghost FV cells
66 
67   Input Parameters:
68 + dm     - The `DMPLEX` object
69 - height - The cell height in the Plex, 0 is the default
70 
71   Output Parameters:
72 + cStart - The first "normal" cell
73 - cEnd   - The upper bound on "normal" cells
74 
75   Level: developer
76 
77   Note:
78   This function requires that tensor cells are ordered last.
79 
80 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexConstructGhostCells()`, `DMPlexGetCellTypeStratum()`
81 @*/
82 PetscErrorCode DMPlexGetSimplexOrBoxCells(DM dm, PetscInt height, PetscInt *cStart, PetscInt *cEnd)
83 {
84   DMLabel         ctLabel;
85   IS              valueIS;
86   const PetscInt *ctypes;
87   PetscInt        Nct, cS = PETSC_MAX_INT, cE = 0;
88 
89   PetscFunctionBegin;
90   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
91   PetscCall(DMLabelGetValueIS(ctLabel, &valueIS));
92   PetscCall(ISGetLocalSize(valueIS, &Nct));
93   PetscCall(ISGetIndices(valueIS, &ctypes));
94   if (!Nct) cS = cE = 0;
95   for (PetscInt t = 0; t < Nct; ++t) {
96     const DMPolytopeType ct = (DMPolytopeType)ctypes[t];
97     PetscInt             ctS, ctE, ht;
98 
99     if (ct == DM_POLYTOPE_UNKNOWN) {
100       // If any cells are not typed, just use all cells
101       PetscCall(DMPlexGetHeightStratum(dm, PetscMax(height, 0), cStart, cEnd));
102       break;
103     }
104     if (DMPolytopeTypeIsHybrid(ct) || ct == DM_POLYTOPE_FV_GHOST) continue;
105     PetscCall(DMLabelGetStratumBounds(ctLabel, ct, &ctS, &ctE));
106     if (ctS >= ctE) continue;
107     // Check that a point has the right height
108     PetscCall(DMPlexGetPointHeight(dm, ctS, &ht));
109     if (ht != height) continue;
110     cS = PetscMin(cS, ctS);
111     cE = PetscMax(cE, ctE);
112   }
113   PetscCall(ISDestroy(&valueIS));
114   // Reset label for fast lookup
115   PetscCall(DMLabelMakeAllInvalid_Internal(ctLabel));
116   if (cStart) *cStart = cS;
117   if (cEnd) *cEnd = cE;
118   PetscFunctionReturn(PETSC_SUCCESS);
119 }
120 
121 PetscErrorCode DMPlexGetFieldTypes_Internal(DM dm, PetscSection section, PetscInt field, PetscInt *types, PetscInt **ssStart, PetscInt **ssEnd, PetscViewerVTKFieldType **sft)
122 {
123   PetscInt                 cdim, pStart, pEnd, vStart, vEnd, cStart, cEnd, c, depth, cellHeight, t;
124   PetscInt                *sStart, *sEnd;
125   PetscViewerVTKFieldType *ft;
126   PetscInt                 vcdof[DM_NUM_POLYTOPES + 1], globalvcdof[DM_NUM_POLYTOPES + 1];
127   DMLabel                  depthLabel, ctLabel;
128 
129   PetscFunctionBegin;
130 
131   /* the vcdof and globalvcdof are sized to allow every polytope type and simple vertex at DM_NUM_POLYTOPES */
132   PetscCall(PetscArrayzero(vcdof, DM_NUM_POLYTOPES + 1));
133   PetscCall(DMGetCoordinateDim(dm, &cdim));
134   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
135   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
136   if (field >= 0) {
137     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, vStart, field, &vcdof[DM_NUM_POLYTOPES]));
138   } else {
139     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetDof(section, vStart, &vcdof[DM_NUM_POLYTOPES]));
140   }
141 
142   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
143   PetscCall(DMPlexGetDepth(dm, &depth));
144   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
145   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
146   for (c = 0; c < DM_NUM_POLYTOPES; ++c) {
147     const DMPolytopeType ict = (DMPolytopeType)c;
148     PetscInt             dep;
149 
150     if (ict == DM_POLYTOPE_FV_GHOST) continue;
151     PetscCall(DMLabelGetStratumBounds(ctLabel, ict, &cStart, &cEnd));
152     if (pStart >= 0) {
153       PetscCall(DMLabelGetValue(depthLabel, cStart, &dep));
154       if (dep != depth - cellHeight) continue;
155     }
156     if (field >= 0) {
157       if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, cStart, field, &vcdof[c]));
158     } else {
159       if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetDof(section, cStart, &vcdof[c]));
160     }
161     PetscCall(MPIU_Allreduce(vcdof, globalvcdof, 2, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
162   }
163 
164   PetscCall(MPIU_Allreduce(vcdof, globalvcdof, DM_NUM_POLYTOPES + 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
165   *types = 0;
166 
167   for (c = 0; c < DM_NUM_POLYTOPES + 1; ++c) {
168     if (globalvcdof[c]) ++(*types);
169   }
170 
171   PetscCall(PetscMalloc3(*types, &sStart, *types, &sEnd, *types, &ft));
172   t = 0;
173   if (globalvcdof[DM_NUM_POLYTOPES]) {
174     sStart[t] = vStart;
175     sEnd[t]   = vEnd;
176     ft[t]     = (globalvcdof[t] == cdim) ? PETSC_VTK_POINT_VECTOR_FIELD : PETSC_VTK_POINT_FIELD;
177     ++t;
178   }
179 
180   for (c = 0; c < DM_NUM_POLYTOPES; ++c) {
181     if (globalvcdof[c]) {
182       const DMPolytopeType ict = (DMPolytopeType)c;
183 
184       PetscCall(DMLabelGetStratumBounds(ctLabel, ict, &cStart, &cEnd));
185       sStart[t] = cStart;
186       sEnd[t]   = cEnd;
187       ft[t]     = (globalvcdof[c] == cdim) ? PETSC_VTK_CELL_VECTOR_FIELD : PETSC_VTK_CELL_FIELD;
188       ++t;
189     }
190   }
191 
192   if (!(*types)) {
193     if (field >= 0) {
194       const char *fieldname;
195 
196       PetscCall(PetscSectionGetFieldName(section, field, &fieldname));
197       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section field %" PetscInt_FMT " \"%s\"\n", field, fieldname));
198     } else {
199       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section\n"));
200     }
201   }
202 
203   *ssStart = sStart;
204   *ssEnd   = sEnd;
205   *sft     = ft;
206   PetscFunctionReturn(PETSC_SUCCESS);
207 }
208 
209 PetscErrorCode DMPlexRestoreFieldTypes_Internal(DM dm, PetscSection section, PetscInt field, PetscInt *types, PetscInt **sStart, PetscInt **sEnd, PetscViewerVTKFieldType **ft)
210 {
211   PetscFunctionBegin;
212   PetscCall(PetscFree3(*sStart, *sEnd, *ft));
213   PetscFunctionReturn(PETSC_SUCCESS);
214 }
215 
216 PetscErrorCode DMPlexGetFieldType_Internal(DM dm, PetscSection section, PetscInt field, PetscInt *sStart, PetscInt *sEnd, PetscViewerVTKFieldType *ft)
217 {
218   PetscInt cdim, pStart, pEnd, vStart, vEnd, cStart, cEnd;
219   PetscInt vcdof[2] = {0, 0}, globalvcdof[2];
220 
221   PetscFunctionBegin;
222   *ft = PETSC_VTK_INVALID;
223   PetscCall(DMGetCoordinateDim(dm, &cdim));
224   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
225   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
226   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
227   if (field >= 0) {
228     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, vStart, field, &vcdof[0]));
229     if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, cStart, field, &vcdof[1]));
230   } else {
231     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetDof(section, vStart, &vcdof[0]));
232     if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetDof(section, cStart, &vcdof[1]));
233   }
234   PetscCall(MPIU_Allreduce(vcdof, globalvcdof, 2, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
235   if (globalvcdof[0]) {
236     *sStart = vStart;
237     *sEnd   = vEnd;
238     if (globalvcdof[0] == cdim) *ft = PETSC_VTK_POINT_VECTOR_FIELD;
239     else *ft = PETSC_VTK_POINT_FIELD;
240   } else if (globalvcdof[1]) {
241     *sStart = cStart;
242     *sEnd   = cEnd;
243     if (globalvcdof[1] == cdim) *ft = PETSC_VTK_CELL_VECTOR_FIELD;
244     else *ft = PETSC_VTK_CELL_FIELD;
245   } else {
246     if (field >= 0) {
247       const char *fieldname;
248 
249       PetscCall(PetscSectionGetFieldName(section, field, &fieldname));
250       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section field %" PetscInt_FMT " \"%s\"\n", field, fieldname));
251     } else {
252       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section\n"));
253     }
254   }
255   PetscFunctionReturn(PETSC_SUCCESS);
256 }
257 
258 /*@
259   DMPlexVecView1D - Plot many 1D solutions on the same line graph
260 
261   Collective
262 
263   Input Parameters:
264 + dm     - The `DMPLEX` object
265 . n      - The number of vectors
266 . u      - The array of local vectors
267 - viewer - The `PetscViewer`
268 
269   Level: advanced
270 
271 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `VecViewFromOptions()`, `VecView()`
272 @*/
273 PetscErrorCode DMPlexVecView1D(DM dm, PetscInt n, Vec u[], PetscViewer viewer)
274 {
275   PetscDS            ds;
276   PetscDraw          draw = NULL;
277   PetscDrawLG        lg;
278   Vec                coordinates;
279   const PetscScalar *coords, **sol;
280   PetscReal         *vals;
281   PetscInt          *Nc;
282   PetscInt           Nf, f, c, Nl, l, i, vStart, vEnd, v;
283   char             **names;
284 
285   PetscFunctionBegin;
286   PetscCall(DMGetDS(dm, &ds));
287   PetscCall(PetscDSGetNumFields(ds, &Nf));
288   PetscCall(PetscDSGetTotalComponents(ds, &Nl));
289   PetscCall(PetscDSGetComponents(ds, &Nc));
290 
291   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
292   if (!draw) PetscFunctionReturn(PETSC_SUCCESS);
293   PetscCall(PetscDrawLGCreate(draw, n * Nl, &lg));
294 
295   PetscCall(PetscMalloc3(n, &sol, n * Nl, &names, n * Nl, &vals));
296   for (i = 0, l = 0; i < n; ++i) {
297     const char *vname;
298 
299     PetscCall(PetscObjectGetName((PetscObject)u[i], &vname));
300     for (f = 0; f < Nf; ++f) {
301       PetscObject disc;
302       const char *fname;
303       char        tmpname[PETSC_MAX_PATH_LEN];
304 
305       PetscCall(PetscDSGetDiscretization(ds, f, &disc));
306       /* TODO Create names for components */
307       for (c = 0; c < Nc[f]; ++c, ++l) {
308         PetscCall(PetscObjectGetName(disc, &fname));
309         PetscCall(PetscStrncpy(tmpname, vname, sizeof(tmpname)));
310         PetscCall(PetscStrlcat(tmpname, ":", sizeof(tmpname)));
311         PetscCall(PetscStrlcat(tmpname, fname, sizeof(tmpname)));
312         PetscCall(PetscStrallocpy(tmpname, &names[l]));
313       }
314     }
315   }
316   PetscCall(PetscDrawLGSetLegend(lg, (const char *const *)names));
317   /* Just add P_1 support for now */
318   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
319   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
320   PetscCall(VecGetArrayRead(coordinates, &coords));
321   for (i = 0; i < n; ++i) PetscCall(VecGetArrayRead(u[i], &sol[i]));
322   for (v = vStart; v < vEnd; ++v) {
323     PetscScalar *x, *svals;
324 
325     PetscCall(DMPlexPointLocalRead(dm, v, coords, &x));
326     for (i = 0; i < n; ++i) {
327       PetscCall(DMPlexPointLocalRead(dm, v, sol[i], &svals));
328       for (l = 0; l < Nl; ++l) vals[i * Nl + l] = PetscRealPart(svals[l]);
329     }
330     PetscCall(PetscDrawLGAddCommonPoint(lg, PetscRealPart(x[0]), vals));
331   }
332   PetscCall(VecRestoreArrayRead(coordinates, &coords));
333   for (i = 0; i < n; ++i) PetscCall(VecRestoreArrayRead(u[i], &sol[i]));
334   for (l = 0; l < n * Nl; ++l) PetscCall(PetscFree(names[l]));
335   PetscCall(PetscFree3(sol, names, vals));
336 
337   PetscCall(PetscDrawLGDraw(lg));
338   PetscCall(PetscDrawLGDestroy(&lg));
339   PetscFunctionReturn(PETSC_SUCCESS);
340 }
341 
342 static PetscErrorCode VecView_Plex_Local_Draw_1D(Vec u, PetscViewer viewer)
343 {
344   DM dm;
345 
346   PetscFunctionBegin;
347   PetscCall(VecGetDM(u, &dm));
348   PetscCall(DMPlexVecView1D(dm, 1, &u, viewer));
349   PetscFunctionReturn(PETSC_SUCCESS);
350 }
351 
352 static PetscErrorCode VecView_Plex_Local_Draw_2D(Vec v, PetscViewer viewer)
353 {
354   DM                 dm;
355   PetscSection       s;
356   PetscDraw          draw, popup;
357   DM                 cdm;
358   PetscSection       coordSection;
359   Vec                coordinates;
360   const PetscScalar *array;
361   PetscReal          lbound[3], ubound[3];
362   PetscReal          vbound[2], time;
363   PetscBool          flg;
364   PetscInt           dim, Nf, f, Nc, comp, vStart, vEnd, cStart, cEnd, c, N, level, step, w = 0;
365   const char        *name;
366   char               title[PETSC_MAX_PATH_LEN];
367 
368   PetscFunctionBegin;
369   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
370   PetscCall(VecGetDM(v, &dm));
371   PetscCall(DMGetCoordinateDim(dm, &dim));
372   PetscCall(DMGetLocalSection(dm, &s));
373   PetscCall(PetscSectionGetNumFields(s, &Nf));
374   PetscCall(DMGetCoarsenLevel(dm, &level));
375   PetscCall(DMGetCoordinateDM(dm, &cdm));
376   PetscCall(DMGetLocalSection(cdm, &coordSection));
377   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
378   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
379   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
380 
381   PetscCall(PetscObjectGetName((PetscObject)v, &name));
382   PetscCall(DMGetOutputSequenceNumber(dm, &step, &time));
383 
384   PetscCall(VecGetLocalSize(coordinates, &N));
385   PetscCall(DMGetBoundingBox(dm, lbound, ubound));
386   PetscCall(PetscDrawClear(draw));
387 
388   /* Could implement something like DMDASelectFields() */
389   for (f = 0; f < Nf; ++f) {
390     DM          fdm = dm;
391     Vec         fv  = v;
392     IS          fis;
393     char        prefix[PETSC_MAX_PATH_LEN];
394     const char *fname;
395 
396     PetscCall(PetscSectionGetFieldComponents(s, f, &Nc));
397     PetscCall(PetscSectionGetFieldName(s, f, &fname));
398 
399     if (v->hdr.prefix) PetscCall(PetscStrncpy(prefix, v->hdr.prefix, sizeof(prefix)));
400     else prefix[0] = '\0';
401     if (Nf > 1) {
402       PetscCall(DMCreateSubDM(dm, 1, &f, &fis, &fdm));
403       PetscCall(VecGetSubVector(v, fis, &fv));
404       PetscCall(PetscStrlcat(prefix, fname, sizeof(prefix)));
405       PetscCall(PetscStrlcat(prefix, "_", sizeof(prefix)));
406     }
407     for (comp = 0; comp < Nc; ++comp, ++w) {
408       PetscInt nmax = 2;
409 
410       PetscCall(PetscViewerDrawGetDraw(viewer, w, &draw));
411       if (Nc > 1) PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s_%" PetscInt_FMT " Step: %" PetscInt_FMT " Time: %.4g", name, fname, comp, step, (double)time));
412       else PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s Step: %" PetscInt_FMT " Time: %.4g", name, fname, step, (double)time));
413       PetscCall(PetscDrawSetTitle(draw, title));
414 
415       /* TODO Get max and min only for this component */
416       PetscCall(PetscOptionsGetRealArray(NULL, prefix, "-vec_view_bounds", vbound, &nmax, &flg));
417       if (!flg) {
418         PetscCall(VecMin(fv, NULL, &vbound[0]));
419         PetscCall(VecMax(fv, NULL, &vbound[1]));
420         if (vbound[1] <= vbound[0]) vbound[1] = vbound[0] + 1.0;
421       }
422 
423       PetscCall(PetscDrawGetPopup(draw, &popup));
424       PetscCall(PetscDrawScalePopup(popup, vbound[0], vbound[1]));
425       PetscCall(PetscDrawSetCoordinates(draw, lbound[0], lbound[1], ubound[0], ubound[1]));
426       PetscCall(VecGetArrayRead(fv, &array));
427       for (c = cStart; c < cEnd; ++c) {
428         PetscScalar       *coords = NULL, *a = NULL;
429         const PetscScalar *coords_arr;
430         PetscBool          isDG;
431         PetscInt           numCoords, color[4] = {-1, -1, -1, -1};
432 
433         PetscCall(DMPlexPointLocalRead(fdm, c, array, &a));
434         if (a) {
435           color[0] = PetscDrawRealToColor(PetscRealPart(a[comp]), vbound[0], vbound[1]);
436           color[1] = color[2] = color[3] = color[0];
437         } else {
438           PetscScalar *vals = NULL;
439           PetscInt     numVals, va;
440 
441           PetscCall(DMPlexVecGetClosure(fdm, NULL, fv, c, &numVals, &vals));
442           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);
443           switch (numVals / Nc) {
444           case 3: /* P1 Triangle */
445           case 4: /* P1 Quadrangle */
446             for (va = 0; va < numVals / Nc; ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp]), vbound[0], vbound[1]);
447             break;
448           case 6: /* P2 Triangle */
449           case 8: /* P2 Quadrangle */
450             for (va = 0; va < numVals / (Nc * 2); ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp + numVals / (Nc * 2)]), vbound[0], vbound[1]);
451             break;
452           default:
453             SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of values for cell closure %" PetscInt_FMT " cannot be handled", numVals / Nc);
454           }
455           PetscCall(DMPlexVecRestoreClosure(fdm, NULL, fv, c, &numVals, &vals));
456         }
457         PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
458         switch (numCoords) {
459         case 6:
460         case 12: /* Localized triangle */
461           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]));
462           break;
463         case 8:
464         case 16: /* Localized quadrilateral */
465           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]));
466           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]));
467           break;
468         default:
469           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells with %" PetscInt_FMT " coordinates", numCoords);
470         }
471         PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
472       }
473       PetscCall(VecRestoreArrayRead(fv, &array));
474       PetscCall(PetscDrawFlush(draw));
475       PetscCall(PetscDrawPause(draw));
476       PetscCall(PetscDrawSave(draw));
477     }
478     if (Nf > 1) {
479       PetscCall(VecRestoreSubVector(v, fis, &fv));
480       PetscCall(ISDestroy(&fis));
481       PetscCall(DMDestroy(&fdm));
482     }
483   }
484   PetscFunctionReturn(PETSC_SUCCESS);
485 }
486 
487 static PetscErrorCode VecView_Plex_Local_Draw(Vec v, PetscViewer viewer)
488 {
489   DM        dm;
490   PetscDraw draw;
491   PetscInt  dim;
492   PetscBool isnull;
493 
494   PetscFunctionBegin;
495   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
496   PetscCall(PetscDrawIsNull(draw, &isnull));
497   if (isnull) PetscFunctionReturn(PETSC_SUCCESS);
498 
499   PetscCall(VecGetDM(v, &dm));
500   PetscCall(DMGetCoordinateDim(dm, &dim));
501   switch (dim) {
502   case 1:
503     PetscCall(VecView_Plex_Local_Draw_1D(v, viewer));
504     break;
505   case 2:
506     PetscCall(VecView_Plex_Local_Draw_2D(v, viewer));
507     break;
508   default:
509     SETERRQ(PetscObjectComm((PetscObject)v), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT ". Try PETSCVIEWERGLVIS", dim);
510   }
511   PetscFunctionReturn(PETSC_SUCCESS);
512 }
513 
514 static PetscErrorCode VecView_Plex_Local_VTK(Vec v, PetscViewer viewer)
515 {
516   DM                      dm;
517   Vec                     locv;
518   const char             *name;
519   PetscSection            section;
520   PetscInt                pStart, pEnd;
521   PetscInt                numFields;
522   PetscViewerVTKFieldType ft;
523 
524   PetscFunctionBegin;
525   PetscCall(VecGetDM(v, &dm));
526   PetscCall(DMCreateLocalVector(dm, &locv)); /* VTK viewer requires exclusive ownership of the vector */
527   PetscCall(PetscObjectGetName((PetscObject)v, &name));
528   PetscCall(PetscObjectSetName((PetscObject)locv, name));
529   PetscCall(VecCopy(v, locv));
530   PetscCall(DMGetLocalSection(dm, &section));
531   PetscCall(PetscSectionGetNumFields(section, &numFields));
532   if (!numFields) {
533     PetscCall(DMPlexGetFieldType_Internal(dm, section, PETSC_DETERMINE, &pStart, &pEnd, &ft));
534     PetscCall(PetscViewerVTKAddField(viewer, (PetscObject)dm, DMPlexVTKWriteAll, PETSC_DEFAULT, ft, PETSC_TRUE, (PetscObject)locv));
535   } else {
536     PetscInt f;
537 
538     for (f = 0; f < numFields; f++) {
539       PetscCall(DMPlexGetFieldType_Internal(dm, section, f, &pStart, &pEnd, &ft));
540       if (ft == PETSC_VTK_INVALID) continue;
541       PetscCall(PetscObjectReference((PetscObject)locv));
542       PetscCall(PetscViewerVTKAddField(viewer, (PetscObject)dm, DMPlexVTKWriteAll, f, ft, PETSC_TRUE, (PetscObject)locv));
543     }
544     PetscCall(VecDestroy(&locv));
545   }
546   PetscFunctionReturn(PETSC_SUCCESS);
547 }
548 
549 PetscErrorCode VecView_Plex_Local(Vec v, PetscViewer viewer)
550 {
551   DM        dm;
552   PetscBool isvtk, ishdf5, isdraw, isglvis, iscgns;
553 
554   PetscFunctionBegin;
555   PetscCall(VecGetDM(v, &dm));
556   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
557   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
558   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
559   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
560   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
561   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
562   if (isvtk || ishdf5 || isdraw || isglvis || iscgns) {
563     PetscInt    i, numFields;
564     PetscObject fe;
565     PetscBool   fem  = PETSC_FALSE;
566     Vec         locv = v;
567     const char *name;
568     PetscInt    step;
569     PetscReal   time;
570 
571     PetscCall(DMGetNumFields(dm, &numFields));
572     for (i = 0; i < numFields; i++) {
573       PetscCall(DMGetField(dm, i, NULL, &fe));
574       if (fe->classid == PETSCFE_CLASSID) {
575         fem = PETSC_TRUE;
576         break;
577       }
578     }
579     if (fem) {
580       PetscObject isZero;
581 
582       PetscCall(DMGetLocalVector(dm, &locv));
583       PetscCall(PetscObjectGetName((PetscObject)v, &name));
584       PetscCall(PetscObjectSetName((PetscObject)locv, name));
585       PetscCall(PetscObjectQuery((PetscObject)v, "__Vec_bc_zero__", &isZero));
586       PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", isZero));
587       PetscCall(VecCopy(v, locv));
588       PetscCall(DMGetOutputSequenceNumber(dm, NULL, &time));
589       PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_TRUE, locv, time, NULL, NULL, NULL));
590     }
591     if (isvtk) {
592       PetscCall(VecView_Plex_Local_VTK(locv, viewer));
593     } else if (ishdf5) {
594 #if defined(PETSC_HAVE_HDF5)
595       PetscCall(VecView_Plex_Local_HDF5_Internal(locv, viewer));
596 #else
597       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
598 #endif
599     } else if (isdraw) {
600       PetscCall(VecView_Plex_Local_Draw(locv, viewer));
601     } else if (isglvis) {
602       PetscCall(DMGetOutputSequenceNumber(dm, &step, NULL));
603       PetscCall(PetscViewerGLVisSetSnapId(viewer, step));
604       PetscCall(VecView_GLVis(locv, viewer));
605     } else if (iscgns) {
606 #if defined(PETSC_HAVE_CGNS)
607       PetscCall(VecView_Plex_Local_CGNS(locv, viewer));
608 #else
609       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "CGNS not supported in this build.\nPlease reconfigure using --download-cgns");
610 #endif
611     }
612     if (fem) {
613       PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", NULL));
614       PetscCall(DMRestoreLocalVector(dm, &locv));
615     }
616   } else {
617     PetscBool isseq;
618 
619     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
620     if (isseq) PetscCall(VecView_Seq(v, viewer));
621     else PetscCall(VecView_MPI(v, viewer));
622   }
623   PetscFunctionReturn(PETSC_SUCCESS);
624 }
625 
626 PetscErrorCode VecView_Plex(Vec v, PetscViewer viewer)
627 {
628   DM        dm;
629   PetscBool isvtk, ishdf5, isdraw, isglvis, isexodusii, iscgns;
630 
631   PetscFunctionBegin;
632   PetscCall(VecGetDM(v, &dm));
633   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
634   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
635   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
636   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
637   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
638   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
639   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodusii));
640   if (isvtk || isdraw || isglvis || iscgns) {
641     Vec         locv;
642     PetscObject isZero;
643     const char *name;
644 
645     PetscCall(DMGetLocalVector(dm, &locv));
646     PetscCall(PetscObjectGetName((PetscObject)v, &name));
647     PetscCall(PetscObjectSetName((PetscObject)locv, name));
648     PetscCall(DMGlobalToLocalBegin(dm, v, INSERT_VALUES, locv));
649     PetscCall(DMGlobalToLocalEnd(dm, v, INSERT_VALUES, locv));
650     PetscCall(PetscObjectQuery((PetscObject)v, "__Vec_bc_zero__", &isZero));
651     PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", isZero));
652     PetscCall(VecView_Plex_Local(locv, viewer));
653     PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", NULL));
654     PetscCall(DMRestoreLocalVector(dm, &locv));
655   } else if (ishdf5) {
656 #if defined(PETSC_HAVE_HDF5)
657     PetscCall(VecView_Plex_HDF5_Internal(v, viewer));
658 #else
659     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
660 #endif
661   } else if (isexodusii) {
662 #if defined(PETSC_HAVE_EXODUSII)
663     PetscCall(VecView_PlexExodusII_Internal(v, viewer));
664 #else
665     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
666 #endif
667   } else {
668     PetscBool isseq;
669 
670     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
671     if (isseq) PetscCall(VecView_Seq(v, viewer));
672     else PetscCall(VecView_MPI(v, viewer));
673   }
674   PetscFunctionReturn(PETSC_SUCCESS);
675 }
676 
677 PetscErrorCode VecView_Plex_Native(Vec originalv, PetscViewer viewer)
678 {
679   DM                dm;
680   MPI_Comm          comm;
681   PetscViewerFormat format;
682   Vec               v;
683   PetscBool         isvtk, ishdf5;
684 
685   PetscFunctionBegin;
686   PetscCall(VecGetDM(originalv, &dm));
687   PetscCall(PetscObjectGetComm((PetscObject)originalv, &comm));
688   PetscCheck(dm, comm, PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
689   PetscCall(PetscViewerGetFormat(viewer, &format));
690   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
691   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
692   if (format == PETSC_VIEWER_NATIVE) {
693     /* Natural ordering is the common case for DMDA, NATIVE means plain vector, for PLEX is the opposite */
694     /* this need a better fix */
695     if (dm->useNatural) {
696       if (dm->sfNatural) {
697         const char *vecname;
698         PetscInt    n, nroots;
699 
700         PetscCall(VecGetLocalSize(originalv, &n));
701         PetscCall(PetscSFGetGraph(dm->sfNatural, &nroots, NULL, NULL, NULL));
702         if (n == nroots) {
703           PetscCall(DMPlexCreateNaturalVector(dm, &v));
704           PetscCall(DMPlexGlobalToNaturalBegin(dm, originalv, v));
705           PetscCall(DMPlexGlobalToNaturalEnd(dm, originalv, v));
706           PetscCall(PetscObjectGetName((PetscObject)originalv, &vecname));
707           PetscCall(PetscObjectSetName((PetscObject)v, vecname));
708         } else SETERRQ(comm, PETSC_ERR_ARG_WRONG, "DM global to natural SF only handles global vectors");
709       } else SETERRQ(comm, PETSC_ERR_ARG_WRONGSTATE, "DM global to natural SF was not created");
710     } else v = originalv;
711   } else v = originalv;
712 
713   if (ishdf5) {
714 #if defined(PETSC_HAVE_HDF5)
715     PetscCall(VecView_Plex_HDF5_Native_Internal(v, viewer));
716 #else
717     SETERRQ(comm, PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
718 #endif
719   } else if (isvtk) {
720     SETERRQ(comm, PETSC_ERR_SUP, "VTK format does not support viewing in natural order. Please switch to HDF5.");
721   } else {
722     PetscBool isseq;
723 
724     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
725     if (isseq) PetscCall(VecView_Seq(v, viewer));
726     else PetscCall(VecView_MPI(v, viewer));
727   }
728   if (v != originalv) PetscCall(VecDestroy(&v));
729   PetscFunctionReturn(PETSC_SUCCESS);
730 }
731 
732 PetscErrorCode VecLoad_Plex_Local(Vec v, PetscViewer viewer)
733 {
734   DM        dm;
735   PetscBool ishdf5;
736 
737   PetscFunctionBegin;
738   PetscCall(VecGetDM(v, &dm));
739   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
740   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
741   if (ishdf5) {
742     DM          dmBC;
743     Vec         gv;
744     const char *name;
745 
746     PetscCall(DMGetOutputDM(dm, &dmBC));
747     PetscCall(DMGetGlobalVector(dmBC, &gv));
748     PetscCall(PetscObjectGetName((PetscObject)v, &name));
749     PetscCall(PetscObjectSetName((PetscObject)gv, name));
750     PetscCall(VecLoad_Default(gv, viewer));
751     PetscCall(DMGlobalToLocalBegin(dmBC, gv, INSERT_VALUES, v));
752     PetscCall(DMGlobalToLocalEnd(dmBC, gv, INSERT_VALUES, v));
753     PetscCall(DMRestoreGlobalVector(dmBC, &gv));
754   } else PetscCall(VecLoad_Default(v, viewer));
755   PetscFunctionReturn(PETSC_SUCCESS);
756 }
757 
758 PetscErrorCode VecLoad_Plex(Vec v, PetscViewer viewer)
759 {
760   DM        dm;
761   PetscBool ishdf5, isexodusii;
762 
763   PetscFunctionBegin;
764   PetscCall(VecGetDM(v, &dm));
765   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
766   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
767   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodusii));
768   if (ishdf5) {
769 #if defined(PETSC_HAVE_HDF5)
770     PetscCall(VecLoad_Plex_HDF5_Internal(v, viewer));
771 #else
772     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
773 #endif
774   } else if (isexodusii) {
775 #if defined(PETSC_HAVE_EXODUSII)
776     PetscCall(VecLoad_PlexExodusII_Internal(v, viewer));
777 #else
778     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
779 #endif
780   } else PetscCall(VecLoad_Default(v, viewer));
781   PetscFunctionReturn(PETSC_SUCCESS);
782 }
783 
784 PetscErrorCode VecLoad_Plex_Native(Vec originalv, PetscViewer viewer)
785 {
786   DM                dm;
787   PetscViewerFormat format;
788   PetscBool         ishdf5;
789 
790   PetscFunctionBegin;
791   PetscCall(VecGetDM(originalv, &dm));
792   PetscCheck(dm, PetscObjectComm((PetscObject)originalv), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
793   PetscCall(PetscViewerGetFormat(viewer, &format));
794   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
795   if (format == PETSC_VIEWER_NATIVE) {
796     if (dm->useNatural) {
797       if (dm->sfNatural) {
798         if (ishdf5) {
799 #if defined(PETSC_HAVE_HDF5)
800           Vec         v;
801           const char *vecname;
802 
803           PetscCall(DMPlexCreateNaturalVector(dm, &v));
804           PetscCall(PetscObjectGetName((PetscObject)originalv, &vecname));
805           PetscCall(PetscObjectSetName((PetscObject)v, vecname));
806           PetscCall(VecLoad_Plex_HDF5_Native_Internal(v, viewer));
807           PetscCall(DMPlexNaturalToGlobalBegin(dm, v, originalv));
808           PetscCall(DMPlexNaturalToGlobalEnd(dm, v, originalv));
809           PetscCall(VecDestroy(&v));
810 #else
811           SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
812 #endif
813         } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Reading in natural order is not supported for anything but HDF5.");
814       }
815     } else PetscCall(VecLoad_Default(originalv, viewer));
816   }
817   PetscFunctionReturn(PETSC_SUCCESS);
818 }
819 
820 PETSC_UNUSED static PetscErrorCode DMPlexView_Ascii_Geometry(DM dm, PetscViewer viewer)
821 {
822   PetscSection       coordSection;
823   Vec                coordinates;
824   DMLabel            depthLabel, celltypeLabel;
825   const char        *name[4];
826   const PetscScalar *a;
827   PetscInt           dim, pStart, pEnd, cStart, cEnd, c;
828 
829   PetscFunctionBegin;
830   PetscCall(DMGetDimension(dm, &dim));
831   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
832   PetscCall(DMGetCoordinateSection(dm, &coordSection));
833   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
834   PetscCall(DMPlexGetCellTypeLabel(dm, &celltypeLabel));
835   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
836   PetscCall(PetscSectionGetChart(coordSection, &pStart, &pEnd));
837   PetscCall(VecGetArrayRead(coordinates, &a));
838   name[0]       = "vertex";
839   name[1]       = "edge";
840   name[dim - 1] = "face";
841   name[dim]     = "cell";
842   for (c = cStart; c < cEnd; ++c) {
843     PetscInt *closure = NULL;
844     PetscInt  closureSize, cl, ct;
845 
846     PetscCall(DMLabelGetValue(celltypeLabel, c, &ct));
847     PetscCall(PetscViewerASCIIPrintf(viewer, "Geometry for cell %" PetscInt_FMT " polytope type %s:\n", c, DMPolytopeTypes[ct]));
848     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
849     PetscCall(PetscViewerASCIIPushTab(viewer));
850     for (cl = 0; cl < closureSize * 2; cl += 2) {
851       PetscInt point = closure[cl], depth, dof, off, d, p;
852 
853       if ((point < pStart) || (point >= pEnd)) continue;
854       PetscCall(PetscSectionGetDof(coordSection, point, &dof));
855       if (!dof) continue;
856       PetscCall(DMLabelGetValue(depthLabel, point, &depth));
857       PetscCall(PetscSectionGetOffset(coordSection, point, &off));
858       PetscCall(PetscViewerASCIIPrintf(viewer, "%s %" PetscInt_FMT " coords:", name[depth], point));
859       for (p = 0; p < dof / dim; ++p) {
860         PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
861         for (d = 0; d < dim; ++d) {
862           if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
863           PetscCall(PetscViewerASCIIPrintf(viewer, "%g", (double)PetscRealPart(a[off + p * dim + d])));
864         }
865         PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
866       }
867       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
868     }
869     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
870     PetscCall(PetscViewerASCIIPopTab(viewer));
871   }
872   PetscCall(VecRestoreArrayRead(coordinates, &a));
873   PetscFunctionReturn(PETSC_SUCCESS);
874 }
875 
876 typedef enum {
877   CS_CARTESIAN,
878   CS_POLAR,
879   CS_CYLINDRICAL,
880   CS_SPHERICAL
881 } CoordSystem;
882 const char *CoordSystems[] = {"cartesian", "polar", "cylindrical", "spherical", "CoordSystem", "CS_", NULL};
883 
884 static PetscErrorCode DMPlexView_Ascii_Coordinates(PetscViewer viewer, CoordSystem cs, PetscInt dim, const PetscScalar x[])
885 {
886   PetscInt i;
887 
888   PetscFunctionBegin;
889   if (dim > 3) {
890     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double)PetscRealPart(x[i])));
891   } else {
892     PetscReal coords[3], trcoords[3] = {0., 0., 0.};
893 
894     for (i = 0; i < dim; ++i) coords[i] = PetscRealPart(x[i]);
895     switch (cs) {
896     case CS_CARTESIAN:
897       for (i = 0; i < dim; ++i) trcoords[i] = coords[i];
898       break;
899     case CS_POLAR:
900       PetscCheck(dim == 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Polar coordinates are for 2 dimension, not %" PetscInt_FMT, dim);
901       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
902       trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
903       break;
904     case CS_CYLINDRICAL:
905       PetscCheck(dim == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cylindrical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
906       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
907       trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
908       trcoords[2] = coords[2];
909       break;
910     case CS_SPHERICAL:
911       PetscCheck(dim == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Spherical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
912       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]) + PetscSqr(coords[2]));
913       trcoords[1] = PetscAtan2Real(PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1])), coords[2]);
914       trcoords[2] = PetscAtan2Real(coords[1], coords[0]);
915       break;
916     }
917     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double)trcoords[i]));
918   }
919   PetscFunctionReturn(PETSC_SUCCESS);
920 }
921 
922 static PetscErrorCode DMPlexView_Ascii(DM dm, PetscViewer viewer)
923 {
924   DM_Plex          *mesh = (DM_Plex *)dm->data;
925   DM                cdm, cdmCell;
926   PetscSection      coordSection, coordSectionCell;
927   Vec               coordinates, coordinatesCell;
928   PetscViewerFormat format;
929 
930   PetscFunctionBegin;
931   PetscCall(PetscViewerGetFormat(viewer, &format));
932   if (format == PETSC_VIEWER_ASCII_INFO_DETAIL) {
933     const char *name;
934     PetscInt    dim, cellHeight, maxConeSize, maxSupportSize;
935     PetscInt    pStart, pEnd, p, numLabels, l;
936     PetscMPIInt rank, size;
937 
938     PetscCall(DMGetCoordinateDM(dm, &cdm));
939     PetscCall(DMGetCoordinateSection(dm, &coordSection));
940     PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
941     PetscCall(DMGetCellCoordinateDM(dm, &cdmCell));
942     PetscCall(DMGetCellCoordinateSection(dm, &coordSectionCell));
943     PetscCall(DMGetCellCoordinatesLocal(dm, &coordinatesCell));
944     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
945     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
946     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
947     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
948     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
949     PetscCall(DMGetDimension(dm, &dim));
950     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
951     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
952     else PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
953     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
954     PetscCall(PetscViewerASCIIPrintf(viewer, "Supports:\n"));
955     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
956     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max support size: %" PetscInt_FMT "\n", rank, maxSupportSize));
957     for (p = pStart; p < pEnd; ++p) {
958       PetscInt dof, off, s;
959 
960       PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
961       PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
962       for (s = off; s < off + dof; ++s) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d]: %" PetscInt_FMT " ----> %" PetscInt_FMT "\n", rank, p, mesh->supports[s]));
963     }
964     PetscCall(PetscViewerFlush(viewer));
965     PetscCall(PetscViewerASCIIPrintf(viewer, "Cones:\n"));
966     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max cone size: %" PetscInt_FMT "\n", rank, maxConeSize));
967     for (p = pStart; p < pEnd; ++p) {
968       PetscInt dof, off, c;
969 
970       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
971       PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
972       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]));
973     }
974     PetscCall(PetscViewerFlush(viewer));
975     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
976     if (coordSection && coordinates) {
977       CoordSystem        cs = CS_CARTESIAN;
978       const PetscScalar *array, *arrayCell = NULL;
979       PetscInt           Nf, Nc, pvStart, pvEnd, pcStart = PETSC_MAX_INT, pcEnd = PETSC_MIN_INT, pStart, pEnd, p;
980       PetscMPIInt        rank;
981       const char        *name;
982 
983       PetscCall(PetscOptionsGetEnum(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_coord_system", CoordSystems, (PetscEnum *)&cs, NULL));
984       PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)viewer), &rank));
985       PetscCall(PetscSectionGetNumFields(coordSection, &Nf));
986       PetscCheck(Nf == 1, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Coordinate section should have 1 field, not %" PetscInt_FMT, Nf);
987       PetscCall(PetscSectionGetFieldComponents(coordSection, 0, &Nc));
988       PetscCall(PetscSectionGetChart(coordSection, &pvStart, &pvEnd));
989       if (coordSectionCell) PetscCall(PetscSectionGetChart(coordSectionCell, &pcStart, &pcEnd));
990       pStart = PetscMin(pvStart, pcStart);
991       pEnd   = PetscMax(pvEnd, pcEnd);
992       PetscCall(PetscObjectGetName((PetscObject)coordinates, &name));
993       PetscCall(PetscViewerASCIIPrintf(viewer, "%s with %" PetscInt_FMT " fields\n", name, Nf));
994       PetscCall(PetscViewerASCIIPrintf(viewer, "  field 0 with %" PetscInt_FMT " components\n", Nc));
995       if (cs != CS_CARTESIAN) PetscCall(PetscViewerASCIIPrintf(viewer, "  output coordinate system: %s\n", CoordSystems[cs]));
996 
997       PetscCall(VecGetArrayRead(coordinates, &array));
998       if (coordinatesCell) PetscCall(VecGetArrayRead(coordinatesCell, &arrayCell));
999       PetscCall(PetscViewerASCIIPushSynchronized(viewer));
1000       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "Process %d:\n", rank));
1001       for (p = pStart; p < pEnd; ++p) {
1002         PetscInt dof, off;
1003 
1004         if (p >= pvStart && p < pvEnd) {
1005           PetscCall(PetscSectionGetDof(coordSection, p, &dof));
1006           PetscCall(PetscSectionGetOffset(coordSection, p, &off));
1007           if (dof) {
1008             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dim %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
1009             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &array[off]));
1010             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
1011           }
1012         }
1013         if (cdmCell && p >= pcStart && p < pcEnd) {
1014           PetscCall(PetscSectionGetDof(coordSectionCell, p, &dof));
1015           PetscCall(PetscSectionGetOffset(coordSectionCell, p, &off));
1016           if (dof) {
1017             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dim %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
1018             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &arrayCell[off]));
1019             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
1020           }
1021         }
1022       }
1023       PetscCall(PetscViewerFlush(viewer));
1024       PetscCall(PetscViewerASCIIPopSynchronized(viewer));
1025       PetscCall(VecRestoreArrayRead(coordinates, &array));
1026       if (coordinatesCell) PetscCall(VecRestoreArrayRead(coordinatesCell, &arrayCell));
1027     }
1028     PetscCall(DMGetNumLabels(dm, &numLabels));
1029     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
1030     for (l = 0; l < numLabels; ++l) {
1031       DMLabel     label;
1032       PetscBool   isdepth;
1033       const char *name;
1034 
1035       PetscCall(DMGetLabelName(dm, l, &name));
1036       PetscCall(PetscStrcmp(name, "depth", &isdepth));
1037       if (isdepth) continue;
1038       PetscCall(DMGetLabel(dm, name, &label));
1039       PetscCall(DMLabelView(label, viewer));
1040     }
1041     if (size > 1) {
1042       PetscSF sf;
1043 
1044       PetscCall(DMGetPointSF(dm, &sf));
1045       PetscCall(PetscSFView(sf, viewer));
1046     }
1047     if (mesh->periodic.face_sf) PetscCall(PetscSFView(mesh->periodic.face_sf, viewer));
1048     PetscCall(PetscViewerFlush(viewer));
1049   } else if (format == PETSC_VIEWER_ASCII_LATEX) {
1050     const char  *name, *color;
1051     const char  *defcolors[3]  = {"gray", "orange", "green"};
1052     const char  *deflcolors[4] = {"blue", "cyan", "red", "magenta"};
1053     char         lname[PETSC_MAX_PATH_LEN];
1054     PetscReal    scale      = 2.0;
1055     PetscReal    tikzscale  = 1.0;
1056     PetscBool    useNumbers = PETSC_TRUE, drawNumbers[4], drawColors[4], useLabels, useColors, plotEdges, drawHasse = PETSC_FALSE;
1057     double       tcoords[3];
1058     PetscScalar *coords;
1059     PetscInt     numLabels, l, numColors, numLColors, dim, d, depth, cStart, cEnd, c, vStart, vEnd, v, eStart = 0, eEnd = 0, e, p, n;
1060     PetscMPIInt  rank, size;
1061     char       **names, **colors, **lcolors;
1062     PetscBool    flg, lflg;
1063     PetscBT      wp = NULL;
1064     PetscInt     pEnd, pStart;
1065 
1066     PetscCall(DMGetCoordinateDM(dm, &cdm));
1067     PetscCall(DMGetCoordinateSection(dm, &coordSection));
1068     PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1069     PetscCall(DMGetCellCoordinateDM(dm, &cdmCell));
1070     PetscCall(DMGetCellCoordinateSection(dm, &coordSectionCell));
1071     PetscCall(DMGetCellCoordinatesLocal(dm, &coordinatesCell));
1072     PetscCall(DMGetDimension(dm, &dim));
1073     PetscCall(DMPlexGetDepth(dm, &depth));
1074     PetscCall(DMGetNumLabels(dm, &numLabels));
1075     numLabels  = PetscMax(numLabels, 10);
1076     numColors  = 10;
1077     numLColors = 10;
1078     PetscCall(PetscCalloc3(numLabels, &names, numColors, &colors, numLColors, &lcolors));
1079     PetscCall(PetscOptionsGetReal(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_scale", &scale, NULL));
1080     PetscCall(PetscOptionsGetReal(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_tikzscale", &tikzscale, NULL));
1081     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_numbers", &useNumbers, NULL));
1082     for (d = 0; d < 4; ++d) drawNumbers[d] = useNumbers;
1083     for (d = 0; d < 4; ++d) drawColors[d] = PETSC_TRUE;
1084     n = 4;
1085     PetscCall(PetscOptionsGetBoolArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_numbers_depth", drawNumbers, &n, &flg));
1086     PetscCheck(!flg || n == dim + 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim + 1);
1087     n = 4;
1088     PetscCall(PetscOptionsGetBoolArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_colors_depth", drawColors, &n, &flg));
1089     PetscCheck(!flg || n == dim + 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim + 1);
1090     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_labels", names, &numLabels, &useLabels));
1091     if (!useLabels) numLabels = 0;
1092     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_colors", colors, &numColors, &useColors));
1093     if (!useColors) {
1094       numColors = 3;
1095       for (c = 0; c < numColors; ++c) PetscCall(PetscStrallocpy(defcolors[c], &colors[c]));
1096     }
1097     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_lcolors", lcolors, &numLColors, &useColors));
1098     if (!useColors) {
1099       numLColors = 4;
1100       for (c = 0; c < numLColors; ++c) PetscCall(PetscStrallocpy(deflcolors[c], &lcolors[c]));
1101     }
1102     PetscCall(PetscOptionsGetString(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_label_filter", lname, sizeof(lname), &lflg));
1103     plotEdges = (PetscBool)(depth > 1 && drawNumbers[1] && dim < 3);
1104     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_edges", &plotEdges, &flg));
1105     PetscCheck(!flg || !plotEdges || depth >= dim, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Mesh must be interpolated");
1106     if (depth < dim) plotEdges = PETSC_FALSE;
1107     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_hasse", &drawHasse, NULL));
1108 
1109     /* filter points with labelvalue != labeldefaultvalue */
1110     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
1111     PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1112     PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
1113     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1114     if (lflg) {
1115       DMLabel lbl;
1116 
1117       PetscCall(DMGetLabel(dm, lname, &lbl));
1118       if (lbl) {
1119         PetscInt val, defval;
1120 
1121         PetscCall(DMLabelGetDefaultValue(lbl, &defval));
1122         PetscCall(PetscBTCreate(pEnd - pStart, &wp));
1123         for (c = pStart; c < pEnd; c++) {
1124           PetscInt *closure = NULL;
1125           PetscInt  closureSize;
1126 
1127           PetscCall(DMLabelGetValue(lbl, c, &val));
1128           if (val == defval) continue;
1129 
1130           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1131           for (p = 0; p < closureSize * 2; p += 2) PetscCall(PetscBTSet(wp, closure[p] - pStart));
1132           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1133         }
1134       }
1135     }
1136 
1137     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1138     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
1139     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1140     PetscCall(PetscViewerASCIIPrintf(viewer, "\
1141 \\documentclass[tikz]{standalone}\n\n\
1142 \\usepackage{pgflibraryshapes}\n\
1143 \\usetikzlibrary{backgrounds}\n\
1144 \\usetikzlibrary{arrows}\n\
1145 \\begin{document}\n"));
1146     if (size > 1) {
1147       PetscCall(PetscViewerASCIIPrintf(viewer, "%s for process ", name));
1148       for (p = 0; p < size; ++p) {
1149         if (p) PetscCall(PetscViewerASCIIPrintf(viewer, (p == size - 1) ? ", and " : ", "));
1150         PetscCall(PetscViewerASCIIPrintf(viewer, "{\\textcolor{%s}%" PetscInt_FMT "}", colors[p % numColors], p));
1151       }
1152       PetscCall(PetscViewerASCIIPrintf(viewer, ".\n\n\n"));
1153     }
1154     if (drawHasse) {
1155       PetscInt maxStratum = PetscMax(vEnd - vStart, PetscMax(eEnd - eStart, cEnd - cStart));
1156 
1157       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vStart}{%" PetscInt_FMT "}\n", vStart));
1158       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vEnd}{%" PetscInt_FMT "}\n", vEnd - 1));
1159       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numVertices}{%" PetscInt_FMT "}\n", vEnd - vStart));
1160       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vShift}{%.2f}\n", 3 + (maxStratum - (vEnd - vStart)) / 2.));
1161       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eStart}{%" PetscInt_FMT "}\n", eStart));
1162       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eEnd}{%" PetscInt_FMT "}\n", eEnd - 1));
1163       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eShift}{%.2f}\n", 3 + (maxStratum - (eEnd - eStart)) / 2.));
1164       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numEdges}{%" PetscInt_FMT "}\n", eEnd - eStart));
1165       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cStart}{%" PetscInt_FMT "}\n", cStart));
1166       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cEnd}{%" PetscInt_FMT "}\n", cEnd - 1));
1167       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numCells}{%" PetscInt_FMT "}\n", cEnd - cStart));
1168       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cShift}{%.2f}\n", 3 + (maxStratum - (cEnd - cStart)) / 2.));
1169     }
1170     PetscCall(PetscViewerASCIIPrintf(viewer, "\\begin{tikzpicture}[scale = %g,font=\\fontsize{8}{8}\\selectfont]\n", (double)tikzscale));
1171 
1172     /* Plot vertices */
1173     PetscCall(VecGetArray(coordinates, &coords));
1174     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
1175     for (v = vStart; v < vEnd; ++v) {
1176       PetscInt  off, dof, d;
1177       PetscBool isLabeled = PETSC_FALSE;
1178 
1179       if (wp && !PetscBTLookup(wp, v - pStart)) continue;
1180       PetscCall(PetscSectionGetDof(coordSection, v, &dof));
1181       PetscCall(PetscSectionGetOffset(coordSection, v, &off));
1182       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1183       PetscCheck(dof <= 3, PETSC_COMM_SELF, PETSC_ERR_PLIB, "coordSection vertex %" PetscInt_FMT " has dof %" PetscInt_FMT " > 3", v, dof);
1184       for (d = 0; d < dof; ++d) {
1185         tcoords[d] = (double)(scale * PetscRealPart(coords[off + d]));
1186         tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1187       }
1188       /* Rotate coordinates since PGF makes z point out of the page instead of up */
1189       if (dim == 3) {
1190         PetscReal tmp = tcoords[1];
1191         tcoords[1]    = tcoords[2];
1192         tcoords[2]    = -tmp;
1193       }
1194       for (d = 0; d < dof; ++d) {
1195         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1196         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)tcoords[d]));
1197       }
1198       if (drawHasse) color = colors[0 % numColors];
1199       else color = colors[rank % numColors];
1200       for (l = 0; l < numLabels; ++l) {
1201         PetscInt val;
1202         PetscCall(DMGetLabelValue(dm, names[l], v, &val));
1203         if (val >= 0) {
1204           color     = lcolors[l % numLColors];
1205           isLabeled = PETSC_TRUE;
1206           break;
1207         }
1208       }
1209       if (drawNumbers[0]) {
1210         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", v, rank, color, v));
1211       } else if (drawColors[0]) {
1212         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", v, rank, !isLabeled ? 1 : 2, color));
1213       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", v, rank));
1214     }
1215     PetscCall(VecRestoreArray(coordinates, &coords));
1216     PetscCall(PetscViewerFlush(viewer));
1217     /* Plot edges */
1218     if (plotEdges) {
1219       PetscCall(VecGetArray(coordinates, &coords));
1220       PetscCall(PetscViewerASCIIPrintf(viewer, "\\path\n"));
1221       for (e = eStart; e < eEnd; ++e) {
1222         const PetscInt *cone;
1223         PetscInt        coneSize, offA, offB, dof, d;
1224 
1225         if (wp && !PetscBTLookup(wp, e - pStart)) continue;
1226         PetscCall(DMPlexGetConeSize(dm, e, &coneSize));
1227         PetscCheck(coneSize == 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Edge %" PetscInt_FMT " cone should have two vertices, not %" PetscInt_FMT, e, coneSize);
1228         PetscCall(DMPlexGetCone(dm, e, &cone));
1229         PetscCall(PetscSectionGetDof(coordSection, cone[0], &dof));
1230         PetscCall(PetscSectionGetOffset(coordSection, cone[0], &offA));
1231         PetscCall(PetscSectionGetOffset(coordSection, cone[1], &offB));
1232         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "("));
1233         for (d = 0; d < dof; ++d) {
1234           tcoords[d] = (double)(0.5 * scale * PetscRealPart(coords[offA + d] + coords[offB + d]));
1235           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1236         }
1237         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1238         if (dim == 3) {
1239           PetscReal tmp = tcoords[1];
1240           tcoords[1]    = tcoords[2];
1241           tcoords[2]    = -tmp;
1242         }
1243         for (d = 0; d < dof; ++d) {
1244           if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1245           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)tcoords[d]));
1246         }
1247         if (drawHasse) color = colors[1 % numColors];
1248         else color = colors[rank % numColors];
1249         for (l = 0; l < numLabels; ++l) {
1250           PetscInt val;
1251           PetscCall(DMGetLabelValue(dm, names[l], e, &val));
1252           if (val >= 0) {
1253             color = lcolors[l % numLColors];
1254             break;
1255           }
1256         }
1257         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "} --\n", e, rank, color, e));
1258       }
1259       PetscCall(VecRestoreArray(coordinates, &coords));
1260       PetscCall(PetscViewerFlush(viewer));
1261       PetscCall(PetscViewerASCIIPrintf(viewer, "(0,0);\n"));
1262     }
1263     /* Plot cells */
1264     if (dim == 3 || !drawNumbers[1]) {
1265       for (e = eStart; e < eEnd; ++e) {
1266         const PetscInt *cone;
1267 
1268         if (wp && !PetscBTLookup(wp, e - pStart)) continue;
1269         color = colors[rank % numColors];
1270         for (l = 0; l < numLabels; ++l) {
1271           PetscInt val;
1272           PetscCall(DMGetLabelValue(dm, names[l], e, &val));
1273           if (val >= 0) {
1274             color = lcolors[l % numLColors];
1275             break;
1276           }
1277         }
1278         PetscCall(DMPlexGetCone(dm, e, &cone));
1279         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", color, cone[0], rank, cone[1], rank));
1280       }
1281     } else {
1282       DMPolytopeType ct;
1283 
1284       /* Drawing a 2D polygon */
1285       for (c = cStart; c < cEnd; ++c) {
1286         if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1287         PetscCall(DMPlexGetCellType(dm, c, &ct));
1288         if (DMPolytopeTypeIsHybrid(ct)) {
1289           const PetscInt *cone;
1290           PetscInt        coneSize, e;
1291 
1292           PetscCall(DMPlexGetCone(dm, c, &cone));
1293           PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
1294           for (e = 0; e < coneSize; ++e) {
1295             const PetscInt *econe;
1296 
1297             PetscCall(DMPlexGetCone(dm, cone[e], &econe));
1298             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));
1299           }
1300         } else {
1301           PetscInt *closure = NULL;
1302           PetscInt  closureSize, Nv = 0, v;
1303 
1304           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1305           for (p = 0; p < closureSize * 2; p += 2) {
1306             const PetscInt point = closure[p];
1307 
1308             if ((point >= vStart) && (point < vEnd)) closure[Nv++] = point;
1309           }
1310           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] ", colors[rank % numColors]));
1311           for (v = 0; v <= Nv; ++v) {
1312             const PetscInt vertex = closure[v % Nv];
1313 
1314             if (v > 0) {
1315               if (plotEdges) {
1316                 const PetscInt *edge;
1317                 PetscInt        endpoints[2], ne;
1318 
1319                 endpoints[0] = closure[v - 1];
1320                 endpoints[1] = vertex;
1321                 PetscCall(DMPlexGetJoin(dm, 2, endpoints, &ne, &edge));
1322                 PetscCheck(ne == 1, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Could not find edge for vertices %" PetscInt_FMT ", %" PetscInt_FMT, endpoints[0], endpoints[1]);
1323                 PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- (%" PetscInt_FMT "_%d) -- ", edge[0], rank));
1324                 PetscCall(DMPlexRestoreJoin(dm, 2, endpoints, &ne, &edge));
1325               } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- "));
1326             }
1327             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "(%" PetscInt_FMT "_%d)", vertex, rank));
1328           }
1329           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ";\n"));
1330           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1331         }
1332       }
1333     }
1334     for (c = cStart; c < cEnd; ++c) {
1335       double             ccoords[3] = {0.0, 0.0, 0.0};
1336       PetscBool          isLabeled  = PETSC_FALSE;
1337       PetscScalar       *cellCoords = NULL;
1338       const PetscScalar *array;
1339       PetscInt           numCoords, cdim, d;
1340       PetscBool          isDG;
1341 
1342       if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1343       PetscCall(DMGetCoordinateDim(dm, &cdim));
1344       PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1345       PetscCheck(!(numCoords % cdim), PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "coordinate dim %" PetscInt_FMT " does not divide numCoords %" PetscInt_FMT, cdim, numCoords);
1346       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1347       for (p = 0; p < numCoords / cdim; ++p) {
1348         for (d = 0; d < cdim; ++d) {
1349           tcoords[d] = (double)(scale * PetscRealPart(cellCoords[p * cdim + d]));
1350           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1351         }
1352         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1353         if (cdim == 3) {
1354           PetscReal tmp = tcoords[1];
1355           tcoords[1]    = tcoords[2];
1356           tcoords[2]    = -tmp;
1357         }
1358         for (d = 0; d < dim; ++d) ccoords[d] += tcoords[d];
1359       }
1360       for (d = 0; d < cdim; ++d) ccoords[d] /= (numCoords / cdim);
1361       PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1362       for (d = 0; d < cdim; ++d) {
1363         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1364         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)ccoords[d]));
1365       }
1366       if (drawHasse) color = colors[depth % numColors];
1367       else color = colors[rank % numColors];
1368       for (l = 0; l < numLabels; ++l) {
1369         PetscInt val;
1370         PetscCall(DMGetLabelValue(dm, names[l], c, &val));
1371         if (val >= 0) {
1372           color     = lcolors[l % numLColors];
1373           isLabeled = PETSC_TRUE;
1374           break;
1375         }
1376       }
1377       if (drawNumbers[dim]) {
1378         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", c, rank, color, c));
1379       } else if (drawColors[dim]) {
1380         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", c, rank, !isLabeled ? 1 : 2, color));
1381       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", c, rank));
1382     }
1383     if (drawHasse) {
1384       color = colors[depth % numColors];
1385       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Cells\n"));
1386       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\c in {\\cStart,...,\\cEnd}\n"));
1387       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1388       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\c_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\cShift+\\c-\\cStart,0) {\\c};\n", rank, color));
1389       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1390 
1391       color = colors[1 % numColors];
1392       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Edges\n"));
1393       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\e in {\\eStart,...,\\eEnd}\n"));
1394       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1395       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\e_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\eShift+\\e-\\eStart,1) {\\e};\n", rank, color));
1396       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1397 
1398       color = colors[0 % numColors];
1399       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Vertices\n"));
1400       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\v in {\\vStart,...,\\vEnd}\n"));
1401       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1402       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\v_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\vShift+\\v-\\vStart,2) {\\v};\n", rank, color));
1403       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1404 
1405       for (p = pStart; p < pEnd; ++p) {
1406         const PetscInt *cone;
1407         PetscInt        coneSize, cp;
1408 
1409         PetscCall(DMPlexGetCone(dm, p, &cone));
1410         PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
1411         for (cp = 0; cp < coneSize; ++cp) PetscCall(PetscViewerASCIIPrintf(viewer, "\\draw[->, shorten >=1pt] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", cone[cp], rank, p, rank));
1412       }
1413     }
1414     PetscCall(PetscViewerFlush(viewer));
1415     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
1416     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{tikzpicture}\n"));
1417     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{document}\n"));
1418     for (l = 0; l < numLabels; ++l) PetscCall(PetscFree(names[l]));
1419     for (c = 0; c < numColors; ++c) PetscCall(PetscFree(colors[c]));
1420     for (c = 0; c < numLColors; ++c) PetscCall(PetscFree(lcolors[c]));
1421     PetscCall(PetscFree3(names, colors, lcolors));
1422     PetscCall(PetscBTDestroy(&wp));
1423   } else if (format == PETSC_VIEWER_LOAD_BALANCE) {
1424     Vec                    cown, acown;
1425     VecScatter             sct;
1426     ISLocalToGlobalMapping g2l;
1427     IS                     gid, acis;
1428     MPI_Comm               comm, ncomm = MPI_COMM_NULL;
1429     MPI_Group              ggroup, ngroup;
1430     PetscScalar           *array, nid;
1431     const PetscInt        *idxs;
1432     PetscInt              *idxs2, *start, *adjacency, *work;
1433     PetscInt64             lm[3], gm[3];
1434     PetscInt               i, c, cStart, cEnd, cum, numVertices, ect, ectn, cellHeight;
1435     PetscMPIInt            d1, d2, rank;
1436 
1437     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1438     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1439 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1440     PetscCallMPI(MPI_Comm_split_type(comm, MPI_COMM_TYPE_SHARED, rank, MPI_INFO_NULL, &ncomm));
1441 #endif
1442     if (ncomm != MPI_COMM_NULL) {
1443       PetscCallMPI(MPI_Comm_group(comm, &ggroup));
1444       PetscCallMPI(MPI_Comm_group(ncomm, &ngroup));
1445       d1 = 0;
1446       PetscCallMPI(MPI_Group_translate_ranks(ngroup, 1, &d1, ggroup, &d2));
1447       nid = d2;
1448       PetscCallMPI(MPI_Group_free(&ggroup));
1449       PetscCallMPI(MPI_Group_free(&ngroup));
1450       PetscCallMPI(MPI_Comm_free(&ncomm));
1451     } else nid = 0.0;
1452 
1453     /* Get connectivity */
1454     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1455     PetscCall(DMPlexCreatePartitionerGraph(dm, cellHeight, &numVertices, &start, &adjacency, &gid));
1456 
1457     /* filter overlapped local cells */
1458     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
1459     PetscCall(ISGetIndices(gid, &idxs));
1460     PetscCall(ISGetLocalSize(gid, &cum));
1461     PetscCall(PetscMalloc1(cum, &idxs2));
1462     for (c = cStart, cum = 0; c < cEnd; c++) {
1463       if (idxs[c - cStart] < 0) continue;
1464       idxs2[cum++] = idxs[c - cStart];
1465     }
1466     PetscCall(ISRestoreIndices(gid, &idxs));
1467     PetscCheck(numVertices == cum, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Unexpected %" PetscInt_FMT " != %" PetscInt_FMT, numVertices, cum);
1468     PetscCall(ISDestroy(&gid));
1469     PetscCall(ISCreateGeneral(comm, numVertices, idxs2, PETSC_OWN_POINTER, &gid));
1470 
1471     /* support for node-aware cell locality */
1472     PetscCall(ISCreateGeneral(comm, start[numVertices], adjacency, PETSC_USE_POINTER, &acis));
1473     PetscCall(VecCreateSeq(PETSC_COMM_SELF, start[numVertices], &acown));
1474     PetscCall(VecCreateMPI(comm, numVertices, PETSC_DECIDE, &cown));
1475     PetscCall(VecGetArray(cown, &array));
1476     for (c = 0; c < numVertices; c++) array[c] = nid;
1477     PetscCall(VecRestoreArray(cown, &array));
1478     PetscCall(VecScatterCreate(cown, acis, acown, NULL, &sct));
1479     PetscCall(VecScatterBegin(sct, cown, acown, INSERT_VALUES, SCATTER_FORWARD));
1480     PetscCall(VecScatterEnd(sct, cown, acown, INSERT_VALUES, SCATTER_FORWARD));
1481     PetscCall(ISDestroy(&acis));
1482     PetscCall(VecScatterDestroy(&sct));
1483     PetscCall(VecDestroy(&cown));
1484 
1485     /* compute edgeCut */
1486     for (c = 0, cum = 0; c < numVertices; c++) cum = PetscMax(cum, start[c + 1] - start[c]);
1487     PetscCall(PetscMalloc1(cum, &work));
1488     PetscCall(ISLocalToGlobalMappingCreateIS(gid, &g2l));
1489     PetscCall(ISLocalToGlobalMappingSetType(g2l, ISLOCALTOGLOBALMAPPINGHASH));
1490     PetscCall(ISDestroy(&gid));
1491     PetscCall(VecGetArray(acown, &array));
1492     for (c = 0, ect = 0, ectn = 0; c < numVertices; c++) {
1493       PetscInt totl;
1494 
1495       totl = start[c + 1] - start[c];
1496       PetscCall(ISGlobalToLocalMappingApply(g2l, IS_GTOLM_MASK, totl, adjacency + start[c], NULL, work));
1497       for (i = 0; i < totl; i++) {
1498         if (work[i] < 0) {
1499           ect += 1;
1500           ectn += (array[i + start[c]] != nid) ? 0 : 1;
1501         }
1502       }
1503     }
1504     PetscCall(PetscFree(work));
1505     PetscCall(VecRestoreArray(acown, &array));
1506     lm[0] = numVertices > 0 ? numVertices : PETSC_MAX_INT;
1507     lm[1] = -numVertices;
1508     PetscCall(MPIU_Allreduce(lm, gm, 2, MPIU_INT64, MPI_MIN, comm));
1509     PetscCall(PetscViewerASCIIPrintf(viewer, "  Cell balance: %.2f (max %" PetscInt_FMT ", min %" PetscInt_FMT, -((double)gm[1]) / ((double)gm[0]), -(PetscInt)gm[1], (PetscInt)gm[0]));
1510     lm[0] = ect;                     /* edgeCut */
1511     lm[1] = ectn;                    /* node-aware edgeCut */
1512     lm[2] = numVertices > 0 ? 0 : 1; /* empty processes */
1513     PetscCall(MPIU_Allreduce(lm, gm, 3, MPIU_INT64, MPI_SUM, comm));
1514     PetscCall(PetscViewerASCIIPrintf(viewer, ", empty %" PetscInt_FMT ")\n", (PetscInt)gm[2]));
1515 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1516     PetscCall(PetscViewerASCIIPrintf(viewer, "  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n", (PetscInt)(gm[0] / 2), gm[0] ? ((double)(gm[1])) / ((double)gm[0]) : 1.));
1517 #else
1518     PetscCall(PetscViewerASCIIPrintf(viewer, "  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n", (PetscInt)(gm[0] / 2), 0.0));
1519 #endif
1520     PetscCall(ISLocalToGlobalMappingDestroy(&g2l));
1521     PetscCall(PetscFree(start));
1522     PetscCall(PetscFree(adjacency));
1523     PetscCall(VecDestroy(&acown));
1524   } else {
1525     const char    *name;
1526     PetscInt      *sizes, *hybsizes, *ghostsizes;
1527     PetscInt       locDepth, depth, cellHeight, dim, d;
1528     PetscInt       pStart, pEnd, p, gcStart, gcEnd, gcNum;
1529     PetscInt       numLabels, l, maxSize = 17;
1530     DMPolytopeType ct0 = DM_POLYTOPE_UNKNOWN;
1531     MPI_Comm       comm;
1532     PetscMPIInt    size, rank;
1533 
1534     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1535     PetscCallMPI(MPI_Comm_size(comm, &size));
1536     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1537     PetscCall(DMGetDimension(dm, &dim));
1538     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1539     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1540     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
1541     else PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
1542     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
1543     PetscCall(DMPlexGetDepth(dm, &locDepth));
1544     PetscCall(MPIU_Allreduce(&locDepth, &depth, 1, MPIU_INT, MPI_MAX, comm));
1545     PetscCall(DMPlexGetCellTypeStratum(dm, DM_POLYTOPE_FV_GHOST, &gcStart, &gcEnd));
1546     gcNum = gcEnd - gcStart;
1547     if (size < maxSize) PetscCall(PetscCalloc3(size, &sizes, size, &hybsizes, size, &ghostsizes));
1548     else PetscCall(PetscCalloc3(3, &sizes, 3, &hybsizes, 3, &ghostsizes));
1549     for (d = 0; d <= depth; d++) {
1550       PetscInt Nc[2] = {0, 0}, ict;
1551 
1552       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
1553       if (pStart < pEnd) PetscCall(DMPlexGetCellType(dm, pStart, &ct0));
1554       ict = ct0;
1555       PetscCallMPI(MPI_Bcast(&ict, 1, MPIU_INT, 0, comm));
1556       ct0 = (DMPolytopeType)ict;
1557       for (p = pStart; p < pEnd; ++p) {
1558         DMPolytopeType ct;
1559 
1560         PetscCall(DMPlexGetCellType(dm, p, &ct));
1561         if (ct == ct0) ++Nc[0];
1562         else ++Nc[1];
1563       }
1564       if (size < maxSize) {
1565         PetscCallMPI(MPI_Gather(&Nc[0], 1, MPIU_INT, sizes, 1, MPIU_INT, 0, comm));
1566         PetscCallMPI(MPI_Gather(&Nc[1], 1, MPIU_INT, hybsizes, 1, MPIU_INT, 0, comm));
1567         if (d == depth) PetscCallMPI(MPI_Gather(&gcNum, 1, MPIU_INT, ghostsizes, 1, MPIU_INT, 0, comm));
1568         PetscCall(PetscViewerASCIIPrintf(viewer, "  Number of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1569         for (p = 0; p < size; ++p) {
1570           if (rank == 0) {
1571             PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT, sizes[p] + hybsizes[p]));
1572             if (hybsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT ")", hybsizes[p]));
1573             if (ghostsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "]", ghostsizes[p]));
1574           }
1575         }
1576       } else {
1577         PetscInt locMinMax[2];
1578 
1579         locMinMax[0] = Nc[0] + Nc[1];
1580         locMinMax[1] = Nc[0] + Nc[1];
1581         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, sizes));
1582         locMinMax[0] = Nc[1];
1583         locMinMax[1] = Nc[1];
1584         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, hybsizes));
1585         if (d == depth) {
1586           locMinMax[0] = gcNum;
1587           locMinMax[1] = gcNum;
1588           PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, ghostsizes));
1589         }
1590         PetscCall(PetscViewerASCIIPrintf(viewer, "  Min/Max of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1591         PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT "/%" PetscInt_FMT, sizes[0], sizes[1]));
1592         if (hybsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT "/%" PetscInt_FMT ")", hybsizes[0], hybsizes[1]));
1593         if (ghostsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "/%" PetscInt_FMT "]", ghostsizes[0], ghostsizes[1]));
1594       }
1595       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
1596     }
1597     PetscCall(PetscFree3(sizes, hybsizes, ghostsizes));
1598     {
1599       const PetscReal *maxCell;
1600       const PetscReal *L;
1601       PetscBool        localized;
1602 
1603       PetscCall(DMGetPeriodicity(dm, &maxCell, NULL, &L));
1604       PetscCall(DMGetCoordinatesLocalized(dm, &localized));
1605       if (L || localized) {
1606         PetscCall(PetscViewerASCIIPrintf(viewer, "Periodic mesh"));
1607         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1608         if (L) {
1609           PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
1610           for (d = 0; d < dim; ++d) {
1611             if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1612             PetscCall(PetscViewerASCIIPrintf(viewer, "%s", L[d] > 0.0 ? "PERIODIC" : "NONE"));
1613           }
1614           PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
1615         }
1616         PetscCall(PetscViewerASCIIPrintf(viewer, " coordinates %s\n", localized ? "localized" : "not localized"));
1617         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1618       }
1619     }
1620     PetscCall(DMGetNumLabels(dm, &numLabels));
1621     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
1622     for (l = 0; l < numLabels; ++l) {
1623       DMLabel         label;
1624       const char     *name;
1625       IS              valueIS;
1626       const PetscInt *values;
1627       PetscInt        numValues, v;
1628 
1629       PetscCall(DMGetLabelName(dm, l, &name));
1630       PetscCall(DMGetLabel(dm, name, &label));
1631       PetscCall(DMLabelGetNumValues(label, &numValues));
1632       PetscCall(PetscViewerASCIIPrintf(viewer, "  %s: %" PetscInt_FMT " strata with value/size (", name, numValues));
1633       PetscCall(DMLabelGetValueIS(label, &valueIS));
1634       PetscCall(ISGetIndices(valueIS, &values));
1635       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1636       for (v = 0; v < numValues; ++v) {
1637         PetscInt size;
1638 
1639         PetscCall(DMLabelGetStratumSize(label, values[v], &size));
1640         if (v > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1641         PetscCall(PetscViewerASCIIPrintf(viewer, "%" PetscInt_FMT " (%" PetscInt_FMT ")", values[v], size));
1642       }
1643       PetscCall(PetscViewerASCIIPrintf(viewer, ")\n"));
1644       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1645       PetscCall(ISRestoreIndices(valueIS, &values));
1646       PetscCall(ISDestroy(&valueIS));
1647     }
1648     {
1649       char    **labelNames;
1650       PetscInt  Nl = numLabels;
1651       PetscBool flg;
1652 
1653       PetscCall(PetscMalloc1(Nl, &labelNames));
1654       PetscCall(PetscOptionsGetStringArray(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_plex_view_labels", labelNames, &Nl, &flg));
1655       for (l = 0; l < Nl; ++l) {
1656         DMLabel label;
1657 
1658         PetscCall(DMHasLabel(dm, labelNames[l], &flg));
1659         if (flg) {
1660           PetscCall(DMGetLabel(dm, labelNames[l], &label));
1661           PetscCall(DMLabelView(label, viewer));
1662         }
1663         PetscCall(PetscFree(labelNames[l]));
1664       }
1665       PetscCall(PetscFree(labelNames));
1666     }
1667     /* If no fields are specified, people do not want to see adjacency */
1668     if (dm->Nf) {
1669       PetscInt f;
1670 
1671       for (f = 0; f < dm->Nf; ++f) {
1672         const char *name;
1673 
1674         PetscCall(PetscObjectGetName(dm->fields[f].disc, &name));
1675         if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Field %s:\n", name));
1676         PetscCall(PetscViewerASCIIPushTab(viewer));
1677         if (dm->fields[f].label) PetscCall(DMLabelView(dm->fields[f].label, viewer));
1678         if (dm->fields[f].adjacency[0]) {
1679           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM++\n"));
1680           else PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM\n"));
1681         } else {
1682           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FEM\n"));
1683           else PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FUNKY\n"));
1684         }
1685         PetscCall(PetscViewerASCIIPopTab(viewer));
1686       }
1687     }
1688     PetscCall(DMGetCoarseDM(dm, &cdm));
1689     if (cdm) {
1690       PetscCall(PetscViewerASCIIPushTab(viewer));
1691       PetscCall(PetscViewerASCIIPrintf(viewer, "Defined by transform from:\n"));
1692       PetscCall(DMPlexView_Ascii(cdm, viewer));
1693       PetscCall(PetscViewerASCIIPopTab(viewer));
1694     }
1695   }
1696   PetscFunctionReturn(PETSC_SUCCESS);
1697 }
1698 
1699 static PetscErrorCode DMPlexDrawCell(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[])
1700 {
1701   DMPolytopeType ct;
1702   PetscMPIInt    rank;
1703   PetscInt       cdim;
1704 
1705   PetscFunctionBegin;
1706   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1707   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1708   PetscCall(DMGetCoordinateDim(dm, &cdim));
1709   switch (ct) {
1710   case DM_POLYTOPE_SEGMENT:
1711   case DM_POLYTOPE_POINT_PRISM_TENSOR:
1712     switch (cdim) {
1713     case 1: {
1714       const PetscReal y  = 0.5;  /* TODO Put it in the middle of the viewport */
1715       const PetscReal dy = 0.05; /* TODO Make it a fraction of the total length */
1716 
1717       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y, PetscRealPart(coords[1]), y, PETSC_DRAW_BLACK));
1718       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y + dy, PetscRealPart(coords[0]), y - dy, PETSC_DRAW_BLACK));
1719       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[1]), y + dy, PetscRealPart(coords[1]), y - dy, PETSC_DRAW_BLACK));
1720     } break;
1721     case 2: {
1722       const PetscReal dx = (PetscRealPart(coords[3]) - PetscRealPart(coords[1]));
1723       const PetscReal dy = (PetscRealPart(coords[2]) - PetscRealPart(coords[0]));
1724       const PetscReal l  = 0.1 / PetscSqrtReal(dx * dx + dy * dy);
1725 
1726       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1727       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));
1728       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));
1729     } break;
1730     default:
1731       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of dimension %" PetscInt_FMT, cdim);
1732     }
1733     break;
1734   case DM_POLYTOPE_TRIANGLE:
1735     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));
1736     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1737     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1738     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1739     break;
1740   case DM_POLYTOPE_QUADRILATERAL:
1741     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));
1742     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));
1743     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1744     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1745     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_BLACK));
1746     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1747     break;
1748   case DM_POLYTOPE_SEG_PRISM_TENSOR:
1749     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));
1750     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));
1751     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1752     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_BLACK));
1753     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1754     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1755     break;
1756   case DM_POLYTOPE_FV_GHOST:
1757     break;
1758   default:
1759     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1760   }
1761   PetscFunctionReturn(PETSC_SUCCESS);
1762 }
1763 
1764 static PetscErrorCode DMPlexDrawCellHighOrder(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[], PetscInt edgeDiv, PetscReal refCoords[], PetscReal edgeCoords[])
1765 {
1766   DMPolytopeType ct;
1767   PetscReal      centroid[2] = {0., 0.};
1768   PetscMPIInt    rank;
1769   PetscInt       fillColor, v, e, d;
1770 
1771   PetscFunctionBegin;
1772   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1773   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1774   fillColor = PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2;
1775   switch (ct) {
1776   case DM_POLYTOPE_TRIANGLE: {
1777     PetscReal refVertices[6] = {-1., -1., 1., -1., -1., 1.};
1778 
1779     for (v = 0; v < 3; ++v) {
1780       centroid[0] += PetscRealPart(coords[v * 2 + 0]) / 3.;
1781       centroid[1] += PetscRealPart(coords[v * 2 + 1]) / 3.;
1782     }
1783     for (e = 0; e < 3; ++e) {
1784       refCoords[0] = refVertices[e * 2 + 0];
1785       refCoords[1] = refVertices[e * 2 + 1];
1786       for (d = 1; d <= edgeDiv; ++d) {
1787         refCoords[d * 2 + 0] = refCoords[0] + (refVertices[(e + 1) % 3 * 2 + 0] - refCoords[0]) * d / edgeDiv;
1788         refCoords[d * 2 + 1] = refCoords[1] + (refVertices[(e + 1) % 3 * 2 + 1] - refCoords[1]) * d / edgeDiv;
1789       }
1790       PetscCall(DMPlexReferenceToCoordinates(dm, cell, edgeDiv + 1, refCoords, edgeCoords));
1791       for (d = 0; d < edgeDiv; ++d) {
1792         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));
1793         PetscCall(PetscDrawLine(draw, edgeCoords[d * 2 + 0], edgeCoords[d * 2 + 1], edgeCoords[(d + 1) * 2 + 0], edgeCoords[(d + 1) * 2 + 1], PETSC_DRAW_BLACK));
1794       }
1795     }
1796   } break;
1797   default:
1798     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1799   }
1800   PetscFunctionReturn(PETSC_SUCCESS);
1801 }
1802 
1803 static PetscErrorCode DMPlexView_Draw(DM dm, PetscViewer viewer)
1804 {
1805   PetscDraw    draw;
1806   DM           cdm;
1807   PetscSection coordSection;
1808   Vec          coordinates;
1809   PetscReal    xyl[3], xyr[3];
1810   PetscReal   *refCoords, *edgeCoords;
1811   PetscBool    isnull, drawAffine = PETSC_TRUE;
1812   PetscInt     dim, vStart, vEnd, cStart, cEnd, c, edgeDiv = 4;
1813 
1814   PetscFunctionBegin;
1815   PetscCall(DMGetCoordinateDim(dm, &dim));
1816   PetscCheck(dim <= 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT, dim);
1817   PetscCall(PetscOptionsGetBool(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_view_draw_affine", &drawAffine, NULL));
1818   if (!drawAffine) PetscCall(PetscMalloc2((edgeDiv + 1) * dim, &refCoords, (edgeDiv + 1) * dim, &edgeCoords));
1819   PetscCall(DMGetCoordinateDM(dm, &cdm));
1820   PetscCall(DMGetLocalSection(cdm, &coordSection));
1821   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1822   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1823   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1824 
1825   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
1826   PetscCall(PetscDrawIsNull(draw, &isnull));
1827   if (isnull) PetscFunctionReturn(PETSC_SUCCESS);
1828   PetscCall(PetscDrawSetTitle(draw, "Mesh"));
1829 
1830   PetscCall(DMGetBoundingBox(dm, xyl, xyr));
1831   PetscCall(PetscDrawSetCoordinates(draw, xyl[0], xyl[1], xyr[0], xyr[1]));
1832   PetscCall(PetscDrawClear(draw));
1833 
1834   for (c = cStart; c < cEnd; ++c) {
1835     PetscScalar       *coords = NULL;
1836     const PetscScalar *coords_arr;
1837     PetscInt           numCoords;
1838     PetscBool          isDG;
1839 
1840     PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
1841     if (drawAffine) PetscCall(DMPlexDrawCell(dm, draw, c, coords));
1842     else PetscCall(DMPlexDrawCellHighOrder(dm, draw, c, coords, edgeDiv, refCoords, edgeCoords));
1843     PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
1844   }
1845   if (!drawAffine) PetscCall(PetscFree2(refCoords, edgeCoords));
1846   PetscCall(PetscDrawFlush(draw));
1847   PetscCall(PetscDrawPause(draw));
1848   PetscCall(PetscDrawSave(draw));
1849   PetscFunctionReturn(PETSC_SUCCESS);
1850 }
1851 
1852 static PetscErrorCode DMPlexCreateHighOrderSurrogate_Internal(DM dm, DM *hdm)
1853 {
1854   DM           odm = dm, rdm = dm, cdm;
1855   PetscFE      fe;
1856   PetscSpace   sp;
1857   PetscClassId id;
1858   PetscInt     degree;
1859   PetscBool    hoView = PETSC_TRUE;
1860 
1861   PetscFunctionBegin;
1862   PetscObjectOptionsBegin((PetscObject)dm);
1863   PetscCall(PetscOptionsBool("-dm_plex_high_order_view", "Subsample to view meshes with high order coordinates", "DMPlexCreateHighOrderSurrogate_Internal", hoView, &hoView, NULL));
1864   PetscOptionsEnd();
1865   PetscCall(PetscObjectReference((PetscObject)dm));
1866   *hdm = dm;
1867   if (!hoView) PetscFunctionReturn(PETSC_SUCCESS);
1868   PetscCall(DMGetCoordinateDM(dm, &cdm));
1869   PetscCall(DMGetField(cdm, 0, NULL, (PetscObject *)&fe));
1870   PetscCall(PetscObjectGetClassId((PetscObject)fe, &id));
1871   if (id != PETSCFE_CLASSID) PetscFunctionReturn(PETSC_SUCCESS);
1872   PetscCall(PetscFEGetBasisSpace(fe, &sp));
1873   PetscCall(PetscSpaceGetDegree(sp, &degree, NULL));
1874   for (PetscInt r = 0, rd = PetscCeilReal(((PetscReal)degree) / 2.); r < (PetscInt)PetscCeilReal(PetscLog2Real(degree)); ++r, rd = PetscCeilReal(((PetscReal)rd) / 2.)) {
1875     DM  cdm, rcdm;
1876     Mat In;
1877     Vec cl, rcl;
1878 
1879     PetscCall(DMRefine(odm, PetscObjectComm((PetscObject)odm), &rdm));
1880     if (rd > 1) PetscCall(DMPlexCreateCoordinateSpace(rdm, rd, PETSC_FALSE, NULL));
1881     PetscCall(PetscObjectSetName((PetscObject)rdm, "Refined Mesh with Linear Coordinates"));
1882     PetscCall(DMGetCoordinateDM(odm, &cdm));
1883     PetscCall(DMGetCoordinateDM(rdm, &rcdm));
1884     PetscCall(DMGetCoordinatesLocal(odm, &cl));
1885     PetscCall(DMGetCoordinatesLocal(rdm, &rcl));
1886     PetscCall(DMSetCoarseDM(rcdm, cdm));
1887     PetscCall(DMCreateInterpolation(cdm, rcdm, &In, NULL));
1888     PetscCall(MatMult(In, cl, rcl));
1889     PetscCall(MatDestroy(&In));
1890     PetscCall(DMSetCoordinatesLocal(rdm, rcl));
1891     PetscCall(DMDestroy(&odm));
1892     odm = rdm;
1893   }
1894   *hdm = rdm;
1895   PetscFunctionReturn(PETSC_SUCCESS);
1896 }
1897 
1898 #if defined(PETSC_HAVE_EXODUSII)
1899   #include <exodusII.h>
1900   #include <petscviewerexodusii.h>
1901 #endif
1902 
1903 PetscErrorCode DMView_Plex(DM dm, PetscViewer viewer)
1904 {
1905   PetscBool iascii, ishdf5, isvtk, isdraw, flg, isglvis, isexodus, iscgns;
1906   char      name[PETSC_MAX_PATH_LEN];
1907 
1908   PetscFunctionBegin;
1909   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1910   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1911   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &iascii));
1912   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
1913   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1914   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
1915   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
1916   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodus));
1917   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
1918   if (iascii) {
1919     PetscViewerFormat format;
1920     PetscCall(PetscViewerGetFormat(viewer, &format));
1921     if (format == PETSC_VIEWER_ASCII_GLVIS) PetscCall(DMPlexView_GLVis(dm, viewer));
1922     else PetscCall(DMPlexView_Ascii(dm, viewer));
1923   } else if (ishdf5) {
1924 #if defined(PETSC_HAVE_HDF5)
1925     PetscCall(DMPlexView_HDF5_Internal(dm, viewer));
1926 #else
1927     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1928 #endif
1929   } else if (isvtk) {
1930     PetscCall(DMPlexVTKWriteAll((PetscObject)dm, viewer));
1931   } else if (isdraw) {
1932     DM hdm;
1933 
1934     PetscCall(DMPlexCreateHighOrderSurrogate_Internal(dm, &hdm));
1935     PetscCall(DMPlexView_Draw(hdm, viewer));
1936     PetscCall(DMDestroy(&hdm));
1937   } else if (isglvis) {
1938     PetscCall(DMPlexView_GLVis(dm, viewer));
1939 #if defined(PETSC_HAVE_EXODUSII)
1940   } else if (isexodus) {
1941     /*
1942       exodusII requires that all sets be part of exactly one cell set.
1943       If the dm does not have a "Cell Sets" label defined, we create one
1944       with ID 1, containing all cells.
1945       Note that if the Cell Sets label is defined but does not cover all cells,
1946       we may still have a problem. This should probably be checked here or in the viewer;
1947     */
1948     PetscInt numCS;
1949     PetscCall(DMGetLabelSize(dm, "Cell Sets", &numCS));
1950     if (!numCS) {
1951       PetscInt cStart, cEnd, c;
1952       PetscCall(DMCreateLabel(dm, "Cell Sets"));
1953       PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1954       for (c = cStart; c < cEnd; ++c) PetscCall(DMSetLabelValue(dm, "Cell Sets", c, 1));
1955     }
1956     PetscCall(DMView_PlexExodusII(dm, viewer));
1957 #endif
1958 #if defined(PETSC_HAVE_CGNS)
1959   } else if (iscgns) {
1960     PetscCall(DMView_PlexCGNS(dm, viewer));
1961 #endif
1962   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex writing", ((PetscObject)viewer)->type_name);
1963   /* Optionally view the partition */
1964   PetscCall(PetscOptionsHasName(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_partition_view", &flg));
1965   if (flg) {
1966     Vec ranks;
1967     PetscCall(DMPlexCreateRankField(dm, &ranks));
1968     PetscCall(VecView(ranks, viewer));
1969     PetscCall(VecDestroy(&ranks));
1970   }
1971   /* Optionally view a label */
1972   PetscCall(PetscOptionsGetString(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_label_view", name, sizeof(name), &flg));
1973   if (flg) {
1974     DMLabel label;
1975     Vec     val;
1976 
1977     PetscCall(DMGetLabel(dm, name, &label));
1978     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Label %s provided to -dm_label_view does not exist in this DM", name);
1979     PetscCall(DMPlexCreateLabelField(dm, label, &val));
1980     PetscCall(VecView(val, viewer));
1981     PetscCall(VecDestroy(&val));
1982   }
1983   PetscFunctionReturn(PETSC_SUCCESS);
1984 }
1985 
1986 /*@
1987   DMPlexTopologyView - Saves a `DMPLEX` topology into a file
1988 
1989   Collective
1990 
1991   Input Parameters:
1992 + dm     - The `DM` whose topology is to be saved
1993 - viewer - The `PetscViewer` to save it in
1994 
1995   Level: advanced
1996 
1997 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexTopologyLoad()`, `PetscViewer`
1998 @*/
1999 PetscErrorCode DMPlexTopologyView(DM dm, PetscViewer viewer)
2000 {
2001   PetscBool ishdf5;
2002 
2003   PetscFunctionBegin;
2004   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2005   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2006   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2007   PetscCall(PetscLogEventBegin(DMPLEX_TopologyView, viewer, 0, 0, 0));
2008   if (ishdf5) {
2009 #if defined(PETSC_HAVE_HDF5)
2010     PetscViewerFormat format;
2011     PetscCall(PetscViewerGetFormat(viewer, &format));
2012     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2013       IS globalPointNumbering;
2014 
2015       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
2016       PetscCall(DMPlexTopologyView_HDF5_Internal(dm, globalPointNumbering, viewer));
2017       PetscCall(ISDestroy(&globalPointNumbering));
2018     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
2019 #else
2020     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2021 #endif
2022   }
2023   PetscCall(PetscLogEventEnd(DMPLEX_TopologyView, viewer, 0, 0, 0));
2024   PetscFunctionReturn(PETSC_SUCCESS);
2025 }
2026 
2027 /*@
2028   DMPlexCoordinatesView - Saves `DMPLEX` coordinates into a file
2029 
2030   Collective
2031 
2032   Input Parameters:
2033 + dm     - The `DM` whose coordinates are to be saved
2034 - viewer - The `PetscViewer` for saving
2035 
2036   Level: advanced
2037 
2038 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexLabelsView()`, `DMPlexCoordinatesLoad()`, `PetscViewer`
2039 @*/
2040 PetscErrorCode DMPlexCoordinatesView(DM dm, PetscViewer viewer)
2041 {
2042   PetscBool ishdf5;
2043 
2044   PetscFunctionBegin;
2045   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2046   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2047   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2048   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
2049   if (ishdf5) {
2050 #if defined(PETSC_HAVE_HDF5)
2051     PetscViewerFormat format;
2052     PetscCall(PetscViewerGetFormat(viewer, &format));
2053     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2054       PetscCall(DMPlexCoordinatesView_HDF5_Internal(dm, viewer));
2055     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
2056 #else
2057     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2058 #endif
2059   }
2060   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
2061   PetscFunctionReturn(PETSC_SUCCESS);
2062 }
2063 
2064 /*@
2065   DMPlexLabelsView - Saves `DMPLEX` labels into a file
2066 
2067   Collective
2068 
2069   Input Parameters:
2070 + dm     - The `DM` whose labels are to be saved
2071 - viewer - The `PetscViewer` for saving
2072 
2073   Level: advanced
2074 
2075 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsLoad()`, `PetscViewer`
2076 @*/
2077 PetscErrorCode DMPlexLabelsView(DM dm, PetscViewer viewer)
2078 {
2079   PetscBool ishdf5;
2080 
2081   PetscFunctionBegin;
2082   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2083   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2084   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2085   PetscCall(PetscLogEventBegin(DMPLEX_LabelsView, viewer, 0, 0, 0));
2086   if (ishdf5) {
2087 #if defined(PETSC_HAVE_HDF5)
2088     IS                globalPointNumbering;
2089     PetscViewerFormat format;
2090 
2091     PetscCall(PetscViewerGetFormat(viewer, &format));
2092     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2093       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
2094       PetscCall(DMPlexLabelsView_HDF5_Internal(dm, globalPointNumbering, viewer));
2095       PetscCall(ISDestroy(&globalPointNumbering));
2096     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2097 #else
2098     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2099 #endif
2100   }
2101   PetscCall(PetscLogEventEnd(DMPLEX_LabelsView, viewer, 0, 0, 0));
2102   PetscFunctionReturn(PETSC_SUCCESS);
2103 }
2104 
2105 /*@
2106   DMPlexSectionView - Saves a section associated with a `DMPLEX`
2107 
2108   Collective
2109 
2110   Input Parameters:
2111 + dm        - The `DM` that contains the topology on which the section to be saved is defined
2112 . viewer    - The `PetscViewer` for saving
2113 - sectiondm - The `DM` that contains the section to be saved
2114 
2115   Level: advanced
2116 
2117   Notes:
2118   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.
2119 
2120   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 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.
2121 
2122 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`, `PetscSectionView()`, `DMPlexSectionLoad()`, `PetscViewer`
2123 @*/
2124 PetscErrorCode DMPlexSectionView(DM dm, PetscViewer viewer, DM sectiondm)
2125 {
2126   PetscBool ishdf5;
2127 
2128   PetscFunctionBegin;
2129   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2130   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2131   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2132   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2133   PetscCall(PetscLogEventBegin(DMPLEX_SectionView, viewer, 0, 0, 0));
2134   if (ishdf5) {
2135 #if defined(PETSC_HAVE_HDF5)
2136     PetscCall(DMPlexSectionView_HDF5_Internal(dm, viewer, sectiondm));
2137 #else
2138     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2139 #endif
2140   }
2141   PetscCall(PetscLogEventEnd(DMPLEX_SectionView, viewer, 0, 0, 0));
2142   PetscFunctionReturn(PETSC_SUCCESS);
2143 }
2144 
2145 /*@
2146   DMPlexGlobalVectorView - Saves a global vector
2147 
2148   Collective
2149 
2150   Input Parameters:
2151 + dm        - The `DM` that represents the topology
2152 . viewer    - The `PetscViewer` to save data with
2153 . sectiondm - The `DM` that contains the global section on which vec is defined
2154 - vec       - The global vector to be saved
2155 
2156   Level: advanced
2157 
2158   Notes:
2159   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 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.
2160 
2161   Calling sequence:
2162 .vb
2163        DMCreate(PETSC_COMM_WORLD, &dm);
2164        DMSetType(dm, DMPLEX);
2165        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2166        DMClone(dm, &sectiondm);
2167        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2168        PetscSectionCreate(PETSC_COMM_WORLD, &section);
2169        DMPlexGetChart(sectiondm, &pStart, &pEnd);
2170        PetscSectionSetChart(section, pStart, pEnd);
2171        PetscSectionSetUp(section);
2172        DMSetLocalSection(sectiondm, section);
2173        PetscSectionDestroy(&section);
2174        DMGetGlobalVector(sectiondm, &vec);
2175        PetscObjectSetName((PetscObject)vec, "vec_name");
2176        DMPlexTopologyView(dm, viewer);
2177        DMPlexSectionView(dm, viewer, sectiondm);
2178        DMPlexGlobalVectorView(dm, viewer, sectiondm, vec);
2179        DMRestoreGlobalVector(sectiondm, &vec);
2180        DMDestroy(&sectiondm);
2181        DMDestroy(&dm);
2182 .ve
2183 
2184 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexLocalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2185 @*/
2186 PetscErrorCode DMPlexGlobalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2187 {
2188   PetscBool ishdf5;
2189 
2190   PetscFunctionBegin;
2191   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2192   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2193   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2194   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2195   /* Check consistency */
2196   {
2197     PetscSection section;
2198     PetscBool    includesConstraints;
2199     PetscInt     m, m1;
2200 
2201     PetscCall(VecGetLocalSize(vec, &m1));
2202     PetscCall(DMGetGlobalSection(sectiondm, &section));
2203     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2204     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2205     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2206     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2207   }
2208   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2209   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorView, viewer, 0, 0, 0));
2210   if (ishdf5) {
2211 #if defined(PETSC_HAVE_HDF5)
2212     PetscCall(DMPlexGlobalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2213 #else
2214     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2215 #endif
2216   }
2217   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorView, viewer, 0, 0, 0));
2218   PetscFunctionReturn(PETSC_SUCCESS);
2219 }
2220 
2221 /*@
2222   DMPlexLocalVectorView - Saves a local vector
2223 
2224   Collective
2225 
2226   Input Parameters:
2227 + dm        - The `DM` that represents the topology
2228 . viewer    - The `PetscViewer` to save data with
2229 . sectiondm - The `DM` that contains the local section on which `vec` is defined; may be the same as `dm`
2230 - vec       - The local vector to be saved
2231 
2232   Level: advanced
2233 
2234   Note:
2235   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 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.
2236 
2237   Calling sequence:
2238 .vb
2239        DMCreate(PETSC_COMM_WORLD, &dm);
2240        DMSetType(dm, DMPLEX);
2241        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2242        DMClone(dm, &sectiondm);
2243        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2244        PetscSectionCreate(PETSC_COMM_WORLD, &section);
2245        DMPlexGetChart(sectiondm, &pStart, &pEnd);
2246        PetscSectionSetChart(section, pStart, pEnd);
2247        PetscSectionSetUp(section);
2248        DMSetLocalSection(sectiondm, section);
2249        DMGetLocalVector(sectiondm, &vec);
2250        PetscObjectSetName((PetscObject)vec, "vec_name");
2251        DMPlexTopologyView(dm, viewer);
2252        DMPlexSectionView(dm, viewer, sectiondm);
2253        DMPlexLocalVectorView(dm, viewer, sectiondm, vec);
2254        DMRestoreLocalVector(sectiondm, &vec);
2255        DMDestroy(&sectiondm);
2256        DMDestroy(&dm);
2257 .ve
2258 
2259 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexGlobalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2260 @*/
2261 PetscErrorCode DMPlexLocalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2262 {
2263   PetscBool ishdf5;
2264 
2265   PetscFunctionBegin;
2266   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2267   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2268   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2269   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2270   /* Check consistency */
2271   {
2272     PetscSection section;
2273     PetscBool    includesConstraints;
2274     PetscInt     m, m1;
2275 
2276     PetscCall(VecGetLocalSize(vec, &m1));
2277     PetscCall(DMGetLocalSection(sectiondm, &section));
2278     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2279     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2280     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2281     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2282   }
2283   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2284   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorView, viewer, 0, 0, 0));
2285   if (ishdf5) {
2286 #if defined(PETSC_HAVE_HDF5)
2287     PetscCall(DMPlexLocalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2288 #else
2289     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2290 #endif
2291   }
2292   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorView, viewer, 0, 0, 0));
2293   PetscFunctionReturn(PETSC_SUCCESS);
2294 }
2295 
2296 PetscErrorCode DMLoad_Plex(DM dm, PetscViewer viewer)
2297 {
2298   PetscBool ishdf5;
2299 
2300   PetscFunctionBegin;
2301   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2302   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2303   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2304   if (ishdf5) {
2305 #if defined(PETSC_HAVE_HDF5)
2306     PetscViewerFormat format;
2307     PetscCall(PetscViewerGetFormat(viewer, &format));
2308     if (format == PETSC_VIEWER_HDF5_XDMF || format == PETSC_VIEWER_HDF5_VIZ) {
2309       PetscCall(DMPlexLoad_HDF5_Xdmf_Internal(dm, viewer));
2310     } else if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2311       PetscCall(DMPlexLoad_HDF5_Internal(dm, viewer));
2312     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2313     PetscFunctionReturn(PETSC_SUCCESS);
2314 #else
2315     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2316 #endif
2317   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex loading", ((PetscObject)viewer)->type_name);
2318 }
2319 
2320 /*@
2321   DMPlexTopologyLoad - Loads a topology into a `DMPLEX`
2322 
2323   Collective
2324 
2325   Input Parameters:
2326 + dm     - The `DM` into which the topology is loaded
2327 - viewer - The `PetscViewer` for the saved topology
2328 
2329   Output Parameter:
2330 . globalToLocalPointSF - The `PetscSF` that pushes points in [0, N) to the associated points in the loaded `DMPLEX`, where N is the global number of points; `NULL` if unneeded
2331 
2332   Level: advanced
2333 
2334 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2335           `PetscViewer`, `PetscSF`
2336 @*/
2337 PetscErrorCode DMPlexTopologyLoad(DM dm, PetscViewer viewer, PetscSF *globalToLocalPointSF)
2338 {
2339   PetscBool ishdf5;
2340 
2341   PetscFunctionBegin;
2342   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2343   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2344   if (globalToLocalPointSF) PetscAssertPointer(globalToLocalPointSF, 3);
2345   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2346   PetscCall(PetscLogEventBegin(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2347   if (ishdf5) {
2348 #if defined(PETSC_HAVE_HDF5)
2349     PetscViewerFormat format;
2350     PetscCall(PetscViewerGetFormat(viewer, &format));
2351     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2352       PetscCall(DMPlexTopologyLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2353     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2354 #else
2355     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2356 #endif
2357   }
2358   PetscCall(PetscLogEventEnd(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2359   PetscFunctionReturn(PETSC_SUCCESS);
2360 }
2361 
2362 /*@
2363   DMPlexCoordinatesLoad - Loads coordinates into a `DMPLEX`
2364 
2365   Collective
2366 
2367   Input Parameters:
2368 + dm                   - The `DM` into which the coordinates are loaded
2369 . viewer               - The `PetscViewer` for the saved coordinates
2370 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad()` when loading dm from viewer
2371 
2372   Level: advanced
2373 
2374 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2375           `PetscSF`, `PetscViewer`
2376 @*/
2377 PetscErrorCode DMPlexCoordinatesLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2378 {
2379   PetscBool ishdf5;
2380 
2381   PetscFunctionBegin;
2382   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2383   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2384   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2385   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2386   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesLoad, viewer, 0, 0, 0));
2387   if (ishdf5) {
2388 #if defined(PETSC_HAVE_HDF5)
2389     PetscViewerFormat format;
2390     PetscCall(PetscViewerGetFormat(viewer, &format));
2391     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2392       PetscCall(DMPlexCoordinatesLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2393     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2394 #else
2395     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2396 #endif
2397   }
2398   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesLoad, viewer, 0, 0, 0));
2399   PetscFunctionReturn(PETSC_SUCCESS);
2400 }
2401 
2402 /*@
2403   DMPlexLabelsLoad - Loads labels into a `DMPLEX`
2404 
2405   Collective
2406 
2407   Input Parameters:
2408 + dm                   - The `DM` into which the labels are loaded
2409 . viewer               - The `PetscViewer` for the saved labels
2410 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad()` when loading `dm` from viewer
2411 
2412   Level: advanced
2413 
2414   Note:
2415   The `PetscSF` argument must not be NULL if the `DM` is distributed, otherwise an error occurs.
2416 
2417 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2418           `PetscSF`, `PetscViewer`
2419 @*/
2420 PetscErrorCode DMPlexLabelsLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2421 {
2422   PetscBool ishdf5;
2423 
2424   PetscFunctionBegin;
2425   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2426   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2427   if (globalToLocalPointSF) PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2428   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2429   PetscCall(PetscLogEventBegin(DMPLEX_LabelsLoad, viewer, 0, 0, 0));
2430   if (ishdf5) {
2431 #if defined(PETSC_HAVE_HDF5)
2432     PetscViewerFormat format;
2433 
2434     PetscCall(PetscViewerGetFormat(viewer, &format));
2435     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2436       PetscCall(DMPlexLabelsLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2437     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2438 #else
2439     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2440 #endif
2441   }
2442   PetscCall(PetscLogEventEnd(DMPLEX_LabelsLoad, viewer, 0, 0, 0));
2443   PetscFunctionReturn(PETSC_SUCCESS);
2444 }
2445 
2446 /*@
2447   DMPlexSectionLoad - Loads section into a `DMPLEX`
2448 
2449   Collective
2450 
2451   Input Parameters:
2452 + dm                   - The `DM` that represents the topology
2453 . viewer               - The `PetscViewer` that represents the on-disk section (sectionA)
2454 . sectiondm            - The `DM` into which the on-disk section (sectionA) is migrated
2455 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad(`) when loading dm from viewer
2456 
2457   Output Parameters:
2458 + 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)
2459 - 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)
2460 
2461   Level: advanced
2462 
2463   Notes:
2464   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.
2465 
2466   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 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.
2467 
2468   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.
2469 
2470   Example using 2 processes:
2471 .vb
2472   NX (number of points on dm): 4
2473   sectionA                   : the on-disk section
2474   vecA                       : a vector associated with sectionA
2475   sectionB                   : sectiondm's local section constructed in this function
2476   vecB (local)               : a vector associated with sectiondm's local section
2477   vecB (global)              : a vector associated with sectiondm's global section
2478 
2479                                      rank 0    rank 1
2480   vecA (global)                  : [.0 .4 .1 | .2 .3]        <- to be loaded in DMPlexGlobalVectorLoad() or DMPlexLocalVectorLoad()
2481   sectionA->atlasOff             :       0 2 | 1             <- loaded in PetscSectionLoad()
2482   sectionA->atlasDof             :       1 3 | 1             <- loaded in PetscSectionLoad()
2483   sectionA's global point numbers:       0 2 | 3             <- loaded in DMPlexSectionLoad()
2484   [0, NX)                        :       0 1 | 2 3           <- conceptual partition used in globalToLocalPointSF
2485   sectionB's global point numbers:     0 1 3 | 3 2           <- associated with [0, NX) by globalToLocalPointSF
2486   sectionB->atlasDof             :     1 0 1 | 1 3
2487   sectionB->atlasOff (no perm)   :     0 1 1 | 0 1
2488   vecB (local)                   :   [.0 .4] | [.4 .1 .2 .3] <- to be constructed by calling DMPlexLocalVectorLoad() with localDofSF
2489   vecB (global)                  :    [.0 .4 | .1 .2 .3]     <- to be constructed by calling DMPlexGlobalVectorLoad() with globalDofSF
2490 .ve
2491   where "|" represents a partition of loaded data, and global point 3 is assumed to be owned by rank 0.
2492 
2493 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`, `PetscSectionLoad()`, `DMPlexSectionView()`, `PetscSF`, `PetscViewer`
2494 @*/
2495 PetscErrorCode DMPlexSectionLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF globalToLocalPointSF, PetscSF *globalDofSF, PetscSF *localDofSF)
2496 {
2497   PetscBool ishdf5;
2498 
2499   PetscFunctionBegin;
2500   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2501   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2502   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2503   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 4);
2504   if (globalDofSF) PetscAssertPointer(globalDofSF, 5);
2505   if (localDofSF) PetscAssertPointer(localDofSF, 6);
2506   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2507   PetscCall(PetscLogEventBegin(DMPLEX_SectionLoad, viewer, 0, 0, 0));
2508   if (ishdf5) {
2509 #if defined(PETSC_HAVE_HDF5)
2510     PetscCall(DMPlexSectionLoad_HDF5_Internal(dm, viewer, sectiondm, globalToLocalPointSF, globalDofSF, localDofSF));
2511 #else
2512     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2513 #endif
2514   }
2515   PetscCall(PetscLogEventEnd(DMPLEX_SectionLoad, viewer, 0, 0, 0));
2516   PetscFunctionReturn(PETSC_SUCCESS);
2517 }
2518 
2519 /*@
2520   DMPlexGlobalVectorLoad - Loads on-disk vector data into a global vector
2521 
2522   Collective
2523 
2524   Input Parameters:
2525 + dm        - The `DM` that represents the topology
2526 . viewer    - The `PetscViewer` that represents the on-disk vector data
2527 . sectiondm - The `DM` that contains the global section on which vec is defined
2528 . sf        - The `PetscSF` that migrates the on-disk vector data into vec
2529 - vec       - The global vector to set values of
2530 
2531   Level: advanced
2532 
2533   Notes:
2534   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 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.
2535 
2536   Calling sequence:
2537 .vb
2538        DMCreate(PETSC_COMM_WORLD, &dm);
2539        DMSetType(dm, DMPLEX);
2540        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2541        DMPlexTopologyLoad(dm, viewer, &sfX);
2542        DMClone(dm, &sectiondm);
2543        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2544        DMPlexSectionLoad(dm, viewer, sectiondm, sfX, &gsf, NULL);
2545        DMGetGlobalVector(sectiondm, &vec);
2546        PetscObjectSetName((PetscObject)vec, "vec_name");
2547        DMPlexGlobalVectorLoad(dm, viewer, sectiondm, gsf, vec);
2548        DMRestoreGlobalVector(sectiondm, &vec);
2549        PetscSFDestroy(&gsf);
2550        PetscSFDestroy(&sfX);
2551        DMDestroy(&sectiondm);
2552        DMDestroy(&dm);
2553 .ve
2554 
2555 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexLocalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`,
2556           `PetscSF`, `PetscViewer`
2557 @*/
2558 PetscErrorCode DMPlexGlobalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2559 {
2560   PetscBool ishdf5;
2561 
2562   PetscFunctionBegin;
2563   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2564   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2565   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2566   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2567   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2568   /* Check consistency */
2569   {
2570     PetscSection section;
2571     PetscBool    includesConstraints;
2572     PetscInt     m, m1;
2573 
2574     PetscCall(VecGetLocalSize(vec, &m1));
2575     PetscCall(DMGetGlobalSection(sectiondm, &section));
2576     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2577     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2578     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2579     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2580   }
2581   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2582   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorLoad, viewer, 0, 0, 0));
2583   if (ishdf5) {
2584 #if defined(PETSC_HAVE_HDF5)
2585     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2586 #else
2587     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2588 #endif
2589   }
2590   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorLoad, viewer, 0, 0, 0));
2591   PetscFunctionReturn(PETSC_SUCCESS);
2592 }
2593 
2594 /*@
2595   DMPlexLocalVectorLoad - Loads on-disk vector data into a local vector
2596 
2597   Collective
2598 
2599   Input Parameters:
2600 + dm        - The `DM` that represents the topology
2601 . viewer    - The `PetscViewer` that represents the on-disk vector data
2602 . sectiondm - The `DM` that contains the local section on which vec is defined
2603 . sf        - The `PetscSF` that migrates the on-disk vector data into vec
2604 - vec       - The local vector to set values of
2605 
2606   Level: advanced
2607 
2608   Notes:
2609   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 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.
2610 
2611   Calling sequence:
2612 .vb
2613        DMCreate(PETSC_COMM_WORLD, &dm);
2614        DMSetType(dm, DMPLEX);
2615        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2616        DMPlexTopologyLoad(dm, viewer, &sfX);
2617        DMClone(dm, &sectiondm);
2618        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2619        DMPlexSectionLoad(dm, viewer, sectiondm, sfX, NULL, &lsf);
2620        DMGetLocalVector(sectiondm, &vec);
2621        PetscObjectSetName((PetscObject)vec, "vec_name");
2622        DMPlexLocalVectorLoad(dm, viewer, sectiondm, lsf, vec);
2623        DMRestoreLocalVector(sectiondm, &vec);
2624        PetscSFDestroy(&lsf);
2625        PetscSFDestroy(&sfX);
2626        DMDestroy(&sectiondm);
2627        DMDestroy(&dm);
2628 .ve
2629 
2630 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`,
2631           `PetscSF`, `PetscViewer`
2632 @*/
2633 PetscErrorCode DMPlexLocalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2634 {
2635   PetscBool ishdf5;
2636 
2637   PetscFunctionBegin;
2638   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2639   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2640   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2641   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2642   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2643   /* Check consistency */
2644   {
2645     PetscSection section;
2646     PetscBool    includesConstraints;
2647     PetscInt     m, m1;
2648 
2649     PetscCall(VecGetLocalSize(vec, &m1));
2650     PetscCall(DMGetLocalSection(sectiondm, &section));
2651     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2652     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2653     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2654     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2655   }
2656   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2657   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorLoad, viewer, 0, 0, 0));
2658   if (ishdf5) {
2659 #if defined(PETSC_HAVE_HDF5)
2660     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2661 #else
2662     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2663 #endif
2664   }
2665   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorLoad, viewer, 0, 0, 0));
2666   PetscFunctionReturn(PETSC_SUCCESS);
2667 }
2668 
2669 PetscErrorCode DMDestroy_Plex(DM dm)
2670 {
2671   DM_Plex *mesh = (DM_Plex *)dm->data;
2672 
2673   PetscFunctionBegin;
2674   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMSetUpGLVisViewer_C", NULL));
2675   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertBoundaryValues_C", NULL));
2676   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMCreateNeumannOverlap_C", NULL));
2677   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMInterpolateSolution_C", NULL));
2678   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertTimeDerivativeBoundaryValues_C", NULL));
2679   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", NULL));
2680   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeGetDefault_C", NULL));
2681   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeSetDefault_C", NULL));
2682   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "MatComputeNeumannOverlap_C", NULL));
2683   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderGetDefault_C", NULL));
2684   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderSetDefault_C", NULL));
2685   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderSectionGetDefault_C", NULL));
2686   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderSectionSetDefault_C", NULL));
2687   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", NULL));
2688   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexSetOverlap_C", NULL));
2689   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetUseCeed_C", NULL));
2690   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexSetUseCeed_C", NULL));
2691   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMGetIsoperiodicPointSF_C", NULL));
2692   if (--mesh->refct > 0) PetscFunctionReturn(PETSC_SUCCESS);
2693   PetscCall(PetscSectionDestroy(&mesh->coneSection));
2694   PetscCall(PetscFree(mesh->cones));
2695   PetscCall(PetscFree(mesh->coneOrientations));
2696   PetscCall(PetscSectionDestroy(&mesh->supportSection));
2697   PetscCall(PetscSectionDestroy(&mesh->subdomainSection));
2698   PetscCall(PetscFree(mesh->supports));
2699   PetscCall(PetscFree(mesh->cellTypes));
2700   PetscCall(DMPlexTransformDestroy(&mesh->tr));
2701   PetscCall(PetscFree(mesh->tetgenOpts));
2702   PetscCall(PetscFree(mesh->triangleOpts));
2703   PetscCall(PetscFree(mesh->transformType));
2704   PetscCall(PetscFree(mesh->distributionName));
2705   PetscCall(PetscPartitionerDestroy(&mesh->partitioner));
2706   PetscCall(DMLabelDestroy(&mesh->subpointMap));
2707   PetscCall(ISDestroy(&mesh->subpointIS));
2708   PetscCall(ISDestroy(&mesh->globalVertexNumbers));
2709   PetscCall(ISDestroy(&mesh->globalCellNumbers));
2710   PetscCall(PetscSFDestroy(&mesh->periodic.face_sf));
2711   PetscCall(PetscSFDestroy(&mesh->periodic.composed_sf));
2712   PetscCall(ISDestroy(&mesh->periodic.periodic_points));
2713   PetscCall(PetscSectionDestroy(&mesh->anchorSection));
2714   PetscCall(ISDestroy(&mesh->anchorIS));
2715   PetscCall(PetscSectionDestroy(&mesh->parentSection));
2716   PetscCall(PetscFree(mesh->parents));
2717   PetscCall(PetscFree(mesh->childIDs));
2718   PetscCall(PetscSectionDestroy(&mesh->childSection));
2719   PetscCall(PetscFree(mesh->children));
2720   PetscCall(DMDestroy(&mesh->referenceTree));
2721   PetscCall(PetscGridHashDestroy(&mesh->lbox));
2722   PetscCall(PetscFree(mesh->neighbors));
2723   if (mesh->metricCtx) PetscCall(PetscFree(mesh->metricCtx));
2724   /* This was originally freed in DMDestroy(), but that prevents reference counting of backend objects */
2725   PetscCall(PetscFree(mesh));
2726   PetscFunctionReturn(PETSC_SUCCESS);
2727 }
2728 
2729 PetscErrorCode DMCreateMatrix_Plex(DM dm, Mat *J)
2730 {
2731   PetscSection           sectionGlobal, sectionLocal;
2732   PetscInt               bs = -1, mbs;
2733   PetscInt               localSize, localStart = 0;
2734   PetscBool              isShell, isBlock, isSeqBlock, isMPIBlock, isSymBlock, isSymSeqBlock, isSymMPIBlock, isMatIS;
2735   MatType                mtype;
2736   ISLocalToGlobalMapping ltog;
2737 
2738   PetscFunctionBegin;
2739   PetscCall(MatInitializePackage());
2740   mtype = dm->mattype;
2741   PetscCall(DMGetLocalSection(dm, &sectionLocal));
2742   PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
2743   /* PetscCall(PetscSectionGetStorageSize(sectionGlobal, &localSize)); */
2744   PetscCall(PetscSectionGetConstrainedStorageSize(sectionGlobal, &localSize));
2745   PetscCallMPI(MPI_Exscan(&localSize, &localStart, 1, MPIU_INT, MPI_SUM, PetscObjectComm((PetscObject)dm)));
2746   PetscCall(MatCreate(PetscObjectComm((PetscObject)dm), J));
2747   PetscCall(MatSetSizes(*J, localSize, localSize, PETSC_DETERMINE, PETSC_DETERMINE));
2748   PetscCall(MatSetType(*J, mtype));
2749   PetscCall(MatSetFromOptions(*J));
2750   PetscCall(MatGetBlockSize(*J, &mbs));
2751   if (mbs > 1) bs = mbs;
2752   PetscCall(PetscStrcmp(mtype, MATSHELL, &isShell));
2753   PetscCall(PetscStrcmp(mtype, MATBAIJ, &isBlock));
2754   PetscCall(PetscStrcmp(mtype, MATSEQBAIJ, &isSeqBlock));
2755   PetscCall(PetscStrcmp(mtype, MATMPIBAIJ, &isMPIBlock));
2756   PetscCall(PetscStrcmp(mtype, MATSBAIJ, &isSymBlock));
2757   PetscCall(PetscStrcmp(mtype, MATSEQSBAIJ, &isSymSeqBlock));
2758   PetscCall(PetscStrcmp(mtype, MATMPISBAIJ, &isSymMPIBlock));
2759   PetscCall(PetscStrcmp(mtype, MATIS, &isMatIS));
2760   if (!isShell) {
2761     PetscBool fillMatrix = (PetscBool)(!dm->prealloc_only && !isMatIS);
2762     PetscInt *dnz, *onz, *dnzu, *onzu, bsLocal[2], bsMinMax[2], *pblocks;
2763     PetscInt  pStart, pEnd, p, dof, cdof, num_fields;
2764 
2765     PetscCall(DMGetLocalToGlobalMapping(dm, &ltog));
2766 
2767     PetscCall(PetscCalloc1(localSize, &pblocks));
2768     PetscCall(PetscSectionGetChart(sectionGlobal, &pStart, &pEnd));
2769     PetscCall(PetscSectionGetNumFields(sectionGlobal, &num_fields));
2770     for (p = pStart; p < pEnd; ++p) {
2771       switch (dm->blocking_type) {
2772       case DM_BLOCKING_TOPOLOGICAL_POINT: { // One block per topological point
2773         PetscInt bdof, offset;
2774 
2775         PetscCall(PetscSectionGetDof(sectionGlobal, p, &dof));
2776         PetscCall(PetscSectionGetOffset(sectionGlobal, p, &offset));
2777         PetscCall(PetscSectionGetConstraintDof(sectionGlobal, p, &cdof));
2778         for (PetscInt i = 0; i < dof - cdof; ++i) pblocks[offset - localStart + i] = dof - cdof;
2779         // Signal block concatenation
2780         if (dof - cdof && sectionLocal->blockStarts && !PetscBTLookup(sectionLocal->blockStarts, p)) pblocks[offset - localStart] = -(dof - cdof);
2781         dof  = dof < 0 ? -(dof + 1) : dof;
2782         bdof = cdof && (dof - cdof) ? 1 : dof;
2783         if (dof) {
2784           if (bs < 0) {
2785             bs = bdof;
2786           } else if (bs != bdof) {
2787             bs = 1;
2788           }
2789         }
2790       } break;
2791       case DM_BLOCKING_FIELD_NODE: {
2792         for (PetscInt field = 0; field < num_fields; field++) {
2793           PetscInt num_comp, bdof, offset;
2794           PetscCall(PetscSectionGetFieldComponents(sectionGlobal, field, &num_comp));
2795           PetscCall(PetscSectionGetFieldDof(sectionGlobal, p, field, &dof));
2796           if (dof < 0) continue;
2797           PetscCall(PetscSectionGetFieldOffset(sectionGlobal, p, field, &offset));
2798           PetscCall(PetscSectionGetFieldConstraintDof(sectionGlobal, p, field, &cdof));
2799           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);
2800           PetscInt num_nodes = dof / num_comp;
2801           for (PetscInt i = 0; i < dof - cdof; i++) pblocks[offset - localStart + i] = (dof - cdof) / num_nodes;
2802           // Handle possibly constant block size (unlikely)
2803           bdof = cdof && (dof - cdof) ? 1 : dof;
2804           if (dof) {
2805             if (bs < 0) {
2806               bs = bdof;
2807             } else if (bs != bdof) {
2808               bs = 1;
2809             }
2810           }
2811         }
2812       } break;
2813       }
2814     }
2815     /* Must have same blocksize on all procs (some might have no points) */
2816     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs;
2817     bsLocal[1] = bs;
2818     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
2819     if (bsMinMax[0] != bsMinMax[1]) bs = 1;
2820     else bs = bsMinMax[0];
2821     bs = PetscMax(1, bs);
2822     PetscCall(MatSetLocalToGlobalMapping(*J, ltog, ltog));
2823     if (dm->prealloc_skip) { // User will likely use MatSetPreallocationCOO(), but still set structural parameters
2824       PetscCall(MatSetBlockSize(*J, bs));
2825       PetscCall(MatSetUp(*J));
2826     } else {
2827       PetscCall(PetscCalloc4(localSize / bs, &dnz, localSize / bs, &onz, localSize / bs, &dnzu, localSize / bs, &onzu));
2828       PetscCall(DMPlexPreallocateOperator(dm, bs, dnz, onz, dnzu, onzu, *J, fillMatrix));
2829       PetscCall(PetscFree4(dnz, onz, dnzu, onzu));
2830     }
2831     { // Consolidate blocks
2832       PetscInt nblocks = 0;
2833       for (PetscInt i = 0; i < localSize; i += PetscMax(1, pblocks[i])) {
2834         if (pblocks[i] == 0) continue;
2835         // Negative block size indicates the blocks should be concatenated
2836         if (pblocks[i] < 0) {
2837           pblocks[i] = -pblocks[i];
2838           pblocks[nblocks - 1] += pblocks[i];
2839         } else {
2840           pblocks[nblocks++] = pblocks[i]; // nblocks always <= i
2841         }
2842         for (PetscInt j = 1; j < pblocks[i]; j++) PetscCheck(pblocks[i + j] == pblocks[i], PETSC_COMM_SELF, PETSC_ERR_PLIB, "Block of size %" PetscInt_FMT " mismatches entry %" PetscInt_FMT, pblocks[i], pblocks[i + j]);
2843       }
2844       PetscCall(MatSetVariableBlockSizes(*J, nblocks, pblocks));
2845     }
2846     PetscCall(PetscFree(pblocks));
2847   }
2848   PetscCall(MatSetDM(*J, dm));
2849   PetscFunctionReturn(PETSC_SUCCESS);
2850 }
2851 
2852 /*@
2853   DMPlexGetSubdomainSection - Returns the section associated with the subdomain
2854 
2855   Not Collective
2856 
2857   Input Parameter:
2858 . dm - The `DMPLEX`
2859 
2860   Output Parameter:
2861 . subsection - The subdomain section
2862 
2863   Level: developer
2864 
2865 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `PetscSection`
2866 @*/
2867 PetscErrorCode DMPlexGetSubdomainSection(DM dm, PetscSection *subsection)
2868 {
2869   DM_Plex *mesh = (DM_Plex *)dm->data;
2870 
2871   PetscFunctionBegin;
2872   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2873   if (!mesh->subdomainSection) {
2874     PetscSection section;
2875     PetscSF      sf;
2876 
2877     PetscCall(PetscSFCreate(PETSC_COMM_SELF, &sf));
2878     PetscCall(DMGetLocalSection(dm, &section));
2879     PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_FALSE, PETSC_TRUE, &mesh->subdomainSection));
2880     PetscCall(PetscSFDestroy(&sf));
2881   }
2882   *subsection = mesh->subdomainSection;
2883   PetscFunctionReturn(PETSC_SUCCESS);
2884 }
2885 
2886 /*@
2887   DMPlexGetChart - Return the interval for all mesh points [`pStart`, `pEnd`)
2888 
2889   Not Collective
2890 
2891   Input Parameter:
2892 . dm - The `DMPLEX`
2893 
2894   Output Parameters:
2895 + pStart - The first mesh point
2896 - pEnd   - The upper bound for mesh points
2897 
2898   Level: beginner
2899 
2900 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetChart()`
2901 @*/
2902 PetscErrorCode DMPlexGetChart(DM dm, PetscInt *pStart, PetscInt *pEnd)
2903 {
2904   DM_Plex *mesh = (DM_Plex *)dm->data;
2905 
2906   PetscFunctionBegin;
2907   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2908   if (mesh->tr) PetscCall(DMPlexTransformGetChart(mesh->tr, pStart, pEnd));
2909   else PetscCall(PetscSectionGetChart(mesh->coneSection, pStart, pEnd));
2910   PetscFunctionReturn(PETSC_SUCCESS);
2911 }
2912 
2913 /*@
2914   DMPlexSetChart - Set the interval for all mesh points [`pStart`, `pEnd`)
2915 
2916   Not Collective
2917 
2918   Input Parameters:
2919 + dm     - The `DMPLEX`
2920 . pStart - The first mesh point
2921 - pEnd   - The upper bound for mesh points
2922 
2923   Level: beginner
2924 
2925 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetChart()`
2926 @*/
2927 PetscErrorCode DMPlexSetChart(DM dm, PetscInt pStart, PetscInt pEnd)
2928 {
2929   DM_Plex *mesh = (DM_Plex *)dm->data;
2930 
2931   PetscFunctionBegin;
2932   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2933   PetscCall(PetscSectionSetChart(mesh->coneSection, pStart, pEnd));
2934   PetscCall(PetscSectionSetChart(mesh->supportSection, pStart, pEnd));
2935   PetscCall(PetscFree(mesh->cellTypes));
2936   PetscFunctionReturn(PETSC_SUCCESS);
2937 }
2938 
2939 /*@
2940   DMPlexGetConeSize - Return the number of in-edges for this point in the DAG
2941 
2942   Not Collective
2943 
2944   Input Parameters:
2945 + dm - The `DMPLEX`
2946 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
2947 
2948   Output Parameter:
2949 . size - The cone size for point `p`
2950 
2951   Level: beginner
2952 
2953 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
2954 @*/
2955 PetscErrorCode DMPlexGetConeSize(DM dm, PetscInt p, PetscInt *size)
2956 {
2957   DM_Plex *mesh = (DM_Plex *)dm->data;
2958 
2959   PetscFunctionBegin;
2960   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2961   PetscAssertPointer(size, 3);
2962   if (mesh->tr) PetscCall(DMPlexTransformGetConeSize(mesh->tr, p, size));
2963   else PetscCall(PetscSectionGetDof(mesh->coneSection, p, size));
2964   PetscFunctionReturn(PETSC_SUCCESS);
2965 }
2966 
2967 /*@
2968   DMPlexSetConeSize - Set the number of in-edges for this point in the DAG
2969 
2970   Not Collective
2971 
2972   Input Parameters:
2973 + dm   - The `DMPLEX`
2974 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
2975 - size - The cone size for point `p`
2976 
2977   Level: beginner
2978 
2979   Note:
2980   This should be called after `DMPlexSetChart()`.
2981 
2982 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetConeSize()`, `DMPlexSetChart()`
2983 @*/
2984 PetscErrorCode DMPlexSetConeSize(DM dm, PetscInt p, PetscInt size)
2985 {
2986   DM_Plex *mesh = (DM_Plex *)dm->data;
2987 
2988   PetscFunctionBegin;
2989   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2990   PetscCheck(!mesh->tr, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Cannot call DMPlexSetConeSize() on a mesh with a transform defined.");
2991   PetscCall(PetscSectionSetDof(mesh->coneSection, p, size));
2992   PetscFunctionReturn(PETSC_SUCCESS);
2993 }
2994 
2995 /*@C
2996   DMPlexGetCone - Return the points on the in-edges for this point in the DAG
2997 
2998   Not Collective
2999 
3000   Input Parameters:
3001 + dm - The `DMPLEX`
3002 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3003 
3004   Output Parameter:
3005 . cone - An array of points which are on the in-edges for point `p`
3006 
3007   Level: beginner
3008 
3009   Fortran Notes:
3010   You must also call `DMPlexRestoreCone()` after you finish using the returned array.
3011   `DMPlexRestoreCone()` is not needed/available in C.
3012 
3013 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSize()`, `DMPlexSetCone()`, `DMPlexGetConeTuple()`, `DMPlexSetChart()`, `DMPlexRestoreCone()`
3014 @*/
3015 PetscErrorCode DMPlexGetCone(DM dm, PetscInt p, const PetscInt *cone[])
3016 {
3017   DM_Plex *mesh = (DM_Plex *)dm->data;
3018   PetscInt off;
3019 
3020   PetscFunctionBegin;
3021   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3022   PetscAssertPointer(cone, 3);
3023   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3024   *cone = PetscSafePointerPlusOffset(mesh->cones, off);
3025   PetscFunctionReturn(PETSC_SUCCESS);
3026 }
3027 
3028 /*@C
3029   DMPlexGetConeTuple - Return the points on the in-edges of several points in the DAG
3030 
3031   Not Collective
3032 
3033   Input Parameters:
3034 + dm - The `DMPLEX`
3035 - p  - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3036 
3037   Output Parameters:
3038 + pConesSection - `PetscSection` describing the layout of `pCones`
3039 - pCones        - An array of points which are on the in-edges for the point set `p`
3040 
3041   Level: intermediate
3042 
3043 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeRecursive()`, `DMPlexSetChart()`, `PetscSection`, `IS`
3044 @*/
3045 PetscErrorCode DMPlexGetConeTuple(DM dm, IS p, PetscSection *pConesSection, IS *pCones)
3046 {
3047   PetscSection cs, newcs;
3048   PetscInt    *cones;
3049   PetscInt    *newarr = NULL;
3050   PetscInt     n;
3051 
3052   PetscFunctionBegin;
3053   PetscCall(DMPlexGetCones(dm, &cones));
3054   PetscCall(DMPlexGetConeSection(dm, &cs));
3055   PetscCall(PetscSectionExtractDofsFromArray(cs, MPIU_INT, cones, p, &newcs, pCones ? ((void **)&newarr) : NULL));
3056   if (pConesSection) *pConesSection = newcs;
3057   if (pCones) {
3058     PetscCall(PetscSectionGetStorageSize(newcs, &n));
3059     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)p), n, newarr, PETSC_OWN_POINTER, pCones));
3060   }
3061   PetscFunctionReturn(PETSC_SUCCESS);
3062 }
3063 
3064 /*@
3065   DMPlexGetConeRecursiveVertices - Expand each given point into its cone points and do that recursively until we end up just with vertices.
3066 
3067   Not Collective
3068 
3069   Input Parameters:
3070 + dm     - The `DMPLEX`
3071 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3072 
3073   Output Parameter:
3074 . expandedPoints - An array of vertices recursively expanded from input points
3075 
3076   Level: advanced
3077 
3078   Notes:
3079   Like `DMPlexGetConeRecursive()` but returns only the 0-depth `IS` (i.e. vertices only) and no sections.
3080 
3081   There is no corresponding Restore function, just call `ISDestroy()` on the returned `IS` to deallocate.
3082 
3083 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexRestoreConeRecursive()`,
3084           `DMPlexGetDepth()`, `IS`
3085 @*/
3086 PetscErrorCode DMPlexGetConeRecursiveVertices(DM dm, IS points, IS *expandedPoints)
3087 {
3088   IS      *expandedPointsAll;
3089   PetscInt depth;
3090 
3091   PetscFunctionBegin;
3092   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3093   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
3094   PetscAssertPointer(expandedPoints, 3);
3095   PetscCall(DMPlexGetConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
3096   *expandedPoints = expandedPointsAll[0];
3097   PetscCall(PetscObjectReference((PetscObject)expandedPointsAll[0]));
3098   PetscCall(DMPlexRestoreConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
3099   PetscFunctionReturn(PETSC_SUCCESS);
3100 }
3101 
3102 /*@
3103   DMPlexGetConeRecursive - Expand each given point into its cone points and do that recursively until we end up just with vertices (DAG points of depth 0, i.e. without cones).
3104 
3105   Not Collective
3106 
3107   Input Parameters:
3108 + dm     - The `DMPLEX`
3109 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3110 
3111   Output Parameters:
3112 + depth          - (optional) Size of the output arrays, equal to `DMPLEX` depth, returned by `DMPlexGetDepth()`
3113 . expandedPoints - (optional) An array of index sets with recursively expanded cones
3114 - sections       - (optional) An array of sections which describe mappings from points to their cone points
3115 
3116   Level: advanced
3117 
3118   Notes:
3119   Like `DMPlexGetConeTuple()` but recursive.
3120 
3121   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.
3122   For example, for d=0 it contains only vertices, for d=1 it can contain vertices and edges, etc.
3123 
3124   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\:
3125   (1) DAG points in `expandedPoints`[d+1] with `depth` d+1 to their cone points in `expandedPoints`[d];
3126   (2) DAG points in `expandedPoints`[d+1] with `depth` in [0,d] to the same points in `expandedPoints`[d].
3127 
3128 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexRestoreConeRecursive()`, `DMPlexGetConeRecursiveVertices()`,
3129           `DMPlexGetDepth()`, `PetscSection`, `IS`
3130 @*/
3131 PetscErrorCode DMPlexGetConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
3132 {
3133   const PetscInt *arr0 = NULL, *cone = NULL;
3134   PetscInt       *arr = NULL, *newarr = NULL;
3135   PetscInt        d, depth_, i, n, newn, cn, co, start, end;
3136   IS             *expandedPoints_;
3137   PetscSection   *sections_;
3138 
3139   PetscFunctionBegin;
3140   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3141   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
3142   if (depth) PetscAssertPointer(depth, 3);
3143   if (expandedPoints) PetscAssertPointer(expandedPoints, 4);
3144   if (sections) PetscAssertPointer(sections, 5);
3145   PetscCall(ISGetLocalSize(points, &n));
3146   PetscCall(ISGetIndices(points, &arr0));
3147   PetscCall(DMPlexGetDepth(dm, &depth_));
3148   PetscCall(PetscCalloc1(depth_, &expandedPoints_));
3149   PetscCall(PetscCalloc1(depth_, &sections_));
3150   arr = (PetscInt *)arr0; /* this is ok because first generation of arr is not modified */
3151   for (d = depth_ - 1; d >= 0; d--) {
3152     PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &sections_[d]));
3153     PetscCall(PetscSectionSetChart(sections_[d], 0, n));
3154     for (i = 0; i < n; i++) {
3155       PetscCall(DMPlexGetDepthStratum(dm, d + 1, &start, &end));
3156       if (arr[i] >= start && arr[i] < end) {
3157         PetscCall(DMPlexGetConeSize(dm, arr[i], &cn));
3158         PetscCall(PetscSectionSetDof(sections_[d], i, cn));
3159       } else {
3160         PetscCall(PetscSectionSetDof(sections_[d], i, 1));
3161       }
3162     }
3163     PetscCall(PetscSectionSetUp(sections_[d]));
3164     PetscCall(PetscSectionGetStorageSize(sections_[d], &newn));
3165     PetscCall(PetscMalloc1(newn, &newarr));
3166     for (i = 0; i < n; i++) {
3167       PetscCall(PetscSectionGetDof(sections_[d], i, &cn));
3168       PetscCall(PetscSectionGetOffset(sections_[d], i, &co));
3169       if (cn > 1) {
3170         PetscCall(DMPlexGetCone(dm, arr[i], &cone));
3171         PetscCall(PetscMemcpy(&newarr[co], cone, cn * sizeof(PetscInt)));
3172       } else {
3173         newarr[co] = arr[i];
3174       }
3175     }
3176     PetscCall(ISCreateGeneral(PETSC_COMM_SELF, newn, newarr, PETSC_OWN_POINTER, &expandedPoints_[d]));
3177     arr = newarr;
3178     n   = newn;
3179   }
3180   PetscCall(ISRestoreIndices(points, &arr0));
3181   *depth = depth_;
3182   if (expandedPoints) *expandedPoints = expandedPoints_;
3183   else {
3184     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&expandedPoints_[d]));
3185     PetscCall(PetscFree(expandedPoints_));
3186   }
3187   if (sections) *sections = sections_;
3188   else {
3189     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&sections_[d]));
3190     PetscCall(PetscFree(sections_));
3191   }
3192   PetscFunctionReturn(PETSC_SUCCESS);
3193 }
3194 
3195 /*@
3196   DMPlexRestoreConeRecursive - Deallocates arrays created by `DMPlexGetConeRecursive()`
3197 
3198   Not Collective
3199 
3200   Input Parameters:
3201 + dm     - The `DMPLEX`
3202 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3203 
3204   Output Parameters:
3205 + depth          - (optional) Size of the output arrays, equal to `DMPLEX` depth, returned by `DMPlexGetDepth()`
3206 . expandedPoints - (optional) An array of recursively expanded cones
3207 - sections       - (optional) An array of sections which describe mappings from points to their cone points
3208 
3209   Level: advanced
3210 
3211   Note:
3212   See `DMPlexGetConeRecursive()`
3213 
3214 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexGetConeRecursiveVertices()`,
3215           `DMPlexGetDepth()`, `IS`, `PetscSection`
3216 @*/
3217 PetscErrorCode DMPlexRestoreConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
3218 {
3219   PetscInt d, depth_;
3220 
3221   PetscFunctionBegin;
3222   PetscCall(DMPlexGetDepth(dm, &depth_));
3223   PetscCheck(!depth || *depth == depth_, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "depth changed since last call to DMPlexGetConeRecursive");
3224   if (depth) *depth = 0;
3225   if (expandedPoints) {
3226     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&((*expandedPoints)[d])));
3227     PetscCall(PetscFree(*expandedPoints));
3228   }
3229   if (sections) {
3230     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&((*sections)[d])));
3231     PetscCall(PetscFree(*sections));
3232   }
3233   PetscFunctionReturn(PETSC_SUCCESS);
3234 }
3235 
3236 /*@
3237   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
3238 
3239   Not Collective
3240 
3241   Input Parameters:
3242 + dm   - The `DMPLEX`
3243 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3244 - cone - An array of points which are on the in-edges for point `p`
3245 
3246   Level: beginner
3247 
3248   Note:
3249   This should be called after all calls to `DMPlexSetConeSize()` and `DMSetUp()`.
3250 
3251 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`, `DMPlexSetSupport()`, `DMPlexSetSupportSize()`
3252 @*/
3253 PetscErrorCode DMPlexSetCone(DM dm, PetscInt p, const PetscInt cone[])
3254 {
3255   DM_Plex *mesh = (DM_Plex *)dm->data;
3256   PetscInt dof, off, c;
3257 
3258   PetscFunctionBegin;
3259   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3260   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3261   if (dof) PetscAssertPointer(cone, 3);
3262   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3263   if (PetscDefined(USE_DEBUG)) {
3264     PetscInt pStart, pEnd;
3265     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3266     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);
3267     for (c = 0; c < dof; ++c) {
3268       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);
3269       mesh->cones[off + c] = cone[c];
3270     }
3271   } else {
3272     for (c = 0; c < dof; ++c) mesh->cones[off + c] = cone[c];
3273   }
3274   PetscFunctionReturn(PETSC_SUCCESS);
3275 }
3276 
3277 /*@C
3278   DMPlexGetConeOrientation - Return the orientations on the in-edges for this point in the DAG
3279 
3280   Not Collective
3281 
3282   Input Parameters:
3283 + dm - The `DMPLEX`
3284 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3285 
3286   Output Parameter:
3287 . coneOrientation - An array of orientations which are on the in-edges for point `p`. An orientation is an
3288                     integer giving the prescription for cone traversal.
3289 
3290   Level: beginner
3291 
3292   Note:
3293   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3294   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3295   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3296   with the identity.
3297 
3298   Fortran Notes:
3299   You must also call `DMPlexRestoreConeOrientation()` after you finish using the returned array.
3300   `DMPlexRestoreConeOrientation()` is not needed/available in C.
3301 
3302 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPolytopeTypeComposeOrientation()`, `DMPolytopeTypeComposeOrientationInv()`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetCone()`, `DMPlexSetChart()`
3303 @*/
3304 PetscErrorCode DMPlexGetConeOrientation(DM dm, PetscInt p, const PetscInt *coneOrientation[])
3305 {
3306   DM_Plex *mesh = (DM_Plex *)dm->data;
3307   PetscInt off;
3308 
3309   PetscFunctionBegin;
3310   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3311   if (PetscDefined(USE_DEBUG)) {
3312     PetscInt dof;
3313     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3314     if (dof) PetscAssertPointer(coneOrientation, 3);
3315   }
3316   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3317 
3318   *coneOrientation = &mesh->coneOrientations[off];
3319   PetscFunctionReturn(PETSC_SUCCESS);
3320 }
3321 
3322 /*@
3323   DMPlexSetConeOrientation - Set the orientations on the in-edges for this point in the DAG
3324 
3325   Not Collective
3326 
3327   Input Parameters:
3328 + dm              - The `DMPLEX`
3329 . p               - The point, which must lie in the chart set with `DMPlexSetChart()`
3330 - coneOrientation - An array of orientations
3331 
3332   Level: beginner
3333 
3334   Notes:
3335   This should be called after all calls to `DMPlexSetConeSize()` and `DMSetUp()`.
3336 
3337   The meaning of coneOrientation is detailed in `DMPlexGetConeOrientation()`.
3338 
3339 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetConeOrientation()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3340 @*/
3341 PetscErrorCode DMPlexSetConeOrientation(DM dm, PetscInt p, const PetscInt coneOrientation[])
3342 {
3343   DM_Plex *mesh = (DM_Plex *)dm->data;
3344   PetscInt pStart, pEnd;
3345   PetscInt dof, off, c;
3346 
3347   PetscFunctionBegin;
3348   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3349   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3350   if (dof) PetscAssertPointer(coneOrientation, 3);
3351   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3352   if (PetscDefined(USE_DEBUG)) {
3353     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3354     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);
3355     for (c = 0; c < dof; ++c) {
3356       PetscInt cdof, o = coneOrientation[c];
3357 
3358       PetscCall(PetscSectionGetDof(mesh->coneSection, mesh->cones[off + c], &cdof));
3359       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);
3360       mesh->coneOrientations[off + c] = o;
3361     }
3362   } else {
3363     for (c = 0; c < dof; ++c) mesh->coneOrientations[off + c] = coneOrientation[c];
3364   }
3365   PetscFunctionReturn(PETSC_SUCCESS);
3366 }
3367 
3368 /*@
3369   DMPlexInsertCone - Insert a point into the in-edges for the point p in the DAG
3370 
3371   Not Collective
3372 
3373   Input Parameters:
3374 + dm        - The `DMPLEX`
3375 . p         - The point, which must lie in the chart set with `DMPlexSetChart()`
3376 . conePos   - The local index in the cone where the point should be put
3377 - conePoint - The mesh point to insert
3378 
3379   Level: beginner
3380 
3381 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3382 @*/
3383 PetscErrorCode DMPlexInsertCone(DM dm, PetscInt p, PetscInt conePos, PetscInt conePoint)
3384 {
3385   DM_Plex *mesh = (DM_Plex *)dm->data;
3386   PetscInt pStart, pEnd;
3387   PetscInt dof, off;
3388 
3389   PetscFunctionBegin;
3390   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3391   if (PetscDefined(USE_DEBUG)) {
3392     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3393     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);
3394     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);
3395     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3396     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);
3397   }
3398   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3399   mesh->cones[off + conePos] = conePoint;
3400   PetscFunctionReturn(PETSC_SUCCESS);
3401 }
3402 
3403 /*@
3404   DMPlexInsertConeOrientation - Insert a point orientation for the in-edge for the point p in the DAG
3405 
3406   Not Collective
3407 
3408   Input Parameters:
3409 + dm              - The `DMPLEX`
3410 . p               - The point, which must lie in the chart set with `DMPlexSetChart()`
3411 . conePos         - The local index in the cone where the point should be put
3412 - coneOrientation - The point orientation to insert
3413 
3414   Level: beginner
3415 
3416   Note:
3417   The meaning of coneOrientation values is detailed in `DMPlexGetConeOrientation()`.
3418 
3419 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3420 @*/
3421 PetscErrorCode DMPlexInsertConeOrientation(DM dm, PetscInt p, PetscInt conePos, PetscInt coneOrientation)
3422 {
3423   DM_Plex *mesh = (DM_Plex *)dm->data;
3424   PetscInt pStart, pEnd;
3425   PetscInt dof, off;
3426 
3427   PetscFunctionBegin;
3428   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3429   if (PetscDefined(USE_DEBUG)) {
3430     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3431     PetscCheck(!(p < pStart) && !(p >= pEnd), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Mesh point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", p, pStart, pEnd);
3432     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3433     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);
3434   }
3435   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3436   mesh->coneOrientations[off + conePos] = coneOrientation;
3437   PetscFunctionReturn(PETSC_SUCCESS);
3438 }
3439 
3440 /*@C
3441   DMPlexGetOrientedCone - Return the points and orientations on the in-edges for this point in the DAG
3442 
3443   Not collective
3444 
3445   Input Parameters:
3446 + dm - The DMPlex
3447 - p  - The point, which must lie in the chart set with DMPlexSetChart()
3448 
3449   Output Parameters:
3450 + cone - An array of points which are on the in-edges for point `p`
3451 - ornt - An array of orientations which are on the in-edges for point `p`. An orientation is an
3452         integer giving the prescription for cone traversal.
3453 
3454   Level: beginner
3455 
3456   Notes:
3457   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3458   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3459   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3460   with the identity.
3461 
3462   Fortran Notes:
3463   You must also call `DMPlexRestoreCone()` after you finish using the returned array.
3464   `DMPlexRestoreCone()` is not needed/available in C.
3465 
3466 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreOrientedCone()`, `DMPlexGetConeSize()`, `DMPlexGetCone()`, `DMPlexGetChart()`
3467 @*/
3468 PetscErrorCode DMPlexGetOrientedCone(DM dm, PetscInt p, const PetscInt *cone[], const PetscInt *ornt[])
3469 {
3470   DM_Plex *mesh = (DM_Plex *)dm->data;
3471 
3472   PetscFunctionBegin;
3473   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3474   if (mesh->tr) {
3475     PetscCall(DMPlexTransformGetCone(mesh->tr, p, cone, ornt));
3476   } else {
3477     PetscInt off;
3478     if (PetscDefined(USE_DEBUG)) {
3479       PetscInt dof;
3480       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3481       if (dof) {
3482         if (cone) PetscAssertPointer(cone, 3);
3483         if (ornt) PetscAssertPointer(ornt, 4);
3484       }
3485     }
3486     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3487     if (cone) *cone = PetscSafePointerPlusOffset(mesh->cones, off);
3488     if (ornt) *ornt = PetscSafePointerPlusOffset(mesh->coneOrientations, off);
3489   }
3490   PetscFunctionReturn(PETSC_SUCCESS);
3491 }
3492 
3493 /*@C
3494   DMPlexRestoreOrientedCone - Restore the points and orientations on the in-edges for this point in the DAG
3495 
3496   Not Collective
3497 
3498   Input Parameters:
3499 + dm   - The DMPlex
3500 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3501 . cone - An array of points which are on the in-edges for point p
3502 - ornt - An array of orientations which are on the in-edges for point `p`. An orientation is an
3503         integer giving the prescription for cone traversal.
3504 
3505   Level: beginner
3506 
3507   Notes:
3508   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3509   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3510   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3511   with the identity.
3512 
3513   Fortran Notes:
3514   You must also call `DMPlexRestoreCone()` after you finish using the returned array.
3515   `DMPlexRestoreCone()` is not needed/available in C.
3516 
3517 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetOrientedCone()`, `DMPlexGetConeSize()`, `DMPlexGetCone()`, `DMPlexGetChart()`
3518 @*/
3519 PetscErrorCode DMPlexRestoreOrientedCone(DM dm, PetscInt p, const PetscInt *cone[], const PetscInt *ornt[])
3520 {
3521   DM_Plex *mesh = (DM_Plex *)dm->data;
3522 
3523   PetscFunctionBegin;
3524   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3525   if (mesh->tr) PetscCall(DMPlexTransformRestoreCone(mesh->tr, p, cone, ornt));
3526   PetscFunctionReturn(PETSC_SUCCESS);
3527 }
3528 
3529 /*@
3530   DMPlexGetSupportSize - Return the number of out-edges for this point in the DAG
3531 
3532   Not Collective
3533 
3534   Input Parameters:
3535 + dm - The `DMPLEX`
3536 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3537 
3538   Output Parameter:
3539 . size - The support size for point `p`
3540 
3541   Level: beginner
3542 
3543 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`, `DMPlexGetConeSize()`
3544 @*/
3545 PetscErrorCode DMPlexGetSupportSize(DM dm, PetscInt p, PetscInt *size)
3546 {
3547   DM_Plex *mesh = (DM_Plex *)dm->data;
3548 
3549   PetscFunctionBegin;
3550   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3551   PetscAssertPointer(size, 3);
3552   PetscCall(PetscSectionGetDof(mesh->supportSection, p, size));
3553   PetscFunctionReturn(PETSC_SUCCESS);
3554 }
3555 
3556 /*@
3557   DMPlexSetSupportSize - Set the number of out-edges for this point in the DAG
3558 
3559   Not Collective
3560 
3561   Input Parameters:
3562 + dm   - The `DMPLEX`
3563 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3564 - size - The support size for point `p`
3565 
3566   Level: beginner
3567 
3568   Note:
3569   This should be called after `DMPlexSetChart()`.
3570 
3571 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetSupportSize()`, `DMPlexSetChart()`
3572 @*/
3573 PetscErrorCode DMPlexSetSupportSize(DM dm, PetscInt p, PetscInt size)
3574 {
3575   DM_Plex *mesh = (DM_Plex *)dm->data;
3576 
3577   PetscFunctionBegin;
3578   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3579   PetscCall(PetscSectionSetDof(mesh->supportSection, p, size));
3580   PetscFunctionReturn(PETSC_SUCCESS);
3581 }
3582 
3583 /*@C
3584   DMPlexGetSupport - Return the points on the out-edges for this point in the DAG
3585 
3586   Not Collective
3587 
3588   Input Parameters:
3589 + dm - The `DMPLEX`
3590 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3591 
3592   Output Parameter:
3593 . support - An array of points which are on the out-edges for point `p`
3594 
3595   Level: beginner
3596 
3597   Fortran Notes:
3598   You must also call `DMPlexRestoreSupport()` after you finish using the returned array.
3599   `DMPlexRestoreSupport()` is not needed/available in C.
3600 
3601 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSupportSize()`, `DMPlexSetSupport()`, `DMPlexGetCone()`, `DMPlexSetChart()`
3602 @*/
3603 PetscErrorCode DMPlexGetSupport(DM dm, PetscInt p, const PetscInt *support[])
3604 {
3605   DM_Plex *mesh = (DM_Plex *)dm->data;
3606   PetscInt off;
3607 
3608   PetscFunctionBegin;
3609   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3610   PetscAssertPointer(support, 3);
3611   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3612   *support = PetscSafePointerPlusOffset(mesh->supports, off);
3613   PetscFunctionReturn(PETSC_SUCCESS);
3614 }
3615 
3616 /*@
3617   DMPlexSetSupport - Set the points on the out-edges for this point in the DAG, that is the list of points that this point covers
3618 
3619   Not Collective
3620 
3621   Input Parameters:
3622 + dm      - The `DMPLEX`
3623 . p       - The point, which must lie in the chart set with `DMPlexSetChart()`
3624 - support - An array of points which are on the out-edges for point `p`
3625 
3626   Level: beginner
3627 
3628   Note:
3629   This should be called after all calls to `DMPlexSetSupportSize()` and `DMSetUp()`.
3630 
3631 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetCone()`, `DMPlexSetConeSize()`, `DMPlexCreate()`, `DMPlexGetSupport()`, `DMPlexSetChart()`, `DMPlexSetSupportSize()`, `DMSetUp()`
3632 @*/
3633 PetscErrorCode DMPlexSetSupport(DM dm, PetscInt p, const PetscInt support[])
3634 {
3635   DM_Plex *mesh = (DM_Plex *)dm->data;
3636   PetscInt pStart, pEnd;
3637   PetscInt dof, off, c;
3638 
3639   PetscFunctionBegin;
3640   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3641   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3642   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3643   if (dof) PetscAssertPointer(support, 3);
3644   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3645   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);
3646   for (c = 0; c < dof; ++c) {
3647     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);
3648     mesh->supports[off + c] = support[c];
3649   }
3650   PetscFunctionReturn(PETSC_SUCCESS);
3651 }
3652 
3653 /*@
3654   DMPlexInsertSupport - Insert a point into the out-edges for the point p in the DAG
3655 
3656   Not Collective
3657 
3658   Input Parameters:
3659 + dm           - The `DMPLEX`
3660 . p            - The point, which must lie in the chart set with `DMPlexSetChart()`
3661 . supportPos   - The local index in the cone where the point should be put
3662 - supportPoint - The mesh point to insert
3663 
3664   Level: beginner
3665 
3666 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3667 @*/
3668 PetscErrorCode DMPlexInsertSupport(DM dm, PetscInt p, PetscInt supportPos, PetscInt supportPoint)
3669 {
3670   DM_Plex *mesh = (DM_Plex *)dm->data;
3671   PetscInt pStart, pEnd;
3672   PetscInt dof, off;
3673 
3674   PetscFunctionBegin;
3675   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3676   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3677   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3678   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3679   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);
3680   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);
3681   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);
3682   mesh->supports[off + supportPos] = supportPoint;
3683   PetscFunctionReturn(PETSC_SUCCESS);
3684 }
3685 
3686 /* Converts an orientation o in the current numbering to the previous scheme used in Plex */
3687 PetscInt DMPolytopeConvertNewOrientation_Internal(DMPolytopeType ct, PetscInt o)
3688 {
3689   switch (ct) {
3690   case DM_POLYTOPE_SEGMENT:
3691     if (o == -1) return -2;
3692     break;
3693   case DM_POLYTOPE_TRIANGLE:
3694     if (o == -3) return -1;
3695     if (o == -2) return -3;
3696     if (o == -1) return -2;
3697     break;
3698   case DM_POLYTOPE_QUADRILATERAL:
3699     if (o == -4) return -2;
3700     if (o == -3) return -1;
3701     if (o == -2) return -4;
3702     if (o == -1) return -3;
3703     break;
3704   default:
3705     return o;
3706   }
3707   return o;
3708 }
3709 
3710 /* Converts an orientation o in the previous scheme used in Plex to the current numbering */
3711 PetscInt DMPolytopeConvertOldOrientation_Internal(DMPolytopeType ct, PetscInt o)
3712 {
3713   switch (ct) {
3714   case DM_POLYTOPE_SEGMENT:
3715     if ((o == -2) || (o == 1)) return -1;
3716     if (o == -1) return 0;
3717     break;
3718   case DM_POLYTOPE_TRIANGLE:
3719     if (o == -3) return -2;
3720     if (o == -2) return -1;
3721     if (o == -1) return -3;
3722     break;
3723   case DM_POLYTOPE_QUADRILATERAL:
3724     if (o == -4) return -2;
3725     if (o == -3) return -1;
3726     if (o == -2) return -4;
3727     if (o == -1) return -3;
3728     break;
3729   default:
3730     return o;
3731   }
3732   return o;
3733 }
3734 
3735 /* Takes in a mesh whose orientations are in the previous scheme and converts them all to the current numbering */
3736 PetscErrorCode DMPlexConvertOldOrientations_Internal(DM dm)
3737 {
3738   PetscInt pStart, pEnd, p;
3739 
3740   PetscFunctionBegin;
3741   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3742   for (p = pStart; p < pEnd; ++p) {
3743     const PetscInt *cone, *ornt;
3744     PetscInt        coneSize, c;
3745 
3746     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
3747     PetscCall(DMPlexGetCone(dm, p, &cone));
3748     PetscCall(DMPlexGetConeOrientation(dm, p, &ornt));
3749     for (c = 0; c < coneSize; ++c) {
3750       DMPolytopeType ct;
3751       const PetscInt o = ornt[c];
3752 
3753       PetscCall(DMPlexGetCellType(dm, cone[c], &ct));
3754       switch (ct) {
3755       case DM_POLYTOPE_SEGMENT:
3756         if ((o == -2) || (o == 1)) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3757         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, 0));
3758         break;
3759       case DM_POLYTOPE_TRIANGLE:
3760         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3761         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3762         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3763         break;
3764       case DM_POLYTOPE_QUADRILATERAL:
3765         if (o == -4) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3766         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3767         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -4));
3768         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3769         break;
3770       default:
3771         break;
3772       }
3773     }
3774   }
3775   PetscFunctionReturn(PETSC_SUCCESS);
3776 }
3777 
3778 static inline PetscErrorCode DMPlexGetTransitiveClosure_Hot_Private(DM dm, PetscInt p, PetscBool useCone, PetscInt *size, const PetscInt *arr[], const PetscInt *ornt[])
3779 {
3780   DM_Plex *mesh = (DM_Plex *)dm->data;
3781 
3782   PetscFunctionBeginHot;
3783   if (PetscDefined(USE_DEBUG) || mesh->tr) {
3784     if (useCone) {
3785       PetscCall(DMPlexGetConeSize(dm, p, size));
3786       PetscCall(DMPlexGetOrientedCone(dm, p, arr, ornt));
3787     } else {
3788       PetscCall(DMPlexGetSupportSize(dm, p, size));
3789       PetscCall(DMPlexGetSupport(dm, p, arr));
3790     }
3791   } else {
3792     if (useCone) {
3793       const PetscSection s   = mesh->coneSection;
3794       const PetscInt     ps  = p - s->pStart;
3795       const PetscInt     off = s->atlasOff[ps];
3796 
3797       *size = s->atlasDof[ps];
3798       *arr  = mesh->cones + off;
3799       *ornt = mesh->coneOrientations + off;
3800     } else {
3801       const PetscSection s   = mesh->supportSection;
3802       const PetscInt     ps  = p - s->pStart;
3803       const PetscInt     off = s->atlasOff[ps];
3804 
3805       *size = s->atlasDof[ps];
3806       *arr  = mesh->supports + off;
3807     }
3808   }
3809   PetscFunctionReturn(PETSC_SUCCESS);
3810 }
3811 
3812 static inline PetscErrorCode DMPlexRestoreTransitiveClosure_Hot_Private(DM dm, PetscInt p, PetscBool useCone, PetscInt *size, const PetscInt *arr[], const PetscInt *ornt[])
3813 {
3814   DM_Plex *mesh = (DM_Plex *)dm->data;
3815 
3816   PetscFunctionBeginHot;
3817   if (PetscDefined(USE_DEBUG) || mesh->tr) {
3818     if (useCone) PetscCall(DMPlexRestoreOrientedCone(dm, p, arr, ornt));
3819   }
3820   PetscFunctionReturn(PETSC_SUCCESS);
3821 }
3822 
3823 static PetscErrorCode DMPlexGetTransitiveClosure_Depth1_Private(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3824 {
3825   DMPolytopeType  ct = DM_POLYTOPE_UNKNOWN;
3826   PetscInt       *closure;
3827   const PetscInt *tmp = NULL, *tmpO = NULL;
3828   PetscInt        off = 0, tmpSize, t;
3829 
3830   PetscFunctionBeginHot;
3831   if (ornt) {
3832     PetscCall(DMPlexGetCellType(dm, p, &ct));
3833     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;
3834   }
3835   if (*points) {
3836     closure = *points;
3837   } else {
3838     PetscInt maxConeSize, maxSupportSize;
3839     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3840     PetscCall(DMGetWorkArray(dm, 2 * (PetscMax(maxConeSize, maxSupportSize) + 1), MPIU_INT, &closure));
3841   }
3842   PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, p, useCone, &tmpSize, &tmp, &tmpO));
3843   if (ct == DM_POLYTOPE_UNKNOWN) {
3844     closure[off++] = p;
3845     closure[off++] = 0;
3846     for (t = 0; t < tmpSize; ++t) {
3847       closure[off++] = tmp[t];
3848       closure[off++] = tmpO ? tmpO[t] : 0;
3849     }
3850   } else {
3851     const PetscInt *arr = DMPolytopeTypeGetArrangement(ct, ornt);
3852 
3853     /* We assume that cells with a valid type have faces with a valid type */
3854     closure[off++] = p;
3855     closure[off++] = ornt;
3856     for (t = 0; t < tmpSize; ++t) {
3857       DMPolytopeType ft;
3858 
3859       PetscCall(DMPlexGetCellType(dm, tmp[t], &ft));
3860       closure[off++] = tmp[arr[t]];
3861       closure[off++] = tmpO ? DMPolytopeTypeComposeOrientation(ft, ornt, tmpO[t]) : 0;
3862     }
3863   }
3864   PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, p, useCone, &tmpSize, &tmp, &tmpO));
3865   if (numPoints) *numPoints = tmpSize + 1;
3866   if (points) *points = closure;
3867   PetscFunctionReturn(PETSC_SUCCESS);
3868 }
3869 
3870 /* We need a special tensor version because we want to allow duplicate points in the endcaps for hybrid cells */
3871 static PetscErrorCode DMPlexTransitiveClosure_Tensor_Internal(DM dm, PetscInt point, DMPolytopeType ct, PetscInt o, PetscBool useCone, PetscInt *numPoints, PetscInt **points)
3872 {
3873   const PetscInt *arr = DMPolytopeTypeGetArrangement(ct, o);
3874   const PetscInt *cone, *ornt;
3875   PetscInt       *pts, *closure = NULL;
3876   DMPolytopeType  ft;
3877   PetscInt        maxConeSize, maxSupportSize, coneSeries, supportSeries, maxSize;
3878   PetscInt        dim, coneSize, c, d, clSize, cl;
3879 
3880   PetscFunctionBeginHot;
3881   PetscCall(DMGetDimension(dm, &dim));
3882   PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, point, PETSC_TRUE, &coneSize, &cone, &ornt));
3883   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3884   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, dim + 1) - 1) / (maxConeSize - 1)) : dim + 1;
3885   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, dim + 1) - 1) / (maxSupportSize - 1)) : dim + 1;
3886   maxSize       = PetscMax(coneSeries, supportSeries);
3887   if (*points) {
3888     pts = *points;
3889   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &pts));
3890   c        = 0;
3891   pts[c++] = point;
3892   pts[c++] = o;
3893   PetscCall(DMPlexGetCellType(dm, cone[arr[0 * 2 + 0]], &ft));
3894   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[0 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[0 * 2 + 1], ornt[0]), useCone, &clSize, &closure));
3895   for (cl = 0; cl < clSize * 2; cl += 2) {
3896     pts[c++] = closure[cl];
3897     pts[c++] = closure[cl + 1];
3898   }
3899   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[1 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[1 * 2 + 1], ornt[1]), useCone, &clSize, &closure));
3900   for (cl = 0; cl < clSize * 2; cl += 2) {
3901     pts[c++] = closure[cl];
3902     pts[c++] = closure[cl + 1];
3903   }
3904   PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[0], useCone, &clSize, &closure));
3905   for (d = 2; d < coneSize; ++d) {
3906     PetscCall(DMPlexGetCellType(dm, cone[arr[d * 2 + 0]], &ft));
3907     pts[c++] = cone[arr[d * 2 + 0]];
3908     pts[c++] = DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]);
3909   }
3910   PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, point, PETSC_TRUE, &coneSize, &cone, &ornt));
3911   if (dim >= 3) {
3912     for (d = 2; d < coneSize; ++d) {
3913       const PetscInt  fpoint = cone[arr[d * 2 + 0]];
3914       const PetscInt *fcone, *fornt;
3915       PetscInt        fconeSize, fc, i;
3916 
3917       PetscCall(DMPlexGetCellType(dm, fpoint, &ft));
3918       const PetscInt *farr = DMPolytopeTypeGetArrangement(ft, DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]));
3919       PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, fpoint, PETSC_TRUE, &fconeSize, &fcone, &fornt));
3920       for (fc = 0; fc < fconeSize; ++fc) {
3921         const PetscInt cp = fcone[farr[fc * 2 + 0]];
3922         const PetscInt co = farr[fc * 2 + 1];
3923 
3924         for (i = 0; i < c; i += 2)
3925           if (pts[i] == cp) break;
3926         if (i == c) {
3927           PetscCall(DMPlexGetCellType(dm, cp, &ft));
3928           pts[c++] = cp;
3929           pts[c++] = DMPolytopeTypeComposeOrientation(ft, co, fornt[farr[fc * 2 + 0]]);
3930         }
3931       }
3932       PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, fpoint, PETSC_TRUE, &fconeSize, &fcone, &fornt));
3933     }
3934   }
3935   *numPoints = c / 2;
3936   *points    = pts;
3937   PetscFunctionReturn(PETSC_SUCCESS);
3938 }
3939 
3940 PetscErrorCode DMPlexGetTransitiveClosure_Internal(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3941 {
3942   DMPolytopeType ct;
3943   PetscInt      *closure, *fifo;
3944   PetscInt       closureSize = 0, fifoStart = 0, fifoSize = 0;
3945   PetscInt       maxConeSize, maxSupportSize, coneSeries, supportSeries;
3946   PetscInt       depth, maxSize;
3947 
3948   PetscFunctionBeginHot;
3949   PetscCall(DMPlexGetDepth(dm, &depth));
3950   if (depth == 1) {
3951     PetscCall(DMPlexGetTransitiveClosure_Depth1_Private(dm, p, ornt, useCone, numPoints, points));
3952     PetscFunctionReturn(PETSC_SUCCESS);
3953   }
3954   PetscCall(DMPlexGetCellType(dm, p, &ct));
3955   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;
3956   if (DMPolytopeTypeIsHybrid(ct) && ct != DM_POLYTOPE_POINT_PRISM_TENSOR) {
3957     PetscCall(DMPlexTransitiveClosure_Tensor_Internal(dm, p, ct, ornt, useCone, numPoints, points));
3958     PetscFunctionReturn(PETSC_SUCCESS);
3959   }
3960   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3961   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, depth + 1) - 1) / (maxConeSize - 1)) : depth + 1;
3962   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, depth + 1) - 1) / (maxSupportSize - 1)) : depth + 1;
3963   maxSize       = PetscMax(coneSeries, supportSeries);
3964   PetscCall(DMGetWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
3965   if (*points) {
3966     closure = *points;
3967   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &closure));
3968   closure[closureSize++] = p;
3969   closure[closureSize++] = ornt;
3970   fifo[fifoSize++]       = p;
3971   fifo[fifoSize++]       = ornt;
3972   fifo[fifoSize++]       = ct;
3973   /* Should kick out early when depth is reached, rather than checking all vertices for empty cones */
3974   while (fifoSize - fifoStart) {
3975     const PetscInt       q    = fifo[fifoStart++];
3976     const PetscInt       o    = fifo[fifoStart++];
3977     const DMPolytopeType qt   = (DMPolytopeType)fifo[fifoStart++];
3978     const PetscInt      *qarr = DMPolytopeTypeGetArrangement(qt, o);
3979     const PetscInt      *tmp, *tmpO = NULL;
3980     PetscInt             tmpSize, t;
3981 
3982     if (PetscDefined(USE_DEBUG)) {
3983       PetscInt nO = DMPolytopeTypeGetNumArrangements(qt) / 2;
3984       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);
3985     }
3986     PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, q, useCone, &tmpSize, &tmp, &tmpO));
3987     for (t = 0; t < tmpSize; ++t) {
3988       const PetscInt ip = useCone && qarr ? qarr[t * 2] : t;
3989       const PetscInt io = useCone && qarr ? qarr[t * 2 + 1] : 0;
3990       const PetscInt cp = tmp[ip];
3991       PetscCall(DMPlexGetCellType(dm, cp, &ct));
3992       const PetscInt co = tmpO ? DMPolytopeTypeComposeOrientation(ct, io, tmpO[ip]) : 0;
3993       PetscInt       c;
3994 
3995       /* Check for duplicate */
3996       for (c = 0; c < closureSize; c += 2) {
3997         if (closure[c] == cp) break;
3998       }
3999       if (c == closureSize) {
4000         closure[closureSize++] = cp;
4001         closure[closureSize++] = co;
4002         fifo[fifoSize++]       = cp;
4003         fifo[fifoSize++]       = co;
4004         fifo[fifoSize++]       = ct;
4005       }
4006     }
4007     PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, q, useCone, &tmpSize, &tmp, &tmpO));
4008   }
4009   PetscCall(DMRestoreWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
4010   if (numPoints) *numPoints = closureSize / 2;
4011   if (points) *points = closure;
4012   PetscFunctionReturn(PETSC_SUCCESS);
4013 }
4014 
4015 /*@C
4016   DMPlexGetTransitiveClosure - Return the points on the transitive closure of the in-edges or out-edges for this point in the DAG
4017 
4018   Not Collective
4019 
4020   Input Parameters:
4021 + dm      - The `DMPLEX`
4022 . p       - The mesh point
4023 - useCone - `PETSC_TRUE` for the closure, otherwise return the star
4024 
4025   Input/Output Parameter:
4026 . points - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...];
4027            if `NULL` on input, internal storage will be returned, otherwise the provided array is used
4028 
4029   Output Parameter:
4030 . numPoints - The number of points in the closure, so points[] is of size 2*`numPoints`
4031 
4032   Level: beginner
4033 
4034   Note:
4035   If using internal storage (points is `NULL` on input), each call overwrites the last output.
4036 
4037   Fortran Notes:
4038   The `numPoints` argument is not present in the Fortran binding since it is internal to the array.
4039 
4040 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
4041 @*/
4042 PetscErrorCode DMPlexGetTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
4043 {
4044   PetscFunctionBeginHot;
4045   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4046   if (numPoints) PetscAssertPointer(numPoints, 4);
4047   if (points) PetscAssertPointer(points, 5);
4048   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, p, 0, useCone, numPoints, points));
4049   PetscFunctionReturn(PETSC_SUCCESS);
4050 }
4051 
4052 /*@C
4053   DMPlexRestoreTransitiveClosure - Restore the array of points on the transitive closure of the in-edges or out-edges for this point in the DAG
4054 
4055   Not Collective
4056 
4057   Input Parameters:
4058 + dm        - The `DMPLEX`
4059 . p         - The mesh point
4060 . useCone   - `PETSC_TRUE` for the closure, otherwise return the star
4061 . numPoints - The number of points in the closure, so points[] is of size 2*`numPoints`
4062 - points    - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...]
4063 
4064   Level: beginner
4065 
4066   Note:
4067   If not using internal storage (points is not `NULL` on input), this call is unnecessary
4068 
4069 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
4070 @*/
4071 PetscErrorCode DMPlexRestoreTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
4072 {
4073   PetscFunctionBeginHot;
4074   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4075   if (numPoints) *numPoints = 0;
4076   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, points));
4077   PetscFunctionReturn(PETSC_SUCCESS);
4078 }
4079 
4080 /*@
4081   DMPlexGetMaxSizes - Return the maximum number of in-edges (cone) and out-edges (support) for any point in the DAG
4082 
4083   Not Collective
4084 
4085   Input Parameter:
4086 . dm - The `DMPLEX`
4087 
4088   Output Parameters:
4089 + maxConeSize    - The maximum number of in-edges
4090 - maxSupportSize - The maximum number of out-edges
4091 
4092   Level: beginner
4093 
4094 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
4095 @*/
4096 PetscErrorCode DMPlexGetMaxSizes(DM dm, PetscInt *maxConeSize, PetscInt *maxSupportSize)
4097 {
4098   DM_Plex *mesh = (DM_Plex *)dm->data;
4099 
4100   PetscFunctionBegin;
4101   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4102   if (maxConeSize) PetscCall(PetscSectionGetMaxDof(mesh->coneSection, maxConeSize));
4103   if (maxSupportSize) PetscCall(PetscSectionGetMaxDof(mesh->supportSection, maxSupportSize));
4104   PetscFunctionReturn(PETSC_SUCCESS);
4105 }
4106 
4107 PetscErrorCode DMSetUp_Plex(DM dm)
4108 {
4109   DM_Plex *mesh = (DM_Plex *)dm->data;
4110   PetscInt size, maxSupportSize;
4111 
4112   PetscFunctionBegin;
4113   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4114   PetscCall(PetscSectionSetUp(mesh->coneSection));
4115   PetscCall(PetscSectionGetStorageSize(mesh->coneSection, &size));
4116   PetscCall(PetscMalloc1(size, &mesh->cones));
4117   PetscCall(PetscCalloc1(size, &mesh->coneOrientations));
4118   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
4119   if (maxSupportSize) {
4120     PetscCall(PetscSectionSetUp(mesh->supportSection));
4121     PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &size));
4122     PetscCall(PetscMalloc1(size, &mesh->supports));
4123   }
4124   PetscFunctionReturn(PETSC_SUCCESS);
4125 }
4126 
4127 PetscErrorCode DMCreateSubDM_Plex(DM dm, PetscInt numFields, const PetscInt fields[], IS *is, DM *subdm)
4128 {
4129   PetscFunctionBegin;
4130   if (subdm) PetscCall(DMClone(dm, subdm));
4131   PetscCall(DMCreateSectionSubDM(dm, numFields, fields, is, subdm));
4132   if (subdm) (*subdm)->useNatural = dm->useNatural;
4133   if (dm->useNatural && dm->sfMigration) {
4134     PetscSF sfNatural;
4135 
4136     (*subdm)->sfMigration = dm->sfMigration;
4137     PetscCall(PetscObjectReference((PetscObject)dm->sfMigration));
4138     PetscCall(DMPlexCreateGlobalToNaturalSF(*subdm, NULL, (*subdm)->sfMigration, &sfNatural));
4139     (*subdm)->sfNatural = sfNatural;
4140   }
4141   PetscFunctionReturn(PETSC_SUCCESS);
4142 }
4143 
4144 PetscErrorCode DMCreateSuperDM_Plex(DM dms[], PetscInt len, IS **is, DM *superdm)
4145 {
4146   PetscInt i = 0;
4147 
4148   PetscFunctionBegin;
4149   PetscCall(DMClone(dms[0], superdm));
4150   PetscCall(DMCreateSectionSuperDM(dms, len, is, superdm));
4151   (*superdm)->useNatural = PETSC_FALSE;
4152   for (i = 0; i < len; i++) {
4153     if (dms[i]->useNatural && dms[i]->sfMigration) {
4154       PetscSF sfNatural;
4155 
4156       (*superdm)->sfMigration = dms[i]->sfMigration;
4157       PetscCall(PetscObjectReference((PetscObject)dms[i]->sfMigration));
4158       (*superdm)->useNatural = PETSC_TRUE;
4159       PetscCall(DMPlexCreateGlobalToNaturalSF(*superdm, NULL, (*superdm)->sfMigration, &sfNatural));
4160       (*superdm)->sfNatural = sfNatural;
4161       break;
4162     }
4163   }
4164   PetscFunctionReturn(PETSC_SUCCESS);
4165 }
4166 
4167 /*@
4168   DMPlexSymmetrize - Create support (out-edge) information from cone (in-edge) information
4169 
4170   Not Collective
4171 
4172   Input Parameter:
4173 . dm - The `DMPLEX`
4174 
4175   Level: beginner
4176 
4177   Note:
4178   This should be called after all calls to `DMPlexSetCone()`
4179 
4180 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMPlexSetCone()`
4181 @*/
4182 PetscErrorCode DMPlexSymmetrize(DM dm)
4183 {
4184   DM_Plex  *mesh = (DM_Plex *)dm->data;
4185   PetscInt *offsets;
4186   PetscInt  supportSize;
4187   PetscInt  pStart, pEnd, p;
4188 
4189   PetscFunctionBegin;
4190   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4191   PetscCheck(!mesh->supports, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONGSTATE, "Supports were already setup in this DMPlex");
4192   PetscCall(PetscLogEventBegin(DMPLEX_Symmetrize, dm, 0, 0, 0));
4193   /* Calculate support sizes */
4194   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4195   for (p = pStart; p < pEnd; ++p) {
4196     PetscInt dof, off, c;
4197 
4198     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
4199     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
4200     for (c = off; c < off + dof; ++c) PetscCall(PetscSectionAddDof(mesh->supportSection, mesh->cones[c], 1));
4201   }
4202   PetscCall(PetscSectionSetUp(mesh->supportSection));
4203   /* Calculate supports */
4204   PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &supportSize));
4205   PetscCall(PetscMalloc1(supportSize, &mesh->supports));
4206   PetscCall(PetscCalloc1(pEnd - pStart, &offsets));
4207   for (p = pStart; p < pEnd; ++p) {
4208     PetscInt dof, off, c;
4209 
4210     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
4211     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
4212     for (c = off; c < off + dof; ++c) {
4213       const PetscInt q = mesh->cones[c];
4214       PetscInt       offS;
4215 
4216       PetscCall(PetscSectionGetOffset(mesh->supportSection, q, &offS));
4217 
4218       mesh->supports[offS + offsets[q]] = p;
4219       ++offsets[q];
4220     }
4221   }
4222   PetscCall(PetscFree(offsets));
4223   PetscCall(PetscLogEventEnd(DMPLEX_Symmetrize, dm, 0, 0, 0));
4224   PetscFunctionReturn(PETSC_SUCCESS);
4225 }
4226 
4227 static PetscErrorCode DMPlexCreateDepthStratum(DM dm, DMLabel label, PetscInt depth, PetscInt pStart, PetscInt pEnd)
4228 {
4229   IS stratumIS;
4230 
4231   PetscFunctionBegin;
4232   if (pStart >= pEnd) PetscFunctionReturn(PETSC_SUCCESS);
4233   if (PetscDefined(USE_DEBUG)) {
4234     PetscInt  qStart, qEnd, numLevels, level;
4235     PetscBool overlap = PETSC_FALSE;
4236     PetscCall(DMLabelGetNumValues(label, &numLevels));
4237     for (level = 0; level < numLevels; level++) {
4238       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4239       if ((pStart >= qStart && pStart < qEnd) || (pEnd > qStart && pEnd <= qEnd)) {
4240         overlap = PETSC_TRUE;
4241         break;
4242       }
4243     }
4244     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);
4245   }
4246   PetscCall(ISCreateStride(PETSC_COMM_SELF, pEnd - pStart, pStart, 1, &stratumIS));
4247   PetscCall(DMLabelSetStratumIS(label, depth, stratumIS));
4248   PetscCall(ISDestroy(&stratumIS));
4249   PetscFunctionReturn(PETSC_SUCCESS);
4250 }
4251 
4252 static PetscErrorCode DMPlexStratify_CellType_Private(DM dm, DMLabel label)
4253 {
4254   PetscInt *pMin, *pMax;
4255   PetscInt  pStart, pEnd;
4256   PetscInt  dmin = PETSC_MAX_INT, dmax = PETSC_MIN_INT;
4257 
4258   PetscFunctionBegin;
4259   {
4260     DMLabel label2;
4261 
4262     PetscCall(DMPlexGetCellTypeLabel(dm, &label2));
4263     PetscCall(PetscObjectViewFromOptions((PetscObject)label2, NULL, "-ct_view"));
4264   }
4265   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4266   for (PetscInt p = pStart; p < pEnd; ++p) {
4267     DMPolytopeType ct;
4268 
4269     PetscCall(DMPlexGetCellType(dm, p, &ct));
4270     dmin = PetscMin(DMPolytopeTypeGetDim(ct), dmin);
4271     dmax = PetscMax(DMPolytopeTypeGetDim(ct), dmax);
4272   }
4273   PetscCall(PetscMalloc2(dmax + 1, &pMin, dmax + 1, &pMax));
4274   for (PetscInt d = dmin; d <= dmax; ++d) {
4275     pMin[d] = PETSC_MAX_INT;
4276     pMax[d] = PETSC_MIN_INT;
4277   }
4278   for (PetscInt p = pStart; p < pEnd; ++p) {
4279     DMPolytopeType ct;
4280     PetscInt       d;
4281 
4282     PetscCall(DMPlexGetCellType(dm, p, &ct));
4283     d       = DMPolytopeTypeGetDim(ct);
4284     pMin[d] = PetscMin(p, pMin[d]);
4285     pMax[d] = PetscMax(p, pMax[d]);
4286   }
4287   for (PetscInt d = dmin; d <= dmax; ++d) {
4288     if (pMin[d] > pMax[d]) continue;
4289     PetscCall(DMPlexCreateDepthStratum(dm, label, d, pMin[d], pMax[d] + 1));
4290   }
4291   PetscCall(PetscFree2(pMin, pMax));
4292   PetscFunctionReturn(PETSC_SUCCESS);
4293 }
4294 
4295 static PetscErrorCode DMPlexStratify_Topological_Private(DM dm, DMLabel label)
4296 {
4297   PetscInt pStart, pEnd;
4298   PetscInt numRoots = 0, numLeaves = 0;
4299 
4300   PetscFunctionBegin;
4301   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4302   {
4303     /* Initialize roots and count leaves */
4304     PetscInt sMin = PETSC_MAX_INT;
4305     PetscInt sMax = PETSC_MIN_INT;
4306     PetscInt coneSize, supportSize;
4307 
4308     for (PetscInt p = pStart; p < pEnd; ++p) {
4309       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4310       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
4311       if (!coneSize && supportSize) {
4312         sMin = PetscMin(p, sMin);
4313         sMax = PetscMax(p, sMax);
4314         ++numRoots;
4315       } else if (!supportSize && coneSize) {
4316         ++numLeaves;
4317       } else if (!supportSize && !coneSize) {
4318         /* Isolated points */
4319         sMin = PetscMin(p, sMin);
4320         sMax = PetscMax(p, sMax);
4321       }
4322     }
4323     PetscCall(DMPlexCreateDepthStratum(dm, label, 0, sMin, sMax + 1));
4324   }
4325 
4326   if (numRoots + numLeaves == (pEnd - pStart)) {
4327     PetscInt sMin = PETSC_MAX_INT;
4328     PetscInt sMax = PETSC_MIN_INT;
4329     PetscInt coneSize, supportSize;
4330 
4331     for (PetscInt p = pStart; p < pEnd; ++p) {
4332       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4333       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
4334       if (!supportSize && coneSize) {
4335         sMin = PetscMin(p, sMin);
4336         sMax = PetscMax(p, sMax);
4337       }
4338     }
4339     PetscCall(DMPlexCreateDepthStratum(dm, label, 1, sMin, sMax + 1));
4340   } else {
4341     PetscInt level = 0;
4342     PetscInt qStart, qEnd;
4343 
4344     PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4345     while (qEnd > qStart) {
4346       PetscInt sMin = PETSC_MAX_INT;
4347       PetscInt sMax = PETSC_MIN_INT;
4348 
4349       for (PetscInt q = qStart; q < qEnd; ++q) {
4350         const PetscInt *support;
4351         PetscInt        supportSize;
4352 
4353         PetscCall(DMPlexGetSupportSize(dm, q, &supportSize));
4354         PetscCall(DMPlexGetSupport(dm, q, &support));
4355         for (PetscInt s = 0; s < supportSize; ++s) {
4356           sMin = PetscMin(support[s], sMin);
4357           sMax = PetscMax(support[s], sMax);
4358         }
4359       }
4360       PetscCall(DMLabelGetNumValues(label, &level));
4361       PetscCall(DMPlexCreateDepthStratum(dm, label, level, sMin, sMax + 1));
4362       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4363     }
4364   }
4365   PetscFunctionReturn(PETSC_SUCCESS);
4366 }
4367 
4368 /*@
4369   DMPlexStratify - Computes the strata for all points in the `DMPLEX`
4370 
4371   Collective
4372 
4373   Input Parameter:
4374 . dm - The `DMPLEX`
4375 
4376   Level: beginner
4377 
4378   Notes:
4379   The strata group all points of the same grade, and this function calculates the strata. This
4380   grade can be seen as the height (or depth) of the point in the DAG.
4381 
4382   The DAG for most topologies is a graded poset (https://en.wikipedia.org/wiki/Graded_poset), and
4383   can be illustrated by a Hasse Diagram (https://en.wikipedia.org/wiki/Hasse_diagram).
4384   Concretely, `DMPlexStratify()` creates a new label named "depth" containing the depth in the DAG of each point. For cell-vertex
4385   meshes, vertices are depth 0 and cells are depth 1. For fully interpolated meshes, depth 0 for vertices, 1 for edges, and so on
4386   until cells have depth equal to the dimension of the mesh. The depth label can be accessed through `DMPlexGetDepthLabel()` or `DMPlexGetDepthStratum()`, or
4387   manually via `DMGetLabel()`.  The height is defined implicitly by height = maxDimension - depth, and can be accessed
4388   via `DMPlexGetHeightStratum()`.  For example, cells have height 0 and faces have height 1.
4389 
4390   The depth of a point is calculated by executing a breadth-first search (BFS) on the DAG. This could produce surprising results
4391   if run on a partially interpolated mesh, meaning one that had some edges and faces, but not others. For example, suppose that
4392   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
4393   to interpolate only that one (e0), so that
4394 .vb
4395   cone(c0) = {e0, v2}
4396   cone(e0) = {v0, v1}
4397 .ve
4398   If `DMPlexStratify()` is run on this mesh, it will give depths
4399 .vb
4400    depth 0 = {v0, v1, v2}
4401    depth 1 = {e0, c0}
4402 .ve
4403   where the triangle has been given depth 1, instead of 2, because it is reachable from vertex v2.
4404 
4405   `DMPlexStratify()` should be called after all calls to `DMPlexSymmetrize()`
4406 
4407 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexComputeCellTypes()`
4408 @*/
4409 PetscErrorCode DMPlexStratify(DM dm)
4410 {
4411   DM_Plex  *mesh = (DM_Plex *)dm->data;
4412   DMLabel   label;
4413   PetscBool flg = PETSC_FALSE;
4414 
4415   PetscFunctionBegin;
4416   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4417   PetscCall(PetscLogEventBegin(DMPLEX_Stratify, dm, 0, 0, 0));
4418 
4419   // Create depth label
4420   PetscCall(DMCreateLabel(dm, "depth"));
4421   PetscCall(DMPlexGetDepthLabel(dm, &label));
4422 
4423   PetscCall(PetscOptionsGetBool(NULL, dm->hdr.prefix, "-dm_plex_stratify_celltype", &flg, NULL));
4424   if (flg) PetscCall(DMPlexStratify_CellType_Private(dm, label));
4425   else PetscCall(DMPlexStratify_Topological_Private(dm, label));
4426 
4427   { /* just in case there is an empty process */
4428     PetscInt numValues, maxValues = 0, v;
4429 
4430     PetscCall(DMLabelGetNumValues(label, &numValues));
4431     PetscCall(MPIU_Allreduce(&numValues, &maxValues, 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
4432     for (v = numValues; v < maxValues; v++) PetscCall(DMLabelAddStratum(label, v));
4433   }
4434   PetscCall(PetscObjectStateGet((PetscObject)label, &mesh->depthState));
4435   PetscCall(PetscLogEventEnd(DMPLEX_Stratify, dm, 0, 0, 0));
4436   PetscFunctionReturn(PETSC_SUCCESS);
4437 }
4438 
4439 PetscErrorCode DMPlexComputeCellType_Internal(DM dm, PetscInt p, PetscInt pdepth, DMPolytopeType *pt)
4440 {
4441   DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4442   PetscInt       dim, depth, pheight, coneSize;
4443 
4444   PetscFunctionBeginHot;
4445   PetscCall(DMGetDimension(dm, &dim));
4446   PetscCall(DMPlexGetDepth(dm, &depth));
4447   PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4448   pheight = depth - pdepth;
4449   if (depth <= 1) {
4450     switch (pdepth) {
4451     case 0:
4452       ct = DM_POLYTOPE_POINT;
4453       break;
4454     case 1:
4455       switch (coneSize) {
4456       case 2:
4457         ct = DM_POLYTOPE_SEGMENT;
4458         break;
4459       case 3:
4460         ct = DM_POLYTOPE_TRIANGLE;
4461         break;
4462       case 4:
4463         switch (dim) {
4464         case 2:
4465           ct = DM_POLYTOPE_QUADRILATERAL;
4466           break;
4467         case 3:
4468           ct = DM_POLYTOPE_TETRAHEDRON;
4469           break;
4470         default:
4471           break;
4472         }
4473         break;
4474       case 5:
4475         ct = DM_POLYTOPE_PYRAMID;
4476         break;
4477       case 6:
4478         ct = DM_POLYTOPE_TRI_PRISM_TENSOR;
4479         break;
4480       case 8:
4481         ct = DM_POLYTOPE_HEXAHEDRON;
4482         break;
4483       default:
4484         break;
4485       }
4486     }
4487   } else {
4488     if (pdepth == 0) {
4489       ct = DM_POLYTOPE_POINT;
4490     } else if (pheight == 0) {
4491       switch (dim) {
4492       case 1:
4493         switch (coneSize) {
4494         case 2:
4495           ct = DM_POLYTOPE_SEGMENT;
4496           break;
4497         default:
4498           break;
4499         }
4500         break;
4501       case 2:
4502         switch (coneSize) {
4503         case 3:
4504           ct = DM_POLYTOPE_TRIANGLE;
4505           break;
4506         case 4:
4507           ct = DM_POLYTOPE_QUADRILATERAL;
4508           break;
4509         default:
4510           break;
4511         }
4512         break;
4513       case 3:
4514         switch (coneSize) {
4515         case 4:
4516           ct = DM_POLYTOPE_TETRAHEDRON;
4517           break;
4518         case 5: {
4519           const PetscInt *cone;
4520           PetscInt        faceConeSize;
4521 
4522           PetscCall(DMPlexGetCone(dm, p, &cone));
4523           PetscCall(DMPlexGetConeSize(dm, cone[0], &faceConeSize));
4524           switch (faceConeSize) {
4525           case 3:
4526             ct = DM_POLYTOPE_TRI_PRISM_TENSOR;
4527             break;
4528           case 4:
4529             ct = DM_POLYTOPE_PYRAMID;
4530             break;
4531           }
4532         } break;
4533         case 6:
4534           ct = DM_POLYTOPE_HEXAHEDRON;
4535           break;
4536         default:
4537           break;
4538         }
4539         break;
4540       default:
4541         break;
4542       }
4543     } else if (pheight > 0) {
4544       switch (coneSize) {
4545       case 2:
4546         ct = DM_POLYTOPE_SEGMENT;
4547         break;
4548       case 3:
4549         ct = DM_POLYTOPE_TRIANGLE;
4550         break;
4551       case 4:
4552         ct = DM_POLYTOPE_QUADRILATERAL;
4553         break;
4554       default:
4555         break;
4556       }
4557     }
4558   }
4559   *pt = ct;
4560   PetscFunctionReturn(PETSC_SUCCESS);
4561 }
4562 
4563 /*@
4564   DMPlexComputeCellTypes - Infer the polytope type of every cell using its dimension and cone size.
4565 
4566   Collective
4567 
4568   Input Parameter:
4569 . dm - The `DMPLEX`
4570 
4571   Level: developer
4572 
4573   Note:
4574   This function is normally called automatically when a cell type is requested. It creates an
4575   internal `DMLabel` named "celltype" which can be directly accessed using `DMGetLabel()`. A user may disable
4576   automatic creation by creating the label manually, using `DMCreateLabel`(dm, "celltype").
4577 
4578   `DMPlexComputeCellTypes()` should be called after all calls to `DMPlexSymmetrize()` and `DMPlexStratify()`
4579 
4580 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexStratify()`, `DMGetLabel()`, `DMCreateLabel()`
4581 @*/
4582 PetscErrorCode DMPlexComputeCellTypes(DM dm)
4583 {
4584   DM_Plex *mesh;
4585   DMLabel  ctLabel;
4586   PetscInt pStart, pEnd, p;
4587 
4588   PetscFunctionBegin;
4589   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4590   mesh = (DM_Plex *)dm->data;
4591   PetscCall(DMCreateLabel(dm, "celltype"));
4592   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
4593   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4594   PetscCall(PetscFree(mesh->cellTypes));
4595   PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
4596   for (p = pStart; p < pEnd; ++p) {
4597     DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4598     PetscInt       pdepth;
4599 
4600     PetscCall(DMPlexGetPointDepth(dm, p, &pdepth));
4601     PetscCall(DMPlexComputeCellType_Internal(dm, p, pdepth, &ct));
4602     PetscCheck(ct != DM_POLYTOPE_UNKNOWN && ct != DM_POLYTOPE_UNKNOWN_CELL && ct != DM_POLYTOPE_UNKNOWN_FACE, PETSC_COMM_SELF, PETSC_ERR_SUP, "Point %" PetscInt_FMT " is screwed up", p);
4603     PetscCall(DMLabelSetValue(ctLabel, p, ct));
4604     mesh->cellTypes[p - pStart].value_as_uint8 = ct;
4605   }
4606   PetscCall(PetscObjectStateGet((PetscObject)ctLabel, &mesh->celltypeState));
4607   PetscCall(PetscObjectViewFromOptions((PetscObject)ctLabel, NULL, "-dm_plex_celltypes_view"));
4608   PetscFunctionReturn(PETSC_SUCCESS);
4609 }
4610 
4611 /*@C
4612   DMPlexGetJoin - Get an array for the join of the set of points
4613 
4614   Not Collective
4615 
4616   Input Parameters:
4617 + dm        - The `DMPLEX` object
4618 . numPoints - The number of input points for the join
4619 - points    - The input points
4620 
4621   Output Parameters:
4622 + numCoveredPoints - The number of points in the join
4623 - coveredPoints    - The points in the join
4624 
4625   Level: intermediate
4626 
4627   Note:
4628   Currently, this is restricted to a single level join
4629 
4630   Fortran Notes:
4631   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4632 
4633 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4634 @*/
4635 PetscErrorCode DMPlexGetJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4636 {
4637   DM_Plex  *mesh = (DM_Plex *)dm->data;
4638   PetscInt *join[2];
4639   PetscInt  joinSize, i = 0;
4640   PetscInt  dof, off, p, c, m;
4641   PetscInt  maxSupportSize;
4642 
4643   PetscFunctionBegin;
4644   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4645   PetscAssertPointer(points, 3);
4646   PetscAssertPointer(numCoveredPoints, 4);
4647   PetscAssertPointer(coveredPoints, 5);
4648   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
4649   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[0]));
4650   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[1]));
4651   /* Copy in support of first point */
4652   PetscCall(PetscSectionGetDof(mesh->supportSection, points[0], &dof));
4653   PetscCall(PetscSectionGetOffset(mesh->supportSection, points[0], &off));
4654   for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = mesh->supports[off + joinSize];
4655   /* Check each successive support */
4656   for (p = 1; p < numPoints; ++p) {
4657     PetscInt newJoinSize = 0;
4658 
4659     PetscCall(PetscSectionGetDof(mesh->supportSection, points[p], &dof));
4660     PetscCall(PetscSectionGetOffset(mesh->supportSection, points[p], &off));
4661     for (c = 0; c < dof; ++c) {
4662       const PetscInt point = mesh->supports[off + c];
4663 
4664       for (m = 0; m < joinSize; ++m) {
4665         if (point == join[i][m]) {
4666           join[1 - i][newJoinSize++] = point;
4667           break;
4668         }
4669       }
4670     }
4671     joinSize = newJoinSize;
4672     i        = 1 - i;
4673   }
4674   *numCoveredPoints = joinSize;
4675   *coveredPoints    = join[i];
4676   PetscCall(DMRestoreWorkArray(dm, maxSupportSize, MPIU_INT, &join[1 - i]));
4677   PetscFunctionReturn(PETSC_SUCCESS);
4678 }
4679 
4680 /*@C
4681   DMPlexRestoreJoin - Restore an array for the join of the set of points
4682 
4683   Not Collective
4684 
4685   Input Parameters:
4686 + dm        - The `DMPLEX` object
4687 . numPoints - The number of input points for the join
4688 - points    - The input points
4689 
4690   Output Parameters:
4691 + numCoveredPoints - The number of points in the join
4692 - coveredPoints    - The points in the join
4693 
4694   Level: intermediate
4695 
4696   Fortran Notes:
4697   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4698 
4699 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetJoin()`, `DMPlexGetFullJoin()`, `DMPlexGetMeet()`
4700 @*/
4701 PetscErrorCode DMPlexRestoreJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4702 {
4703   PetscFunctionBegin;
4704   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4705   if (points) PetscAssertPointer(points, 3);
4706   if (numCoveredPoints) PetscAssertPointer(numCoveredPoints, 4);
4707   PetscAssertPointer(coveredPoints, 5);
4708   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
4709   if (numCoveredPoints) *numCoveredPoints = 0;
4710   PetscFunctionReturn(PETSC_SUCCESS);
4711 }
4712 
4713 /*@C
4714   DMPlexGetFullJoin - Get an array for the join of the set of points
4715 
4716   Not Collective
4717 
4718   Input Parameters:
4719 + dm        - The `DMPLEX` object
4720 . numPoints - The number of input points for the join
4721 - points    - The input points
4722 
4723   Output Parameters:
4724 + numCoveredPoints - The number of points in the join
4725 - coveredPoints    - The points in the join
4726 
4727   Level: intermediate
4728 
4729   Fortran Notes:
4730   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4731 
4732 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetJoin()`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4733 @*/
4734 PetscErrorCode DMPlexGetFullJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4735 {
4736   PetscInt *offsets, **closures;
4737   PetscInt *join[2];
4738   PetscInt  depth = 0, maxSize, joinSize = 0, i = 0;
4739   PetscInt  p, d, c, m, ms;
4740 
4741   PetscFunctionBegin;
4742   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4743   PetscAssertPointer(points, 3);
4744   PetscAssertPointer(numCoveredPoints, 4);
4745   PetscAssertPointer(coveredPoints, 5);
4746 
4747   PetscCall(DMPlexGetDepth(dm, &depth));
4748   PetscCall(PetscCalloc1(numPoints, &closures));
4749   PetscCall(DMGetWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
4750   PetscCall(DMPlexGetMaxSizes(dm, NULL, &ms));
4751   maxSize = (ms > 1) ? ((PetscPowInt(ms, depth + 1) - 1) / (ms - 1)) : depth + 1;
4752   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[0]));
4753   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[1]));
4754 
4755   for (p = 0; p < numPoints; ++p) {
4756     PetscInt closureSize;
4757 
4758     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_FALSE, &closureSize, &closures[p]));
4759 
4760     offsets[p * (depth + 2) + 0] = 0;
4761     for (d = 0; d < depth + 1; ++d) {
4762       PetscInt pStart, pEnd, i;
4763 
4764       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
4765       for (i = offsets[p * (depth + 2) + d]; i < closureSize; ++i) {
4766         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
4767           offsets[p * (depth + 2) + d + 1] = i;
4768           break;
4769         }
4770       }
4771       if (i == closureSize) offsets[p * (depth + 2) + d + 1] = i;
4772     }
4773     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);
4774   }
4775   for (d = 0; d < depth + 1; ++d) {
4776     PetscInt dof;
4777 
4778     /* Copy in support of first point */
4779     dof = offsets[d + 1] - offsets[d];
4780     for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = closures[0][(offsets[d] + joinSize) * 2];
4781     /* Check each successive cone */
4782     for (p = 1; p < numPoints && joinSize; ++p) {
4783       PetscInt newJoinSize = 0;
4784 
4785       dof = offsets[p * (depth + 2) + d + 1] - offsets[p * (depth + 2) + d];
4786       for (c = 0; c < dof; ++c) {
4787         const PetscInt point = closures[p][(offsets[p * (depth + 2) + d] + c) * 2];
4788 
4789         for (m = 0; m < joinSize; ++m) {
4790           if (point == join[i][m]) {
4791             join[1 - i][newJoinSize++] = point;
4792             break;
4793           }
4794         }
4795       }
4796       joinSize = newJoinSize;
4797       i        = 1 - i;
4798     }
4799     if (joinSize) break;
4800   }
4801   *numCoveredPoints = joinSize;
4802   *coveredPoints    = join[i];
4803   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_FALSE, NULL, &closures[p]));
4804   PetscCall(PetscFree(closures));
4805   PetscCall(DMRestoreWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
4806   PetscCall(DMRestoreWorkArray(dm, ms, MPIU_INT, &join[1 - i]));
4807   PetscFunctionReturn(PETSC_SUCCESS);
4808 }
4809 
4810 /*@C
4811   DMPlexGetMeet - Get an array for the meet of the set of points
4812 
4813   Not Collective
4814 
4815   Input Parameters:
4816 + dm        - The `DMPLEX` object
4817 . numPoints - The number of input points for the meet
4818 - points    - The input points
4819 
4820   Output Parameters:
4821 + numCoveringPoints - The number of points in the meet
4822 - coveringPoints    - The points in the meet
4823 
4824   Level: intermediate
4825 
4826   Note:
4827   Currently, this is restricted to a single level meet
4828 
4829   Fortran Notes:
4830   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4831 
4832 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
4833 @*/
4834 PetscErrorCode DMPlexGetMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveringPoints, const PetscInt **coveringPoints)
4835 {
4836   DM_Plex  *mesh = (DM_Plex *)dm->data;
4837   PetscInt *meet[2];
4838   PetscInt  meetSize, i = 0;
4839   PetscInt  dof, off, p, c, m;
4840   PetscInt  maxConeSize;
4841 
4842   PetscFunctionBegin;
4843   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4844   PetscAssertPointer(points, 3);
4845   PetscAssertPointer(numCoveringPoints, 4);
4846   PetscAssertPointer(coveringPoints, 5);
4847   PetscCall(PetscSectionGetMaxDof(mesh->coneSection, &maxConeSize));
4848   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[0]));
4849   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[1]));
4850   /* Copy in cone of first point */
4851   PetscCall(PetscSectionGetDof(mesh->coneSection, points[0], &dof));
4852   PetscCall(PetscSectionGetOffset(mesh->coneSection, points[0], &off));
4853   for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = mesh->cones[off + meetSize];
4854   /* Check each successive cone */
4855   for (p = 1; p < numPoints; ++p) {
4856     PetscInt newMeetSize = 0;
4857 
4858     PetscCall(PetscSectionGetDof(mesh->coneSection, points[p], &dof));
4859     PetscCall(PetscSectionGetOffset(mesh->coneSection, points[p], &off));
4860     for (c = 0; c < dof; ++c) {
4861       const PetscInt point = mesh->cones[off + c];
4862 
4863       for (m = 0; m < meetSize; ++m) {
4864         if (point == meet[i][m]) {
4865           meet[1 - i][newMeetSize++] = point;
4866           break;
4867         }
4868       }
4869     }
4870     meetSize = newMeetSize;
4871     i        = 1 - i;
4872   }
4873   *numCoveringPoints = meetSize;
4874   *coveringPoints    = meet[i];
4875   PetscCall(DMRestoreWorkArray(dm, maxConeSize, MPIU_INT, &meet[1 - i]));
4876   PetscFunctionReturn(PETSC_SUCCESS);
4877 }
4878 
4879 /*@C
4880   DMPlexRestoreMeet - Restore an array for the meet of the set of points
4881 
4882   Not Collective
4883 
4884   Input Parameters:
4885 + dm        - The `DMPLEX` object
4886 . numPoints - The number of input points for the meet
4887 - points    - The input points
4888 
4889   Output Parameters:
4890 + numCoveredPoints - The number of points in the meet
4891 - coveredPoints    - The points in the meet
4892 
4893   Level: intermediate
4894 
4895   Fortran Notes:
4896   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4897 
4898 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetMeet()`, `DMPlexGetFullMeet()`, `DMPlexGetJoin()`
4899 @*/
4900 PetscErrorCode DMPlexRestoreMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4901 {
4902   PetscFunctionBegin;
4903   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4904   if (points) PetscAssertPointer(points, 3);
4905   if (numCoveredPoints) PetscAssertPointer(numCoveredPoints, 4);
4906   PetscAssertPointer(coveredPoints, 5);
4907   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
4908   if (numCoveredPoints) *numCoveredPoints = 0;
4909   PetscFunctionReturn(PETSC_SUCCESS);
4910 }
4911 
4912 /*@C
4913   DMPlexGetFullMeet - Get an array for the meet of the set of points
4914 
4915   Not Collective
4916 
4917   Input Parameters:
4918 + dm        - The `DMPLEX` object
4919 . numPoints - The number of input points for the meet
4920 - points    - The input points
4921 
4922   Output Parameters:
4923 + numCoveredPoints - The number of points in the meet
4924 - coveredPoints    - The points in the meet
4925 
4926   Level: intermediate
4927 
4928   Fortran Notes:
4929   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4930 
4931 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetMeet()`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
4932 @*/
4933 PetscErrorCode DMPlexGetFullMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4934 {
4935   PetscInt *offsets, **closures;
4936   PetscInt *meet[2];
4937   PetscInt  height = 0, maxSize, meetSize = 0, i = 0;
4938   PetscInt  p, h, c, m, mc;
4939 
4940   PetscFunctionBegin;
4941   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4942   PetscAssertPointer(points, 3);
4943   PetscAssertPointer(numCoveredPoints, 4);
4944   PetscAssertPointer(coveredPoints, 5);
4945 
4946   PetscCall(DMPlexGetDepth(dm, &height));
4947   PetscCall(PetscMalloc1(numPoints, &closures));
4948   PetscCall(DMGetWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
4949   PetscCall(DMPlexGetMaxSizes(dm, &mc, NULL));
4950   maxSize = (mc > 1) ? ((PetscPowInt(mc, height + 1) - 1) / (mc - 1)) : height + 1;
4951   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[0]));
4952   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[1]));
4953 
4954   for (p = 0; p < numPoints; ++p) {
4955     PetscInt closureSize;
4956 
4957     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_TRUE, &closureSize, &closures[p]));
4958 
4959     offsets[p * (height + 2) + 0] = 0;
4960     for (h = 0; h < height + 1; ++h) {
4961       PetscInt pStart, pEnd, i;
4962 
4963       PetscCall(DMPlexGetHeightStratum(dm, h, &pStart, &pEnd));
4964       for (i = offsets[p * (height + 2) + h]; i < closureSize; ++i) {
4965         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
4966           offsets[p * (height + 2) + h + 1] = i;
4967           break;
4968         }
4969       }
4970       if (i == closureSize) offsets[p * (height + 2) + h + 1] = i;
4971     }
4972     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);
4973   }
4974   for (h = 0; h < height + 1; ++h) {
4975     PetscInt dof;
4976 
4977     /* Copy in cone of first point */
4978     dof = offsets[h + 1] - offsets[h];
4979     for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = closures[0][(offsets[h] + meetSize) * 2];
4980     /* Check each successive cone */
4981     for (p = 1; p < numPoints && meetSize; ++p) {
4982       PetscInt newMeetSize = 0;
4983 
4984       dof = offsets[p * (height + 2) + h + 1] - offsets[p * (height + 2) + h];
4985       for (c = 0; c < dof; ++c) {
4986         const PetscInt point = closures[p][(offsets[p * (height + 2) + h] + c) * 2];
4987 
4988         for (m = 0; m < meetSize; ++m) {
4989           if (point == meet[i][m]) {
4990             meet[1 - i][newMeetSize++] = point;
4991             break;
4992           }
4993         }
4994       }
4995       meetSize = newMeetSize;
4996       i        = 1 - i;
4997     }
4998     if (meetSize) break;
4999   }
5000   *numCoveredPoints = meetSize;
5001   *coveredPoints    = meet[i];
5002   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_TRUE, NULL, &closures[p]));
5003   PetscCall(PetscFree(closures));
5004   PetscCall(DMRestoreWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
5005   PetscCall(DMRestoreWorkArray(dm, mc, MPIU_INT, &meet[1 - i]));
5006   PetscFunctionReturn(PETSC_SUCCESS);
5007 }
5008 
5009 /*@C
5010   DMPlexEqual - Determine if two `DM` have the same topology
5011 
5012   Not Collective
5013 
5014   Input Parameters:
5015 + dmA - A `DMPLEX` object
5016 - dmB - A `DMPLEX` object
5017 
5018   Output Parameter:
5019 . equal - `PETSC_TRUE` if the topologies are identical
5020 
5021   Level: intermediate
5022 
5023   Note:
5024   We are not solving graph isomorphism, so we do not permute.
5025 
5026 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCone()`
5027 @*/
5028 PetscErrorCode DMPlexEqual(DM dmA, DM dmB, PetscBool *equal)
5029 {
5030   PetscInt depth, depthB, pStart, pEnd, pStartB, pEndB, p;
5031 
5032   PetscFunctionBegin;
5033   PetscValidHeaderSpecific(dmA, DM_CLASSID, 1);
5034   PetscValidHeaderSpecific(dmB, DM_CLASSID, 2);
5035   PetscAssertPointer(equal, 3);
5036 
5037   *equal = PETSC_FALSE;
5038   PetscCall(DMPlexGetDepth(dmA, &depth));
5039   PetscCall(DMPlexGetDepth(dmB, &depthB));
5040   if (depth != depthB) PetscFunctionReturn(PETSC_SUCCESS);
5041   PetscCall(DMPlexGetChart(dmA, &pStart, &pEnd));
5042   PetscCall(DMPlexGetChart(dmB, &pStartB, &pEndB));
5043   if ((pStart != pStartB) || (pEnd != pEndB)) PetscFunctionReturn(PETSC_SUCCESS);
5044   for (p = pStart; p < pEnd; ++p) {
5045     const PetscInt *cone, *coneB, *ornt, *orntB, *support, *supportB;
5046     PetscInt        coneSize, coneSizeB, c, supportSize, supportSizeB, s;
5047 
5048     PetscCall(DMPlexGetConeSize(dmA, p, &coneSize));
5049     PetscCall(DMPlexGetCone(dmA, p, &cone));
5050     PetscCall(DMPlexGetConeOrientation(dmA, p, &ornt));
5051     PetscCall(DMPlexGetConeSize(dmB, p, &coneSizeB));
5052     PetscCall(DMPlexGetCone(dmB, p, &coneB));
5053     PetscCall(DMPlexGetConeOrientation(dmB, p, &orntB));
5054     if (coneSize != coneSizeB) PetscFunctionReturn(PETSC_SUCCESS);
5055     for (c = 0; c < coneSize; ++c) {
5056       if (cone[c] != coneB[c]) PetscFunctionReturn(PETSC_SUCCESS);
5057       if (ornt[c] != orntB[c]) PetscFunctionReturn(PETSC_SUCCESS);
5058     }
5059     PetscCall(DMPlexGetSupportSize(dmA, p, &supportSize));
5060     PetscCall(DMPlexGetSupport(dmA, p, &support));
5061     PetscCall(DMPlexGetSupportSize(dmB, p, &supportSizeB));
5062     PetscCall(DMPlexGetSupport(dmB, p, &supportB));
5063     if (supportSize != supportSizeB) PetscFunctionReturn(PETSC_SUCCESS);
5064     for (s = 0; s < supportSize; ++s) {
5065       if (support[s] != supportB[s]) PetscFunctionReturn(PETSC_SUCCESS);
5066     }
5067   }
5068   *equal = PETSC_TRUE;
5069   PetscFunctionReturn(PETSC_SUCCESS);
5070 }
5071 
5072 /*@C
5073   DMPlexGetNumFaceVertices - Returns the number of vertices on a face
5074 
5075   Not Collective
5076 
5077   Input Parameters:
5078 + dm         - The `DMPLEX`
5079 . cellDim    - The cell dimension
5080 - numCorners - The number of vertices on a cell
5081 
5082   Output Parameter:
5083 . numFaceVertices - The number of vertices on a face
5084 
5085   Level: developer
5086 
5087   Note:
5088   Of course this can only work for a restricted set of symmetric shapes
5089 
5090 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCone()`
5091 @*/
5092 PetscErrorCode DMPlexGetNumFaceVertices(DM dm, PetscInt cellDim, PetscInt numCorners, PetscInt *numFaceVertices)
5093 {
5094   MPI_Comm comm;
5095 
5096   PetscFunctionBegin;
5097   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
5098   PetscAssertPointer(numFaceVertices, 4);
5099   switch (cellDim) {
5100   case 0:
5101     *numFaceVertices = 0;
5102     break;
5103   case 1:
5104     *numFaceVertices = 1;
5105     break;
5106   case 2:
5107     switch (numCorners) {
5108     case 3:                 /* triangle */
5109       *numFaceVertices = 2; /* Edge has 2 vertices */
5110       break;
5111     case 4:                 /* quadrilateral */
5112       *numFaceVertices = 2; /* Edge has 2 vertices */
5113       break;
5114     case 6:                 /* quadratic triangle, tri and quad cohesive Lagrange cells */
5115       *numFaceVertices = 3; /* Edge has 3 vertices */
5116       break;
5117     case 9:                 /* quadratic quadrilateral, quadratic quad cohesive Lagrange cells */
5118       *numFaceVertices = 3; /* Edge has 3 vertices */
5119       break;
5120     default:
5121       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
5122     }
5123     break;
5124   case 3:
5125     switch (numCorners) {
5126     case 4:                 /* tetradehdron */
5127       *numFaceVertices = 3; /* Face has 3 vertices */
5128       break;
5129     case 6:                 /* tet cohesive cells */
5130       *numFaceVertices = 4; /* Face has 4 vertices */
5131       break;
5132     case 8:                 /* hexahedron */
5133       *numFaceVertices = 4; /* Face has 4 vertices */
5134       break;
5135     case 9:                 /* tet cohesive Lagrange cells */
5136       *numFaceVertices = 6; /* Face has 6 vertices */
5137       break;
5138     case 10:                /* quadratic tetrahedron */
5139       *numFaceVertices = 6; /* Face has 6 vertices */
5140       break;
5141     case 12:                /* hex cohesive Lagrange cells */
5142       *numFaceVertices = 6; /* Face has 6 vertices */
5143       break;
5144     case 18:                /* quadratic tet cohesive Lagrange cells */
5145       *numFaceVertices = 6; /* Face has 6 vertices */
5146       break;
5147     case 27:                /* quadratic hexahedron, quadratic hex cohesive Lagrange cells */
5148       *numFaceVertices = 9; /* Face has 9 vertices */
5149       break;
5150     default:
5151       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
5152     }
5153     break;
5154   default:
5155     SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid cell dimension %" PetscInt_FMT, cellDim);
5156   }
5157   PetscFunctionReturn(PETSC_SUCCESS);
5158 }
5159 
5160 /*@
5161   DMPlexGetDepthLabel - Get the `DMLabel` recording the depth of each point
5162 
5163   Not Collective
5164 
5165   Input Parameter:
5166 . dm - The `DMPLEX` object
5167 
5168   Output Parameter:
5169 . depthLabel - The `DMLabel` recording point depth
5170 
5171   Level: developer
5172 
5173 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepth()`, `DMPlexGetHeightStratum()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`,
5174 @*/
5175 PetscErrorCode DMPlexGetDepthLabel(DM dm, DMLabel *depthLabel)
5176 {
5177   PetscFunctionBegin;
5178   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5179   PetscAssertPointer(depthLabel, 2);
5180   *depthLabel = dm->depthLabel;
5181   PetscFunctionReturn(PETSC_SUCCESS);
5182 }
5183 
5184 /*@
5185   DMPlexGetDepth - Get the depth of the DAG representing this mesh
5186 
5187   Not Collective
5188 
5189   Input Parameter:
5190 . dm - The `DMPLEX` object
5191 
5192   Output Parameter:
5193 . depth - The number of strata (breadth first levels) in the DAG
5194 
5195   Level: developer
5196 
5197   Notes:
5198   This returns maximum of point depths over all points, i.e. maximum value of the label returned by `DMPlexGetDepthLabel()`.
5199 
5200   The point depth is described more in detail in `DMPlexGetDepthStratum()`.
5201 
5202   An empty mesh gives -1.
5203 
5204 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepthLabel()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`
5205 @*/
5206 PetscErrorCode DMPlexGetDepth(DM dm, PetscInt *depth)
5207 {
5208   DM_Plex *mesh = (DM_Plex *)dm->data;
5209   DMLabel  label;
5210   PetscInt d = 0;
5211 
5212   PetscFunctionBegin;
5213   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5214   PetscAssertPointer(depth, 2);
5215   if (mesh->tr) {
5216     PetscCall(DMPlexTransformGetDepth(mesh->tr, depth));
5217   } else {
5218     PetscCall(DMPlexGetDepthLabel(dm, &label));
5219     if (label) PetscCall(DMLabelGetNumValues(label, &d));
5220     *depth = d - 1;
5221   }
5222   PetscFunctionReturn(PETSC_SUCCESS);
5223 }
5224 
5225 /*@
5226   DMPlexGetDepthStratum - Get the bounds [`start`, `end`) for all points at a certain depth.
5227 
5228   Not Collective
5229 
5230   Input Parameters:
5231 + dm    - The `DMPLEX` object
5232 - depth - The requested depth
5233 
5234   Output Parameters:
5235 + start - The first point at this `depth`
5236 - end   - One beyond the last point at this `depth`
5237 
5238   Level: developer
5239 
5240   Notes:
5241   Depth indexing is related to topological dimension.  Depth stratum 0 contains the lowest topological dimension points,
5242   often "vertices".  If the mesh is "interpolated" (see `DMPlexInterpolate()`), then depth stratum 1 contains the next
5243   higher dimension, e.g., "edges".
5244 
5245 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetHeightStratum()`, `DMPlexGetCellTypeStratum()`, `DMPlexGetDepth()`, `DMPlexGetDepthLabel()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`, `DMPlexInterpolate()`
5246 @*/
5247 PetscErrorCode DMPlexGetDepthStratum(DM dm, PetscInt depth, PetscInt *start, PetscInt *end)
5248 {
5249   DM_Plex *mesh = (DM_Plex *)dm->data;
5250   DMLabel  label;
5251   PetscInt pStart, pEnd;
5252 
5253   PetscFunctionBegin;
5254   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5255   if (start) {
5256     PetscAssertPointer(start, 3);
5257     *start = 0;
5258   }
5259   if (end) {
5260     PetscAssertPointer(end, 4);
5261     *end = 0;
5262   }
5263   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
5264   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
5265   if (depth < 0) {
5266     if (start) *start = pStart;
5267     if (end) *end = pEnd;
5268     PetscFunctionReturn(PETSC_SUCCESS);
5269   }
5270   if (mesh->tr) {
5271     PetscCall(DMPlexTransformGetDepthStratum(mesh->tr, depth, start, end));
5272   } else {
5273     PetscCall(DMPlexGetDepthLabel(dm, &label));
5274     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named depth was found");
5275     PetscCall(DMLabelGetStratumBounds(label, depth, start, end));
5276   }
5277   PetscFunctionReturn(PETSC_SUCCESS);
5278 }
5279 
5280 /*@
5281   DMPlexGetHeightStratum - Get the bounds [`start`, `end`) for all points at a certain height.
5282 
5283   Not Collective
5284 
5285   Input Parameters:
5286 + dm     - The `DMPLEX` object
5287 - height - The requested height
5288 
5289   Output Parameters:
5290 + start - The first point at this `height`
5291 - end   - One beyond the last point at this `height`
5292 
5293   Level: developer
5294 
5295   Notes:
5296   Height indexing is related to topological codimension.  Height stratum 0 contains the highest topological dimension
5297   points, often called "cells" or "elements".  If the mesh is "interpolated" (see `DMPlexInterpolate()`), then height
5298   stratum 1 contains the boundary of these "cells", often called "faces" or "facets".
5299 
5300 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepthStratum()`, `DMPlexGetCellTypeStratum()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
5301 @*/
5302 PetscErrorCode DMPlexGetHeightStratum(DM dm, PetscInt height, PetscInt *start, PetscInt *end)
5303 {
5304   DMLabel  label;
5305   PetscInt depth, pStart, pEnd;
5306 
5307   PetscFunctionBegin;
5308   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5309   if (start) {
5310     PetscAssertPointer(start, 3);
5311     *start = 0;
5312   }
5313   if (end) {
5314     PetscAssertPointer(end, 4);
5315     *end = 0;
5316   }
5317   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
5318   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
5319   if (height < 0) {
5320     if (start) *start = pStart;
5321     if (end) *end = pEnd;
5322     PetscFunctionReturn(PETSC_SUCCESS);
5323   }
5324   PetscCall(DMPlexGetDepthLabel(dm, &label));
5325   if (label) PetscCall(DMLabelGetNumValues(label, &depth));
5326   else PetscCall(DMGetDimension(dm, &depth));
5327   PetscCheck(depth >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Depth not yet computed");
5328   PetscCall(DMPlexGetDepthStratum(dm, depth - 1 - height, start, end));
5329   PetscFunctionReturn(PETSC_SUCCESS);
5330 }
5331 
5332 /*@
5333   DMPlexGetPointDepth - Get the `depth` of a given point
5334 
5335   Not Collective
5336 
5337   Input Parameters:
5338 + dm    - The `DMPLEX` object
5339 - point - The point
5340 
5341   Output Parameter:
5342 . depth - The depth of the `point`
5343 
5344   Level: intermediate
5345 
5346 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
5347 @*/
5348 PetscErrorCode DMPlexGetPointDepth(DM dm, PetscInt point, PetscInt *depth)
5349 {
5350   PetscFunctionBegin;
5351   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5352   PetscAssertPointer(depth, 3);
5353   PetscCall(DMLabelGetValue(dm->depthLabel, point, depth));
5354   PetscFunctionReturn(PETSC_SUCCESS);
5355 }
5356 
5357 /*@
5358   DMPlexGetPointHeight - Get the `height` of a given point
5359 
5360   Not Collective
5361 
5362   Input Parameters:
5363 + dm    - The `DMPLEX` object
5364 - point - The point
5365 
5366   Output Parameter:
5367 . height - The height of the `point`
5368 
5369   Level: intermediate
5370 
5371 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointDepth()`
5372 @*/
5373 PetscErrorCode DMPlexGetPointHeight(DM dm, PetscInt point, PetscInt *height)
5374 {
5375   PetscInt n, pDepth;
5376 
5377   PetscFunctionBegin;
5378   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5379   PetscAssertPointer(height, 3);
5380   PetscCall(DMLabelGetNumValues(dm->depthLabel, &n));
5381   PetscCall(DMLabelGetValue(dm->depthLabel, point, &pDepth));
5382   *height = n - 1 - pDepth; /* DAG depth is n-1 */
5383   PetscFunctionReturn(PETSC_SUCCESS);
5384 }
5385 
5386 /*@
5387   DMPlexGetCellTypeLabel - Get the `DMLabel` recording the polytope type of each cell
5388 
5389   Not Collective
5390 
5391   Input Parameter:
5392 . dm - The `DMPLEX` object
5393 
5394   Output Parameter:
5395 . celltypeLabel - The `DMLabel` recording cell polytope type
5396 
5397   Level: developer
5398 
5399   Note:
5400   This function will trigger automatica computation of cell types. This can be disabled by calling
5401   `DMCreateLabel`(dm, "celltype") beforehand.
5402 
5403 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMCreateLabel()`
5404 @*/
5405 PetscErrorCode DMPlexGetCellTypeLabel(DM dm, DMLabel *celltypeLabel)
5406 {
5407   PetscFunctionBegin;
5408   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5409   PetscAssertPointer(celltypeLabel, 2);
5410   if (!dm->celltypeLabel) PetscCall(DMPlexComputeCellTypes(dm));
5411   *celltypeLabel = dm->celltypeLabel;
5412   PetscFunctionReturn(PETSC_SUCCESS);
5413 }
5414 
5415 /*@
5416   DMPlexGetCellType - Get the polytope type of a given cell
5417 
5418   Not Collective
5419 
5420   Input Parameters:
5421 + dm   - The `DMPLEX` object
5422 - cell - The cell
5423 
5424   Output Parameter:
5425 . celltype - The polytope type of the cell
5426 
5427   Level: intermediate
5428 
5429 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPolytopeType`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`
5430 @*/
5431 PetscErrorCode DMPlexGetCellType(DM dm, PetscInt cell, DMPolytopeType *celltype)
5432 {
5433   DM_Plex *mesh = (DM_Plex *)dm->data;
5434   DMLabel  label;
5435   PetscInt ct;
5436 
5437   PetscFunctionBegin;
5438   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5439   PetscAssertPointer(celltype, 3);
5440   if (mesh->tr) {
5441     PetscCall(DMPlexTransformGetCellType(mesh->tr, cell, celltype));
5442   } else {
5443     PetscInt pStart, pEnd;
5444 
5445     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, NULL));
5446     if (!mesh->cellTypes) { /* XXX remove? optimize? */
5447       PetscCall(PetscSectionGetChart(mesh->coneSection, NULL, &pEnd));
5448       PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
5449       PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5450       for (PetscInt p = pStart; p < pEnd; p++) {
5451         PetscCall(DMLabelGetValue(label, p, &ct));
5452         mesh->cellTypes[p - pStart].value_as_uint8 = (DMPolytopeType)ct;
5453       }
5454     }
5455     *celltype = (DMPolytopeType)mesh->cellTypes[cell - pStart].value_as_uint8;
5456     if (PetscDefined(USE_DEBUG)) {
5457       PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5458       PetscCall(DMLabelGetValue(label, cell, &ct));
5459       PetscCheck(ct >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Cell %" PetscInt_FMT " has not been assigned a cell type", cell);
5460       PetscCheck(ct == (PetscInt)*celltype, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid cellType for %" PetscInt_FMT ": %d != %" PetscInt_FMT, cell, (int)*celltype, ct);
5461     }
5462   }
5463   PetscFunctionReturn(PETSC_SUCCESS);
5464 }
5465 
5466 /*@
5467   DMPlexSetCellType - Set the polytope type of a given cell
5468 
5469   Not Collective
5470 
5471   Input Parameters:
5472 + dm       - The `DMPLEX` object
5473 . cell     - The cell
5474 - celltype - The polytope type of the cell
5475 
5476   Level: advanced
5477 
5478   Note:
5479   By default, cell types will be automatically computed using `DMPlexComputeCellTypes()` before this function
5480   is executed. This function will override the computed type. However, if automatic classification will not succeed
5481   and a user wants to manually specify all types, the classification must be disabled by calling
5482   DMCreateLabel(dm, "celltype") before getting or setting any cell types.
5483 
5484 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexComputeCellTypes()`, `DMCreateLabel()`
5485 @*/
5486 PetscErrorCode DMPlexSetCellType(DM dm, PetscInt cell, DMPolytopeType celltype)
5487 {
5488   DM_Plex *mesh = (DM_Plex *)dm->data;
5489   DMLabel  label;
5490   PetscInt pStart, pEnd;
5491 
5492   PetscFunctionBegin;
5493   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5494   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
5495   PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5496   PetscCall(DMLabelSetValue(label, cell, celltype));
5497   if (!mesh->cellTypes) PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
5498   mesh->cellTypes[cell - pStart].value_as_uint8 = celltype;
5499   PetscFunctionReturn(PETSC_SUCCESS);
5500 }
5501 
5502 PetscErrorCode DMCreateCoordinateDM_Plex(DM dm, DM *cdm)
5503 {
5504   PetscSection section, s;
5505   Mat          m;
5506   PetscInt     maxHeight;
5507   const char  *prefix;
5508 
5509   PetscFunctionBegin;
5510   PetscCall(DMClone(dm, cdm));
5511   PetscCall(PetscObjectGetOptionsPrefix((PetscObject)dm, &prefix));
5512   PetscCall(PetscObjectSetOptionsPrefix((PetscObject)*cdm, prefix));
5513   PetscCall(PetscObjectAppendOptionsPrefix((PetscObject)*cdm, "cdm_"));
5514   PetscCall(DMPlexGetMaxProjectionHeight(dm, &maxHeight));
5515   PetscCall(DMPlexSetMaxProjectionHeight(*cdm, maxHeight));
5516   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
5517   PetscCall(DMSetLocalSection(*cdm, section));
5518   PetscCall(PetscSectionDestroy(&section));
5519   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &s));
5520   PetscCall(MatCreate(PETSC_COMM_SELF, &m));
5521   PetscCall(DMSetDefaultConstraints(*cdm, s, m, NULL));
5522   PetscCall(PetscSectionDestroy(&s));
5523   PetscCall(MatDestroy(&m));
5524 
5525   PetscCall(DMSetNumFields(*cdm, 1));
5526   PetscCall(DMCreateDS(*cdm));
5527   (*cdm)->cloneOpts = PETSC_TRUE;
5528   if (dm->setfromoptionscalled) PetscCall(DMSetFromOptions(*cdm));
5529   PetscFunctionReturn(PETSC_SUCCESS);
5530 }
5531 
5532 PetscErrorCode DMCreateCoordinateField_Plex(DM dm, DMField *field)
5533 {
5534   Vec coordsLocal, cellCoordsLocal;
5535   DM  coordsDM, cellCoordsDM;
5536 
5537   PetscFunctionBegin;
5538   *field = NULL;
5539   PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
5540   PetscCall(DMGetCoordinateDM(dm, &coordsDM));
5541   PetscCall(DMGetCellCoordinatesLocal(dm, &cellCoordsLocal));
5542   PetscCall(DMGetCellCoordinateDM(dm, &cellCoordsDM));
5543   if (coordsLocal && coordsDM) {
5544     if (cellCoordsLocal && cellCoordsDM) PetscCall(DMFieldCreateDSWithDG(coordsDM, cellCoordsDM, 0, coordsLocal, cellCoordsLocal, field));
5545     else PetscCall(DMFieldCreateDS(coordsDM, 0, coordsLocal, field));
5546   }
5547   PetscFunctionReturn(PETSC_SUCCESS);
5548 }
5549 
5550 /*@C
5551   DMPlexGetConeSection - Return a section which describes the layout of cone data
5552 
5553   Not Collective
5554 
5555   Input Parameter:
5556 . dm - The `DMPLEX` object
5557 
5558   Output Parameter:
5559 . section - The `PetscSection` object
5560 
5561   Level: developer
5562 
5563 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSupportSection()`, `DMPlexGetCones()`, `DMPlexGetConeOrientations()`, `PetscSection`
5564 @*/
5565 PetscErrorCode DMPlexGetConeSection(DM dm, PetscSection *section)
5566 {
5567   DM_Plex *mesh = (DM_Plex *)dm->data;
5568 
5569   PetscFunctionBegin;
5570   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5571   if (section) *section = mesh->coneSection;
5572   PetscFunctionReturn(PETSC_SUCCESS);
5573 }
5574 
5575 /*@C
5576   DMPlexGetSupportSection - Return a section which describes the layout of support data
5577 
5578   Not Collective
5579 
5580   Input Parameter:
5581 . dm - The `DMPLEX` object
5582 
5583   Output Parameter:
5584 . section - The `PetscSection` object
5585 
5586   Level: developer
5587 
5588 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `PetscSection`
5589 @*/
5590 PetscErrorCode DMPlexGetSupportSection(DM dm, PetscSection *section)
5591 {
5592   DM_Plex *mesh = (DM_Plex *)dm->data;
5593 
5594   PetscFunctionBegin;
5595   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5596   if (section) *section = mesh->supportSection;
5597   PetscFunctionReturn(PETSC_SUCCESS);
5598 }
5599 
5600 /*@C
5601   DMPlexGetCones - Return cone data
5602 
5603   Not Collective
5604 
5605   Input Parameter:
5606 . dm - The `DMPLEX` object
5607 
5608   Output Parameter:
5609 . cones - The cone for each point
5610 
5611   Level: developer
5612 
5613 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`
5614 @*/
5615 PetscErrorCode DMPlexGetCones(DM dm, PetscInt *cones[])
5616 {
5617   DM_Plex *mesh = (DM_Plex *)dm->data;
5618 
5619   PetscFunctionBegin;
5620   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5621   if (cones) *cones = mesh->cones;
5622   PetscFunctionReturn(PETSC_SUCCESS);
5623 }
5624 
5625 /*@C
5626   DMPlexGetConeOrientations - Return cone orientation data
5627 
5628   Not Collective
5629 
5630   Input Parameter:
5631 . dm - The `DMPLEX` object
5632 
5633   Output Parameter:
5634 . coneOrientations - The array of cone orientations for all points
5635 
5636   Level: developer
5637 
5638   Notes:
5639   The `PetscSection` returned by `DMPlexGetConeSection()` partitions coneOrientations into cone orientations of particular points as returned by `DMPlexGetConeOrientation()`.
5640 
5641   The meaning of coneOrientations values is detailed in `DMPlexGetConeOrientation()`.
5642 
5643 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `DMPlexGetConeOrientation()`, `PetscSection`
5644 @*/
5645 PetscErrorCode DMPlexGetConeOrientations(DM dm, PetscInt *coneOrientations[])
5646 {
5647   DM_Plex *mesh = (DM_Plex *)dm->data;
5648 
5649   PetscFunctionBegin;
5650   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5651   if (coneOrientations) *coneOrientations = mesh->coneOrientations;
5652   PetscFunctionReturn(PETSC_SUCCESS);
5653 }
5654 
5655 /******************************** FEM Support **********************************/
5656 
5657 PetscErrorCode DMPlexGetAllCells_Internal(DM plex, IS *cellIS)
5658 {
5659   PetscInt depth;
5660 
5661   PetscFunctionBegin;
5662   PetscCall(DMPlexGetDepth(plex, &depth));
5663   PetscCall(DMGetStratumIS(plex, "dim", depth, cellIS));
5664   if (!*cellIS) PetscCall(DMGetStratumIS(plex, "depth", depth, cellIS));
5665   PetscFunctionReturn(PETSC_SUCCESS);
5666 }
5667 
5668 PetscErrorCode DMPlexGetAllFaces_Internal(DM plex, IS *faceIS)
5669 {
5670   PetscInt depth;
5671 
5672   PetscFunctionBegin;
5673   PetscCall(DMPlexGetDepth(plex, &depth));
5674   PetscCall(DMGetStratumIS(plex, "dim", depth - 1, faceIS));
5675   if (!*faceIS) PetscCall(DMGetStratumIS(plex, "depth", depth - 1, faceIS));
5676   PetscFunctionReturn(PETSC_SUCCESS);
5677 }
5678 
5679 /*
5680  Returns number of components and tensor degree for the field.  For interpolated meshes, line should be a point
5681  representing a line in the section.
5682 */
5683 static PetscErrorCode PetscSectionFieldGetTensorDegree_Private(DM dm, PetscSection section, PetscInt field, PetscInt line, PetscInt *Nc, PetscInt *k, PetscBool *continuous, PetscBool *tensor)
5684 {
5685   PetscObject  obj;
5686   PetscClassId id;
5687   PetscFE      fe = NULL;
5688 
5689   PetscFunctionBeginHot;
5690   PetscCall(PetscSectionGetFieldComponents(section, field, Nc));
5691   PetscCall(DMGetField(dm, field, NULL, &obj));
5692   PetscCall(PetscObjectGetClassId(obj, &id));
5693   if (id == PETSCFE_CLASSID) fe = (PetscFE)obj;
5694 
5695   if (!fe) {
5696     /* Assume the full interpolated mesh is in the chart; lines in particular */
5697     /* An order k SEM disc has k-1 dofs on an edge */
5698     PetscCall(PetscSectionGetFieldDof(section, line, field, k));
5699     *k = *k / *Nc + 1;
5700   } else {
5701     PetscInt       dual_space_size, dim;
5702     PetscDualSpace dsp;
5703 
5704     PetscCall(DMGetDimension(dm, &dim));
5705     PetscCall(PetscFEGetDualSpace(fe, &dsp));
5706     PetscCall(PetscDualSpaceGetDimension(dsp, &dual_space_size));
5707     *k = (PetscInt)PetscCeilReal(PetscPowReal(dual_space_size / *Nc, 1.0 / dim)) - 1;
5708     PetscCall(PetscDualSpaceLagrangeGetContinuity(dsp, continuous));
5709     PetscCall(PetscDualSpaceLagrangeGetTensor(dsp, tensor));
5710   }
5711   PetscFunctionReturn(PETSC_SUCCESS);
5712 }
5713 
5714 static PetscErrorCode GetFieldSize_Private(PetscInt dim, PetscInt k, PetscBool tensor, PetscInt *dof)
5715 {
5716   PetscFunctionBeginHot;
5717   if (tensor) {
5718     *dof = PetscPowInt(k + 1, dim);
5719   } else {
5720     switch (dim) {
5721     case 1:
5722       *dof = k + 1;
5723       break;
5724     case 2:
5725       *dof = ((k + 1) * (k + 2)) / 2;
5726       break;
5727     case 3:
5728       *dof = ((k + 1) * (k + 2) * (k + 3)) / 6;
5729       break;
5730     default:
5731       *dof = 0;
5732     }
5733   }
5734   PetscFunctionReturn(PETSC_SUCCESS);
5735 }
5736 
5737 /*@
5738 
5739   DMPlexSetClosurePermutationTensor - Create a permutation from the default (BFS) point ordering in the closure, to a
5740   lexicographic ordering over the tensor product cell (i.e., line, quad, hex, etc.), and set this permutation in the
5741   section provided (or the section of the `DM`).
5742 
5743   Input Parameters:
5744 + dm      - The `DM`
5745 . point   - Either a cell (highest dim point) or an edge (dim 1 point), or `PETSC_DETERMINE`
5746 - section - The `PetscSection` to reorder, or `NULL` for the default section
5747 
5748   Example:
5749   A typical interpolated single-quad mesh might order points as
5750 .vb
5751   [c0, v1, v2, v3, v4, e5, e6, e7, e8]
5752 
5753   v4 -- e6 -- v3
5754   |           |
5755   e7    c0    e8
5756   |           |
5757   v1 -- e5 -- v2
5758 .ve
5759 
5760   (There is no significance to the ordering described here.)  The default section for a Q3 quad might typically assign
5761   dofs in the order of points, e.g.,
5762 .vb
5763     c0 -> [0,1,2,3]
5764     v1 -> [4]
5765     ...
5766     e5 -> [8, 9]
5767 .ve
5768 
5769   which corresponds to the dofs
5770 .vb
5771     6   10  11  7
5772     13  2   3   15
5773     12  0   1   14
5774     4   8   9   5
5775 .ve
5776 
5777   The closure in BFS ordering works through height strata (cells, edges, vertices) to produce the ordering
5778 .vb
5779   0 1 2 3 8 9 14 15 11 10 13 12 4 5 7 6
5780 .ve
5781 
5782   After calling DMPlexSetClosurePermutationTensor(), the closure will be ordered lexicographically,
5783 .vb
5784    4 8 9 5 12 0 1 14 13 2 3 15 6 10 11 7
5785 .ve
5786 
5787   Level: developer
5788 
5789   Notes:
5790   The point is used to determine the number of dofs/field on an edge. For SEM, this is related to the polynomial
5791   degree of the basis.
5792 
5793   This is required to run with libCEED.
5794 
5795 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMGetLocalSection()`, `PetscSectionSetClosurePermutation()`, `DMSetGlobalSection()`
5796 @*/
5797 PetscErrorCode DMPlexSetClosurePermutationTensor(DM dm, PetscInt point, PetscSection section)
5798 {
5799   DMLabel   label;
5800   PetscInt  dim, depth = -1, eStart = -1, Nf;
5801   PetscBool continuous = PETSC_TRUE, tensor = PETSC_TRUE;
5802 
5803   PetscFunctionBegin;
5804   PetscCall(DMGetDimension(dm, &dim));
5805   if (dim < 1) PetscFunctionReturn(PETSC_SUCCESS);
5806   if (point < 0) {
5807     PetscInt sStart, sEnd;
5808 
5809     PetscCall(DMPlexGetDepthStratum(dm, 1, &sStart, &sEnd));
5810     point = sEnd - sStart ? sStart : point;
5811   }
5812   PetscCall(DMPlexGetDepthLabel(dm, &label));
5813   if (point >= 0) PetscCall(DMLabelGetValue(label, point, &depth));
5814   if (!section) PetscCall(DMGetLocalSection(dm, &section));
5815   if (depth == 1) {
5816     eStart = point;
5817   } else if (depth == dim) {
5818     const PetscInt *cone;
5819 
5820     PetscCall(DMPlexGetCone(dm, point, &cone));
5821     if (dim == 2) eStart = cone[0];
5822     else if (dim == 3) {
5823       const PetscInt *cone2;
5824       PetscCall(DMPlexGetCone(dm, cone[0], &cone2));
5825       eStart = cone2[0];
5826     } 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);
5827   } 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);
5828 
5829   PetscCall(PetscSectionGetNumFields(section, &Nf));
5830   for (PetscInt d = 1; d <= dim; d++) {
5831     PetscInt  k, f, Nc, c, i, j, size = 0, offset = 0, foffset = 0;
5832     PetscInt *perm;
5833 
5834     for (f = 0; f < Nf; ++f) {
5835       PetscInt dof;
5836 
5837       PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
5838       PetscCheck(dim == 1 || tensor || !continuous, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Continuous field %" PetscInt_FMT " must have a tensor product discretization", f);
5839       if (!continuous && d < dim) continue;
5840       PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
5841       size += dof * Nc;
5842     }
5843     PetscCall(PetscMalloc1(size, &perm));
5844     for (f = 0; f < Nf; ++f) {
5845       switch (d) {
5846       case 1:
5847         PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
5848         if (!continuous && d < dim) continue;
5849         /*
5850          Original ordering is [ edge of length k-1; vtx0; vtx1 ]
5851          We want              [ vtx0; edge of length k-1; vtx1 ]
5852          */
5853         if (continuous) {
5854           for (c = 0; c < Nc; c++, offset++) perm[offset] = (k - 1) * Nc + c + foffset;
5855           for (i = 0; i < k - 1; i++)
5856             for (c = 0; c < Nc; c++, offset++) perm[offset] = i * Nc + c + foffset;
5857           for (c = 0; c < Nc; c++, offset++) perm[offset] = k * Nc + c + foffset;
5858           foffset = offset;
5859         } else {
5860           PetscInt dof;
5861 
5862           PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
5863           for (i = 0; i < dof * Nc; ++i, ++offset) perm[offset] = i + foffset;
5864           foffset = offset;
5865         }
5866         break;
5867       case 2:
5868         /* The original quad closure is oriented clockwise, {f, e_b, e_r, e_t, e_l, v_lb, v_rb, v_tr, v_tl} */
5869         PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
5870         if (!continuous && d < dim) continue;
5871         /* The SEM order is
5872 
5873          v_lb, {e_b}, v_rb,
5874          e^{(k-1)-i}_l, {f^{i*(k-1)}}, e^i_r,
5875          v_lt, reverse {e_t}, v_rt
5876          */
5877         if (continuous) {
5878           const PetscInt of   = 0;
5879           const PetscInt oeb  = of + PetscSqr(k - 1);
5880           const PetscInt oer  = oeb + (k - 1);
5881           const PetscInt oet  = oer + (k - 1);
5882           const PetscInt oel  = oet + (k - 1);
5883           const PetscInt ovlb = oel + (k - 1);
5884           const PetscInt ovrb = ovlb + 1;
5885           const PetscInt ovrt = ovrb + 1;
5886           const PetscInt ovlt = ovrt + 1;
5887           PetscInt       o;
5888 
5889           /* bottom */
5890           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlb * Nc + c + foffset;
5891           for (o = oeb; o < oer; ++o)
5892             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5893           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrb * Nc + c + foffset;
5894           /* middle */
5895           for (i = 0; i < k - 1; ++i) {
5896             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oel + (k - 2) - i) * Nc + c + foffset;
5897             for (o = of + (k - 1) * i; o < of + (k - 1) * (i + 1); ++o)
5898               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5899             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oer + i) * Nc + c + foffset;
5900           }
5901           /* top */
5902           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlt * Nc + c + foffset;
5903           for (o = oel - 1; o >= oet; --o)
5904             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5905           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrt * Nc + c + foffset;
5906           foffset = offset;
5907         } else {
5908           PetscInt dof;
5909 
5910           PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
5911           for (i = 0; i < dof * Nc; ++i, ++offset) perm[offset] = i + foffset;
5912           foffset = offset;
5913         }
5914         break;
5915       case 3:
5916         /* The original hex closure is
5917 
5918          {c,
5919          f_b, f_t, f_f, f_b, f_r, f_l,
5920          e_bl, e_bb, e_br, e_bf,  e_tf, e_tr, e_tb, e_tl,  e_rf, e_lf, e_lb, e_rb,
5921          v_blf, v_blb, v_brb, v_brf, v_tlf, v_trf, v_trb, v_tlb}
5922          */
5923         PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
5924         if (!continuous && d < dim) continue;
5925         /* The SEM order is
5926          Bottom Slice
5927          v_blf, {e^{(k-1)-n}_bf}, v_brf,
5928          e^{i}_bl, f^{n*(k-1)+(k-1)-i}_b, e^{(k-1)-i}_br,
5929          v_blb, {e_bb}, v_brb,
5930 
5931          Middle Slice (j)
5932          {e^{(k-1)-j}_lf}, {f^{j*(k-1)+n}_f}, e^j_rf,
5933          f^{i*(k-1)+j}_l, {c^{(j*(k-1) + i)*(k-1)+n}_t}, f^{j*(k-1)+i}_r,
5934          e^j_lb, {f^{j*(k-1)+(k-1)-n}_b}, e^{(k-1)-j}_rb,
5935 
5936          Top Slice
5937          v_tlf, {e_tf}, v_trf,
5938          e^{(k-1)-i}_tl, {f^{i*(k-1)}_t}, e^{i}_tr,
5939          v_tlb, {e^{(k-1)-n}_tb}, v_trb,
5940          */
5941         if (continuous) {
5942           const PetscInt oc    = 0;
5943           const PetscInt ofb   = oc + PetscSqr(k - 1) * (k - 1);
5944           const PetscInt oft   = ofb + PetscSqr(k - 1);
5945           const PetscInt off   = oft + PetscSqr(k - 1);
5946           const PetscInt ofk   = off + PetscSqr(k - 1);
5947           const PetscInt ofr   = ofk + PetscSqr(k - 1);
5948           const PetscInt ofl   = ofr + PetscSqr(k - 1);
5949           const PetscInt oebl  = ofl + PetscSqr(k - 1);
5950           const PetscInt oebb  = oebl + (k - 1);
5951           const PetscInt oebr  = oebb + (k - 1);
5952           const PetscInt oebf  = oebr + (k - 1);
5953           const PetscInt oetf  = oebf + (k - 1);
5954           const PetscInt oetr  = oetf + (k - 1);
5955           const PetscInt oetb  = oetr + (k - 1);
5956           const PetscInt oetl  = oetb + (k - 1);
5957           const PetscInt oerf  = oetl + (k - 1);
5958           const PetscInt oelf  = oerf + (k - 1);
5959           const PetscInt oelb  = oelf + (k - 1);
5960           const PetscInt oerb  = oelb + (k - 1);
5961           const PetscInt ovblf = oerb + (k - 1);
5962           const PetscInt ovblb = ovblf + 1;
5963           const PetscInt ovbrb = ovblb + 1;
5964           const PetscInt ovbrf = ovbrb + 1;
5965           const PetscInt ovtlf = ovbrf + 1;
5966           const PetscInt ovtrf = ovtlf + 1;
5967           const PetscInt ovtrb = ovtrf + 1;
5968           const PetscInt ovtlb = ovtrb + 1;
5969           PetscInt       o, n;
5970 
5971           /* Bottom Slice */
5972           /*   bottom */
5973           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblf * Nc + c + foffset;
5974           for (o = oetf - 1; o >= oebf; --o)
5975             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5976           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrf * Nc + c + foffset;
5977           /*   middle */
5978           for (i = 0; i < k - 1; ++i) {
5979             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebl + i) * Nc + c + foffset;
5980             for (n = 0; n < k - 1; ++n) {
5981               o = ofb + n * (k - 1) + i;
5982               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5983             }
5984             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebr + (k - 2) - i) * Nc + c + foffset;
5985           }
5986           /*   top */
5987           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblb * Nc + c + foffset;
5988           for (o = oebb; o < oebr; ++o)
5989             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5990           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrb * Nc + c + foffset;
5991 
5992           /* Middle Slice */
5993           for (j = 0; j < k - 1; ++j) {
5994             /*   bottom */
5995             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelf + (k - 2) - j) * Nc + c + foffset;
5996             for (o = off + j * (k - 1); o < off + (j + 1) * (k - 1); ++o)
5997               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5998             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerf + j) * Nc + c + foffset;
5999             /*   middle */
6000             for (i = 0; i < k - 1; ++i) {
6001               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofl + i * (k - 1) + j) * Nc + c + foffset;
6002               for (n = 0; n < k - 1; ++n)
6003                 for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oc + (j * (k - 1) + i) * (k - 1) + n) * Nc + c + foffset;
6004               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofr + j * (k - 1) + i) * Nc + c + foffset;
6005             }
6006             /*   top */
6007             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelb + j) * Nc + c + foffset;
6008             for (o = ofk + j * (k - 1) + (k - 2); o >= ofk + j * (k - 1); --o)
6009               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6010             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerb + (k - 2) - j) * Nc + c + foffset;
6011           }
6012 
6013           /* Top Slice */
6014           /*   bottom */
6015           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlf * Nc + c + foffset;
6016           for (o = oetf; o < oetr; ++o)
6017             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6018           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrf * Nc + c + foffset;
6019           /*   middle */
6020           for (i = 0; i < k - 1; ++i) {
6021             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetl + (k - 2) - i) * Nc + c + foffset;
6022             for (n = 0; n < k - 1; ++n)
6023               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oft + i * (k - 1) + n) * Nc + c + foffset;
6024             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetr + i) * Nc + c + foffset;
6025           }
6026           /*   top */
6027           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlb * Nc + c + foffset;
6028           for (o = oetl - 1; o >= oetb; --o)
6029             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6030           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrb * Nc + c + foffset;
6031 
6032           foffset = offset;
6033         } else {
6034           PetscInt dof;
6035 
6036           PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
6037           for (i = 0; i < dof * Nc; ++i, ++offset) perm[offset] = i + foffset;
6038           foffset = offset;
6039         }
6040         break;
6041       default:
6042         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "No spectral ordering for dimension %" PetscInt_FMT, d);
6043       }
6044     }
6045     PetscCheck(offset == size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Number of permutation entries %" PetscInt_FMT " != %" PetscInt_FMT, offset, size);
6046     /* Check permutation */
6047     {
6048       PetscInt *check;
6049 
6050       PetscCall(PetscMalloc1(size, &check));
6051       for (i = 0; i < size; ++i) {
6052         check[i] = -1;
6053         PetscCheck(perm[i] >= 0 && perm[i] < size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid permutation index p[%" PetscInt_FMT "] = %" PetscInt_FMT, i, perm[i]);
6054       }
6055       for (i = 0; i < size; ++i) check[perm[i]] = i;
6056       for (i = 0; i < size; ++i) PetscCheck(check[i] >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Missing permutation index %" PetscInt_FMT, i);
6057       PetscCall(PetscFree(check));
6058     }
6059     PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size, PETSC_OWN_POINTER, perm));
6060     if (d == dim) { // Add permutation for localized (in case this is a coordinate DM)
6061       PetscInt *loc_perm;
6062       PetscCall(PetscMalloc1(size * 2, &loc_perm));
6063       for (PetscInt i = 0; i < size; i++) {
6064         loc_perm[i]        = perm[i];
6065         loc_perm[size + i] = size + perm[i];
6066       }
6067       PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size * 2, PETSC_OWN_POINTER, loc_perm));
6068     }
6069   }
6070   PetscFunctionReturn(PETSC_SUCCESS);
6071 }
6072 
6073 PetscErrorCode DMPlexGetPointDualSpaceFEM(DM dm, PetscInt point, PetscInt field, PetscDualSpace *dspace)
6074 {
6075   PetscDS  prob;
6076   PetscInt depth, Nf, h;
6077   DMLabel  label;
6078 
6079   PetscFunctionBeginHot;
6080   PetscCall(DMGetDS(dm, &prob));
6081   Nf      = prob->Nf;
6082   label   = dm->depthLabel;
6083   *dspace = NULL;
6084   if (field < Nf) {
6085     PetscObject disc = prob->disc[field];
6086 
6087     if (disc->classid == PETSCFE_CLASSID) {
6088       PetscDualSpace dsp;
6089 
6090       PetscCall(PetscFEGetDualSpace((PetscFE)disc, &dsp));
6091       PetscCall(DMLabelGetNumValues(label, &depth));
6092       PetscCall(DMLabelGetValue(label, point, &h));
6093       h = depth - 1 - h;
6094       if (h) {
6095         PetscCall(PetscDualSpaceGetHeightSubspace(dsp, h, dspace));
6096       } else {
6097         *dspace = dsp;
6098       }
6099     }
6100   }
6101   PetscFunctionReturn(PETSC_SUCCESS);
6102 }
6103 
6104 static inline PetscErrorCode DMPlexVecGetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6105 {
6106   PetscScalar       *array;
6107   const PetscScalar *vArray;
6108   const PetscInt    *cone, *coneO;
6109   PetscInt           pStart, pEnd, p, numPoints, size = 0, offset = 0;
6110 
6111   PetscFunctionBeginHot;
6112   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6113   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
6114   PetscCall(DMPlexGetCone(dm, point, &cone));
6115   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
6116   if (!values || !*values) {
6117     if ((point >= pStart) && (point < pEnd)) {
6118       PetscInt dof;
6119 
6120       PetscCall(PetscSectionGetDof(section, point, &dof));
6121       size += dof;
6122     }
6123     for (p = 0; p < numPoints; ++p) {
6124       const PetscInt cp = cone[p];
6125       PetscInt       dof;
6126 
6127       if ((cp < pStart) || (cp >= pEnd)) continue;
6128       PetscCall(PetscSectionGetDof(section, cp, &dof));
6129       size += dof;
6130     }
6131     if (!values) {
6132       if (csize) *csize = size;
6133       PetscFunctionReturn(PETSC_SUCCESS);
6134     }
6135     PetscCall(DMGetWorkArray(dm, size, MPIU_SCALAR, &array));
6136   } else {
6137     array = *values;
6138   }
6139   size = 0;
6140   PetscCall(VecGetArrayRead(v, &vArray));
6141   if ((point >= pStart) && (point < pEnd)) {
6142     PetscInt           dof, off, d;
6143     const PetscScalar *varr;
6144 
6145     PetscCall(PetscSectionGetDof(section, point, &dof));
6146     PetscCall(PetscSectionGetOffset(section, point, &off));
6147     varr = PetscSafePointerPlusOffset(vArray, off);
6148     for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
6149     size += dof;
6150   }
6151   for (p = 0; p < numPoints; ++p) {
6152     const PetscInt     cp = cone[p];
6153     PetscInt           o  = coneO[p];
6154     PetscInt           dof, off, d;
6155     const PetscScalar *varr;
6156 
6157     if ((cp < pStart) || (cp >= pEnd)) continue;
6158     PetscCall(PetscSectionGetDof(section, cp, &dof));
6159     PetscCall(PetscSectionGetOffset(section, cp, &off));
6160     varr = PetscSafePointerPlusOffset(vArray, off);
6161     if (o >= 0) {
6162       for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
6163     } else {
6164       for (d = dof - 1; d >= 0; --d, ++offset) array[offset] = varr[d];
6165     }
6166     size += dof;
6167   }
6168   PetscCall(VecRestoreArrayRead(v, &vArray));
6169   if (!*values) {
6170     if (csize) *csize = size;
6171     *values = array;
6172   } else {
6173     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
6174     *csize = size;
6175   }
6176   PetscFunctionReturn(PETSC_SUCCESS);
6177 }
6178 
6179 /* Compress out points not in the section */
6180 static inline PetscErrorCode CompressPoints_Private(PetscSection section, PetscInt *numPoints, PetscInt points[])
6181 {
6182   const PetscInt np = *numPoints;
6183   PetscInt       pStart, pEnd, p, q;
6184 
6185   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6186   for (p = 0, q = 0; p < np; ++p) {
6187     const PetscInt r = points[p * 2];
6188     if ((r >= pStart) && (r < pEnd)) {
6189       points[q * 2]     = r;
6190       points[q * 2 + 1] = points[p * 2 + 1];
6191       ++q;
6192     }
6193   }
6194   *numPoints = q;
6195   return PETSC_SUCCESS;
6196 }
6197 
6198 /* Compressed closure does not apply closure permutation */
6199 PetscErrorCode DMPlexGetCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt ornt, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
6200 {
6201   const PetscInt *cla = NULL;
6202   PetscInt        np, *pts = NULL;
6203 
6204   PetscFunctionBeginHot;
6205   PetscCall(PetscSectionGetClosureIndex(section, (PetscObject)dm, clSec, clPoints));
6206   if (!ornt && *clPoints) {
6207     PetscInt dof, off;
6208 
6209     PetscCall(PetscSectionGetDof(*clSec, point, &dof));
6210     PetscCall(PetscSectionGetOffset(*clSec, point, &off));
6211     PetscCall(ISGetIndices(*clPoints, &cla));
6212     np  = dof / 2;
6213     pts = PetscSafePointerPlusOffset((PetscInt *)cla, off);
6214   } else {
6215     PetscCall(DMPlexGetTransitiveClosure_Internal(dm, point, ornt, PETSC_TRUE, &np, &pts));
6216     PetscCall(CompressPoints_Private(section, &np, pts));
6217   }
6218   *numPoints = np;
6219   *points    = pts;
6220   *clp       = cla;
6221   PetscFunctionReturn(PETSC_SUCCESS);
6222 }
6223 
6224 PetscErrorCode DMPlexRestoreCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
6225 {
6226   PetscFunctionBeginHot;
6227   if (!*clPoints) {
6228     PetscCall(DMPlexRestoreTransitiveClosure(dm, point, PETSC_TRUE, numPoints, points));
6229   } else {
6230     PetscCall(ISRestoreIndices(*clPoints, clp));
6231   }
6232   *numPoints = 0;
6233   *points    = NULL;
6234   *clSec     = NULL;
6235   *clPoints  = NULL;
6236   *clp       = NULL;
6237   PetscFunctionReturn(PETSC_SUCCESS);
6238 }
6239 
6240 static inline PetscErrorCode DMPlexVecGetClosure_Static(DM dm, PetscSection section, PetscInt numPoints, const PetscInt points[], const PetscInt clperm[], const PetscScalar vArray[], PetscInt *size, PetscScalar array[])
6241 {
6242   PetscInt            offset = 0, p;
6243   const PetscInt    **perms  = NULL;
6244   const PetscScalar **flips  = NULL;
6245 
6246   PetscFunctionBeginHot;
6247   *size = 0;
6248   PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
6249   for (p = 0; p < numPoints; p++) {
6250     const PetscInt     point = points[2 * p];
6251     const PetscInt    *perm  = perms ? perms[p] : NULL;
6252     const PetscScalar *flip  = flips ? flips[p] : NULL;
6253     PetscInt           dof, off, d;
6254     const PetscScalar *varr;
6255 
6256     PetscCall(PetscSectionGetDof(section, point, &dof));
6257     PetscCall(PetscSectionGetOffset(section, point, &off));
6258     varr = PetscSafePointerPlusOffset(vArray, off);
6259     if (clperm) {
6260       if (perm) {
6261         for (d = 0; d < dof; d++) array[clperm[offset + perm[d]]] = varr[d];
6262       } else {
6263         for (d = 0; d < dof; d++) array[clperm[offset + d]] = varr[d];
6264       }
6265       if (flip) {
6266         for (d = 0; d < dof; d++) array[clperm[offset + d]] *= flip[d];
6267       }
6268     } else {
6269       if (perm) {
6270         for (d = 0; d < dof; d++) array[offset + perm[d]] = varr[d];
6271       } else {
6272         for (d = 0; d < dof; d++) array[offset + d] = varr[d];
6273       }
6274       if (flip) {
6275         for (d = 0; d < dof; d++) array[offset + d] *= flip[d];
6276       }
6277     }
6278     offset += dof;
6279   }
6280   PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
6281   *size = offset;
6282   PetscFunctionReturn(PETSC_SUCCESS);
6283 }
6284 
6285 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[])
6286 {
6287   PetscInt offset = 0, f;
6288 
6289   PetscFunctionBeginHot;
6290   *size = 0;
6291   for (f = 0; f < numFields; ++f) {
6292     PetscInt            p;
6293     const PetscInt    **perms = NULL;
6294     const PetscScalar **flips = NULL;
6295 
6296     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6297     for (p = 0; p < numPoints; p++) {
6298       const PetscInt     point = points[2 * p];
6299       PetscInt           fdof, foff, b;
6300       const PetscScalar *varr;
6301       const PetscInt    *perm = perms ? perms[p] : NULL;
6302       const PetscScalar *flip = flips ? flips[p] : NULL;
6303 
6304       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6305       PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6306       varr = &vArray[foff];
6307       if (clperm) {
6308         if (perm) {
6309           for (b = 0; b < fdof; b++) array[clperm[offset + perm[b]]] = varr[b];
6310         } else {
6311           for (b = 0; b < fdof; b++) array[clperm[offset + b]] = varr[b];
6312         }
6313         if (flip) {
6314           for (b = 0; b < fdof; b++) array[clperm[offset + b]] *= flip[b];
6315         }
6316       } else {
6317         if (perm) {
6318           for (b = 0; b < fdof; b++) array[offset + perm[b]] = varr[b];
6319         } else {
6320           for (b = 0; b < fdof; b++) array[offset + b] = varr[b];
6321         }
6322         if (flip) {
6323           for (b = 0; b < fdof; b++) array[offset + b] *= flip[b];
6324         }
6325       }
6326       offset += fdof;
6327     }
6328     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6329   }
6330   *size = offset;
6331   PetscFunctionReturn(PETSC_SUCCESS);
6332 }
6333 
6334 PetscErrorCode DMPlexVecGetOrientedClosure_Internal(DM dm, PetscSection section, PetscBool useClPerm, Vec v, PetscInt point, PetscInt ornt, PetscInt *csize, PetscScalar *values[])
6335 {
6336   PetscSection    clSection;
6337   IS              clPoints;
6338   PetscInt       *points = NULL;
6339   const PetscInt *clp, *perm = NULL;
6340   PetscInt        depth, numFields, numPoints, asize;
6341 
6342   PetscFunctionBeginHot;
6343   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6344   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6345   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6346   PetscValidHeaderSpecific(v, VEC_CLASSID, 4);
6347   PetscCall(DMPlexGetDepth(dm, &depth));
6348   PetscCall(PetscSectionGetNumFields(section, &numFields));
6349   if (depth == 1 && numFields < 2) {
6350     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6351     PetscFunctionReturn(PETSC_SUCCESS);
6352   }
6353   /* Get points */
6354   PetscCall(DMPlexGetCompressedClosure(dm, section, point, ornt, &numPoints, &points, &clSection, &clPoints, &clp));
6355   /* Get sizes */
6356   asize = 0;
6357   for (PetscInt p = 0; p < numPoints * 2; p += 2) {
6358     PetscInt dof;
6359     PetscCall(PetscSectionGetDof(section, points[p], &dof));
6360     asize += dof;
6361   }
6362   if (values) {
6363     const PetscScalar *vArray;
6364     PetscInt           size;
6365 
6366     if (*values) {
6367       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);
6368     } else PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, values));
6369     if (useClPerm) PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, asize, &perm));
6370     PetscCall(VecGetArrayRead(v, &vArray));
6371     /* Get values */
6372     if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, numPoints, points, numFields, perm, vArray, &size, *values));
6373     else PetscCall(DMPlexVecGetClosure_Static(dm, section, numPoints, points, perm, vArray, &size, *values));
6374     PetscCheck(asize == size, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Section size %" PetscInt_FMT " does not match Vec closure size %" PetscInt_FMT, asize, size);
6375     /* Cleanup array */
6376     PetscCall(VecRestoreArrayRead(v, &vArray));
6377   }
6378   if (csize) *csize = asize;
6379   /* Cleanup points */
6380   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6381   PetscFunctionReturn(PETSC_SUCCESS);
6382 }
6383 
6384 /*@C
6385   DMPlexVecGetClosure - Get an array of the values on the closure of 'point'
6386 
6387   Not collective
6388 
6389   Input Parameters:
6390 + dm      - The `DM`
6391 . section - The section describing the layout in `v`, or `NULL` to use the default section
6392 . v       - The local vector
6393 - point   - The point in the `DM`
6394 
6395   Input/Output Parameters:
6396 + csize  - The size of the input values array, or `NULL`; on output the number of values in the closure
6397 - values - An array to use for the values, or `NULL` to have it allocated automatically;
6398            if the user provided `NULL`, it is a borrowed array and should not be freed
6399 
6400   Level: intermediate
6401 
6402   Notes:
6403   `DMPlexVecGetClosure()`/`DMPlexVecRestoreClosure()` only allocates the values array if it set to `NULL` in the
6404   calling function. This is because `DMPlexVecGetClosure()` is typically called in the inner loop of a `Vec` or `Mat`
6405   assembly function, and a user may already have allocated storage for this operation.
6406 
6407   A typical use could be
6408 .vb
6409    values = NULL;
6410    PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
6411    for (cl = 0; cl < clSize; ++cl) {
6412      <Compute on closure>
6413    }
6414    PetscCall(DMPlexVecRestoreClosure(dm, NULL, v, p, &clSize, &values));
6415 .ve
6416   or
6417 .vb
6418    PetscMalloc1(clMaxSize, &values);
6419    for (p = pStart; p < pEnd; ++p) {
6420      clSize = clMaxSize;
6421      PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
6422      for (cl = 0; cl < clSize; ++cl) {
6423        <Compute on closure>
6424      }
6425    }
6426    PetscFree(values);
6427 .ve
6428 
6429   Fortran Notes:
6430   The `csize` argument is not present in the Fortran binding since it is internal to the array.
6431 
6432 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecRestoreClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6433 @*/
6434 PetscErrorCode DMPlexVecGetClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6435 {
6436   PetscFunctionBeginHot;
6437   PetscCall(DMPlexVecGetOrientedClosure_Internal(dm, section, PETSC_TRUE, v, point, 0, csize, values));
6438   PetscFunctionReturn(PETSC_SUCCESS);
6439 }
6440 
6441 PetscErrorCode DMPlexVecGetClosureAtDepth_Internal(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt depth, PetscInt *csize, PetscScalar *values[])
6442 {
6443   DMLabel            depthLabel;
6444   PetscSection       clSection;
6445   IS                 clPoints;
6446   PetscScalar       *array;
6447   const PetscScalar *vArray;
6448   PetscInt          *points = NULL;
6449   const PetscInt    *clp, *perm = NULL;
6450   PetscInt           mdepth, numFields, numPoints, Np = 0, p, clsize, size;
6451 
6452   PetscFunctionBeginHot;
6453   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6454   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6455   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6456   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6457   PetscCall(DMPlexGetDepth(dm, &mdepth));
6458   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
6459   PetscCall(PetscSectionGetNumFields(section, &numFields));
6460   if (mdepth == 1 && numFields < 2) {
6461     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6462     PetscFunctionReturn(PETSC_SUCCESS);
6463   }
6464   /* Get points */
6465   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
6466   for (clsize = 0, p = 0; p < Np; p++) {
6467     PetscInt dof;
6468     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
6469     clsize += dof;
6470   }
6471   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &perm));
6472   /* Filter points */
6473   for (p = 0; p < numPoints * 2; p += 2) {
6474     PetscInt dep;
6475 
6476     PetscCall(DMLabelGetValue(depthLabel, points[p], &dep));
6477     if (dep != depth) continue;
6478     points[Np * 2 + 0] = points[p];
6479     points[Np * 2 + 1] = points[p + 1];
6480     ++Np;
6481   }
6482   /* Get array */
6483   if (!values || !*values) {
6484     PetscInt asize = 0, dof;
6485 
6486     for (p = 0; p < Np * 2; p += 2) {
6487       PetscCall(PetscSectionGetDof(section, points[p], &dof));
6488       asize += dof;
6489     }
6490     if (!values) {
6491       PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6492       if (csize) *csize = asize;
6493       PetscFunctionReturn(PETSC_SUCCESS);
6494     }
6495     PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, &array));
6496   } else {
6497     array = *values;
6498   }
6499   PetscCall(VecGetArrayRead(v, &vArray));
6500   /* Get values */
6501   if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, Np, points, numFields, perm, vArray, &size, array));
6502   else PetscCall(DMPlexVecGetClosure_Static(dm, section, Np, points, perm, vArray, &size, array));
6503   /* Cleanup points */
6504   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6505   /* Cleanup array */
6506   PetscCall(VecRestoreArrayRead(v, &vArray));
6507   if (!*values) {
6508     if (csize) *csize = size;
6509     *values = array;
6510   } else {
6511     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
6512     *csize = size;
6513   }
6514   PetscFunctionReturn(PETSC_SUCCESS);
6515 }
6516 
6517 /*@C
6518   DMPlexVecRestoreClosure - Restore the array of the values on the closure of 'point'
6519 
6520   Not collective
6521 
6522   Input Parameters:
6523 + dm      - The `DM`
6524 . section - The section describing the layout in `v`, or `NULL` to use the default section
6525 . v       - The local vector
6526 . point   - The point in the `DM`
6527 . csize   - The number of values in the closure, or `NULL`
6528 - values  - The array of values, which is a borrowed array and should not be freed
6529 
6530   Level: intermediate
6531 
6532   Note:
6533   The array values are discarded and not copied back into `v`. In order to copy values back to `v`, use `DMPlexVecSetClosure()`
6534 
6535   Fortran Notes:
6536   The `csize` argument is not present in the Fortran binding since it is internal to the array.
6537 
6538 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6539 @*/
6540 PetscErrorCode DMPlexVecRestoreClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6541 {
6542   PetscInt size = 0;
6543 
6544   PetscFunctionBegin;
6545   /* Should work without recalculating size */
6546   PetscCall(DMRestoreWorkArray(dm, size, MPIU_SCALAR, (void *)values));
6547   *values = NULL;
6548   PetscFunctionReturn(PETSC_SUCCESS);
6549 }
6550 
6551 static inline void add(PetscScalar *x, PetscScalar y)
6552 {
6553   *x += y;
6554 }
6555 static inline void insert(PetscScalar *x, PetscScalar y)
6556 {
6557   *x = y;
6558 }
6559 
6560 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[])
6561 {
6562   PetscInt        cdof;  /* The number of constraints on this point */
6563   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6564   PetscScalar    *a;
6565   PetscInt        off, cind = 0, k;
6566 
6567   PetscFunctionBegin;
6568   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6569   PetscCall(PetscSectionGetOffset(section, point, &off));
6570   a = &array[off];
6571   if (!cdof || setBC) {
6572     if (clperm) {
6573       if (perm) {
6574         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6575       } else {
6576         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6577       }
6578     } else {
6579       if (perm) {
6580         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6581       } else {
6582         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6583       }
6584     }
6585   } else {
6586     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6587     if (clperm) {
6588       if (perm) {
6589         for (k = 0; k < dof; ++k) {
6590           if ((cind < cdof) && (k == cdofs[cind])) {
6591             ++cind;
6592             continue;
6593           }
6594           fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6595         }
6596       } else {
6597         for (k = 0; k < dof; ++k) {
6598           if ((cind < cdof) && (k == cdofs[cind])) {
6599             ++cind;
6600             continue;
6601           }
6602           fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6603         }
6604       }
6605     } else {
6606       if (perm) {
6607         for (k = 0; k < dof; ++k) {
6608           if ((cind < cdof) && (k == cdofs[cind])) {
6609             ++cind;
6610             continue;
6611           }
6612           fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6613         }
6614       } else {
6615         for (k = 0; k < dof; ++k) {
6616           if ((cind < cdof) && (k == cdofs[cind])) {
6617             ++cind;
6618             continue;
6619           }
6620           fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6621         }
6622       }
6623     }
6624   }
6625   PetscFunctionReturn(PETSC_SUCCESS);
6626 }
6627 
6628 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[])
6629 {
6630   PetscInt        cdof;  /* The number of constraints on this point */
6631   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6632   PetscScalar    *a;
6633   PetscInt        off, cind = 0, k;
6634 
6635   PetscFunctionBegin;
6636   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6637   PetscCall(PetscSectionGetOffset(section, point, &off));
6638   a = &array[off];
6639   if (cdof) {
6640     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6641     if (clperm) {
6642       if (perm) {
6643         for (k = 0; k < dof; ++k) {
6644           if ((cind < cdof) && (k == cdofs[cind])) {
6645             fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6646             cind++;
6647           }
6648         }
6649       } else {
6650         for (k = 0; k < dof; ++k) {
6651           if ((cind < cdof) && (k == cdofs[cind])) {
6652             fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6653             cind++;
6654           }
6655         }
6656       }
6657     } else {
6658       if (perm) {
6659         for (k = 0; k < dof; ++k) {
6660           if ((cind < cdof) && (k == cdofs[cind])) {
6661             fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6662             cind++;
6663           }
6664         }
6665       } else {
6666         for (k = 0; k < dof; ++k) {
6667           if ((cind < cdof) && (k == cdofs[cind])) {
6668             fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6669             cind++;
6670           }
6671         }
6672       }
6673     }
6674   }
6675   PetscFunctionReturn(PETSC_SUCCESS);
6676 }
6677 
6678 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[])
6679 {
6680   PetscScalar    *a;
6681   PetscInt        fdof, foff, fcdof, foffset = *offset;
6682   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6683   PetscInt        cind = 0, b;
6684 
6685   PetscFunctionBegin;
6686   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6687   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6688   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6689   a = &array[foff];
6690   if (!fcdof || setBC) {
6691     if (clperm) {
6692       if (perm) {
6693         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6694       } else {
6695         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6696       }
6697     } else {
6698       if (perm) {
6699         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6700       } else {
6701         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6702       }
6703     }
6704   } else {
6705     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6706     if (clperm) {
6707       if (perm) {
6708         for (b = 0; b < fdof; b++) {
6709           if ((cind < fcdof) && (b == fcdofs[cind])) {
6710             ++cind;
6711             continue;
6712           }
6713           fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6714         }
6715       } else {
6716         for (b = 0; b < fdof; b++) {
6717           if ((cind < fcdof) && (b == fcdofs[cind])) {
6718             ++cind;
6719             continue;
6720           }
6721           fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6722         }
6723       }
6724     } else {
6725       if (perm) {
6726         for (b = 0; b < fdof; b++) {
6727           if ((cind < fcdof) && (b == fcdofs[cind])) {
6728             ++cind;
6729             continue;
6730           }
6731           fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6732         }
6733       } else {
6734         for (b = 0; b < fdof; b++) {
6735           if ((cind < fcdof) && (b == fcdofs[cind])) {
6736             ++cind;
6737             continue;
6738           }
6739           fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6740         }
6741       }
6742     }
6743   }
6744   *offset += fdof;
6745   PetscFunctionReturn(PETSC_SUCCESS);
6746 }
6747 
6748 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[])
6749 {
6750   PetscScalar    *a;
6751   PetscInt        fdof, foff, fcdof, foffset = *offset;
6752   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6753   PetscInt        Nc, cind = 0, ncind = 0, b;
6754   PetscBool       ncSet, fcSet;
6755 
6756   PetscFunctionBegin;
6757   PetscCall(PetscSectionGetFieldComponents(section, f, &Nc));
6758   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6759   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6760   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6761   a = &array[foff];
6762   if (fcdof) {
6763     /* We just override fcdof and fcdofs with Ncc and comps */
6764     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6765     if (clperm) {
6766       if (perm) {
6767         if (comps) {
6768           for (b = 0; b < fdof; b++) {
6769             ncSet = fcSet = PETSC_FALSE;
6770             if (b % Nc == comps[ncind]) {
6771               ncind = (ncind + 1) % Ncc;
6772               ncSet = PETSC_TRUE;
6773             }
6774             if ((cind < fcdof) && (b == fcdofs[cind])) {
6775               ++cind;
6776               fcSet = PETSC_TRUE;
6777             }
6778             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6779           }
6780         } else {
6781           for (b = 0; b < fdof; b++) {
6782             if ((cind < fcdof) && (b == fcdofs[cind])) {
6783               fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6784               ++cind;
6785             }
6786           }
6787         }
6788       } else {
6789         if (comps) {
6790           for (b = 0; b < fdof; b++) {
6791             ncSet = fcSet = PETSC_FALSE;
6792             if (b % Nc == comps[ncind]) {
6793               ncind = (ncind + 1) % Ncc;
6794               ncSet = PETSC_TRUE;
6795             }
6796             if ((cind < fcdof) && (b == fcdofs[cind])) {
6797               ++cind;
6798               fcSet = PETSC_TRUE;
6799             }
6800             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6801           }
6802         } else {
6803           for (b = 0; b < fdof; b++) {
6804             if ((cind < fcdof) && (b == fcdofs[cind])) {
6805               fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6806               ++cind;
6807             }
6808           }
6809         }
6810       }
6811     } else {
6812       if (perm) {
6813         if (comps) {
6814           for (b = 0; b < fdof; b++) {
6815             ncSet = fcSet = PETSC_FALSE;
6816             if (b % Nc == comps[ncind]) {
6817               ncind = (ncind + 1) % Ncc;
6818               ncSet = PETSC_TRUE;
6819             }
6820             if ((cind < fcdof) && (b == fcdofs[cind])) {
6821               ++cind;
6822               fcSet = PETSC_TRUE;
6823             }
6824             if (ncSet && fcSet) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6825           }
6826         } else {
6827           for (b = 0; b < fdof; b++) {
6828             if ((cind < fcdof) && (b == fcdofs[cind])) {
6829               fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6830               ++cind;
6831             }
6832           }
6833         }
6834       } else {
6835         if (comps) {
6836           for (b = 0; b < fdof; b++) {
6837             ncSet = fcSet = PETSC_FALSE;
6838             if (b % Nc == comps[ncind]) {
6839               ncind = (ncind + 1) % Ncc;
6840               ncSet = PETSC_TRUE;
6841             }
6842             if ((cind < fcdof) && (b == fcdofs[cind])) {
6843               ++cind;
6844               fcSet = PETSC_TRUE;
6845             }
6846             if (ncSet && fcSet) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6847           }
6848         } else {
6849           for (b = 0; b < fdof; b++) {
6850             if ((cind < fcdof) && (b == fcdofs[cind])) {
6851               fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6852               ++cind;
6853             }
6854           }
6855         }
6856       }
6857     }
6858   }
6859   *offset += fdof;
6860   PetscFunctionReturn(PETSC_SUCCESS);
6861 }
6862 
6863 static inline PetscErrorCode DMPlexVecSetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6864 {
6865   PetscScalar    *array;
6866   const PetscInt *cone, *coneO;
6867   PetscInt        pStart, pEnd, p, numPoints, off, dof;
6868 
6869   PetscFunctionBeginHot;
6870   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6871   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
6872   PetscCall(DMPlexGetCone(dm, point, &cone));
6873   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
6874   PetscCall(VecGetArray(v, &array));
6875   for (p = 0, off = 0; p <= numPoints; ++p, off += dof) {
6876     const PetscInt cp = !p ? point : cone[p - 1];
6877     const PetscInt o  = !p ? 0 : coneO[p - 1];
6878 
6879     if ((cp < pStart) || (cp >= pEnd)) {
6880       dof = 0;
6881       continue;
6882     }
6883     PetscCall(PetscSectionGetDof(section, cp, &dof));
6884     /* ADD_VALUES */
6885     {
6886       const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6887       PetscScalar    *a;
6888       PetscInt        cdof, coff, cind = 0, k;
6889 
6890       PetscCall(PetscSectionGetConstraintDof(section, cp, &cdof));
6891       PetscCall(PetscSectionGetOffset(section, cp, &coff));
6892       a = &array[coff];
6893       if (!cdof) {
6894         if (o >= 0) {
6895           for (k = 0; k < dof; ++k) a[k] += values[off + k];
6896         } else {
6897           for (k = 0; k < dof; ++k) a[k] += values[off + dof - k - 1];
6898         }
6899       } else {
6900         PetscCall(PetscSectionGetConstraintIndices(section, cp, &cdofs));
6901         if (o >= 0) {
6902           for (k = 0; k < dof; ++k) {
6903             if ((cind < cdof) && (k == cdofs[cind])) {
6904               ++cind;
6905               continue;
6906             }
6907             a[k] += values[off + k];
6908           }
6909         } else {
6910           for (k = 0; k < dof; ++k) {
6911             if ((cind < cdof) && (k == cdofs[cind])) {
6912               ++cind;
6913               continue;
6914             }
6915             a[k] += values[off + dof - k - 1];
6916           }
6917         }
6918       }
6919     }
6920   }
6921   PetscCall(VecRestoreArray(v, &array));
6922   PetscFunctionReturn(PETSC_SUCCESS);
6923 }
6924 
6925 /*@C
6926   DMPlexVecSetClosure - Set an array of the values on the closure of `point`
6927 
6928   Not collective
6929 
6930   Input Parameters:
6931 + dm      - The `DM`
6932 . section - The section describing the layout in `v`, or `NULL` to use the default section
6933 . v       - The local vector
6934 . point   - The point in the `DM`
6935 . values  - The array of values
6936 - mode    - The insert mode. One of `INSERT_ALL_VALUES`, `ADD_ALL_VALUES`, `INSERT_VALUES`, `ADD_VALUES`, `INSERT_BC_VALUES`, and `ADD_BC_VALUES`,
6937          where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions.
6938 
6939   Level: intermediate
6940 
6941 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`
6942 @*/
6943 PetscErrorCode DMPlexVecSetClosure(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6944 {
6945   PetscSection    clSection;
6946   IS              clPoints;
6947   PetscScalar    *array;
6948   PetscInt       *points = NULL;
6949   const PetscInt *clp, *clperm = NULL;
6950   PetscInt        depth, numFields, numPoints, p, clsize;
6951 
6952   PetscFunctionBeginHot;
6953   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6954   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6955   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6956   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6957   PetscCall(DMPlexGetDepth(dm, &depth));
6958   PetscCall(PetscSectionGetNumFields(section, &numFields));
6959   if (depth == 1 && numFields < 2 && mode == ADD_VALUES) {
6960     PetscCall(DMPlexVecSetClosure_Depth1_Static(dm, section, v, point, values, mode));
6961     PetscFunctionReturn(PETSC_SUCCESS);
6962   }
6963   /* Get points */
6964   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
6965   for (clsize = 0, p = 0; p < numPoints; p++) {
6966     PetscInt dof;
6967     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
6968     clsize += dof;
6969   }
6970   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
6971   /* Get array */
6972   PetscCall(VecGetArray(v, &array));
6973   /* Get values */
6974   if (numFields > 0) {
6975     PetscInt offset = 0, f;
6976     for (f = 0; f < numFields; ++f) {
6977       const PetscInt    **perms = NULL;
6978       const PetscScalar **flips = NULL;
6979 
6980       PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6981       switch (mode) {
6982       case INSERT_VALUES:
6983         for (p = 0; p < numPoints; p++) {
6984           const PetscInt     point = points[2 * p];
6985           const PetscInt    *perm  = perms ? perms[p] : NULL;
6986           const PetscScalar *flip  = flips ? flips[p] : NULL;
6987           PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, clperm, values, &offset, array));
6988         }
6989         break;
6990       case INSERT_ALL_VALUES:
6991         for (p = 0; p < numPoints; p++) {
6992           const PetscInt     point = points[2 * p];
6993           const PetscInt    *perm  = perms ? perms[p] : NULL;
6994           const PetscScalar *flip  = flips ? flips[p] : NULL;
6995           PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, clperm, values, &offset, array));
6996         }
6997         break;
6998       case INSERT_BC_VALUES:
6999         for (p = 0; p < numPoints; p++) {
7000           const PetscInt     point = points[2 * p];
7001           const PetscInt    *perm  = perms ? perms[p] : NULL;
7002           const PetscScalar *flip  = flips ? flips[p] : NULL;
7003           PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, insert, clperm, values, &offset, array));
7004         }
7005         break;
7006       case ADD_VALUES:
7007         for (p = 0; p < numPoints; p++) {
7008           const PetscInt     point = points[2 * p];
7009           const PetscInt    *perm  = perms ? perms[p] : NULL;
7010           const PetscScalar *flip  = flips ? flips[p] : NULL;
7011           PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, clperm, values, &offset, array));
7012         }
7013         break;
7014       case ADD_ALL_VALUES:
7015         for (p = 0; p < numPoints; p++) {
7016           const PetscInt     point = points[2 * p];
7017           const PetscInt    *perm  = perms ? perms[p] : NULL;
7018           const PetscScalar *flip  = flips ? flips[p] : NULL;
7019           PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, clperm, values, &offset, array));
7020         }
7021         break;
7022       case ADD_BC_VALUES:
7023         for (p = 0; p < numPoints; p++) {
7024           const PetscInt     point = points[2 * p];
7025           const PetscInt    *perm  = perms ? perms[p] : NULL;
7026           const PetscScalar *flip  = flips ? flips[p] : NULL;
7027           PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, add, clperm, values, &offset, array));
7028         }
7029         break;
7030       default:
7031         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
7032       }
7033       PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7034     }
7035   } else {
7036     PetscInt            dof, off;
7037     const PetscInt    **perms = NULL;
7038     const PetscScalar **flips = NULL;
7039 
7040     PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
7041     switch (mode) {
7042     case INSERT_VALUES:
7043       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7044         const PetscInt     point = points[2 * p];
7045         const PetscInt    *perm  = perms ? perms[p] : NULL;
7046         const PetscScalar *flip  = flips ? flips[p] : NULL;
7047         PetscCall(PetscSectionGetDof(section, point, &dof));
7048         PetscCall(updatePoint_private(section, point, dof, insert, PETSC_FALSE, perm, flip, clperm, values, off, array));
7049       }
7050       break;
7051     case INSERT_ALL_VALUES:
7052       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7053         const PetscInt     point = points[2 * p];
7054         const PetscInt    *perm  = perms ? perms[p] : NULL;
7055         const PetscScalar *flip  = flips ? flips[p] : NULL;
7056         PetscCall(PetscSectionGetDof(section, point, &dof));
7057         PetscCall(updatePoint_private(section, point, dof, insert, PETSC_TRUE, perm, flip, clperm, values, off, array));
7058       }
7059       break;
7060     case INSERT_BC_VALUES:
7061       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7062         const PetscInt     point = points[2 * p];
7063         const PetscInt    *perm  = perms ? perms[p] : NULL;
7064         const PetscScalar *flip  = flips ? flips[p] : NULL;
7065         PetscCall(PetscSectionGetDof(section, point, &dof));
7066         PetscCall(updatePointBC_private(section, point, dof, insert, perm, flip, clperm, values, off, array));
7067       }
7068       break;
7069     case ADD_VALUES:
7070       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7071         const PetscInt     point = points[2 * p];
7072         const PetscInt    *perm  = perms ? perms[p] : NULL;
7073         const PetscScalar *flip  = flips ? flips[p] : NULL;
7074         PetscCall(PetscSectionGetDof(section, point, &dof));
7075         PetscCall(updatePoint_private(section, point, dof, add, PETSC_FALSE, perm, flip, clperm, values, off, array));
7076       }
7077       break;
7078     case ADD_ALL_VALUES:
7079       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7080         const PetscInt     point = points[2 * p];
7081         const PetscInt    *perm  = perms ? perms[p] : NULL;
7082         const PetscScalar *flip  = flips ? flips[p] : NULL;
7083         PetscCall(PetscSectionGetDof(section, point, &dof));
7084         PetscCall(updatePoint_private(section, point, dof, add, PETSC_TRUE, perm, flip, clperm, values, off, array));
7085       }
7086       break;
7087     case ADD_BC_VALUES:
7088       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7089         const PetscInt     point = points[2 * p];
7090         const PetscInt    *perm  = perms ? perms[p] : NULL;
7091         const PetscScalar *flip  = flips ? flips[p] : NULL;
7092         PetscCall(PetscSectionGetDof(section, point, &dof));
7093         PetscCall(updatePointBC_private(section, point, dof, add, perm, flip, clperm, values, off, array));
7094       }
7095       break;
7096     default:
7097       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
7098     }
7099     PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
7100   }
7101   /* Cleanup points */
7102   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
7103   /* Cleanup array */
7104   PetscCall(VecRestoreArray(v, &array));
7105   PetscFunctionReturn(PETSC_SUCCESS);
7106 }
7107 
7108 /* Check whether the given point is in the label. If not, update the offset to skip this point */
7109 static inline PetscErrorCode CheckPoint_Private(DMLabel label, PetscInt labelId, PetscSection section, PetscInt point, PetscInt f, PetscInt *offset, PetscBool *contains)
7110 {
7111   PetscFunctionBegin;
7112   *contains = PETSC_TRUE;
7113   if (label) {
7114     PetscInt fdof;
7115 
7116     PetscCall(DMLabelStratumHasPoint(label, labelId, point, contains));
7117     if (!*contains) {
7118       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7119       *offset += fdof;
7120       PetscFunctionReturn(PETSC_SUCCESS);
7121     }
7122   }
7123   PetscFunctionReturn(PETSC_SUCCESS);
7124 }
7125 
7126 /* Unlike DMPlexVecSetClosure(), this uses plex-native closure permutation, not a user-specified permutation such as DMPlexSetClosurePermutationTensor(). */
7127 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)
7128 {
7129   PetscSection    clSection;
7130   IS              clPoints;
7131   PetscScalar    *array;
7132   PetscInt       *points = NULL;
7133   const PetscInt *clp;
7134   PetscInt        numFields, numPoints, p;
7135   PetscInt        offset = 0, f;
7136 
7137   PetscFunctionBeginHot;
7138   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7139   if (!section) PetscCall(DMGetLocalSection(dm, &section));
7140   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7141   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
7142   PetscCall(PetscSectionGetNumFields(section, &numFields));
7143   /* Get points */
7144   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
7145   /* Get array */
7146   PetscCall(VecGetArray(v, &array));
7147   /* Get values */
7148   for (f = 0; f < numFields; ++f) {
7149     const PetscInt    **perms = NULL;
7150     const PetscScalar **flips = NULL;
7151     PetscBool           contains;
7152 
7153     if (!fieldActive[f]) {
7154       for (p = 0; p < numPoints * 2; p += 2) {
7155         PetscInt fdof;
7156         PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
7157         offset += fdof;
7158       }
7159       continue;
7160     }
7161     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7162     switch (mode) {
7163     case INSERT_VALUES:
7164       for (p = 0; p < numPoints; p++) {
7165         const PetscInt     point = points[2 * p];
7166         const PetscInt    *perm  = perms ? perms[p] : NULL;
7167         const PetscScalar *flip  = flips ? flips[p] : NULL;
7168         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7169         if (!contains) continue;
7170         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, NULL, values, &offset, array));
7171       }
7172       break;
7173     case INSERT_ALL_VALUES:
7174       for (p = 0; p < numPoints; p++) {
7175         const PetscInt     point = points[2 * p];
7176         const PetscInt    *perm  = perms ? perms[p] : NULL;
7177         const PetscScalar *flip  = flips ? flips[p] : NULL;
7178         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7179         if (!contains) continue;
7180         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, NULL, values, &offset, array));
7181       }
7182       break;
7183     case INSERT_BC_VALUES:
7184       for (p = 0; p < numPoints; p++) {
7185         const PetscInt     point = points[2 * p];
7186         const PetscInt    *perm  = perms ? perms[p] : NULL;
7187         const PetscScalar *flip  = flips ? flips[p] : NULL;
7188         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7189         if (!contains) continue;
7190         PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, Ncc, comps, insert, NULL, values, &offset, array));
7191       }
7192       break;
7193     case ADD_VALUES:
7194       for (p = 0; p < numPoints; p++) {
7195         const PetscInt     point = points[2 * p];
7196         const PetscInt    *perm  = perms ? perms[p] : NULL;
7197         const PetscScalar *flip  = flips ? flips[p] : NULL;
7198         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7199         if (!contains) continue;
7200         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, NULL, values, &offset, array));
7201       }
7202       break;
7203     case ADD_ALL_VALUES:
7204       for (p = 0; p < numPoints; p++) {
7205         const PetscInt     point = points[2 * p];
7206         const PetscInt    *perm  = perms ? perms[p] : NULL;
7207         const PetscScalar *flip  = flips ? flips[p] : NULL;
7208         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7209         if (!contains) continue;
7210         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, NULL, values, &offset, array));
7211       }
7212       break;
7213     default:
7214       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
7215     }
7216     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7217   }
7218   /* Cleanup points */
7219   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
7220   /* Cleanup array */
7221   PetscCall(VecRestoreArray(v, &array));
7222   PetscFunctionReturn(PETSC_SUCCESS);
7223 }
7224 
7225 static PetscErrorCode DMPlexPrintMatSetValues(PetscViewer viewer, Mat A, PetscInt point, PetscInt numRIndices, const PetscInt rindices[], PetscInt numCIndices, const PetscInt cindices[], const PetscScalar values[])
7226 {
7227   PetscMPIInt rank;
7228   PetscInt    i, j;
7229 
7230   PetscFunctionBegin;
7231   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7232   PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat for point %" PetscInt_FMT "\n", rank, point));
7233   for (i = 0; i < numRIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat row indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, rindices[i]));
7234   for (i = 0; i < numCIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat col indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, cindices[i]));
7235   numCIndices = numCIndices ? numCIndices : numRIndices;
7236   if (!values) PetscFunctionReturn(PETSC_SUCCESS);
7237   for (i = 0; i < numRIndices; i++) {
7238     PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]", rank));
7239     for (j = 0; j < numCIndices; j++) {
7240 #if defined(PETSC_USE_COMPLEX)
7241       PetscCall(PetscViewerASCIIPrintf(viewer, " (%g,%g)", (double)PetscRealPart(values[i * numCIndices + j]), (double)PetscImaginaryPart(values[i * numCIndices + j])));
7242 #else
7243       PetscCall(PetscViewerASCIIPrintf(viewer, " %g", (double)values[i * numCIndices + j]));
7244 #endif
7245     }
7246     PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
7247   }
7248   PetscFunctionReturn(PETSC_SUCCESS);
7249 }
7250 
7251 /*
7252   DMPlexGetIndicesPoint_Internal - Add the indices for dofs on a point to an index array
7253 
7254   Input Parameters:
7255 + section - The section for this data layout
7256 . islocal - Is the section (and thus indices being requested) local or global?
7257 . point   - The point contributing dofs with these indices
7258 . off     - The global offset of this point
7259 . loff    - The local offset of each field
7260 . setBC   - The flag determining whether to include indices of boundary values
7261 . perm    - A permutation of the dofs on this point, or NULL
7262 - indperm - A permutation of the entire indices array, or NULL
7263 
7264   Output Parameter:
7265 . indices - Indices for dofs on this point
7266 
7267   Level: developer
7268 
7269   Note: The indices could be local or global, depending on the value of 'off'.
7270 */
7271 PetscErrorCode DMPlexGetIndicesPoint_Internal(PetscSection section, PetscBool islocal, PetscInt point, PetscInt off, PetscInt *loff, PetscBool setBC, const PetscInt perm[], const PetscInt indperm[], PetscInt indices[])
7272 {
7273   PetscInt        dof;   /* The number of unknowns on this point */
7274   PetscInt        cdof;  /* The number of constraints on this point */
7275   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
7276   PetscInt        cind = 0, k;
7277 
7278   PetscFunctionBegin;
7279   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
7280   PetscCall(PetscSectionGetDof(section, point, &dof));
7281   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
7282   if (!cdof || setBC) {
7283     for (k = 0; k < dof; ++k) {
7284       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
7285       const PetscInt ind    = indperm ? indperm[preind] : preind;
7286 
7287       indices[ind] = off + k;
7288     }
7289   } else {
7290     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
7291     for (k = 0; k < dof; ++k) {
7292       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
7293       const PetscInt ind    = indperm ? indperm[preind] : preind;
7294 
7295       if ((cind < cdof) && (k == cdofs[cind])) {
7296         /* Insert check for returning constrained indices */
7297         indices[ind] = -(off + k + 1);
7298         ++cind;
7299       } else {
7300         indices[ind] = off + k - (islocal ? 0 : cind);
7301       }
7302     }
7303   }
7304   *loff += dof;
7305   PetscFunctionReturn(PETSC_SUCCESS);
7306 }
7307 
7308 /*
7309  DMPlexGetIndicesPointFields_Internal - gets section indices for a point in its canonical ordering.
7310 
7311  Input Parameters:
7312 + section - a section (global or local)
7313 - islocal - `PETSC_TRUE` if requesting local indices (i.e., section is local); `PETSC_FALSE` for global
7314 . point - point within section
7315 . off - The offset of this point in the (local or global) indexed space - should match islocal and (usually) the section
7316 . foffs - array of length numFields containing the offset in canonical point ordering (the location in indices) of each field
7317 . setBC - identify constrained (boundary condition) points via involution.
7318 . perms - perms[f][permsoff][:] is a permutation of dofs within each field
7319 . permsoff - offset
7320 - indperm - index permutation
7321 
7322  Output Parameter:
7323 . foffs - each entry is incremented by the number of (unconstrained if setBC=FALSE) dofs in that field
7324 . indices - array to hold indices (as defined by section) of each dof associated with point
7325 
7326  Notes:
7327  If section is local and setBC=true, there is no distinction between constrained and unconstrained dofs.
7328  If section is local and setBC=false, the indices for constrained points are the involution -(i+1) of their position
7329  in the local vector.
7330 
7331  If section is global and setBC=false, the indices for constrained points are negative (and their value is not
7332  significant).  It is invalid to call with a global section and setBC=true.
7333 
7334  Developer Note:
7335  The section is only used for field layout, so islocal is technically a statement about the offset (off).  At some point
7336  in the future, global sections may have fields set, in which case we could pass the global section and obtain the
7337  offset could be obtained from the section instead of passing it explicitly as we do now.
7338 
7339  Example:
7340  Suppose a point contains one field with three components, and for which the unconstrained indices are {10, 11, 12}.
7341  When the middle component is constrained, we get the array {10, -12, 12} for (islocal=TRUE, setBC=FALSE).
7342  Note that -12 is the involution of 11, so the user can involute negative indices to recover local indices.
7343  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.
7344 
7345  Level: developer
7346 */
7347 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[])
7348 {
7349   PetscInt numFields, foff, f;
7350 
7351   PetscFunctionBegin;
7352   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
7353   PetscCall(PetscSectionGetNumFields(section, &numFields));
7354   for (f = 0, foff = 0; f < numFields; ++f) {
7355     PetscInt        fdof, cfdof;
7356     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7357     PetscInt        cind = 0, b;
7358     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7359 
7360     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7361     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7362     if (!cfdof || setBC) {
7363       for (b = 0; b < fdof; ++b) {
7364         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7365         const PetscInt ind    = indperm ? indperm[preind] : preind;
7366 
7367         indices[ind] = off + foff + b;
7368       }
7369     } else {
7370       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7371       for (b = 0; b < fdof; ++b) {
7372         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7373         const PetscInt ind    = indperm ? indperm[preind] : preind;
7374 
7375         if ((cind < cfdof) && (b == fcdofs[cind])) {
7376           indices[ind] = -(off + foff + b + 1);
7377           ++cind;
7378         } else {
7379           indices[ind] = off + foff + b - (islocal ? 0 : cind);
7380         }
7381       }
7382     }
7383     foff += (setBC || islocal ? fdof : (fdof - cfdof));
7384     foffs[f] += fdof;
7385   }
7386   PetscFunctionReturn(PETSC_SUCCESS);
7387 }
7388 
7389 /*
7390   This version believes the globalSection offsets for each field, rather than just the point offset
7391 
7392  . foffs - The offset into 'indices' for each field, since it is segregated by field
7393 
7394  Notes:
7395  The semantics of this function relate to that of setBC=FALSE in DMPlexGetIndicesPointFields_Internal.
7396  Since this function uses global indices, setBC=TRUE would be invalid, so no such argument exists.
7397 */
7398 static PetscErrorCode DMPlexGetIndicesPointFieldsSplit_Internal(PetscSection section, PetscSection globalSection, PetscInt point, PetscInt foffs[], const PetscInt ***perms, PetscInt permsoff, const PetscInt indperm[], PetscInt indices[])
7399 {
7400   PetscInt numFields, foff, f;
7401 
7402   PetscFunctionBegin;
7403   PetscCall(PetscSectionGetNumFields(section, &numFields));
7404   for (f = 0; f < numFields; ++f) {
7405     PetscInt        fdof, cfdof;
7406     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7407     PetscInt        cind = 0, b;
7408     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7409 
7410     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7411     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7412     PetscCall(PetscSectionGetFieldOffset(globalSection, point, f, &foff));
7413     if (!cfdof) {
7414       for (b = 0; b < fdof; ++b) {
7415         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7416         const PetscInt ind    = indperm ? indperm[preind] : preind;
7417 
7418         indices[ind] = foff + b;
7419       }
7420     } else {
7421       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7422       for (b = 0; b < fdof; ++b) {
7423         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7424         const PetscInt ind    = indperm ? indperm[preind] : preind;
7425 
7426         if ((cind < cfdof) && (b == fcdofs[cind])) {
7427           indices[ind] = -(foff + b + 1);
7428           ++cind;
7429         } else {
7430           indices[ind] = foff + b - cind;
7431         }
7432       }
7433     }
7434     foffs[f] += fdof;
7435   }
7436   PetscFunctionReturn(PETSC_SUCCESS);
7437 }
7438 
7439 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)
7440 {
7441   Mat             cMat;
7442   PetscSection    aSec, cSec;
7443   IS              aIS;
7444   PetscInt        aStart = -1, aEnd = -1;
7445   const PetscInt *anchors;
7446   PetscInt        numFields, f, p, q, newP = 0;
7447   PetscInt        newNumPoints = 0, newNumIndices = 0;
7448   PetscInt       *newPoints, *indices, *newIndices;
7449   PetscInt        maxAnchor, maxDof;
7450   PetscInt        newOffsets[32];
7451   PetscInt       *pointMatOffsets[32];
7452   PetscInt       *newPointOffsets[32];
7453   PetscScalar    *pointMat[32];
7454   PetscScalar    *newValues      = NULL, *tmpValues;
7455   PetscBool       anyConstrained = PETSC_FALSE;
7456 
7457   PetscFunctionBegin;
7458   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7459   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7460   PetscCall(PetscSectionGetNumFields(section, &numFields));
7461 
7462   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
7463   /* if there are point-to-point constraints */
7464   if (aSec) {
7465     PetscCall(PetscArrayzero(newOffsets, 32));
7466     PetscCall(ISGetIndices(aIS, &anchors));
7467     PetscCall(PetscSectionGetChart(aSec, &aStart, &aEnd));
7468     /* figure out how many points are going to be in the new element matrix
7469      * (we allow double counting, because it's all just going to be summed
7470      * into the global matrix anyway) */
7471     for (p = 0; p < 2 * numPoints; p += 2) {
7472       PetscInt b    = points[p];
7473       PetscInt bDof = 0, bSecDof;
7474 
7475       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7476       if (!bSecDof) continue;
7477       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7478       if (bDof) {
7479         /* this point is constrained */
7480         /* it is going to be replaced by its anchors */
7481         PetscInt bOff, q;
7482 
7483         anyConstrained = PETSC_TRUE;
7484         newNumPoints += bDof;
7485         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7486         for (q = 0; q < bDof; q++) {
7487           PetscInt a = anchors[bOff + q];
7488           PetscInt aDof;
7489 
7490           PetscCall(PetscSectionGetDof(section, a, &aDof));
7491           newNumIndices += aDof;
7492           for (f = 0; f < numFields; ++f) {
7493             PetscInt fDof;
7494 
7495             PetscCall(PetscSectionGetFieldDof(section, a, f, &fDof));
7496             newOffsets[f + 1] += fDof;
7497           }
7498         }
7499       } else {
7500         /* this point is not constrained */
7501         newNumPoints++;
7502         newNumIndices += bSecDof;
7503         for (f = 0; f < numFields; ++f) {
7504           PetscInt fDof;
7505 
7506           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7507           newOffsets[f + 1] += fDof;
7508         }
7509       }
7510     }
7511   }
7512   if (!anyConstrained) {
7513     if (outNumPoints) *outNumPoints = 0;
7514     if (outNumIndices) *outNumIndices = 0;
7515     if (outPoints) *outPoints = NULL;
7516     if (outValues) *outValues = NULL;
7517     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7518     PetscFunctionReturn(PETSC_SUCCESS);
7519   }
7520 
7521   if (outNumPoints) *outNumPoints = newNumPoints;
7522   if (outNumIndices) *outNumIndices = newNumIndices;
7523 
7524   for (f = 0; f < numFields; ++f) newOffsets[f + 1] += newOffsets[f];
7525 
7526   if (!outPoints && !outValues) {
7527     if (offsets) {
7528       for (f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7529     }
7530     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7531     PetscFunctionReturn(PETSC_SUCCESS);
7532   }
7533 
7534   PetscCheck(!numFields || newOffsets[numFields] == newNumIndices, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, newOffsets[numFields], newNumIndices);
7535 
7536   PetscCall(DMGetDefaultConstraints(dm, &cSec, &cMat, NULL));
7537 
7538   /* workspaces */
7539   if (numFields) {
7540     for (f = 0; f < numFields; f++) {
7541       PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[f]));
7542       PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[f]));
7543     }
7544   } else {
7545     PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[0]));
7546     PetscCall(DMGetWorkArray(dm, numPoints, MPIU_INT, &newPointOffsets[0]));
7547   }
7548 
7549   /* get workspaces for the point-to-point matrices */
7550   if (numFields) {
7551     PetscInt totalOffset, totalMatOffset;
7552 
7553     for (p = 0; p < numPoints; p++) {
7554       PetscInt b    = points[2 * p];
7555       PetscInt bDof = 0, bSecDof;
7556 
7557       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7558       if (!bSecDof) {
7559         for (f = 0; f < numFields; f++) {
7560           newPointOffsets[f][p + 1] = 0;
7561           pointMatOffsets[f][p + 1] = 0;
7562         }
7563         continue;
7564       }
7565       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7566       if (bDof) {
7567         for (f = 0; f < numFields; f++) {
7568           PetscInt fDof, q, bOff, allFDof = 0;
7569 
7570           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7571           PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7572           for (q = 0; q < bDof; q++) {
7573             PetscInt a = anchors[bOff + q];
7574             PetscInt aFDof;
7575 
7576             PetscCall(PetscSectionGetFieldDof(section, a, f, &aFDof));
7577             allFDof += aFDof;
7578           }
7579           newPointOffsets[f][p + 1] = allFDof;
7580           pointMatOffsets[f][p + 1] = fDof * allFDof;
7581         }
7582       } else {
7583         for (f = 0; f < numFields; f++) {
7584           PetscInt fDof;
7585 
7586           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7587           newPointOffsets[f][p + 1] = fDof;
7588           pointMatOffsets[f][p + 1] = 0;
7589         }
7590       }
7591     }
7592     for (f = 0, totalOffset = 0, totalMatOffset = 0; f < numFields; f++) {
7593       newPointOffsets[f][0] = totalOffset;
7594       pointMatOffsets[f][0] = totalMatOffset;
7595       for (p = 0; p < numPoints; p++) {
7596         newPointOffsets[f][p + 1] += newPointOffsets[f][p];
7597         pointMatOffsets[f][p + 1] += pointMatOffsets[f][p];
7598       }
7599       totalOffset    = newPointOffsets[f][numPoints];
7600       totalMatOffset = pointMatOffsets[f][numPoints];
7601       PetscCall(DMGetWorkArray(dm, pointMatOffsets[f][numPoints], MPIU_SCALAR, &pointMat[f]));
7602     }
7603   } else {
7604     for (p = 0; p < numPoints; p++) {
7605       PetscInt b    = points[2 * p];
7606       PetscInt bDof = 0, bSecDof;
7607 
7608       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7609       if (!bSecDof) {
7610         newPointOffsets[0][p + 1] = 0;
7611         pointMatOffsets[0][p + 1] = 0;
7612         continue;
7613       }
7614       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7615       if (bDof) {
7616         PetscInt bOff, q, allDof = 0;
7617 
7618         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7619         for (q = 0; q < bDof; q++) {
7620           PetscInt a = anchors[bOff + q], aDof;
7621 
7622           PetscCall(PetscSectionGetDof(section, a, &aDof));
7623           allDof += aDof;
7624         }
7625         newPointOffsets[0][p + 1] = allDof;
7626         pointMatOffsets[0][p + 1] = bSecDof * allDof;
7627       } else {
7628         newPointOffsets[0][p + 1] = bSecDof;
7629         pointMatOffsets[0][p + 1] = 0;
7630       }
7631     }
7632     newPointOffsets[0][0] = 0;
7633     pointMatOffsets[0][0] = 0;
7634     for (p = 0; p < numPoints; p++) {
7635       newPointOffsets[0][p + 1] += newPointOffsets[0][p];
7636       pointMatOffsets[0][p + 1] += pointMatOffsets[0][p];
7637     }
7638     PetscCall(DMGetWorkArray(dm, pointMatOffsets[0][numPoints], MPIU_SCALAR, &pointMat[0]));
7639   }
7640 
7641   /* output arrays */
7642   PetscCall(DMGetWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7643 
7644   /* get the point-to-point matrices; construct newPoints */
7645   PetscCall(PetscSectionGetMaxDof(aSec, &maxAnchor));
7646   PetscCall(PetscSectionGetMaxDof(section, &maxDof));
7647   PetscCall(DMGetWorkArray(dm, maxDof, MPIU_INT, &indices));
7648   PetscCall(DMGetWorkArray(dm, maxAnchor * maxDof, MPIU_INT, &newIndices));
7649   if (numFields) {
7650     for (p = 0, newP = 0; p < numPoints; p++) {
7651       PetscInt b    = points[2 * p];
7652       PetscInt o    = points[2 * p + 1];
7653       PetscInt bDof = 0, bSecDof;
7654 
7655       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7656       if (!bSecDof) continue;
7657       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7658       if (bDof) {
7659         PetscInt fStart[32], fEnd[32], fAnchorStart[32], fAnchorEnd[32], bOff, q;
7660 
7661         fStart[0] = 0;
7662         fEnd[0]   = 0;
7663         for (f = 0; f < numFields; f++) {
7664           PetscInt fDof;
7665 
7666           PetscCall(PetscSectionGetFieldDof(cSec, b, f, &fDof));
7667           fStart[f + 1] = fStart[f] + fDof;
7668           fEnd[f + 1]   = fStart[f + 1];
7669         }
7670         PetscCall(PetscSectionGetOffset(cSec, b, &bOff));
7671         PetscCall(DMPlexGetIndicesPointFields_Internal(cSec, PETSC_TRUE, b, bOff, fEnd, PETSC_TRUE, perms, p, NULL, indices));
7672 
7673         fAnchorStart[0] = 0;
7674         fAnchorEnd[0]   = 0;
7675         for (f = 0; f < numFields; f++) {
7676           PetscInt fDof = newPointOffsets[f][p + 1] - newPointOffsets[f][p];
7677 
7678           fAnchorStart[f + 1] = fAnchorStart[f] + fDof;
7679           fAnchorEnd[f + 1]   = fAnchorStart[f + 1];
7680         }
7681         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7682         for (q = 0; q < bDof; q++) {
7683           PetscInt a = anchors[bOff + q], aOff;
7684 
7685           /* we take the orientation of ap into account in the order that we constructed the indices above: the newly added points have no orientation */
7686           newPoints[2 * (newP + q)]     = a;
7687           newPoints[2 * (newP + q) + 1] = 0;
7688           PetscCall(PetscSectionGetOffset(section, a, &aOff));
7689           PetscCall(DMPlexGetIndicesPointFields_Internal(section, PETSC_TRUE, a, aOff, fAnchorEnd, PETSC_TRUE, NULL, -1, NULL, newIndices));
7690         }
7691         newP += bDof;
7692 
7693         if (outValues) {
7694           /* get the point-to-point submatrix */
7695           for (f = 0; f < numFields; f++) PetscCall(MatGetValues(cMat, fEnd[f] - fStart[f], indices + fStart[f], fAnchorEnd[f] - fAnchorStart[f], newIndices + fAnchorStart[f], pointMat[f] + pointMatOffsets[f][p]));
7696         }
7697       } else {
7698         newPoints[2 * newP]     = b;
7699         newPoints[2 * newP + 1] = o;
7700         newP++;
7701       }
7702     }
7703   } else {
7704     for (p = 0; p < numPoints; p++) {
7705       PetscInt b    = points[2 * p];
7706       PetscInt o    = points[2 * p + 1];
7707       PetscInt bDof = 0, bSecDof;
7708 
7709       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7710       if (!bSecDof) continue;
7711       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7712       if (bDof) {
7713         PetscInt bEnd = 0, bAnchorEnd = 0, bOff;
7714 
7715         PetscCall(PetscSectionGetOffset(cSec, b, &bOff));
7716         PetscCall(DMPlexGetIndicesPoint_Internal(cSec, PETSC_TRUE, b, bOff, &bEnd, PETSC_TRUE, (perms && perms[0]) ? perms[0][p] : NULL, NULL, indices));
7717 
7718         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7719         for (q = 0; q < bDof; q++) {
7720           PetscInt a = anchors[bOff + q], aOff;
7721 
7722           /* we take the orientation of ap into account in the order that we constructed the indices above: the newly added points have no orientation */
7723 
7724           newPoints[2 * (newP + q)]     = a;
7725           newPoints[2 * (newP + q) + 1] = 0;
7726           PetscCall(PetscSectionGetOffset(section, a, &aOff));
7727           PetscCall(DMPlexGetIndicesPoint_Internal(section, PETSC_TRUE, a, aOff, &bAnchorEnd, PETSC_TRUE, NULL, NULL, newIndices));
7728         }
7729         newP += bDof;
7730 
7731         /* get the point-to-point submatrix */
7732         if (outValues) PetscCall(MatGetValues(cMat, bEnd, indices, bAnchorEnd, newIndices, pointMat[0] + pointMatOffsets[0][p]));
7733       } else {
7734         newPoints[2 * newP]     = b;
7735         newPoints[2 * newP + 1] = o;
7736         newP++;
7737       }
7738     }
7739   }
7740 
7741   if (outValues) {
7742     PetscCall(DMGetWorkArray(dm, newNumIndices * numIndices, MPIU_SCALAR, &tmpValues));
7743     PetscCall(PetscArrayzero(tmpValues, newNumIndices * numIndices));
7744     /* multiply constraints on the right */
7745     if (numFields) {
7746       for (f = 0; f < numFields; f++) {
7747         PetscInt oldOff = offsets[f];
7748 
7749         for (p = 0; p < numPoints; p++) {
7750           PetscInt cStart = newPointOffsets[f][p];
7751           PetscInt b      = points[2 * p];
7752           PetscInt c, r, k;
7753           PetscInt dof;
7754 
7755           PetscCall(PetscSectionGetFieldDof(section, b, f, &dof));
7756           if (!dof) continue;
7757           if (pointMatOffsets[f][p] < pointMatOffsets[f][p + 1]) {
7758             PetscInt           nCols = newPointOffsets[f][p + 1] - cStart;
7759             const PetscScalar *mat   = pointMat[f] + pointMatOffsets[f][p];
7760 
7761             for (r = 0; r < numIndices; r++) {
7762               for (c = 0; c < nCols; c++) {
7763                 for (k = 0; k < dof; k++) tmpValues[r * newNumIndices + cStart + c] += values[r * numIndices + oldOff + k] * mat[k * nCols + c];
7764               }
7765             }
7766           } else {
7767             /* copy this column as is */
7768             for (r = 0; r < numIndices; r++) {
7769               for (c = 0; c < dof; c++) tmpValues[r * newNumIndices + cStart + c] = values[r * numIndices + oldOff + c];
7770             }
7771           }
7772           oldOff += dof;
7773         }
7774       }
7775     } else {
7776       PetscInt oldOff = 0;
7777       for (p = 0; p < numPoints; p++) {
7778         PetscInt cStart = newPointOffsets[0][p];
7779         PetscInt b      = points[2 * p];
7780         PetscInt c, r, k;
7781         PetscInt dof;
7782 
7783         PetscCall(PetscSectionGetDof(section, b, &dof));
7784         if (!dof) continue;
7785         if (pointMatOffsets[0][p] < pointMatOffsets[0][p + 1]) {
7786           PetscInt           nCols = newPointOffsets[0][p + 1] - cStart;
7787           const PetscScalar *mat   = pointMat[0] + pointMatOffsets[0][p];
7788 
7789           for (r = 0; r < numIndices; r++) {
7790             for (c = 0; c < nCols; c++) {
7791               for (k = 0; k < dof; k++) tmpValues[r * newNumIndices + cStart + c] += mat[k * nCols + c] * values[r * numIndices + oldOff + k];
7792             }
7793           }
7794         } else {
7795           /* copy this column as is */
7796           for (r = 0; r < numIndices; r++) {
7797             for (c = 0; c < dof; c++) tmpValues[r * newNumIndices + cStart + c] = values[r * numIndices + oldOff + c];
7798           }
7799         }
7800         oldOff += dof;
7801       }
7802     }
7803 
7804     if (multiplyLeft) {
7805       PetscCall(DMGetWorkArray(dm, newNumIndices * newNumIndices, MPIU_SCALAR, &newValues));
7806       PetscCall(PetscArrayzero(newValues, newNumIndices * newNumIndices));
7807       /* multiply constraints transpose on the left */
7808       if (numFields) {
7809         for (f = 0; f < numFields; f++) {
7810           PetscInt oldOff = offsets[f];
7811 
7812           for (p = 0; p < numPoints; p++) {
7813             PetscInt rStart = newPointOffsets[f][p];
7814             PetscInt b      = points[2 * p];
7815             PetscInt c, r, k;
7816             PetscInt dof;
7817 
7818             PetscCall(PetscSectionGetFieldDof(section, b, f, &dof));
7819             if (pointMatOffsets[f][p] < pointMatOffsets[f][p + 1]) {
7820               PetscInt                          nRows = newPointOffsets[f][p + 1] - rStart;
7821               const PetscScalar *PETSC_RESTRICT mat   = pointMat[f] + pointMatOffsets[f][p];
7822 
7823               for (r = 0; r < nRows; r++) {
7824                 for (c = 0; c < newNumIndices; c++) {
7825                   for (k = 0; k < dof; k++) newValues[(rStart + r) * newNumIndices + c] += mat[k * nRows + r] * tmpValues[(oldOff + k) * newNumIndices + c];
7826                 }
7827               }
7828             } else {
7829               /* copy this row as is */
7830               for (r = 0; r < dof; r++) {
7831                 for (c = 0; c < newNumIndices; c++) newValues[(rStart + r) * newNumIndices + c] = tmpValues[(oldOff + r) * newNumIndices + c];
7832               }
7833             }
7834             oldOff += dof;
7835           }
7836         }
7837       } else {
7838         PetscInt oldOff = 0;
7839 
7840         for (p = 0; p < numPoints; p++) {
7841           PetscInt rStart = newPointOffsets[0][p];
7842           PetscInt b      = points[2 * p];
7843           PetscInt c, r, k;
7844           PetscInt dof;
7845 
7846           PetscCall(PetscSectionGetDof(section, b, &dof));
7847           if (pointMatOffsets[0][p] < pointMatOffsets[0][p + 1]) {
7848             PetscInt                          nRows = newPointOffsets[0][p + 1] - rStart;
7849             const PetscScalar *PETSC_RESTRICT mat   = pointMat[0] + pointMatOffsets[0][p];
7850 
7851             for (r = 0; r < nRows; r++) {
7852               for (c = 0; c < newNumIndices; c++) {
7853                 for (k = 0; k < dof; k++) newValues[(rStart + r) * newNumIndices + c] += mat[k * nRows + r] * tmpValues[(oldOff + k) * newNumIndices + c];
7854               }
7855             }
7856           } else {
7857             /* copy this row as is */
7858             for (r = 0; r < dof; r++) {
7859               for (c = 0; c < newNumIndices; c++) newValues[(rStart + r) * newNumIndices + c] = tmpValues[(oldOff + r) * newNumIndices + c];
7860             }
7861           }
7862           oldOff += dof;
7863         }
7864       }
7865 
7866       PetscCall(DMRestoreWorkArray(dm, newNumIndices * numIndices, MPIU_SCALAR, &tmpValues));
7867     } else {
7868       newValues = tmpValues;
7869     }
7870   }
7871 
7872   /* clean up */
7873   PetscCall(DMRestoreWorkArray(dm, maxDof, MPIU_INT, &indices));
7874   PetscCall(DMRestoreWorkArray(dm, maxAnchor * maxDof, MPIU_INT, &newIndices));
7875 
7876   if (numFields) {
7877     for (f = 0; f < numFields; f++) {
7878       PetscCall(DMRestoreWorkArray(dm, pointMatOffsets[f][numPoints], MPIU_SCALAR, &pointMat[f]));
7879       PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[f]));
7880       PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[f]));
7881     }
7882   } else {
7883     PetscCall(DMRestoreWorkArray(dm, pointMatOffsets[0][numPoints], MPIU_SCALAR, &pointMat[0]));
7884     PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[0]));
7885     PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[0]));
7886   }
7887   PetscCall(ISRestoreIndices(aIS, &anchors));
7888 
7889   /* output */
7890   if (outPoints) {
7891     *outPoints = newPoints;
7892   } else {
7893     PetscCall(DMRestoreWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7894   }
7895   if (outValues) *outValues = newValues;
7896   for (f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7897   PetscFunctionReturn(PETSC_SUCCESS);
7898 }
7899 
7900 /*@C
7901   DMPlexGetClosureIndices - Gets the global dof indices associated with the closure of the given point within the provided sections.
7902 
7903   Not collective
7904 
7905   Input Parameters:
7906 + dm         - The `DM`
7907 . section    - The `PetscSection` describing the points (a local section)
7908 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
7909 . point      - The point defining the closure
7910 - useClPerm  - Use the closure point permutation if available
7911 
7912   Output Parameters:
7913 + numIndices - The number of dof indices in the closure of point with the input sections
7914 . indices    - The dof indices
7915 . outOffsets - Array to write the field offsets into, or `NULL`
7916 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or `NULL`
7917 
7918   Level: advanced
7919 
7920   Notes:
7921   Must call `DMPlexRestoreClosureIndices()` to free allocated memory
7922 
7923   If `idxSection` is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
7924   of those indices is not significant.  If `idxSection` is local, the constrained dofs will yield the involution -(idx+1)
7925   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
7926   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when `idxSection` == section, otherwise global
7927   indices (with the above semantics) are implied.
7928 
7929 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`,
7930           `PetscSection`, `DMGetGlobalSection()`
7931 @*/
7932 PetscErrorCode DMPlexGetClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
7933 {
7934   /* Closure ordering */
7935   PetscSection    clSection;
7936   IS              clPoints;
7937   const PetscInt *clp;
7938   PetscInt       *points;
7939   const PetscInt *clperm = NULL;
7940   /* Dof permutation and sign flips */
7941   const PetscInt    **perms[32] = {NULL};
7942   const PetscScalar **flips[32] = {NULL};
7943   PetscScalar        *valCopy   = NULL;
7944   /* Hanging node constraints */
7945   PetscInt    *pointsC = NULL;
7946   PetscScalar *valuesC = NULL;
7947   PetscInt     NclC, NiC;
7948 
7949   PetscInt *idx;
7950   PetscInt  Nf, Ncl, Ni = 0, offsets[32], p, f;
7951   PetscBool isLocal = (section == idxSection) ? PETSC_TRUE : PETSC_FALSE;
7952 
7953   PetscFunctionBeginHot;
7954   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7955   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7956   PetscValidHeaderSpecific(idxSection, PETSC_SECTION_CLASSID, 3);
7957   if (numIndices) PetscAssertPointer(numIndices, 6);
7958   if (indices) PetscAssertPointer(indices, 7);
7959   if (outOffsets) PetscAssertPointer(outOffsets, 8);
7960   if (values) PetscAssertPointer(values, 9);
7961   PetscCall(PetscSectionGetNumFields(section, &Nf));
7962   PetscCheck(Nf <= 31, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", Nf);
7963   PetscCall(PetscArrayzero(offsets, 32));
7964   /* 1) Get points in closure */
7965   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &Ncl, &points, &clSection, &clPoints, &clp));
7966   if (useClPerm) {
7967     PetscInt depth, clsize;
7968     PetscCall(DMPlexGetPointDepth(dm, point, &depth));
7969     for (clsize = 0, p = 0; p < Ncl; p++) {
7970       PetscInt dof;
7971       PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
7972       clsize += dof;
7973     }
7974     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
7975   }
7976   /* 2) Get number of indices on these points and field offsets from section */
7977   for (p = 0; p < Ncl * 2; p += 2) {
7978     PetscInt dof, fdof;
7979 
7980     PetscCall(PetscSectionGetDof(section, points[p], &dof));
7981     for (f = 0; f < Nf; ++f) {
7982       PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
7983       offsets[f + 1] += fdof;
7984     }
7985     Ni += dof;
7986   }
7987   for (f = 1; f < Nf; ++f) offsets[f + 1] += offsets[f];
7988   PetscCheck(!Nf || offsets[Nf] == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, offsets[Nf], Ni);
7989   /* 3) Get symmetries and sign flips. Apply sign flips to values if passed in (only works for square values matrix) */
7990   for (f = 0; f < PetscMax(1, Nf); ++f) {
7991     if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7992     else PetscCall(PetscSectionGetPointSyms(section, Ncl, points, &perms[f], &flips[f]));
7993     /* may need to apply sign changes to the element matrix */
7994     if (values && flips[f]) {
7995       PetscInt foffset = offsets[f];
7996 
7997       for (p = 0; p < Ncl; ++p) {
7998         PetscInt           pnt  = points[2 * p], fdof;
7999         const PetscScalar *flip = flips[f] ? flips[f][p] : NULL;
8000 
8001         if (!Nf) PetscCall(PetscSectionGetDof(section, pnt, &fdof));
8002         else PetscCall(PetscSectionGetFieldDof(section, pnt, f, &fdof));
8003         if (flip) {
8004           PetscInt i, j, k;
8005 
8006           if (!valCopy) {
8007             PetscCall(DMGetWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
8008             for (j = 0; j < Ni * Ni; ++j) valCopy[j] = (*values)[j];
8009             *values = valCopy;
8010           }
8011           for (i = 0; i < fdof; ++i) {
8012             PetscScalar fval = flip[i];
8013 
8014             for (k = 0; k < Ni; ++k) {
8015               valCopy[Ni * (foffset + i) + k] *= fval;
8016               valCopy[Ni * k + (foffset + i)] *= fval;
8017             }
8018           }
8019         }
8020         foffset += fdof;
8021       }
8022     }
8023   }
8024   /* 4) Apply hanging node constraints. Get new symmetries and replace all storage with constrained storage */
8025   PetscCall(DMPlexAnchorsModifyMat(dm, section, Ncl, Ni, points, perms, values ? *values : NULL, &NclC, &NiC, &pointsC, values ? &valuesC : NULL, offsets, PETSC_TRUE));
8026   if (NclC) {
8027     if (valCopy) PetscCall(DMRestoreWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
8028     for (f = 0; f < PetscMax(1, Nf); ++f) {
8029       if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
8030       else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
8031     }
8032     for (f = 0; f < PetscMax(1, Nf); ++f) {
8033       if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, NclC, pointsC, &perms[f], &flips[f]));
8034       else PetscCall(PetscSectionGetPointSyms(section, NclC, pointsC, &perms[f], &flips[f]));
8035     }
8036     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
8037     Ncl    = NclC;
8038     Ni     = NiC;
8039     points = pointsC;
8040     if (values) *values = valuesC;
8041   }
8042   /* 5) Calculate indices */
8043   PetscCall(DMGetWorkArray(dm, Ni, MPIU_INT, &idx));
8044   if (Nf) {
8045     PetscInt  idxOff;
8046     PetscBool useFieldOffsets;
8047 
8048     if (outOffsets) {
8049       for (f = 0; f <= Nf; f++) outOffsets[f] = offsets[f];
8050     }
8051     PetscCall(PetscSectionGetUseFieldOffsets(idxSection, &useFieldOffsets));
8052     if (useFieldOffsets) {
8053       for (p = 0; p < Ncl; ++p) {
8054         const PetscInt pnt = points[p * 2];
8055 
8056         PetscCall(DMPlexGetIndicesPointFieldsSplit_Internal(section, idxSection, pnt, offsets, perms, p, clperm, idx));
8057       }
8058     } else {
8059       for (p = 0; p < Ncl; ++p) {
8060         const PetscInt pnt = points[p * 2];
8061 
8062         PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
8063         /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
8064          * not (at the time of this writing) have fields set. They probably should, in which case we would pass the
8065          * global section. */
8066         PetscCall(DMPlexGetIndicesPointFields_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, offsets, PETSC_FALSE, perms, p, clperm, idx));
8067       }
8068     }
8069   } else {
8070     PetscInt off = 0, idxOff;
8071 
8072     for (p = 0; p < Ncl; ++p) {
8073       const PetscInt  pnt  = points[p * 2];
8074       const PetscInt *perm = perms[0] ? perms[0][p] : NULL;
8075 
8076       PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
8077       /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
8078        * not (at the time of this writing) have fields set. They probably should, in which case we would pass the global section. */
8079       PetscCall(DMPlexGetIndicesPoint_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, &off, PETSC_FALSE, perm, clperm, idx));
8080     }
8081   }
8082   /* 6) Cleanup */
8083   for (f = 0; f < PetscMax(1, Nf); ++f) {
8084     if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
8085     else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
8086   }
8087   if (NclC) {
8088     PetscCall(DMRestoreWorkArray(dm, NclC * 2, MPIU_INT, &pointsC));
8089   } else {
8090     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
8091   }
8092 
8093   if (numIndices) *numIndices = Ni;
8094   if (indices) *indices = idx;
8095   PetscFunctionReturn(PETSC_SUCCESS);
8096 }
8097 
8098 /*@C
8099   DMPlexRestoreClosureIndices - Restores the global dof indices associated with the closure of the given point within the provided sections.
8100 
8101   Not collective
8102 
8103   Input Parameters:
8104 + dm         - The `DM`
8105 . section    - The `PetscSection` describing the points (a local section)
8106 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
8107 . point      - The point defining the closure
8108 - useClPerm  - Use the closure point permutation if available
8109 
8110   Output Parameters:
8111 + numIndices - The number of dof indices in the closure of point with the input sections
8112 . indices    - The dof indices
8113 . outOffsets - Array to write the field offsets into, or `NULL`
8114 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or `NULL`
8115 
8116   Level: advanced
8117 
8118   Notes:
8119   If values were modified, the user is responsible for calling `DMRestoreWorkArray`(dm, 0, `MPIU_SCALAR`, &values).
8120 
8121   If idxSection is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
8122   of those indices is not significant.  If idxSection is local, the constrained dofs will yield the involution -(idx+1)
8123   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
8124   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when idxSection == section, otherwise global
8125   indices (with the above semantics) are implied.
8126 
8127 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`, `DMGetGlobalSection()`
8128 @*/
8129 PetscErrorCode DMPlexRestoreClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
8130 {
8131   PetscFunctionBegin;
8132   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8133   PetscAssertPointer(indices, 7);
8134   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, indices));
8135   PetscFunctionReturn(PETSC_SUCCESS);
8136 }
8137 
8138 PetscErrorCode DMPlexMatSetClosure_Internal(DM dm, PetscSection section, PetscSection globalSection, PetscBool useClPerm, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8139 {
8140   DM_Plex           *mesh = (DM_Plex *)dm->data;
8141   PetscInt          *indices;
8142   PetscInt           numIndices;
8143   const PetscScalar *valuesOrig = values;
8144   PetscErrorCode     ierr;
8145 
8146   PetscFunctionBegin;
8147   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8148   if (!section) PetscCall(DMGetLocalSection(dm, &section));
8149   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
8150   if (!globalSection) PetscCall(DMGetGlobalSection(dm, &globalSection));
8151   PetscValidHeaderSpecific(globalSection, PETSC_SECTION_CLASSID, 3);
8152   PetscValidHeaderSpecific(A, MAT_CLASSID, 5);
8153 
8154   PetscCall(DMPlexGetClosureIndices(dm, section, globalSection, point, useClPerm, &numIndices, &indices, NULL, (PetscScalar **)&values));
8155 
8156   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndices, indices, 0, NULL, values));
8157   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8158   ierr = MatSetValues(A, numIndices, indices, numIndices, indices, values, mode);
8159   if (ierr) {
8160     PetscMPIInt rank;
8161 
8162     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8163     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8164     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndices, indices, 0, NULL, values));
8165     PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
8166     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
8167     SETERRQ(PetscObjectComm((PetscObject)dm), ierr, "Not possible to set matrix values");
8168   }
8169   if (mesh->printFEM > 1) {
8170     PetscInt i;
8171     PetscCall(PetscPrintf(PETSC_COMM_SELF, "  Indices:"));
8172     for (i = 0; i < numIndices; ++i) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, indices[i]));
8173     PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8174   }
8175 
8176   PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
8177   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
8178   PetscFunctionReturn(PETSC_SUCCESS);
8179 }
8180 
8181 /*@C
8182   DMPlexMatSetClosure - Set an array of the values on the closure of 'point'
8183 
8184   Not collective
8185 
8186   Input Parameters:
8187 + dm            - The `DM`
8188 . section       - The section describing the layout in `v`, or `NULL` to use the default section
8189 . globalSection - The section describing the layout in `v`, or `NULL` to use the default global section
8190 . A             - The matrix
8191 . point         - The point in the `DM`
8192 . values        - The array of values
8193 - mode          - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
8194 
8195   Level: intermediate
8196 
8197 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosureGeneral()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
8198 @*/
8199 PetscErrorCode DMPlexMatSetClosure(DM dm, PetscSection section, PetscSection globalSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8200 {
8201   PetscFunctionBegin;
8202   PetscCall(DMPlexMatSetClosure_Internal(dm, section, globalSection, PETSC_TRUE, A, point, values, mode));
8203   PetscFunctionReturn(PETSC_SUCCESS);
8204 }
8205 
8206 /*@C
8207   DMPlexMatSetClosureGeneral - Set an array of the values on the closure of 'point' using a different row and column section
8208 
8209   Not collective
8210 
8211   Input Parameters:
8212 + dmRow            - The `DM` for the row fields
8213 . sectionRow       - The section describing the layout, or `NULL` to use the default section in `dmRow`
8214 . useRowPerm       - The flag to use the closure permutation of the `dmRow` if available
8215 . globalSectionRow - The section describing the layout, or `NULL` to use the default global section in `dmRow`
8216 . dmCol            - The `DM` for the column fields
8217 . sectionCol       - The section describing the layout, or `NULL` to use the default section in `dmCol`
8218 . useColPerm       - The flag to use the closure permutation of the `dmCol` if available
8219 . globalSectionCol - The section describing the layout, or `NULL` to use the default global section in `dmCol`
8220 . A                - The matrix
8221 . point            - The point in the `DM`
8222 . values           - The array of values
8223 - mode             - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
8224 
8225   Level: intermediate
8226 
8227 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosure()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
8228 @*/
8229 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)
8230 {
8231   DM_Plex           *mesh = (DM_Plex *)dmRow->data;
8232   PetscInt          *indicesRow, *indicesCol;
8233   PetscInt           numIndicesRow, numIndicesCol;
8234   const PetscScalar *valuesOrig = values;
8235   PetscErrorCode     ierr;
8236 
8237   PetscFunctionBegin;
8238   PetscValidHeaderSpecific(dmRow, DM_CLASSID, 1);
8239   if (!sectionRow) PetscCall(DMGetLocalSection(dmRow, &sectionRow));
8240   PetscValidHeaderSpecific(sectionRow, PETSC_SECTION_CLASSID, 2);
8241   if (!globalSectionRow) PetscCall(DMGetGlobalSection(dmRow, &globalSectionRow));
8242   PetscValidHeaderSpecific(globalSectionRow, PETSC_SECTION_CLASSID, 3);
8243   PetscValidHeaderSpecific(dmCol, DM_CLASSID, 5);
8244   if (!sectionCol) PetscCall(DMGetLocalSection(dmCol, &sectionCol));
8245   PetscValidHeaderSpecific(sectionCol, PETSC_SECTION_CLASSID, 6);
8246   if (!globalSectionCol) PetscCall(DMGetGlobalSection(dmCol, &globalSectionCol));
8247   PetscValidHeaderSpecific(globalSectionCol, PETSC_SECTION_CLASSID, 7);
8248   PetscValidHeaderSpecific(A, MAT_CLASSID, 9);
8249 
8250   PetscCall(DMPlexGetClosureIndices(dmRow, sectionRow, globalSectionRow, point, useRowPerm, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&values));
8251   PetscCall(DMPlexGetClosureIndices(dmCol, sectionCol, globalSectionCol, point, useColPerm, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&values));
8252 
8253   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
8254   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8255   ierr = MatSetValues(A, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values, mode);
8256   if (ierr) {
8257     PetscMPIInt rank;
8258 
8259     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8260     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8261     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
8262     PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&values));
8263     PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesRow, NULL, (PetscScalar **)&values));
8264     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &values));
8265   }
8266 
8267   PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, useRowPerm, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&values));
8268   PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, useColPerm, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&values));
8269   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &values));
8270   PetscFunctionReturn(PETSC_SUCCESS);
8271 }
8272 
8273 PetscErrorCode DMPlexMatSetClosureRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8274 {
8275   DM_Plex        *mesh    = (DM_Plex *)dmf->data;
8276   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
8277   PetscInt       *cpoints = NULL;
8278   PetscInt       *findices, *cindices;
8279   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
8280   PetscInt        foffsets[32], coffsets[32];
8281   DMPolytopeType  ct;
8282   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
8283   PetscErrorCode  ierr;
8284 
8285   PetscFunctionBegin;
8286   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
8287   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
8288   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
8289   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
8290   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
8291   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
8292   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
8293   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
8294   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
8295   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
8296   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
8297   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
8298   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
8299   PetscCall(PetscArrayzero(foffsets, 32));
8300   PetscCall(PetscArrayzero(coffsets, 32));
8301   /* Column indices */
8302   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8303   maxFPoints = numCPoints;
8304   /* Compress out points not in the section */
8305   /*   TODO: Squeeze out points with 0 dof as well */
8306   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
8307   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
8308     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
8309       cpoints[q * 2]     = cpoints[p];
8310       cpoints[q * 2 + 1] = cpoints[p + 1];
8311       ++q;
8312     }
8313   }
8314   numCPoints = q;
8315   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
8316     PetscInt fdof;
8317 
8318     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
8319     if (!dof) continue;
8320     for (f = 0; f < numFields; ++f) {
8321       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
8322       coffsets[f + 1] += fdof;
8323     }
8324     numCIndices += dof;
8325   }
8326   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
8327   /* Row indices */
8328   PetscCall(DMPlexGetCellType(dmc, point, &ct));
8329   {
8330     DMPlexTransform tr;
8331     DMPolytopeType *rct;
8332     PetscInt       *rsize, *rcone, *rornt, Nt;
8333 
8334     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8335     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8336     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8337     numSubcells = rsize[Nt - 1];
8338     PetscCall(DMPlexTransformDestroy(&tr));
8339   }
8340   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8341   for (r = 0, q = 0; r < numSubcells; ++r) {
8342     /* TODO Map from coarse to fine cells */
8343     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8344     /* Compress out points not in the section */
8345     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8346     for (p = 0; p < numFPoints * 2; p += 2) {
8347       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8348         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8349         if (!dof) continue;
8350         for (s = 0; s < q; ++s)
8351           if (fpoints[p] == ftotpoints[s * 2]) break;
8352         if (s < q) continue;
8353         ftotpoints[q * 2]     = fpoints[p];
8354         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8355         ++q;
8356       }
8357     }
8358     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8359   }
8360   numFPoints = q;
8361   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8362     PetscInt fdof;
8363 
8364     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8365     if (!dof) continue;
8366     for (f = 0; f < numFields; ++f) {
8367       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8368       foffsets[f + 1] += fdof;
8369     }
8370     numFIndices += dof;
8371   }
8372   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8373 
8374   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8375   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8376   PetscCall(DMGetWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8377   PetscCall(DMGetWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8378   if (numFields) {
8379     const PetscInt **permsF[32] = {NULL};
8380     const PetscInt **permsC[32] = {NULL};
8381 
8382     for (f = 0; f < numFields; f++) {
8383       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8384       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8385     }
8386     for (p = 0; p < numFPoints; p++) {
8387       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8388       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8389     }
8390     for (p = 0; p < numCPoints; p++) {
8391       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8392       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8393     }
8394     for (f = 0; f < numFields; f++) {
8395       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8396       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8397     }
8398   } else {
8399     const PetscInt **permsF = NULL;
8400     const PetscInt **permsC = NULL;
8401 
8402     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8403     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8404     for (p = 0, off = 0; p < numFPoints; p++) {
8405       const PetscInt *perm = permsF ? permsF[p] : NULL;
8406 
8407       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8408       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8409     }
8410     for (p = 0, off = 0; p < numCPoints; p++) {
8411       const PetscInt *perm = permsC ? permsC[p] : NULL;
8412 
8413       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8414       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8415     }
8416     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8417     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8418   }
8419   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8420   /* TODO: flips */
8421   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8422   ierr = MatSetValues(A, numFIndices, findices, numCIndices, cindices, values, mode);
8423   if (ierr) {
8424     PetscMPIInt rank;
8425 
8426     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8427     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8428     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8429     PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8430     PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8431   }
8432   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8433   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8434   PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8435   PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8436   PetscFunctionReturn(PETSC_SUCCESS);
8437 }
8438 
8439 PetscErrorCode DMPlexMatGetClosureIndicesRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, PetscInt point, PetscInt cindices[], PetscInt findices[])
8440 {
8441   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
8442   PetscInt       *cpoints = NULL;
8443   PetscInt        foffsets[32], coffsets[32];
8444   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
8445   DMPolytopeType  ct;
8446   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
8447 
8448   PetscFunctionBegin;
8449   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
8450   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
8451   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
8452   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
8453   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
8454   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
8455   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
8456   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
8457   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
8458   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
8459   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
8460   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
8461   PetscCall(PetscArrayzero(foffsets, 32));
8462   PetscCall(PetscArrayzero(coffsets, 32));
8463   /* Column indices */
8464   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8465   maxFPoints = numCPoints;
8466   /* Compress out points not in the section */
8467   /*   TODO: Squeeze out points with 0 dof as well */
8468   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
8469   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
8470     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
8471       cpoints[q * 2]     = cpoints[p];
8472       cpoints[q * 2 + 1] = cpoints[p + 1];
8473       ++q;
8474     }
8475   }
8476   numCPoints = q;
8477   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
8478     PetscInt fdof;
8479 
8480     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
8481     if (!dof) continue;
8482     for (f = 0; f < numFields; ++f) {
8483       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
8484       coffsets[f + 1] += fdof;
8485     }
8486     numCIndices += dof;
8487   }
8488   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
8489   /* Row indices */
8490   PetscCall(DMPlexGetCellType(dmc, point, &ct));
8491   {
8492     DMPlexTransform tr;
8493     DMPolytopeType *rct;
8494     PetscInt       *rsize, *rcone, *rornt, Nt;
8495 
8496     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8497     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8498     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8499     numSubcells = rsize[Nt - 1];
8500     PetscCall(DMPlexTransformDestroy(&tr));
8501   }
8502   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8503   for (r = 0, q = 0; r < numSubcells; ++r) {
8504     /* TODO Map from coarse to fine cells */
8505     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8506     /* Compress out points not in the section */
8507     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8508     for (p = 0; p < numFPoints * 2; p += 2) {
8509       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8510         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8511         if (!dof) continue;
8512         for (s = 0; s < q; ++s)
8513           if (fpoints[p] == ftotpoints[s * 2]) break;
8514         if (s < q) continue;
8515         ftotpoints[q * 2]     = fpoints[p];
8516         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8517         ++q;
8518       }
8519     }
8520     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8521   }
8522   numFPoints = q;
8523   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8524     PetscInt fdof;
8525 
8526     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8527     if (!dof) continue;
8528     for (f = 0; f < numFields; ++f) {
8529       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8530       foffsets[f + 1] += fdof;
8531     }
8532     numFIndices += dof;
8533   }
8534   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8535 
8536   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8537   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8538   if (numFields) {
8539     const PetscInt **permsF[32] = {NULL};
8540     const PetscInt **permsC[32] = {NULL};
8541 
8542     for (f = 0; f < numFields; f++) {
8543       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8544       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8545     }
8546     for (p = 0; p < numFPoints; p++) {
8547       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8548       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8549     }
8550     for (p = 0; p < numCPoints; p++) {
8551       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8552       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8553     }
8554     for (f = 0; f < numFields; f++) {
8555       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8556       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8557     }
8558   } else {
8559     const PetscInt **permsF = NULL;
8560     const PetscInt **permsC = NULL;
8561 
8562     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8563     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8564     for (p = 0, off = 0; p < numFPoints; p++) {
8565       const PetscInt *perm = permsF ? permsF[p] : NULL;
8566 
8567       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8568       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8569     }
8570     for (p = 0, off = 0; p < numCPoints; p++) {
8571       const PetscInt *perm = permsC ? permsC[p] : NULL;
8572 
8573       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8574       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8575     }
8576     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8577     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8578   }
8579   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8580   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8581   PetscFunctionReturn(PETSC_SUCCESS);
8582 }
8583 
8584 /*@C
8585   DMPlexGetVTKCellHeight - Returns the height in the DAG used to determine which points are cells (normally 0)
8586 
8587   Input Parameter:
8588 . dm - The `DMPLEX` object
8589 
8590   Output Parameter:
8591 . cellHeight - The height of a cell
8592 
8593   Level: developer
8594 
8595 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetVTKCellHeight()`
8596 @*/
8597 PetscErrorCode DMPlexGetVTKCellHeight(DM dm, PetscInt *cellHeight)
8598 {
8599   DM_Plex *mesh = (DM_Plex *)dm->data;
8600 
8601   PetscFunctionBegin;
8602   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8603   PetscAssertPointer(cellHeight, 2);
8604   *cellHeight = mesh->vtkCellHeight;
8605   PetscFunctionReturn(PETSC_SUCCESS);
8606 }
8607 
8608 /*@C
8609   DMPlexSetVTKCellHeight - Sets the height in the DAG used to determine which points are cells (normally 0)
8610 
8611   Input Parameters:
8612 + dm         - The `DMPLEX` object
8613 - cellHeight - The height of a cell
8614 
8615   Level: developer
8616 
8617 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetVTKCellHeight()`
8618 @*/
8619 PetscErrorCode DMPlexSetVTKCellHeight(DM dm, PetscInt cellHeight)
8620 {
8621   DM_Plex *mesh = (DM_Plex *)dm->data;
8622 
8623   PetscFunctionBegin;
8624   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8625   mesh->vtkCellHeight = cellHeight;
8626   PetscFunctionReturn(PETSC_SUCCESS);
8627 }
8628 
8629 /*@
8630   DMPlexGetCellTypeStratum - Get the range of cells of a given celltype
8631 
8632   Input Parameters:
8633 + dm - The `DMPLEX` object
8634 - ct - The `DMPolytopeType` of the cell
8635 
8636   Output Parameters:
8637 + start - The first cell of this type, or `NULL`
8638 - end   - The upper bound on this celltype, or `NULL`
8639 
8640   Level: advanced
8641 
8642 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexConstructGhostCells()`, `DMPlexGetDepthStratum()`, `DMPlexGetHeightStratum()`
8643 @*/
8644 PetscErrorCode DMPlexGetCellTypeStratum(DM dm, DMPolytopeType ct, PetscInt *start, PetscInt *end)
8645 {
8646   DM_Plex *mesh = (DM_Plex *)dm->data;
8647   DMLabel  label;
8648   PetscInt pStart, pEnd;
8649 
8650   PetscFunctionBegin;
8651   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8652   if (start) {
8653     PetscAssertPointer(start, 3);
8654     *start = 0;
8655   }
8656   if (end) {
8657     PetscAssertPointer(end, 4);
8658     *end = 0;
8659   }
8660   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8661   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
8662   if (mesh->tr) {
8663     PetscCall(DMPlexTransformGetCellTypeStratum(mesh->tr, ct, start, end));
8664   } else {
8665     PetscCall(DMPlexGetCellTypeLabel(dm, &label));
8666     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named celltype was found");
8667     PetscCall(DMLabelGetStratumBounds(label, ct, start, end));
8668   }
8669   PetscFunctionReturn(PETSC_SUCCESS);
8670 }
8671 
8672 PetscErrorCode DMPlexCreateNumbering_Plex(DM dm, PetscInt pStart, PetscInt pEnd, PetscInt shift, PetscInt *globalSize, PetscSF sf, IS *numbering)
8673 {
8674   PetscSection section, globalSection;
8675   PetscInt    *numbers, p;
8676 
8677   PetscFunctionBegin;
8678   if (PetscDefined(USE_DEBUG)) PetscCall(DMPlexCheckPointSF(dm, sf, PETSC_TRUE));
8679   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
8680   PetscCall(PetscSectionSetChart(section, pStart, pEnd));
8681   for (p = pStart; p < pEnd; ++p) PetscCall(PetscSectionSetDof(section, p, 1));
8682   PetscCall(PetscSectionSetUp(section));
8683   PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_FALSE, PETSC_FALSE, &globalSection));
8684   PetscCall(PetscMalloc1(pEnd - pStart, &numbers));
8685   for (p = pStart; p < pEnd; ++p) {
8686     PetscCall(PetscSectionGetOffset(globalSection, p, &numbers[p - pStart]));
8687     if (numbers[p - pStart] < 0) numbers[p - pStart] -= shift;
8688     else numbers[p - pStart] += shift;
8689   }
8690   PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), pEnd - pStart, numbers, PETSC_OWN_POINTER, numbering));
8691   if (globalSize) {
8692     PetscLayout layout;
8693     PetscCall(PetscSectionGetPointLayout(PetscObjectComm((PetscObject)dm), globalSection, &layout));
8694     PetscCall(PetscLayoutGetSize(layout, globalSize));
8695     PetscCall(PetscLayoutDestroy(&layout));
8696   }
8697   PetscCall(PetscSectionDestroy(&section));
8698   PetscCall(PetscSectionDestroy(&globalSection));
8699   PetscFunctionReturn(PETSC_SUCCESS);
8700 }
8701 
8702 PetscErrorCode DMPlexCreateCellNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalCellNumbers)
8703 {
8704   PetscInt cellHeight, cStart, cEnd;
8705 
8706   PetscFunctionBegin;
8707   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8708   if (includeHybrid) PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8709   else PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd));
8710   PetscCall(DMPlexCreateNumbering_Plex(dm, cStart, cEnd, 0, NULL, dm->sf, globalCellNumbers));
8711   PetscFunctionReturn(PETSC_SUCCESS);
8712 }
8713 
8714 /*@
8715   DMPlexGetCellNumbering - Get a global cell numbering for all cells on this process
8716 
8717   Input Parameter:
8718 . dm - The `DMPLEX` object
8719 
8720   Output Parameter:
8721 . globalCellNumbers - Global cell numbers for all cells on this process
8722 
8723   Level: developer
8724 
8725 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetVertexNumbering()`
8726 @*/
8727 PetscErrorCode DMPlexGetCellNumbering(DM dm, IS *globalCellNumbers)
8728 {
8729   DM_Plex *mesh = (DM_Plex *)dm->data;
8730 
8731   PetscFunctionBegin;
8732   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8733   if (!mesh->globalCellNumbers) PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_FALSE, &mesh->globalCellNumbers));
8734   *globalCellNumbers = mesh->globalCellNumbers;
8735   PetscFunctionReturn(PETSC_SUCCESS);
8736 }
8737 
8738 PetscErrorCode DMPlexCreateVertexNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalVertexNumbers)
8739 {
8740   PetscInt vStart, vEnd;
8741 
8742   PetscFunctionBegin;
8743   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8744   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8745   PetscCall(DMPlexCreateNumbering_Plex(dm, vStart, vEnd, 0, NULL, dm->sf, globalVertexNumbers));
8746   PetscFunctionReturn(PETSC_SUCCESS);
8747 }
8748 
8749 /*@
8750   DMPlexGetVertexNumbering - Get a global vertex numbering for all vertices on this process
8751 
8752   Input Parameter:
8753 . dm - The `DMPLEX` object
8754 
8755   Output Parameter:
8756 . globalVertexNumbers - Global vertex numbers for all vertices on this process
8757 
8758   Level: developer
8759 
8760 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
8761 @*/
8762 PetscErrorCode DMPlexGetVertexNumbering(DM dm, IS *globalVertexNumbers)
8763 {
8764   DM_Plex *mesh = (DM_Plex *)dm->data;
8765 
8766   PetscFunctionBegin;
8767   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8768   if (!mesh->globalVertexNumbers) PetscCall(DMPlexCreateVertexNumbering_Internal(dm, PETSC_FALSE, &mesh->globalVertexNumbers));
8769   *globalVertexNumbers = mesh->globalVertexNumbers;
8770   PetscFunctionReturn(PETSC_SUCCESS);
8771 }
8772 
8773 /*@
8774   DMPlexCreatePointNumbering - Create a global numbering for all points.
8775 
8776   Collective
8777 
8778   Input Parameter:
8779 . dm - The `DMPLEX` object
8780 
8781   Output Parameter:
8782 . globalPointNumbers - Global numbers for all points on this process
8783 
8784   Level: developer
8785 
8786   Notes:
8787   The point numbering `IS` is parallel, with local portion indexed by local points (see `DMGetLocalSection()`). The global
8788   points are taken as stratified, with each MPI rank owning a contiguous subset of each stratum. In the IS, owned points
8789   will have their non-negative value while points owned by different ranks will be involuted -(idx+1). As an example,
8790   consider a parallel mesh in which the first two elements and first two vertices are owned by rank 0.
8791 
8792   The partitioned mesh is
8793   ```
8794   (2)--0--(3)--1--(4)    (1)--0--(2)
8795   ```
8796   and its global numbering is
8797   ```
8798   (3)--0--(4)--1--(5)--2--(6)
8799   ```
8800   Then the global numbering is provided as
8801   ```
8802   [0] Number of indices in set 5
8803   [0] 0 0
8804   [0] 1 1
8805   [0] 2 3
8806   [0] 3 4
8807   [0] 4 -6
8808   [1] Number of indices in set 3
8809   [1] 0 2
8810   [1] 1 5
8811   [1] 2 6
8812   ```
8813 
8814 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
8815 @*/
8816 PetscErrorCode DMPlexCreatePointNumbering(DM dm, IS *globalPointNumbers)
8817 {
8818   IS        nums[4];
8819   PetscInt  depths[4], gdepths[4], starts[4];
8820   PetscInt  depth, d, shift = 0;
8821   PetscBool empty = PETSC_FALSE;
8822 
8823   PetscFunctionBegin;
8824   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8825   PetscCall(DMPlexGetDepth(dm, &depth));
8826   // For unstratified meshes use dim instead of depth
8827   if (depth < 0) PetscCall(DMGetDimension(dm, &depth));
8828   // If any stratum is empty, we must mark all empty
8829   for (d = 0; d <= depth; ++d) {
8830     PetscInt end;
8831 
8832     depths[d] = depth - d;
8833     PetscCall(DMPlexGetDepthStratum(dm, depths[d], &starts[d], &end));
8834     if (!(starts[d] - end)) empty = PETSC_TRUE;
8835   }
8836   if (empty)
8837     for (d = 0; d <= depth; ++d) {
8838       depths[d] = -1;
8839       starts[d] = -1;
8840     }
8841   else PetscCall(PetscSortIntWithArray(depth + 1, starts, depths));
8842   PetscCall(MPIU_Allreduce(depths, gdepths, depth + 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
8843   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]);
8844   // Note here that 'shift' is collective, so that the numbering is stratified by depth
8845   for (d = 0; d <= depth; ++d) {
8846     PetscInt pStart, pEnd, gsize;
8847 
8848     PetscCall(DMPlexGetDepthStratum(dm, gdepths[d], &pStart, &pEnd));
8849     PetscCall(DMPlexCreateNumbering_Plex(dm, pStart, pEnd, shift, &gsize, dm->sf, &nums[d]));
8850     shift += gsize;
8851   }
8852   PetscCall(ISConcatenate(PETSC_COMM_SELF, depth + 1, nums, globalPointNumbers));
8853   for (d = 0; d <= depth; ++d) PetscCall(ISDestroy(&nums[d]));
8854   PetscFunctionReturn(PETSC_SUCCESS);
8855 }
8856 
8857 /*@
8858   DMPlexCreateRankField - Create a cell field whose value is the rank of the owner
8859 
8860   Input Parameter:
8861 . dm - The `DMPLEX` object
8862 
8863   Output Parameter:
8864 . ranks - The rank field
8865 
8866   Options Database Key:
8867 . -dm_partition_view - Adds the rank field into the `DM` output from `-dm_view` using the same viewer
8868 
8869   Level: intermediate
8870 
8871 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`
8872 @*/
8873 PetscErrorCode DMPlexCreateRankField(DM dm, Vec *ranks)
8874 {
8875   DM             rdm;
8876   PetscFE        fe;
8877   PetscScalar   *r;
8878   PetscMPIInt    rank;
8879   DMPolytopeType ct;
8880   PetscInt       dim, cStart, cEnd, c;
8881   PetscBool      simplex;
8882 
8883   PetscFunctionBeginUser;
8884   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8885   PetscAssertPointer(ranks, 2);
8886   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
8887   PetscCall(DMClone(dm, &rdm));
8888   PetscCall(DMGetDimension(rdm, &dim));
8889   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
8890   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
8891   simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct) + 1 ? PETSC_TRUE : PETSC_FALSE;
8892   PetscCall(PetscFECreateDefault(PETSC_COMM_SELF, dim, 1, simplex, "PETSc___rank_", -1, &fe));
8893   PetscCall(PetscObjectSetName((PetscObject)fe, "rank"));
8894   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
8895   PetscCall(PetscFEDestroy(&fe));
8896   PetscCall(DMCreateDS(rdm));
8897   PetscCall(DMCreateGlobalVector(rdm, ranks));
8898   PetscCall(PetscObjectSetName((PetscObject)*ranks, "partition"));
8899   PetscCall(VecGetArray(*ranks, &r));
8900   for (c = cStart; c < cEnd; ++c) {
8901     PetscScalar *lr;
8902 
8903     PetscCall(DMPlexPointGlobalRef(rdm, c, r, &lr));
8904     if (lr) *lr = rank;
8905   }
8906   PetscCall(VecRestoreArray(*ranks, &r));
8907   PetscCall(DMDestroy(&rdm));
8908   PetscFunctionReturn(PETSC_SUCCESS);
8909 }
8910 
8911 /*@
8912   DMPlexCreateLabelField - Create a cell field whose value is the label value for that cell
8913 
8914   Input Parameters:
8915 + dm    - The `DMPLEX`
8916 - label - The `DMLabel`
8917 
8918   Output Parameter:
8919 . val - The label value field
8920 
8921   Options Database Key:
8922 . -dm_label_view - Adds the label value field into the `DM` output from `-dm_view` using the same viewer
8923 
8924   Level: intermediate
8925 
8926 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`
8927 @*/
8928 PetscErrorCode DMPlexCreateLabelField(DM dm, DMLabel label, Vec *val)
8929 {
8930   DM           rdm;
8931   PetscFE      fe;
8932   PetscScalar *v;
8933   PetscInt     dim, cStart, cEnd, c;
8934 
8935   PetscFunctionBeginUser;
8936   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8937   PetscAssertPointer(label, 2);
8938   PetscAssertPointer(val, 3);
8939   PetscCall(DMClone(dm, &rdm));
8940   PetscCall(DMGetDimension(rdm, &dim));
8941   PetscCall(PetscFECreateDefault(PetscObjectComm((PetscObject)rdm), dim, 1, PETSC_TRUE, "PETSc___label_value_", -1, &fe));
8942   PetscCall(PetscObjectSetName((PetscObject)fe, "label_value"));
8943   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
8944   PetscCall(PetscFEDestroy(&fe));
8945   PetscCall(DMCreateDS(rdm));
8946   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
8947   PetscCall(DMCreateGlobalVector(rdm, val));
8948   PetscCall(PetscObjectSetName((PetscObject)*val, "label_value"));
8949   PetscCall(VecGetArray(*val, &v));
8950   for (c = cStart; c < cEnd; ++c) {
8951     PetscScalar *lv;
8952     PetscInt     cval;
8953 
8954     PetscCall(DMPlexPointGlobalRef(rdm, c, v, &lv));
8955     PetscCall(DMLabelGetValue(label, c, &cval));
8956     *lv = cval;
8957   }
8958   PetscCall(VecRestoreArray(*val, &v));
8959   PetscCall(DMDestroy(&rdm));
8960   PetscFunctionReturn(PETSC_SUCCESS);
8961 }
8962 
8963 /*@
8964   DMPlexCheckSymmetry - Check that the adjacency information in the mesh is symmetric.
8965 
8966   Input Parameter:
8967 . dm - The `DMPLEX` object
8968 
8969   Level: developer
8970 
8971   Notes:
8972   This is a useful diagnostic when creating meshes programmatically.
8973 
8974   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
8975 
8976 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
8977 @*/
8978 PetscErrorCode DMPlexCheckSymmetry(DM dm)
8979 {
8980   PetscSection    coneSection, supportSection;
8981   const PetscInt *cone, *support;
8982   PetscInt        coneSize, c, supportSize, s;
8983   PetscInt        pStart, pEnd, p, pp, csize, ssize;
8984   PetscBool       storagecheck = PETSC_TRUE;
8985 
8986   PetscFunctionBegin;
8987   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8988   PetscCall(DMViewFromOptions(dm, NULL, "-sym_dm_view"));
8989   PetscCall(DMPlexGetConeSection(dm, &coneSection));
8990   PetscCall(DMPlexGetSupportSection(dm, &supportSection));
8991   /* Check that point p is found in the support of its cone points, and vice versa */
8992   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8993   for (p = pStart; p < pEnd; ++p) {
8994     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
8995     PetscCall(DMPlexGetCone(dm, p, &cone));
8996     for (c = 0; c < coneSize; ++c) {
8997       PetscBool dup = PETSC_FALSE;
8998       PetscInt  d;
8999       for (d = c - 1; d >= 0; --d) {
9000         if (cone[c] == cone[d]) {
9001           dup = PETSC_TRUE;
9002           break;
9003         }
9004       }
9005       PetscCall(DMPlexGetSupportSize(dm, cone[c], &supportSize));
9006       PetscCall(DMPlexGetSupport(dm, cone[c], &support));
9007       for (s = 0; s < supportSize; ++s) {
9008         if (support[s] == p) break;
9009       }
9010       if ((s >= supportSize) || (dup && (support[s + 1] != p))) {
9011         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", p));
9012         for (s = 0; s < coneSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[s]));
9013         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9014         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", cone[c]));
9015         for (s = 0; s < supportSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[s]));
9016         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9017         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]);
9018         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in support of cone point %" PetscInt_FMT, p, cone[c]);
9019       }
9020     }
9021     PetscCall(DMPlexGetTreeParent(dm, p, &pp, NULL));
9022     if (p != pp) {
9023       storagecheck = PETSC_FALSE;
9024       continue;
9025     }
9026     PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
9027     PetscCall(DMPlexGetSupport(dm, p, &support));
9028     for (s = 0; s < supportSize; ++s) {
9029       PetscCall(DMPlexGetConeSize(dm, support[s], &coneSize));
9030       PetscCall(DMPlexGetCone(dm, support[s], &cone));
9031       for (c = 0; c < coneSize; ++c) {
9032         PetscCall(DMPlexGetTreeParent(dm, cone[c], &pp, NULL));
9033         if (cone[c] != pp) {
9034           c = 0;
9035           break;
9036         }
9037         if (cone[c] == p) break;
9038       }
9039       if (c >= coneSize) {
9040         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", p));
9041         for (c = 0; c < supportSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[c]));
9042         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9043         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", support[s]));
9044         for (c = 0; c < coneSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[c]));
9045         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9046         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in cone of support point %" PetscInt_FMT, p, support[s]);
9047       }
9048     }
9049   }
9050   if (storagecheck) {
9051     PetscCall(PetscSectionGetStorageSize(coneSection, &csize));
9052     PetscCall(PetscSectionGetStorageSize(supportSection, &ssize));
9053     PetscCheck(csize == ssize, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Total cone size %" PetscInt_FMT " != Total support size %" PetscInt_FMT, csize, ssize);
9054   }
9055   PetscFunctionReturn(PETSC_SUCCESS);
9056 }
9057 
9058 /*
9059   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.
9060 */
9061 static PetscErrorCode DMPlexCellUnsplitVertices_Private(DM dm, PetscInt c, DMPolytopeType ct, PetscInt *unsplit)
9062 {
9063   DMPolytopeType  cct;
9064   PetscInt        ptpoints[4];
9065   const PetscInt *cone, *ccone, *ptcone;
9066   PetscInt        coneSize, cp, cconeSize, ccp, npt = 0, pt;
9067 
9068   PetscFunctionBegin;
9069   *unsplit = 0;
9070   switch (ct) {
9071   case DM_POLYTOPE_POINT_PRISM_TENSOR:
9072     ptpoints[npt++] = c;
9073     break;
9074   case DM_POLYTOPE_SEG_PRISM_TENSOR:
9075     PetscCall(DMPlexGetCone(dm, c, &cone));
9076     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9077     for (cp = 0; cp < coneSize; ++cp) {
9078       PetscCall(DMPlexGetCellType(dm, cone[cp], &cct));
9079       if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) ptpoints[npt++] = cone[cp];
9080     }
9081     break;
9082   case DM_POLYTOPE_TRI_PRISM_TENSOR:
9083   case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9084     PetscCall(DMPlexGetCone(dm, c, &cone));
9085     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9086     for (cp = 0; cp < coneSize; ++cp) {
9087       PetscCall(DMPlexGetCone(dm, cone[cp], &ccone));
9088       PetscCall(DMPlexGetConeSize(dm, cone[cp], &cconeSize));
9089       for (ccp = 0; ccp < cconeSize; ++ccp) {
9090         PetscCall(DMPlexGetCellType(dm, ccone[ccp], &cct));
9091         if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) {
9092           PetscInt p;
9093           for (p = 0; p < npt; ++p)
9094             if (ptpoints[p] == ccone[ccp]) break;
9095           if (p == npt) ptpoints[npt++] = ccone[ccp];
9096         }
9097       }
9098     }
9099     break;
9100   default:
9101     break;
9102   }
9103   for (pt = 0; pt < npt; ++pt) {
9104     PetscCall(DMPlexGetCone(dm, ptpoints[pt], &ptcone));
9105     if (ptcone[0] == ptcone[1]) ++(*unsplit);
9106   }
9107   PetscFunctionReturn(PETSC_SUCCESS);
9108 }
9109 
9110 /*@
9111   DMPlexCheckSkeleton - Check that each cell has the correct number of vertices
9112 
9113   Input Parameters:
9114 + dm         - The `DMPLEX` object
9115 - cellHeight - Normally 0
9116 
9117   Level: developer
9118 
9119   Notes:
9120   This is a useful diagnostic when creating meshes programmatically.
9121   Currently applicable only to homogeneous simplex or tensor meshes.
9122 
9123   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9124 
9125 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9126 @*/
9127 PetscErrorCode DMPlexCheckSkeleton(DM dm, PetscInt cellHeight)
9128 {
9129   DMPlexInterpolatedFlag interp;
9130   DMPolytopeType         ct;
9131   PetscInt               vStart, vEnd, cStart, cEnd, c;
9132 
9133   PetscFunctionBegin;
9134   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9135   PetscCall(DMPlexIsInterpolated(dm, &interp));
9136   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9137   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
9138   for (c = cStart; c < cEnd; ++c) {
9139     PetscInt *closure = NULL;
9140     PetscInt  coneSize, closureSize, cl, Nv = 0;
9141 
9142     PetscCall(DMPlexGetCellType(dm, c, &ct));
9143     PetscCheck((PetscInt)ct >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell %" PetscInt_FMT " has no cell type", c);
9144     if (ct == DM_POLYTOPE_UNKNOWN) continue;
9145     if (interp == DMPLEX_INTERPOLATED_FULL) {
9146       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9147       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));
9148     }
9149     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9150     for (cl = 0; cl < closureSize * 2; cl += 2) {
9151       const PetscInt p = closure[cl];
9152       if ((p >= vStart) && (p < vEnd)) ++Nv;
9153     }
9154     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9155     /* Special Case: Tensor faces with identified vertices */
9156     if (Nv < DMPolytopeTypeGetNumVertices(ct)) {
9157       PetscInt unsplit;
9158 
9159       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9160       if (Nv + unsplit == DMPolytopeTypeGetNumVertices(ct)) continue;
9161     }
9162     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));
9163   }
9164   PetscFunctionReturn(PETSC_SUCCESS);
9165 }
9166 
9167 /*@
9168   DMPlexCheckFaces - Check that the faces of each cell give a vertex order this is consistent with what we expect from the cell type
9169 
9170   Collective
9171 
9172   Input Parameters:
9173 + dm         - The `DMPLEX` object
9174 - cellHeight - Normally 0
9175 
9176   Level: developer
9177 
9178   Notes:
9179   This is a useful diagnostic when creating meshes programmatically.
9180   This routine is only relevant for meshes that are fully interpolated across all ranks.
9181   It will error out if a partially interpolated mesh is given on some rank.
9182   It will do nothing for locally uninterpolated mesh (as there is nothing to check).
9183 
9184   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9185 
9186 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMPlexGetVTKCellHeight()`, `DMSetFromOptions()`
9187 @*/
9188 PetscErrorCode DMPlexCheckFaces(DM dm, PetscInt cellHeight)
9189 {
9190   PetscInt               dim, depth, vStart, vEnd, cStart, cEnd, c, h;
9191   DMPlexInterpolatedFlag interpEnum;
9192 
9193   PetscFunctionBegin;
9194   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9195   PetscCall(DMPlexIsInterpolatedCollective(dm, &interpEnum));
9196   if (interpEnum == DMPLEX_INTERPOLATED_NONE) PetscFunctionReturn(PETSC_SUCCESS);
9197   if (interpEnum != DMPLEX_INTERPOLATED_FULL) {
9198     PetscCall(PetscPrintf(PetscObjectComm((PetscObject)dm), "DMPlexCheckFaces() warning: Mesh is only partially interpolated, this is currently not supported"));
9199     PetscFunctionReturn(PETSC_SUCCESS);
9200   }
9201 
9202   PetscCall(DMGetDimension(dm, &dim));
9203   PetscCall(DMPlexGetDepth(dm, &depth));
9204   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
9205   for (h = cellHeight; h < PetscMin(depth, dim); ++h) {
9206     PetscCall(DMPlexGetHeightStratum(dm, h, &cStart, &cEnd));
9207     for (c = cStart; c < cEnd; ++c) {
9208       const PetscInt       *cone, *ornt, *faceSizes, *faces;
9209       const DMPolytopeType *faceTypes;
9210       DMPolytopeType        ct;
9211       PetscInt              numFaces, coneSize, f;
9212       PetscInt             *closure = NULL, closureSize, cl, numCorners = 0, fOff = 0, unsplit;
9213 
9214       PetscCall(DMPlexGetCellType(dm, c, &ct));
9215       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9216       if (unsplit) continue;
9217       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9218       PetscCall(DMPlexGetCone(dm, c, &cone));
9219       PetscCall(DMPlexGetConeOrientation(dm, c, &ornt));
9220       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9221       for (cl = 0; cl < closureSize * 2; cl += 2) {
9222         const PetscInt p = closure[cl];
9223         if ((p >= vStart) && (p < vEnd)) closure[numCorners++] = p;
9224       }
9225       PetscCall(DMPlexGetRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
9226       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);
9227       for (f = 0; f < numFaces; ++f) {
9228         DMPolytopeType fct;
9229         PetscInt      *fclosure = NULL, fclosureSize, cl, fnumCorners = 0, v;
9230 
9231         PetscCall(DMPlexGetCellType(dm, cone[f], &fct));
9232         PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[f], ornt[f], PETSC_TRUE, &fclosureSize, &fclosure));
9233         for (cl = 0; cl < fclosureSize * 2; cl += 2) {
9234           const PetscInt p = fclosure[cl];
9235           if ((p >= vStart) && (p < vEnd)) fclosure[fnumCorners++] = p;
9236         }
9237         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]);
9238         for (v = 0; v < fnumCorners; ++v) {
9239           if (fclosure[v] != faces[fOff + v]) {
9240             PetscInt v1;
9241 
9242             PetscCall(PetscPrintf(PETSC_COMM_SELF, "face closure:"));
9243             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, fclosure[v1]));
9244             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\ncell face:"));
9245             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, faces[fOff + v1]));
9246             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9247             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]);
9248           }
9249         }
9250         PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[f], PETSC_TRUE, &fclosureSize, &fclosure));
9251         fOff += faceSizes[f];
9252       }
9253       PetscCall(DMPlexRestoreRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
9254       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9255     }
9256   }
9257   PetscFunctionReturn(PETSC_SUCCESS);
9258 }
9259 
9260 /*@
9261   DMPlexCheckGeometry - Check the geometry of mesh cells
9262 
9263   Input Parameter:
9264 . dm - The `DMPLEX` object
9265 
9266   Level: developer
9267 
9268   Notes:
9269   This is a useful diagnostic when creating meshes programmatically.
9270 
9271   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9272 
9273 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9274 @*/
9275 PetscErrorCode DMPlexCheckGeometry(DM dm)
9276 {
9277   Vec       coordinates;
9278   PetscReal detJ, J[9], refVol = 1.0;
9279   PetscReal vol;
9280   PetscInt  dim, depth, dE, d, cStart, cEnd, c;
9281 
9282   PetscFunctionBegin;
9283   PetscCall(DMGetDimension(dm, &dim));
9284   PetscCall(DMGetCoordinateDim(dm, &dE));
9285   if (dim != dE) PetscFunctionReturn(PETSC_SUCCESS);
9286   PetscCall(DMPlexGetDepth(dm, &depth));
9287   for (d = 0; d < dim; ++d) refVol *= 2.0;
9288   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
9289   /* Make sure local coordinates are created, because that step is collective */
9290   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
9291   if (!coordinates) PetscFunctionReturn(PETSC_SUCCESS);
9292   for (c = cStart; c < cEnd; ++c) {
9293     DMPolytopeType ct;
9294     PetscInt       unsplit;
9295     PetscBool      ignoreZeroVol = PETSC_FALSE;
9296 
9297     PetscCall(DMPlexGetCellType(dm, c, &ct));
9298     switch (ct) {
9299     case DM_POLYTOPE_SEG_PRISM_TENSOR:
9300     case DM_POLYTOPE_TRI_PRISM_TENSOR:
9301     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9302       ignoreZeroVol = PETSC_TRUE;
9303       break;
9304     default:
9305       break;
9306     }
9307     switch (ct) {
9308     case DM_POLYTOPE_TRI_PRISM:
9309     case DM_POLYTOPE_TRI_PRISM_TENSOR:
9310     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9311     case DM_POLYTOPE_PYRAMID:
9312       continue;
9313     default:
9314       break;
9315     }
9316     PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9317     if (unsplit) continue;
9318     PetscCall(DMPlexComputeCellGeometryFEM(dm, c, NULL, NULL, J, NULL, &detJ));
9319     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);
9320     PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FEM Volume %g\n", c, (double)(detJ * refVol)));
9321     /* This should work with periodicity since DG coordinates should be used */
9322     if (depth > 1) {
9323       PetscCall(DMPlexComputeCellGeometryFVM(dm, c, &vol, NULL, NULL));
9324       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);
9325       PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FVM Volume %g\n", c, (double)vol));
9326     }
9327   }
9328   PetscFunctionReturn(PETSC_SUCCESS);
9329 }
9330 
9331 /*@
9332   DMPlexCheckPointSF - Check that several necessary conditions are met for the point `PetscSF` of this plex.
9333 
9334   Collective
9335 
9336   Input Parameters:
9337 + dm              - The `DMPLEX` object
9338 . pointSF         - The `PetscSF`, or `NULL` for `PointSF` attached to `DM`
9339 - allowExtraRoots - Flag to allow extra points not present in the `DM`
9340 
9341   Level: developer
9342 
9343   Notes:
9344   This is mainly intended for debugging/testing purposes.
9345 
9346   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9347 
9348   Extra roots can come from periodic cuts, where additional points appear on the boundary
9349 
9350 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMGetPointSF()`, `DMSetFromOptions()`
9351 @*/
9352 PetscErrorCode DMPlexCheckPointSF(DM dm, PetscSF pointSF, PetscBool allowExtraRoots)
9353 {
9354   PetscInt           l, nleaves, nroots, overlap;
9355   const PetscInt    *locals;
9356   const PetscSFNode *remotes;
9357   PetscBool          distributed;
9358   MPI_Comm           comm;
9359   PetscMPIInt        rank;
9360 
9361   PetscFunctionBegin;
9362   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9363   if (pointSF) PetscValidHeaderSpecific(pointSF, PETSCSF_CLASSID, 2);
9364   else pointSF = dm->sf;
9365   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9366   PetscCheck(pointSF, comm, PETSC_ERR_ARG_WRONGSTATE, "DMPlex must have Point SF attached");
9367   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9368   {
9369     PetscMPIInt mpiFlag;
9370 
9371     PetscCallMPI(MPI_Comm_compare(comm, PetscObjectComm((PetscObject)pointSF), &mpiFlag));
9372     PetscCheck(mpiFlag == MPI_CONGRUENT || mpiFlag == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "DM and Point SF have different communicators (flag %d)", mpiFlag);
9373   }
9374   PetscCall(PetscSFGetGraph(pointSF, &nroots, &nleaves, &locals, &remotes));
9375   PetscCall(DMPlexIsDistributed(dm, &distributed));
9376   if (!distributed) {
9377     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);
9378     PetscFunctionReturn(PETSC_SUCCESS);
9379   }
9380   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);
9381   PetscCall(DMPlexGetOverlap(dm, &overlap));
9382 
9383   /* Check SF graph is compatible with DMPlex chart */
9384   {
9385     PetscInt pStart, pEnd, maxLeaf;
9386 
9387     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
9388     PetscCall(PetscSFGetLeafRange(pointSF, NULL, &maxLeaf));
9389     PetscCheck(allowExtraRoots || pEnd - pStart == nroots, PETSC_COMM_SELF, PETSC_ERR_PLIB, "pEnd - pStart = %" PetscInt_FMT " != nroots = %" PetscInt_FMT, pEnd - pStart, nroots);
9390     PetscCheck(maxLeaf < pEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "maxLeaf = %" PetscInt_FMT " >= pEnd = %" PetscInt_FMT, maxLeaf, pEnd);
9391   }
9392 
9393   /* Check Point SF has no local points referenced */
9394   for (l = 0; l < nleaves; l++) {
9395     PetscAssert(remotes[l].rank != (PetscInt)rank, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains local point %" PetscInt_FMT " <- (%" PetscInt_FMT ",%" PetscInt_FMT ")", locals ? locals[l] : l, remotes[l].rank, remotes[l].index);
9396   }
9397 
9398   /* Check there are no cells in interface */
9399   if (!overlap) {
9400     PetscInt cellHeight, cStart, cEnd;
9401 
9402     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9403     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9404     for (l = 0; l < nleaves; ++l) {
9405       const PetscInt point = locals ? locals[l] : l;
9406 
9407       PetscCheck(point < cStart || point >= cEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " which is a cell", point);
9408     }
9409   }
9410 
9411   /* If some point is in interface, then all its cone points must be also in interface (either as leaves or roots) */
9412   {
9413     const PetscInt *rootdegree;
9414 
9415     PetscCall(PetscSFComputeDegreeBegin(pointSF, &rootdegree));
9416     PetscCall(PetscSFComputeDegreeEnd(pointSF, &rootdegree));
9417     for (l = 0; l < nleaves; ++l) {
9418       const PetscInt  point = locals ? locals[l] : l;
9419       const PetscInt *cone;
9420       PetscInt        coneSize, c, idx;
9421 
9422       PetscCall(DMPlexGetConeSize(dm, point, &coneSize));
9423       PetscCall(DMPlexGetCone(dm, point, &cone));
9424       for (c = 0; c < coneSize; ++c) {
9425         if (!rootdegree[cone[c]]) {
9426           if (locals) {
9427             PetscCall(PetscFindInt(cone[c], nleaves, locals, &idx));
9428           } else {
9429             idx = (cone[c] < nleaves) ? cone[c] : -1;
9430           }
9431           PetscCheck(idx >= 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " but not %" PetscInt_FMT " from its cone", point, cone[c]);
9432         }
9433       }
9434     }
9435   }
9436   PetscFunctionReturn(PETSC_SUCCESS);
9437 }
9438 
9439 /*@
9440   DMPlexCheck - Perform various checks of `DMPLEX` sanity
9441 
9442   Input Parameter:
9443 . dm - The `DMPLEX` object
9444 
9445   Level: developer
9446 
9447   Notes:
9448   This is a useful diagnostic when creating meshes programmatically.
9449 
9450   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9451 
9452   Currently does not include `DMPlexCheckCellShape()`.
9453 
9454 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9455 @*/
9456 PetscErrorCode DMPlexCheck(DM dm)
9457 {
9458   PetscInt cellHeight;
9459 
9460   PetscFunctionBegin;
9461   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9462   PetscCall(DMPlexCheckSymmetry(dm));
9463   PetscCall(DMPlexCheckSkeleton(dm, cellHeight));
9464   PetscCall(DMPlexCheckFaces(dm, cellHeight));
9465   PetscCall(DMPlexCheckGeometry(dm));
9466   PetscCall(DMPlexCheckPointSF(dm, NULL, PETSC_FALSE));
9467   PetscCall(DMPlexCheckInterfaceCones(dm));
9468   PetscFunctionReturn(PETSC_SUCCESS);
9469 }
9470 
9471 typedef struct cell_stats {
9472   PetscReal min, max, sum, squaresum;
9473   PetscInt  count;
9474 } cell_stats_t;
9475 
9476 static void MPIAPI cell_stats_reduce(void *a, void *b, int *len, MPI_Datatype *datatype)
9477 {
9478   PetscInt i, N = *len;
9479 
9480   for (i = 0; i < N; i++) {
9481     cell_stats_t *A = (cell_stats_t *)a;
9482     cell_stats_t *B = (cell_stats_t *)b;
9483 
9484     B->min = PetscMin(A->min, B->min);
9485     B->max = PetscMax(A->max, B->max);
9486     B->sum += A->sum;
9487     B->squaresum += A->squaresum;
9488     B->count += A->count;
9489   }
9490 }
9491 
9492 /*@
9493   DMPlexCheckCellShape - Checks the Jacobian of the mapping from reference to real cells and computes some minimal statistics.
9494 
9495   Collective
9496 
9497   Input Parameters:
9498 + dm        - The `DMPLEX` object
9499 . output    - If true, statistics will be displayed on `stdout`
9500 - condLimit - Display all cells above this condition number, or `PETSC_DETERMINE` for no cell output
9501 
9502   Level: developer
9503 
9504   Notes:
9505   This is mainly intended for debugging/testing purposes.
9506 
9507   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9508 
9509 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexComputeOrthogonalQuality()`
9510 @*/
9511 PetscErrorCode DMPlexCheckCellShape(DM dm, PetscBool output, PetscReal condLimit)
9512 {
9513   DM           dmCoarse;
9514   cell_stats_t stats, globalStats;
9515   MPI_Comm     comm = PetscObjectComm((PetscObject)dm);
9516   PetscReal   *J, *invJ, min = 0, max = 0, mean = 0, stdev = 0;
9517   PetscReal    limit = condLimit > 0 ? condLimit : PETSC_MAX_REAL;
9518   PetscInt     cdim, cStart, cEnd, c, eStart, eEnd, count = 0;
9519   PetscMPIInt  rank, size;
9520 
9521   PetscFunctionBegin;
9522   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9523   stats.min = PETSC_MAX_REAL;
9524   stats.max = PETSC_MIN_REAL;
9525   stats.sum = stats.squaresum = 0.;
9526   stats.count                 = 0;
9527 
9528   PetscCallMPI(MPI_Comm_size(comm, &size));
9529   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9530   PetscCall(DMGetCoordinateDim(dm, &cdim));
9531   PetscCall(PetscMalloc2(PetscSqr(cdim), &J, PetscSqr(cdim), &invJ));
9532   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
9533   PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
9534   for (c = cStart; c < cEnd; c++) {
9535     PetscInt  i;
9536     PetscReal frobJ = 0., frobInvJ = 0., cond2, cond, detJ;
9537 
9538     PetscCall(DMPlexComputeCellGeometryAffineFEM(dm, c, NULL, J, invJ, &detJ));
9539     PetscCheck(detJ >= 0.0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Mesh cell %" PetscInt_FMT " is inverted", c);
9540     for (i = 0; i < PetscSqr(cdim); ++i) {
9541       frobJ += J[i] * J[i];
9542       frobInvJ += invJ[i] * invJ[i];
9543     }
9544     cond2 = frobJ * frobInvJ;
9545     cond  = PetscSqrtReal(cond2);
9546 
9547     stats.min = PetscMin(stats.min, cond);
9548     stats.max = PetscMax(stats.max, cond);
9549     stats.sum += cond;
9550     stats.squaresum += cond2;
9551     stats.count++;
9552     if (output && cond > limit) {
9553       PetscSection coordSection;
9554       Vec          coordsLocal;
9555       PetscScalar *coords = NULL;
9556       PetscInt     Nv, d, clSize, cl, *closure = NULL;
9557 
9558       PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
9559       PetscCall(DMGetCoordinateSection(dm, &coordSection));
9560       PetscCall(DMPlexVecGetClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9561       PetscCall(PetscSynchronizedPrintf(comm, "[%d] Cell %" PetscInt_FMT " cond %g\n", rank, c, (double)cond));
9562       for (i = 0; i < Nv / cdim; ++i) {
9563         PetscCall(PetscSynchronizedPrintf(comm, "  Vertex %" PetscInt_FMT ": (", i));
9564         for (d = 0; d < cdim; ++d) {
9565           if (d > 0) PetscCall(PetscSynchronizedPrintf(comm, ", "));
9566           PetscCall(PetscSynchronizedPrintf(comm, "%g", (double)PetscRealPart(coords[i * cdim + d])));
9567         }
9568         PetscCall(PetscSynchronizedPrintf(comm, ")\n"));
9569       }
9570       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9571       for (cl = 0; cl < clSize * 2; cl += 2) {
9572         const PetscInt edge = closure[cl];
9573 
9574         if ((edge >= eStart) && (edge < eEnd)) {
9575           PetscReal len;
9576 
9577           PetscCall(DMPlexComputeCellGeometryFVM(dm, edge, &len, NULL, NULL));
9578           PetscCall(PetscSynchronizedPrintf(comm, "  Edge %" PetscInt_FMT ": length %g\n", edge, (double)len));
9579         }
9580       }
9581       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9582       PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9583     }
9584   }
9585   if (output) PetscCall(PetscSynchronizedFlush(comm, NULL));
9586 
9587   if (size > 1) {
9588     PetscMPIInt  blockLengths[2] = {4, 1};
9589     MPI_Aint     blockOffsets[2] = {offsetof(cell_stats_t, min), offsetof(cell_stats_t, count)};
9590     MPI_Datatype blockTypes[2]   = {MPIU_REAL, MPIU_INT}, statType;
9591     MPI_Op       statReduce;
9592 
9593     PetscCallMPI(MPI_Type_create_struct(2, blockLengths, blockOffsets, blockTypes, &statType));
9594     PetscCallMPI(MPI_Type_commit(&statType));
9595     PetscCallMPI(MPI_Op_create(cell_stats_reduce, PETSC_TRUE, &statReduce));
9596     PetscCallMPI(MPI_Reduce(&stats, &globalStats, 1, statType, statReduce, 0, comm));
9597     PetscCallMPI(MPI_Op_free(&statReduce));
9598     PetscCallMPI(MPI_Type_free(&statType));
9599   } else {
9600     PetscCall(PetscArraycpy(&globalStats, &stats, 1));
9601   }
9602   if (rank == 0) {
9603     count = globalStats.count;
9604     min   = globalStats.min;
9605     max   = globalStats.max;
9606     mean  = globalStats.sum / globalStats.count;
9607     stdev = globalStats.count > 1 ? PetscSqrtReal(PetscMax((globalStats.squaresum - globalStats.count * mean * mean) / (globalStats.count - 1), 0)) : 0.0;
9608   }
9609 
9610   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));
9611   PetscCall(PetscFree2(J, invJ));
9612 
9613   PetscCall(DMGetCoarseDM(dm, &dmCoarse));
9614   if (dmCoarse) {
9615     PetscBool isplex;
9616 
9617     PetscCall(PetscObjectTypeCompare((PetscObject)dmCoarse, DMPLEX, &isplex));
9618     if (isplex) PetscCall(DMPlexCheckCellShape(dmCoarse, output, condLimit));
9619   }
9620   PetscFunctionReturn(PETSC_SUCCESS);
9621 }
9622 
9623 /*@
9624   DMPlexComputeOrthogonalQuality - Compute cell-wise orthogonal quality mesh statistic. Optionally tags all cells with
9625   orthogonal quality below given tolerance.
9626 
9627   Collective
9628 
9629   Input Parameters:
9630 + dm   - The `DMPLEX` object
9631 . fv   - Optional `PetscFV` object for pre-computed cell/face centroid information
9632 - atol - [0, 1] Absolute tolerance for tagging cells.
9633 
9634   Output Parameters:
9635 + OrthQual      - `Vec` containing orthogonal quality per cell
9636 - OrthQualLabel - `DMLabel` tagging cells below atol with `DM_ADAPT_REFINE`
9637 
9638   Options Database Keys:
9639 + -dm_plex_orthogonal_quality_label_view - view OrthQualLabel if label is requested. Currently only `PETSCVIEWERASCII` is supported.
9640 - -dm_plex_orthogonal_quality_vec_view   - view OrthQual vector.
9641 
9642   Level: intermediate
9643 
9644   Notes:
9645   Orthogonal quality is given by the following formula\:
9646 
9647   $ \min \left[ \frac{A_i \cdot f_i}{\|A_i\| \|f_i\|} , \frac{A_i \cdot c_i}{\|A_i\| \|c_i\|} \right]$
9648 
9649   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
9650   is the vector from the current cells centroid to the centroid of its i'th neighbor (which shares a face with the
9651   current cell). This computes the vector similarity between each cell face and its corresponding neighbor centroid by
9652   calculating the cosine of the angle between these vectors.
9653 
9654   Orthogonal quality ranges from 1 (best) to 0 (worst).
9655 
9656   This routine is mainly useful for FVM, however is not restricted to only FVM. The `PetscFV` object is optionally used to check for
9657   pre-computed FVM cell data, but if it is not passed in then this data will be computed.
9658 
9659   Cells are tagged if they have an orthogonal quality less than or equal to the absolute tolerance.
9660 
9661 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCheckCellShape()`, `DMCreateLabel()`, `PetscFV`, `DMLabel`, `Vec`
9662 @*/
9663 PetscErrorCode DMPlexComputeOrthogonalQuality(DM dm, PetscFV fv, PetscReal atol, Vec *OrthQual, DMLabel *OrthQualLabel)
9664 {
9665   PetscInt               nc, cellHeight, cStart, cEnd, cell, cellIter = 0;
9666   PetscInt              *idx;
9667   PetscScalar           *oqVals;
9668   const PetscScalar     *cellGeomArr, *faceGeomArr;
9669   PetscReal             *ci, *fi, *Ai;
9670   MPI_Comm               comm;
9671   Vec                    cellgeom, facegeom;
9672   DM                     dmFace, dmCell;
9673   IS                     glob;
9674   ISLocalToGlobalMapping ltog;
9675   PetscViewer            vwr;
9676 
9677   PetscFunctionBegin;
9678   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9679   if (fv) PetscValidHeaderSpecific(fv, PETSCFV_CLASSID, 2);
9680   PetscAssertPointer(OrthQual, 4);
9681   PetscCheck(atol >= 0.0 && atol <= 1.0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Absolute tolerance %g not in [0,1]", (double)atol);
9682   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9683   PetscCall(DMGetDimension(dm, &nc));
9684   PetscCheck(nc >= 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must have dimension >= 2 (current %" PetscInt_FMT ")", nc);
9685   {
9686     DMPlexInterpolatedFlag interpFlag;
9687 
9688     PetscCall(DMPlexIsInterpolated(dm, &interpFlag));
9689     if (interpFlag != DMPLEX_INTERPOLATED_FULL) {
9690       PetscMPIInt rank;
9691 
9692       PetscCallMPI(MPI_Comm_rank(comm, &rank));
9693       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must be fully interpolated, DM on rank %d is not fully interpolated", rank);
9694     }
9695   }
9696   if (OrthQualLabel) {
9697     PetscAssertPointer(OrthQualLabel, 5);
9698     PetscCall(DMCreateLabel(dm, "Orthogonal_Quality"));
9699     PetscCall(DMGetLabel(dm, "Orthogonal_Quality", OrthQualLabel));
9700   } else {
9701     *OrthQualLabel = NULL;
9702   }
9703   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9704   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9705   PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_TRUE, &glob));
9706   PetscCall(ISLocalToGlobalMappingCreateIS(glob, &ltog));
9707   PetscCall(ISLocalToGlobalMappingSetType(ltog, ISLOCALTOGLOBALMAPPINGHASH));
9708   PetscCall(VecCreate(comm, OrthQual));
9709   PetscCall(VecSetType(*OrthQual, VECSTANDARD));
9710   PetscCall(VecSetSizes(*OrthQual, cEnd - cStart, PETSC_DETERMINE));
9711   PetscCall(VecSetLocalToGlobalMapping(*OrthQual, ltog));
9712   PetscCall(VecSetUp(*OrthQual));
9713   PetscCall(ISDestroy(&glob));
9714   PetscCall(ISLocalToGlobalMappingDestroy(&ltog));
9715   PetscCall(DMPlexGetDataFVM(dm, fv, &cellgeom, &facegeom, NULL));
9716   PetscCall(VecGetArrayRead(cellgeom, &cellGeomArr));
9717   PetscCall(VecGetArrayRead(facegeom, &faceGeomArr));
9718   PetscCall(VecGetDM(cellgeom, &dmCell));
9719   PetscCall(VecGetDM(facegeom, &dmFace));
9720   PetscCall(PetscMalloc5(cEnd - cStart, &idx, cEnd - cStart, &oqVals, nc, &ci, nc, &fi, nc, &Ai));
9721   for (cell = cStart; cell < cEnd; cellIter++, cell++) {
9722     PetscInt         cellneigh, cellneighiter = 0, adjSize = PETSC_DETERMINE;
9723     PetscInt         cellarr[2], *adj = NULL;
9724     PetscScalar     *cArr, *fArr;
9725     PetscReal        minvalc = 1.0, minvalf = 1.0;
9726     PetscFVCellGeom *cg;
9727 
9728     idx[cellIter] = cell - cStart;
9729     cellarr[0]    = cell;
9730     /* Make indexing into cellGeom easier */
9731     PetscCall(DMPlexPointLocalRead(dmCell, cell, cellGeomArr, &cg));
9732     PetscCall(DMPlexGetAdjacency_Internal(dm, cell, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &adjSize, &adj));
9733     /* Technically 1 too big, but easier than fiddling with empty adjacency array */
9734     PetscCall(PetscCalloc2(adjSize, &cArr, adjSize, &fArr));
9735     for (cellneigh = 0; cellneigh < adjSize; cellneighiter++, cellneigh++) {
9736       PetscInt         i;
9737       const PetscInt   neigh  = adj[cellneigh];
9738       PetscReal        normci = 0, normfi = 0, normai = 0;
9739       PetscFVCellGeom *cgneigh;
9740       PetscFVFaceGeom *fg;
9741 
9742       /* Don't count ourselves in the neighbor list */
9743       if (neigh == cell) continue;
9744       PetscCall(DMPlexPointLocalRead(dmCell, neigh, cellGeomArr, &cgneigh));
9745       cellarr[1] = neigh;
9746       {
9747         PetscInt        numcovpts;
9748         const PetscInt *covpts;
9749 
9750         PetscCall(DMPlexGetMeet(dm, 2, cellarr, &numcovpts, &covpts));
9751         PetscCall(DMPlexPointLocalRead(dmFace, covpts[0], faceGeomArr, &fg));
9752         PetscCall(DMPlexRestoreMeet(dm, 2, cellarr, &numcovpts, &covpts));
9753       }
9754 
9755       /* Compute c_i, f_i and their norms */
9756       for (i = 0; i < nc; i++) {
9757         ci[i] = cgneigh->centroid[i] - cg->centroid[i];
9758         fi[i] = fg->centroid[i] - cg->centroid[i];
9759         Ai[i] = fg->normal[i];
9760         normci += PetscPowReal(ci[i], 2);
9761         normfi += PetscPowReal(fi[i], 2);
9762         normai += PetscPowReal(Ai[i], 2);
9763       }
9764       normci = PetscSqrtReal(normci);
9765       normfi = PetscSqrtReal(normfi);
9766       normai = PetscSqrtReal(normai);
9767 
9768       /* Normalize and compute for each face-cell-normal pair */
9769       for (i = 0; i < nc; i++) {
9770         ci[i] = ci[i] / normci;
9771         fi[i] = fi[i] / normfi;
9772         Ai[i] = Ai[i] / normai;
9773         /* PetscAbs because I don't know if normals are guaranteed to point out */
9774         cArr[cellneighiter] += PetscAbs(Ai[i] * ci[i]);
9775         fArr[cellneighiter] += PetscAbs(Ai[i] * fi[i]);
9776       }
9777       if (PetscRealPart(cArr[cellneighiter]) < minvalc) minvalc = PetscRealPart(cArr[cellneighiter]);
9778       if (PetscRealPart(fArr[cellneighiter]) < minvalf) minvalf = PetscRealPart(fArr[cellneighiter]);
9779     }
9780     PetscCall(PetscFree(adj));
9781     PetscCall(PetscFree2(cArr, fArr));
9782     /* Defer to cell if they're equal */
9783     oqVals[cellIter] = PetscMin(minvalf, minvalc);
9784     if (OrthQualLabel) {
9785       if (PetscRealPart(oqVals[cellIter]) <= atol) PetscCall(DMLabelSetValue(*OrthQualLabel, cell, DM_ADAPT_REFINE));
9786     }
9787   }
9788   PetscCall(VecSetValuesLocal(*OrthQual, cEnd - cStart, idx, oqVals, INSERT_VALUES));
9789   PetscCall(VecAssemblyBegin(*OrthQual));
9790   PetscCall(VecAssemblyEnd(*OrthQual));
9791   PetscCall(VecRestoreArrayRead(cellgeom, &cellGeomArr));
9792   PetscCall(VecRestoreArrayRead(facegeom, &faceGeomArr));
9793   PetscCall(PetscOptionsGetViewer(comm, NULL, NULL, "-dm_plex_orthogonal_quality_label_view", &vwr, NULL, NULL));
9794   if (OrthQualLabel) {
9795     if (vwr) PetscCall(DMLabelView(*OrthQualLabel, vwr));
9796   }
9797   PetscCall(PetscFree5(idx, oqVals, ci, fi, Ai));
9798   PetscCall(PetscOptionsRestoreViewer(&vwr));
9799   PetscCall(VecViewFromOptions(*OrthQual, NULL, "-dm_plex_orthogonal_quality_vec_view"));
9800   PetscFunctionReturn(PETSC_SUCCESS);
9801 }
9802 
9803 /* this is here instead of DMGetOutputDM because output DM still has constraints in the local indices that affect
9804  * interpolator construction */
9805 static PetscErrorCode DMGetFullDM(DM dm, DM *odm)
9806 {
9807   PetscSection section, newSection, gsection;
9808   PetscSF      sf;
9809   PetscBool    hasConstraints, ghasConstraints;
9810 
9811   PetscFunctionBegin;
9812   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9813   PetscAssertPointer(odm, 2);
9814   PetscCall(DMGetLocalSection(dm, &section));
9815   PetscCall(PetscSectionHasConstraints(section, &hasConstraints));
9816   PetscCall(MPIU_Allreduce(&hasConstraints, &ghasConstraints, 1, MPIU_BOOL, MPI_LOR, PetscObjectComm((PetscObject)dm)));
9817   if (!ghasConstraints) {
9818     PetscCall(PetscObjectReference((PetscObject)dm));
9819     *odm = dm;
9820     PetscFunctionReturn(PETSC_SUCCESS);
9821   }
9822   PetscCall(DMClone(dm, odm));
9823   PetscCall(DMCopyFields(dm, *odm));
9824   PetscCall(DMGetLocalSection(*odm, &newSection));
9825   PetscCall(DMGetPointSF(*odm, &sf));
9826   PetscCall(PetscSectionCreateGlobalSection(newSection, sf, PETSC_TRUE, PETSC_FALSE, &gsection));
9827   PetscCall(DMSetGlobalSection(*odm, gsection));
9828   PetscCall(PetscSectionDestroy(&gsection));
9829   PetscFunctionReturn(PETSC_SUCCESS);
9830 }
9831 
9832 static PetscErrorCode DMCreateAffineInterpolationCorrection_Plex(DM dmc, DM dmf, Vec *shift)
9833 {
9834   DM        dmco, dmfo;
9835   Mat       interpo;
9836   Vec       rscale;
9837   Vec       cglobalo, clocal;
9838   Vec       fglobal, fglobalo, flocal;
9839   PetscBool regular;
9840 
9841   PetscFunctionBegin;
9842   PetscCall(DMGetFullDM(dmc, &dmco));
9843   PetscCall(DMGetFullDM(dmf, &dmfo));
9844   PetscCall(DMSetCoarseDM(dmfo, dmco));
9845   PetscCall(DMPlexGetRegularRefinement(dmf, &regular));
9846   PetscCall(DMPlexSetRegularRefinement(dmfo, regular));
9847   PetscCall(DMCreateInterpolation(dmco, dmfo, &interpo, &rscale));
9848   PetscCall(DMCreateGlobalVector(dmco, &cglobalo));
9849   PetscCall(DMCreateLocalVector(dmc, &clocal));
9850   PetscCall(VecSet(cglobalo, 0.));
9851   PetscCall(VecSet(clocal, 0.));
9852   PetscCall(DMCreateGlobalVector(dmf, &fglobal));
9853   PetscCall(DMCreateGlobalVector(dmfo, &fglobalo));
9854   PetscCall(DMCreateLocalVector(dmf, &flocal));
9855   PetscCall(VecSet(fglobal, 0.));
9856   PetscCall(VecSet(fglobalo, 0.));
9857   PetscCall(VecSet(flocal, 0.));
9858   PetscCall(DMPlexInsertBoundaryValues(dmc, PETSC_TRUE, clocal, 0., NULL, NULL, NULL));
9859   PetscCall(DMLocalToGlobalBegin(dmco, clocal, INSERT_VALUES, cglobalo));
9860   PetscCall(DMLocalToGlobalEnd(dmco, clocal, INSERT_VALUES, cglobalo));
9861   PetscCall(MatMult(interpo, cglobalo, fglobalo));
9862   PetscCall(DMGlobalToLocalBegin(dmfo, fglobalo, INSERT_VALUES, flocal));
9863   PetscCall(DMGlobalToLocalEnd(dmfo, fglobalo, INSERT_VALUES, flocal));
9864   PetscCall(DMLocalToGlobalBegin(dmf, flocal, INSERT_VALUES, fglobal));
9865   PetscCall(DMLocalToGlobalEnd(dmf, flocal, INSERT_VALUES, fglobal));
9866   *shift = fglobal;
9867   PetscCall(VecDestroy(&flocal));
9868   PetscCall(VecDestroy(&fglobalo));
9869   PetscCall(VecDestroy(&clocal));
9870   PetscCall(VecDestroy(&cglobalo));
9871   PetscCall(VecDestroy(&rscale));
9872   PetscCall(MatDestroy(&interpo));
9873   PetscCall(DMDestroy(&dmfo));
9874   PetscCall(DMDestroy(&dmco));
9875   PetscFunctionReturn(PETSC_SUCCESS);
9876 }
9877 
9878 PETSC_INTERN PetscErrorCode DMInterpolateSolution_Plex(DM coarse, DM fine, Mat interp, Vec coarseSol, Vec fineSol)
9879 {
9880   PetscObject shifto;
9881   Vec         shift;
9882 
9883   PetscFunctionBegin;
9884   if (!interp) {
9885     Vec rscale;
9886 
9887     PetscCall(DMCreateInterpolation(coarse, fine, &interp, &rscale));
9888     PetscCall(VecDestroy(&rscale));
9889   } else {
9890     PetscCall(PetscObjectReference((PetscObject)interp));
9891   }
9892   PetscCall(PetscObjectQuery((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", &shifto));
9893   if (!shifto) {
9894     PetscCall(DMCreateAffineInterpolationCorrection_Plex(coarse, fine, &shift));
9895     PetscCall(PetscObjectCompose((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", (PetscObject)shift));
9896     shifto = (PetscObject)shift;
9897     PetscCall(VecDestroy(&shift));
9898   }
9899   shift = (Vec)shifto;
9900   PetscCall(MatInterpolate(interp, coarseSol, fineSol));
9901   PetscCall(VecAXPY(fineSol, 1.0, shift));
9902   PetscCall(MatDestroy(&interp));
9903   PetscFunctionReturn(PETSC_SUCCESS);
9904 }
9905 
9906 /* Pointwise interpolation
9907      Just code FEM for now
9908      u^f = I u^c
9909      sum_k u^f_k phi^f_k = I sum_j u^c_j phi^c_j
9910      u^f_i = sum_j psi^f_i I phi^c_j u^c_j
9911      I_{ij} = psi^f_i phi^c_j
9912 */
9913 PetscErrorCode DMCreateInterpolation_Plex(DM dmCoarse, DM dmFine, Mat *interpolation, Vec *scaling)
9914 {
9915   PetscSection gsc, gsf;
9916   PetscInt     m, n;
9917   void        *ctx;
9918   DM           cdm;
9919   PetscBool    regular, ismatis, isRefined = dmCoarse->data == dmFine->data ? PETSC_FALSE : PETSC_TRUE;
9920 
9921   PetscFunctionBegin;
9922   PetscCall(DMGetGlobalSection(dmFine, &gsf));
9923   PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
9924   PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
9925   PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
9926 
9927   PetscCall(PetscStrcmp(dmCoarse->mattype, MATIS, &ismatis));
9928   PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), interpolation));
9929   PetscCall(MatSetSizes(*interpolation, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
9930   PetscCall(MatSetType(*interpolation, ismatis ? MATAIJ : dmCoarse->mattype));
9931   PetscCall(DMGetApplicationContext(dmFine, &ctx));
9932 
9933   PetscCall(DMGetCoarseDM(dmFine, &cdm));
9934   PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
9935   if (!isRefined || (regular && cdm == dmCoarse)) PetscCall(DMPlexComputeInterpolatorNested(dmCoarse, dmFine, isRefined, *interpolation, ctx));
9936   else PetscCall(DMPlexComputeInterpolatorGeneral(dmCoarse, dmFine, *interpolation, ctx));
9937   PetscCall(MatViewFromOptions(*interpolation, NULL, "-interp_mat_view"));
9938   if (scaling) {
9939     /* Use naive scaling */
9940     PetscCall(DMCreateInterpolationScale(dmCoarse, dmFine, *interpolation, scaling));
9941   }
9942   PetscFunctionReturn(PETSC_SUCCESS);
9943 }
9944 
9945 PetscErrorCode DMCreateInjection_Plex(DM dmCoarse, DM dmFine, Mat *mat)
9946 {
9947   VecScatter ctx;
9948 
9949   PetscFunctionBegin;
9950   PetscCall(DMPlexComputeInjectorFEM(dmCoarse, dmFine, &ctx, NULL));
9951   PetscCall(MatCreateScatter(PetscObjectComm((PetscObject)ctx), ctx, mat));
9952   PetscCall(VecScatterDestroy(&ctx));
9953   PetscFunctionReturn(PETSC_SUCCESS);
9954 }
9955 
9956 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[])
9957 {
9958   const PetscInt Nc = uOff[1] - uOff[0];
9959   PetscInt       c;
9960   for (c = 0; c < Nc; ++c) g0[c * Nc + c] = 1.0;
9961 }
9962 
9963 PetscErrorCode DMCreateMassMatrixLumped_Plex(DM dm, Vec *mass)
9964 {
9965   DM           dmc;
9966   PetscDS      ds;
9967   Vec          ones, locmass;
9968   IS           cellIS;
9969   PetscFormKey key;
9970   PetscInt     depth;
9971 
9972   PetscFunctionBegin;
9973   PetscCall(DMClone(dm, &dmc));
9974   PetscCall(DMCopyDisc(dm, dmc));
9975   PetscCall(DMGetDS(dmc, &ds));
9976   PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
9977   PetscCall(DMCreateGlobalVector(dmc, mass));
9978   PetscCall(DMGetLocalVector(dmc, &ones));
9979   PetscCall(DMGetLocalVector(dmc, &locmass));
9980   PetscCall(DMPlexGetDepth(dmc, &depth));
9981   PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
9982   PetscCall(VecSet(locmass, 0.0));
9983   PetscCall(VecSet(ones, 1.0));
9984   key.label = NULL;
9985   key.value = 0;
9986   key.field = 0;
9987   key.part  = 0;
9988   PetscCall(DMPlexComputeJacobian_Action_Internal(dmc, key, cellIS, 0.0, 0.0, ones, NULL, ones, locmass, NULL));
9989   PetscCall(ISDestroy(&cellIS));
9990   PetscCall(VecSet(*mass, 0.0));
9991   PetscCall(DMLocalToGlobalBegin(dmc, locmass, ADD_VALUES, *mass));
9992   PetscCall(DMLocalToGlobalEnd(dmc, locmass, ADD_VALUES, *mass));
9993   PetscCall(DMRestoreLocalVector(dmc, &ones));
9994   PetscCall(DMRestoreLocalVector(dmc, &locmass));
9995   PetscCall(DMDestroy(&dmc));
9996   PetscFunctionReturn(PETSC_SUCCESS);
9997 }
9998 
9999 PetscErrorCode DMCreateMassMatrix_Plex(DM dmCoarse, DM dmFine, Mat *mass)
10000 {
10001   PetscSection gsc, gsf;
10002   PetscInt     m, n;
10003   void        *ctx;
10004   DM           cdm;
10005   PetscBool    regular;
10006 
10007   PetscFunctionBegin;
10008   if (dmFine == dmCoarse) {
10009     DM            dmc;
10010     PetscDS       ds;
10011     PetscWeakForm wf;
10012     Vec           u;
10013     IS            cellIS;
10014     PetscFormKey  key;
10015     PetscInt      depth;
10016 
10017     PetscCall(DMClone(dmFine, &dmc));
10018     PetscCall(DMCopyDisc(dmFine, dmc));
10019     PetscCall(DMGetDS(dmc, &ds));
10020     PetscCall(PetscDSGetWeakForm(ds, &wf));
10021     PetscCall(PetscWeakFormClear(wf));
10022     PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
10023     PetscCall(DMCreateMatrix(dmc, mass));
10024     PetscCall(DMGetLocalVector(dmc, &u));
10025     PetscCall(DMPlexGetDepth(dmc, &depth));
10026     PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
10027     PetscCall(MatZeroEntries(*mass));
10028     key.label = NULL;
10029     key.value = 0;
10030     key.field = 0;
10031     key.part  = 0;
10032     PetscCall(DMPlexComputeJacobian_Internal(dmc, key, cellIS, 0.0, 0.0, u, NULL, *mass, *mass, NULL));
10033     PetscCall(ISDestroy(&cellIS));
10034     PetscCall(DMRestoreLocalVector(dmc, &u));
10035     PetscCall(DMDestroy(&dmc));
10036   } else {
10037     PetscCall(DMGetGlobalSection(dmFine, &gsf));
10038     PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
10039     PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
10040     PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
10041 
10042     PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), mass));
10043     PetscCall(MatSetSizes(*mass, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
10044     PetscCall(MatSetType(*mass, dmCoarse->mattype));
10045     PetscCall(DMGetApplicationContext(dmFine, &ctx));
10046 
10047     PetscCall(DMGetCoarseDM(dmFine, &cdm));
10048     PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
10049     if (regular && cdm == dmCoarse) PetscCall(DMPlexComputeMassMatrixNested(dmCoarse, dmFine, *mass, ctx));
10050     else PetscCall(DMPlexComputeMassMatrixGeneral(dmCoarse, dmFine, *mass, ctx));
10051   }
10052   PetscCall(MatViewFromOptions(*mass, NULL, "-mass_mat_view"));
10053   PetscFunctionReturn(PETSC_SUCCESS);
10054 }
10055 
10056 /*@
10057   DMPlexGetRegularRefinement - Get the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
10058 
10059   Input Parameter:
10060 . dm - The `DMPLEX` object
10061 
10062   Output Parameter:
10063 . regular - The flag
10064 
10065   Level: intermediate
10066 
10067 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetRegularRefinement()`
10068 @*/
10069 PetscErrorCode DMPlexGetRegularRefinement(DM dm, PetscBool *regular)
10070 {
10071   PetscFunctionBegin;
10072   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10073   PetscAssertPointer(regular, 2);
10074   *regular = ((DM_Plex *)dm->data)->regularRefinement;
10075   PetscFunctionReturn(PETSC_SUCCESS);
10076 }
10077 
10078 /*@
10079   DMPlexSetRegularRefinement - Set the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
10080 
10081   Input Parameters:
10082 + dm      - The `DMPLEX` object
10083 - regular - The flag
10084 
10085   Level: intermediate
10086 
10087 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetRegularRefinement()`
10088 @*/
10089 PetscErrorCode DMPlexSetRegularRefinement(DM dm, PetscBool regular)
10090 {
10091   PetscFunctionBegin;
10092   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10093   ((DM_Plex *)dm->data)->regularRefinement = regular;
10094   PetscFunctionReturn(PETSC_SUCCESS);
10095 }
10096 
10097 /*@
10098   DMPlexGetAnchors - Get the layout of the anchor (point-to-point) constraints.  Typically, the user will not have to
10099   call DMPlexGetAnchors() directly: if there are anchors, then `DMPlexGetAnchors()` is called during `DMGetDefaultConstraints()`.
10100 
10101   Not Collective
10102 
10103   Input Parameter:
10104 . dm - The `DMPLEX` object
10105 
10106   Output Parameters:
10107 + anchorSection - If not `NULL`, set to the section describing which points anchor the constrained points.
10108 - anchorIS      - If not `NULL`, set to the list of anchors indexed by `anchorSection`
10109 
10110   Level: intermediate
10111 
10112 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`, `IS`, `PetscSection`
10113 @*/
10114 PetscErrorCode DMPlexGetAnchors(DM dm, PetscSection *anchorSection, IS *anchorIS)
10115 {
10116   DM_Plex *plex = (DM_Plex *)dm->data;
10117 
10118   PetscFunctionBegin;
10119   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10120   if (!plex->anchorSection && !plex->anchorIS && plex->createanchors) PetscCall((*plex->createanchors)(dm));
10121   if (anchorSection) *anchorSection = plex->anchorSection;
10122   if (anchorIS) *anchorIS = plex->anchorIS;
10123   PetscFunctionReturn(PETSC_SUCCESS);
10124 }
10125 
10126 /*@
10127   DMPlexSetAnchors - Set the layout of the local anchor (point-to-point) constraints.
10128 
10129   Collective
10130 
10131   Input Parameters:
10132 + dm            - The `DMPLEX` object
10133 . anchorSection - The section that describes the mapping from constrained points to the anchor points listed in anchorIS.
10134                   Must have a local communicator (`PETSC_COMM_SELF` or derivative).
10135 - anchorIS      - The list of all anchor points.  Must have a local communicator (`PETSC_COMM_SELF` or derivative).
10136 
10137   Level: intermediate
10138 
10139   Notes:
10140   Unlike boundary conditions, when a point's degrees of freedom in a section are constrained to
10141   an outside value, the anchor constraints set a point's degrees of freedom to be a linear
10142   combination of other points' degrees of freedom.
10143 
10144   After specifying the layout of constraints with `DMPlexSetAnchors()`, one specifies the constraints by calling
10145   `DMGetDefaultConstraints()` and filling in the entries in the constraint matrix.
10146 
10147   The reference counts of `anchorSection` and `anchorIS` are incremented.
10148 
10149 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`
10150 @*/
10151 PetscErrorCode DMPlexSetAnchors(DM dm, PetscSection anchorSection, IS anchorIS)
10152 {
10153   DM_Plex    *plex = (DM_Plex *)dm->data;
10154   PetscMPIInt result;
10155 
10156   PetscFunctionBegin;
10157   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10158   if (anchorSection) {
10159     PetscValidHeaderSpecific(anchorSection, PETSC_SECTION_CLASSID, 2);
10160     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorSection), &result));
10161     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor section must have local communicator");
10162   }
10163   if (anchorIS) {
10164     PetscValidHeaderSpecific(anchorIS, IS_CLASSID, 3);
10165     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorIS), &result));
10166     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor IS must have local communicator");
10167   }
10168 
10169   PetscCall(PetscObjectReference((PetscObject)anchorSection));
10170   PetscCall(PetscSectionDestroy(&plex->anchorSection));
10171   plex->anchorSection = anchorSection;
10172 
10173   PetscCall(PetscObjectReference((PetscObject)anchorIS));
10174   PetscCall(ISDestroy(&plex->anchorIS));
10175   plex->anchorIS = anchorIS;
10176 
10177   if (PetscUnlikelyDebug(anchorIS && anchorSection)) {
10178     PetscInt        size, a, pStart, pEnd;
10179     const PetscInt *anchors;
10180 
10181     PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
10182     PetscCall(ISGetLocalSize(anchorIS, &size));
10183     PetscCall(ISGetIndices(anchorIS, &anchors));
10184     for (a = 0; a < size; a++) {
10185       PetscInt p;
10186 
10187       p = anchors[a];
10188       if (p >= pStart && p < pEnd) {
10189         PetscInt dof;
10190 
10191         PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
10192         if (dof) {
10193           PetscCall(ISRestoreIndices(anchorIS, &anchors));
10194           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Point %" PetscInt_FMT " cannot be constrained and an anchor", p);
10195         }
10196       }
10197     }
10198     PetscCall(ISRestoreIndices(anchorIS, &anchors));
10199   }
10200   /* reset the generic constraints */
10201   PetscCall(DMSetDefaultConstraints(dm, NULL, NULL, NULL));
10202   PetscFunctionReturn(PETSC_SUCCESS);
10203 }
10204 
10205 static PetscErrorCode DMPlexCreateConstraintSection_Anchors(DM dm, PetscSection section, PetscSection *cSec)
10206 {
10207   PetscSection anchorSection;
10208   PetscInt     pStart, pEnd, sStart, sEnd, p, dof, numFields, f;
10209 
10210   PetscFunctionBegin;
10211   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10212   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
10213   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, cSec));
10214   PetscCall(PetscSectionGetNumFields(section, &numFields));
10215   if (numFields) {
10216     PetscInt f;
10217     PetscCall(PetscSectionSetNumFields(*cSec, numFields));
10218 
10219     for (f = 0; f < numFields; f++) {
10220       PetscInt numComp;
10221 
10222       PetscCall(PetscSectionGetFieldComponents(section, f, &numComp));
10223       PetscCall(PetscSectionSetFieldComponents(*cSec, f, numComp));
10224     }
10225   }
10226   PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
10227   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
10228   pStart = PetscMax(pStart, sStart);
10229   pEnd   = PetscMin(pEnd, sEnd);
10230   pEnd   = PetscMax(pStart, pEnd);
10231   PetscCall(PetscSectionSetChart(*cSec, pStart, pEnd));
10232   for (p = pStart; p < pEnd; p++) {
10233     PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
10234     if (dof) {
10235       PetscCall(PetscSectionGetDof(section, p, &dof));
10236       PetscCall(PetscSectionSetDof(*cSec, p, dof));
10237       for (f = 0; f < numFields; f++) {
10238         PetscCall(PetscSectionGetFieldDof(section, p, f, &dof));
10239         PetscCall(PetscSectionSetFieldDof(*cSec, p, f, dof));
10240       }
10241     }
10242   }
10243   PetscCall(PetscSectionSetUp(*cSec));
10244   PetscCall(PetscObjectSetName((PetscObject)*cSec, "Constraint Section"));
10245   PetscFunctionReturn(PETSC_SUCCESS);
10246 }
10247 
10248 static PetscErrorCode DMPlexCreateConstraintMatrix_Anchors(DM dm, PetscSection section, PetscSection cSec, Mat *cMat)
10249 {
10250   PetscSection    aSec;
10251   PetscInt        pStart, pEnd, p, sStart, sEnd, dof, aDof, aOff, off, nnz, annz, m, n, q, a, offset, *i, *j;
10252   const PetscInt *anchors;
10253   PetscInt        numFields, f;
10254   IS              aIS;
10255   MatType         mtype;
10256   PetscBool       iscuda, iskokkos;
10257 
10258   PetscFunctionBegin;
10259   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10260   PetscCall(PetscSectionGetStorageSize(cSec, &m));
10261   PetscCall(PetscSectionGetStorageSize(section, &n));
10262   PetscCall(MatCreate(PETSC_COMM_SELF, cMat));
10263   PetscCall(MatSetSizes(*cMat, m, n, m, n));
10264   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJCUSPARSE, &iscuda));
10265   if (!iscuda) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJCUSPARSE, &iscuda));
10266   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJKOKKOS, &iskokkos));
10267   if (!iskokkos) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJKOKKOS, &iskokkos));
10268   if (iscuda) mtype = MATSEQAIJCUSPARSE;
10269   else if (iskokkos) mtype = MATSEQAIJKOKKOS;
10270   else mtype = MATSEQAIJ;
10271   PetscCall(MatSetType(*cMat, mtype));
10272   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
10273   PetscCall(ISGetIndices(aIS, &anchors));
10274   /* cSec will be a subset of aSec and section */
10275   PetscCall(PetscSectionGetChart(cSec, &pStart, &pEnd));
10276   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
10277   PetscCall(PetscMalloc1(m + 1, &i));
10278   i[0] = 0;
10279   PetscCall(PetscSectionGetNumFields(section, &numFields));
10280   for (p = pStart; p < pEnd; p++) {
10281     PetscInt rDof, rOff, r;
10282 
10283     PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10284     if (!rDof) continue;
10285     PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10286     if (numFields) {
10287       for (f = 0; f < numFields; f++) {
10288         annz = 0;
10289         for (r = 0; r < rDof; r++) {
10290           a = anchors[rOff + r];
10291           if (a < sStart || a >= sEnd) continue;
10292           PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
10293           annz += aDof;
10294         }
10295         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
10296         PetscCall(PetscSectionGetFieldOffset(cSec, p, f, &off));
10297         for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
10298       }
10299     } else {
10300       annz = 0;
10301       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10302       for (q = 0; q < dof; q++) {
10303         a = anchors[rOff + q];
10304         if (a < sStart || a >= sEnd) continue;
10305         PetscCall(PetscSectionGetDof(section, a, &aDof));
10306         annz += aDof;
10307       }
10308       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10309       PetscCall(PetscSectionGetOffset(cSec, p, &off));
10310       for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
10311     }
10312   }
10313   nnz = i[m];
10314   PetscCall(PetscMalloc1(nnz, &j));
10315   offset = 0;
10316   for (p = pStart; p < pEnd; p++) {
10317     if (numFields) {
10318       for (f = 0; f < numFields; f++) {
10319         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
10320         for (q = 0; q < dof; q++) {
10321           PetscInt rDof, rOff, r;
10322           PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10323           PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10324           for (r = 0; r < rDof; r++) {
10325             PetscInt s;
10326 
10327             a = anchors[rOff + r];
10328             if (a < sStart || a >= sEnd) continue;
10329             PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
10330             PetscCall(PetscSectionGetFieldOffset(section, a, f, &aOff));
10331             for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
10332           }
10333         }
10334       }
10335     } else {
10336       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10337       for (q = 0; q < dof; q++) {
10338         PetscInt rDof, rOff, r;
10339         PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10340         PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10341         for (r = 0; r < rDof; r++) {
10342           PetscInt s;
10343 
10344           a = anchors[rOff + r];
10345           if (a < sStart || a >= sEnd) continue;
10346           PetscCall(PetscSectionGetDof(section, a, &aDof));
10347           PetscCall(PetscSectionGetOffset(section, a, &aOff));
10348           for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
10349         }
10350       }
10351     }
10352   }
10353   PetscCall(MatSeqAIJSetPreallocationCSR(*cMat, i, j, NULL));
10354   PetscCall(PetscFree(i));
10355   PetscCall(PetscFree(j));
10356   PetscCall(ISRestoreIndices(aIS, &anchors));
10357   PetscFunctionReturn(PETSC_SUCCESS);
10358 }
10359 
10360 PetscErrorCode DMCreateDefaultConstraints_Plex(DM dm)
10361 {
10362   DM_Plex     *plex = (DM_Plex *)dm->data;
10363   PetscSection anchorSection, section, cSec;
10364   Mat          cMat;
10365 
10366   PetscFunctionBegin;
10367   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10368   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
10369   if (anchorSection) {
10370     PetscInt Nf;
10371 
10372     PetscCall(DMGetLocalSection(dm, &section));
10373     PetscCall(DMPlexCreateConstraintSection_Anchors(dm, section, &cSec));
10374     PetscCall(DMPlexCreateConstraintMatrix_Anchors(dm, section, cSec, &cMat));
10375     PetscCall(DMGetNumFields(dm, &Nf));
10376     if (Nf && plex->computeanchormatrix) PetscCall((*plex->computeanchormatrix)(dm, section, cSec, cMat));
10377     PetscCall(DMSetDefaultConstraints(dm, cSec, cMat, NULL));
10378     PetscCall(PetscSectionDestroy(&cSec));
10379     PetscCall(MatDestroy(&cMat));
10380   }
10381   PetscFunctionReturn(PETSC_SUCCESS);
10382 }
10383 
10384 PetscErrorCode DMCreateSubDomainDM_Plex(DM dm, DMLabel label, PetscInt value, IS *is, DM *subdm)
10385 {
10386   IS           subis;
10387   PetscSection section, subsection;
10388 
10389   PetscFunctionBegin;
10390   PetscCall(DMGetLocalSection(dm, &section));
10391   PetscCheck(section, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set default section for DM before splitting subdomain");
10392   PetscCheck(subdm, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set output subDM for splitting subdomain");
10393   /* Create subdomain */
10394   PetscCall(DMPlexFilter(dm, label, value, subdm));
10395   /* Create submodel */
10396   PetscCall(DMPlexGetSubpointIS(*subdm, &subis));
10397   PetscCall(PetscSectionCreateSubmeshSection(section, subis, &subsection));
10398   PetscCall(DMSetLocalSection(*subdm, subsection));
10399   PetscCall(PetscSectionDestroy(&subsection));
10400   PetscCall(DMCopyDisc(dm, *subdm));
10401   /* Create map from submodel to global model */
10402   if (is) {
10403     PetscSection    sectionGlobal, subsectionGlobal;
10404     IS              spIS;
10405     const PetscInt *spmap;
10406     PetscInt       *subIndices;
10407     PetscInt        subSize = 0, subOff = 0, pStart, pEnd, p;
10408     PetscInt        Nf, f, bs = -1, bsLocal[2], bsMinMax[2];
10409 
10410     PetscCall(DMPlexGetSubpointIS(*subdm, &spIS));
10411     PetscCall(ISGetIndices(spIS, &spmap));
10412     PetscCall(PetscSectionGetNumFields(section, &Nf));
10413     PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
10414     PetscCall(DMGetGlobalSection(*subdm, &subsectionGlobal));
10415     PetscCall(PetscSectionGetChart(subsection, &pStart, &pEnd));
10416     for (p = pStart; p < pEnd; ++p) {
10417       PetscInt gdof, pSubSize = 0;
10418 
10419       PetscCall(PetscSectionGetDof(sectionGlobal, p, &gdof));
10420       if (gdof > 0) {
10421         for (f = 0; f < Nf; ++f) {
10422           PetscInt fdof, fcdof;
10423 
10424           PetscCall(PetscSectionGetFieldDof(subsection, p, f, &fdof));
10425           PetscCall(PetscSectionGetFieldConstraintDof(subsection, p, f, &fcdof));
10426           pSubSize += fdof - fcdof;
10427         }
10428         subSize += pSubSize;
10429         if (pSubSize) {
10430           if (bs < 0) {
10431             bs = pSubSize;
10432           } else if (bs != pSubSize) {
10433             /* Layout does not admit a pointwise block size */
10434             bs = 1;
10435           }
10436         }
10437       }
10438     }
10439     /* Must have same blocksize on all procs (some might have no points) */
10440     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs;
10441     bsLocal[1] = bs;
10442     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
10443     if (bsMinMax[0] != bsMinMax[1]) {
10444       bs = 1;
10445     } else {
10446       bs = bsMinMax[0];
10447     }
10448     PetscCall(PetscMalloc1(subSize, &subIndices));
10449     for (p = pStart; p < pEnd; ++p) {
10450       PetscInt gdof, goff;
10451 
10452       PetscCall(PetscSectionGetDof(subsectionGlobal, p, &gdof));
10453       if (gdof > 0) {
10454         const PetscInt point = spmap[p];
10455 
10456         PetscCall(PetscSectionGetOffset(sectionGlobal, point, &goff));
10457         for (f = 0; f < Nf; ++f) {
10458           PetscInt fdof, fcdof, fc, f2, poff = 0;
10459 
10460           /* Can get rid of this loop by storing field information in the global section */
10461           for (f2 = 0; f2 < f; ++f2) {
10462             PetscCall(PetscSectionGetFieldDof(section, p, f2, &fdof));
10463             PetscCall(PetscSectionGetFieldConstraintDof(section, p, f2, &fcdof));
10464             poff += fdof - fcdof;
10465           }
10466           PetscCall(PetscSectionGetFieldDof(section, p, f, &fdof));
10467           PetscCall(PetscSectionGetFieldConstraintDof(section, p, f, &fcdof));
10468           for (fc = 0; fc < fdof - fcdof; ++fc, ++subOff) subIndices[subOff] = goff + poff + fc;
10469         }
10470       }
10471     }
10472     PetscCall(ISRestoreIndices(spIS, &spmap));
10473     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), subSize, subIndices, PETSC_OWN_POINTER, is));
10474     if (bs > 1) {
10475       /* We need to check that the block size does not come from non-contiguous fields */
10476       PetscInt i, j, set = 1;
10477       for (i = 0; i < subSize; i += bs) {
10478         for (j = 0; j < bs; ++j) {
10479           if (subIndices[i + j] != subIndices[i] + j) {
10480             set = 0;
10481             break;
10482           }
10483         }
10484       }
10485       if (set) PetscCall(ISSetBlockSize(*is, bs));
10486     }
10487     /* Attach nullspace */
10488     for (f = 0; f < Nf; ++f) {
10489       (*subdm)->nullspaceConstructors[f] = dm->nullspaceConstructors[f];
10490       if ((*subdm)->nullspaceConstructors[f]) break;
10491     }
10492     if (f < Nf) {
10493       MatNullSpace nullSpace;
10494       PetscCall((*(*subdm)->nullspaceConstructors[f])(*subdm, f, f, &nullSpace));
10495 
10496       PetscCall(PetscObjectCompose((PetscObject)*is, "nullspace", (PetscObject)nullSpace));
10497       PetscCall(MatNullSpaceDestroy(&nullSpace));
10498     }
10499   }
10500   PetscFunctionReturn(PETSC_SUCCESS);
10501 }
10502 
10503 /*@
10504   DMPlexMonitorThroughput - Report the cell throughput of FE integration
10505 
10506   Input Parameters:
10507 + dm    - The `DM`
10508 - dummy - unused argument
10509 
10510   Options Database Key:
10511 . -dm_plex_monitor_throughput - Activate the monitor
10512 
10513   Level: developer
10514 
10515 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexCreate()`
10516 @*/
10517 PetscErrorCode DMPlexMonitorThroughput(DM dm, void *dummy)
10518 {
10519   PetscLogHandler default_handler;
10520 
10521   PetscFunctionBegin;
10522   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10523   PetscCall(PetscLogGetDefaultHandler(&default_handler));
10524   if (default_handler) {
10525     PetscLogEvent      event;
10526     PetscEventPerfInfo eventInfo;
10527     PetscReal          cellRate, flopRate;
10528     PetscInt           cStart, cEnd, Nf, N;
10529     const char        *name;
10530 
10531     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
10532     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
10533     PetscCall(DMGetNumFields(dm, &Nf));
10534     PetscCall(PetscLogEventGetId("DMPlexResidualFE", &event));
10535     PetscCall(PetscLogEventGetPerfInfo(PETSC_DEFAULT, event, &eventInfo));
10536     N        = (cEnd - cStart) * Nf * eventInfo.count;
10537     flopRate = eventInfo.flops / eventInfo.time;
10538     cellRate = N / eventInfo.time;
10539     PetscCall(PetscPrintf(PetscObjectComm((PetscObject)dm), "DM (%s) FE Residual Integration: %" PetscInt_FMT " integrals %d reps\n  Cell rate: %.2g/s flop rate: %.2g MF/s\n", name ? name : "unknown", N, eventInfo.count, (double)cellRate, (double)(flopRate / 1.e6)));
10540   } else {
10541     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.");
10542   }
10543   PetscFunctionReturn(PETSC_SUCCESS);
10544 }
10545