xref: /petsc/src/dm/impls/plex/plex.c (revision e2f8920626cf9c22ca23018b9bd6ee28a3d90c05)
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   }
162 
163   PetscCall(MPIU_Allreduce(vcdof, globalvcdof, DM_NUM_POLYTOPES + 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
164   *types = 0;
165 
166   for (c = 0; c < DM_NUM_POLYTOPES + 1; ++c) {
167     if (globalvcdof[c]) ++(*types);
168   }
169 
170   PetscCall(PetscMalloc3(*types, &sStart, *types, &sEnd, *types, &ft));
171   t = 0;
172   if (globalvcdof[DM_NUM_POLYTOPES]) {
173     sStart[t] = vStart;
174     sEnd[t]   = vEnd;
175     ft[t]     = (globalvcdof[t] == cdim) ? PETSC_VTK_POINT_VECTOR_FIELD : PETSC_VTK_POINT_FIELD;
176     ++t;
177   }
178 
179   for (c = 0; c < DM_NUM_POLYTOPES; ++c) {
180     if (globalvcdof[c]) {
181       const DMPolytopeType ict = (DMPolytopeType)c;
182 
183       PetscCall(DMLabelGetStratumBounds(ctLabel, ict, &cStart, &cEnd));
184       sStart[t] = cStart;
185       sEnd[t]   = cEnd;
186       ft[t]     = (globalvcdof[c] == cdim) ? PETSC_VTK_CELL_VECTOR_FIELD : PETSC_VTK_CELL_FIELD;
187       ++t;
188     }
189   }
190 
191   if (!(*types)) {
192     if (field >= 0) {
193       const char *fieldname;
194 
195       PetscCall(PetscSectionGetFieldName(section, field, &fieldname));
196       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section field %" PetscInt_FMT " \"%s\"\n", field, fieldname));
197     } else {
198       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section\n"));
199     }
200   }
201 
202   *ssStart = sStart;
203   *ssEnd   = sEnd;
204   *sft     = ft;
205   PetscFunctionReturn(PETSC_SUCCESS);
206 }
207 
208 PetscErrorCode DMPlexRestoreFieldTypes_Internal(DM dm, PetscSection section, PetscInt field, PetscInt *types, PetscInt **sStart, PetscInt **sEnd, PetscViewerVTKFieldType **ft)
209 {
210   PetscFunctionBegin;
211   PetscCall(PetscFree3(*sStart, *sEnd, *ft));
212   PetscFunctionReturn(PETSC_SUCCESS);
213 }
214 
215 PetscErrorCode DMPlexGetFieldType_Internal(DM dm, PetscSection section, PetscInt field, PetscInt *sStart, PetscInt *sEnd, PetscViewerVTKFieldType *ft)
216 {
217   PetscInt cdim, pStart, pEnd, vStart, vEnd, cStart, cEnd;
218   PetscInt vcdof[2] = {0, 0}, globalvcdof[2];
219 
220   PetscFunctionBegin;
221   *ft = PETSC_VTK_INVALID;
222   PetscCall(DMGetCoordinateDim(dm, &cdim));
223   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
224   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
225   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
226   if (field >= 0) {
227     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, vStart, field, &vcdof[0]));
228     if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, cStart, field, &vcdof[1]));
229   } else {
230     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetDof(section, vStart, &vcdof[0]));
231     if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetDof(section, cStart, &vcdof[1]));
232   }
233   PetscCall(MPIU_Allreduce(vcdof, globalvcdof, 2, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
234   if (globalvcdof[0]) {
235     *sStart = vStart;
236     *sEnd   = vEnd;
237     if (globalvcdof[0] == cdim) *ft = PETSC_VTK_POINT_VECTOR_FIELD;
238     else *ft = PETSC_VTK_POINT_FIELD;
239   } else if (globalvcdof[1]) {
240     *sStart = cStart;
241     *sEnd   = cEnd;
242     if (globalvcdof[1] == cdim) *ft = PETSC_VTK_CELL_VECTOR_FIELD;
243     else *ft = PETSC_VTK_CELL_FIELD;
244   } else {
245     if (field >= 0) {
246       const char *fieldname;
247 
248       PetscCall(PetscSectionGetFieldName(section, field, &fieldname));
249       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section field %" PetscInt_FMT " \"%s\"\n", field, fieldname));
250     } else {
251       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section\n"));
252     }
253   }
254   PetscFunctionReturn(PETSC_SUCCESS);
255 }
256 
257 /*@
258   DMPlexVecView1D - Plot many 1D solutions on the same line graph
259 
260   Collective
261 
262   Input Parameters:
263 + dm     - The `DMPLEX` object
264 . n      - The number of vectors
265 . u      - The array of local vectors
266 - viewer - The `PetscViewer`
267 
268   Level: advanced
269 
270 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `VecViewFromOptions()`, `VecView()`
271 @*/
272 PetscErrorCode DMPlexVecView1D(DM dm, PetscInt n, Vec u[], PetscViewer viewer)
273 {
274   PetscDS            ds;
275   PetscDraw          draw = NULL;
276   PetscDrawLG        lg;
277   Vec                coordinates;
278   const PetscScalar *coords, **sol;
279   PetscReal         *vals;
280   PetscInt          *Nc;
281   PetscInt           Nf, f, c, Nl, l, i, vStart, vEnd, v;
282   char             **names;
283 
284   PetscFunctionBegin;
285   PetscCall(DMGetDS(dm, &ds));
286   PetscCall(PetscDSGetNumFields(ds, &Nf));
287   PetscCall(PetscDSGetTotalComponents(ds, &Nl));
288   PetscCall(PetscDSGetComponents(ds, &Nc));
289 
290   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
291   if (!draw) PetscFunctionReturn(PETSC_SUCCESS);
292   PetscCall(PetscDrawLGCreate(draw, n * Nl, &lg));
293 
294   PetscCall(PetscMalloc3(n, &sol, n * Nl, &names, n * Nl, &vals));
295   for (i = 0, l = 0; i < n; ++i) {
296     const char *vname;
297 
298     PetscCall(PetscObjectGetName((PetscObject)u[i], &vname));
299     for (f = 0; f < Nf; ++f) {
300       PetscObject disc;
301       const char *fname;
302       char        tmpname[PETSC_MAX_PATH_LEN];
303 
304       PetscCall(PetscDSGetDiscretization(ds, f, &disc));
305       /* TODO Create names for components */
306       for (c = 0; c < Nc[f]; ++c, ++l) {
307         PetscCall(PetscObjectGetName(disc, &fname));
308         PetscCall(PetscStrncpy(tmpname, vname, sizeof(tmpname)));
309         PetscCall(PetscStrlcat(tmpname, ":", sizeof(tmpname)));
310         PetscCall(PetscStrlcat(tmpname, fname, sizeof(tmpname)));
311         PetscCall(PetscStrallocpy(tmpname, &names[l]));
312       }
313     }
314   }
315   PetscCall(PetscDrawLGSetLegend(lg, (const char *const *)names));
316   /* Just add P_1 support for now */
317   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
318   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
319   PetscCall(VecGetArrayRead(coordinates, &coords));
320   for (i = 0; i < n; ++i) PetscCall(VecGetArrayRead(u[i], &sol[i]));
321   for (v = vStart; v < vEnd; ++v) {
322     PetscScalar *x, *svals;
323 
324     PetscCall(DMPlexPointLocalRead(dm, v, coords, &x));
325     for (i = 0; i < n; ++i) {
326       PetscCall(DMPlexPointLocalRead(dm, v, sol[i], &svals));
327       for (l = 0; l < Nl; ++l) vals[i * Nl + l] = PetscRealPart(svals[l]);
328     }
329     PetscCall(PetscDrawLGAddCommonPoint(lg, PetscRealPart(x[0]), vals));
330   }
331   PetscCall(VecRestoreArrayRead(coordinates, &coords));
332   for (i = 0; i < n; ++i) PetscCall(VecRestoreArrayRead(u[i], &sol[i]));
333   for (l = 0; l < n * Nl; ++l) PetscCall(PetscFree(names[l]));
334   PetscCall(PetscFree3(sol, names, vals));
335 
336   PetscCall(PetscDrawLGDraw(lg));
337   PetscCall(PetscDrawLGDestroy(&lg));
338   PetscFunctionReturn(PETSC_SUCCESS);
339 }
340 
341 static PetscErrorCode VecView_Plex_Local_Draw_1D(Vec u, PetscViewer viewer)
342 {
343   DM dm;
344 
345   PetscFunctionBegin;
346   PetscCall(VecGetDM(u, &dm));
347   PetscCall(DMPlexVecView1D(dm, 1, &u, viewer));
348   PetscFunctionReturn(PETSC_SUCCESS);
349 }
350 
351 static PetscErrorCode VecView_Plex_Local_Draw_2D(Vec v, PetscViewer viewer)
352 {
353   DM                 dm;
354   PetscSection       s;
355   PetscDraw          draw, popup;
356   DM                 cdm;
357   PetscSection       coordSection;
358   Vec                coordinates;
359   const PetscScalar *array;
360   PetscReal          lbound[3], ubound[3];
361   PetscReal          vbound[2], time;
362   PetscBool          flg;
363   PetscInt           dim, Nf, f, Nc, comp, vStart, vEnd, cStart, cEnd, c, N, level, step, w = 0;
364   const char        *name;
365   char               title[PETSC_MAX_PATH_LEN];
366 
367   PetscFunctionBegin;
368   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
369   PetscCall(VecGetDM(v, &dm));
370   PetscCall(DMGetCoordinateDim(dm, &dim));
371   PetscCall(DMGetLocalSection(dm, &s));
372   PetscCall(PetscSectionGetNumFields(s, &Nf));
373   PetscCall(DMGetCoarsenLevel(dm, &level));
374   PetscCall(DMGetCoordinateDM(dm, &cdm));
375   PetscCall(DMGetLocalSection(cdm, &coordSection));
376   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
377   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
378   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
379 
380   PetscCall(PetscObjectGetName((PetscObject)v, &name));
381   PetscCall(DMGetOutputSequenceNumber(dm, &step, &time));
382 
383   PetscCall(VecGetLocalSize(coordinates, &N));
384   PetscCall(DMGetBoundingBox(dm, lbound, ubound));
385   PetscCall(PetscDrawClear(draw));
386 
387   /* Could implement something like DMDASelectFields() */
388   for (f = 0; f < Nf; ++f) {
389     DM          fdm = dm;
390     Vec         fv  = v;
391     IS          fis;
392     char        prefix[PETSC_MAX_PATH_LEN];
393     const char *fname;
394 
395     PetscCall(PetscSectionGetFieldComponents(s, f, &Nc));
396     PetscCall(PetscSectionGetFieldName(s, f, &fname));
397 
398     if (v->hdr.prefix) PetscCall(PetscStrncpy(prefix, v->hdr.prefix, sizeof(prefix)));
399     else prefix[0] = '\0';
400     if (Nf > 1) {
401       PetscCall(DMCreateSubDM(dm, 1, &f, &fis, &fdm));
402       PetscCall(VecGetSubVector(v, fis, &fv));
403       PetscCall(PetscStrlcat(prefix, fname, sizeof(prefix)));
404       PetscCall(PetscStrlcat(prefix, "_", sizeof(prefix)));
405     }
406     for (comp = 0; comp < Nc; ++comp, ++w) {
407       PetscInt nmax = 2;
408 
409       PetscCall(PetscViewerDrawGetDraw(viewer, w, &draw));
410       if (Nc > 1) PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s_%" PetscInt_FMT " Step: %" PetscInt_FMT " Time: %.4g", name, fname, comp, step, (double)time));
411       else PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s Step: %" PetscInt_FMT " Time: %.4g", name, fname, step, (double)time));
412       PetscCall(PetscDrawSetTitle(draw, title));
413 
414       /* TODO Get max and min only for this component */
415       PetscCall(PetscOptionsGetRealArray(NULL, prefix, "-vec_view_bounds", vbound, &nmax, &flg));
416       if (!flg) {
417         PetscCall(VecMin(fv, NULL, &vbound[0]));
418         PetscCall(VecMax(fv, NULL, &vbound[1]));
419         if (vbound[1] <= vbound[0]) vbound[1] = vbound[0] + 1.0;
420       }
421 
422       PetscCall(PetscDrawGetPopup(draw, &popup));
423       PetscCall(PetscDrawScalePopup(popup, vbound[0], vbound[1]));
424       PetscCall(PetscDrawSetCoordinates(draw, lbound[0], lbound[1], ubound[0], ubound[1]));
425       PetscCall(VecGetArrayRead(fv, &array));
426       for (c = cStart; c < cEnd; ++c) {
427         PetscScalar       *coords = NULL, *a = NULL;
428         const PetscScalar *coords_arr;
429         PetscBool          isDG;
430         PetscInt           numCoords, color[4] = {-1, -1, -1, -1};
431 
432         PetscCall(DMPlexPointLocalRead(fdm, c, array, &a));
433         if (a) {
434           color[0] = PetscDrawRealToColor(PetscRealPart(a[comp]), vbound[0], vbound[1]);
435           color[1] = color[2] = color[3] = color[0];
436         } else {
437           PetscScalar *vals = NULL;
438           PetscInt     numVals, va;
439 
440           PetscCall(DMPlexVecGetClosure(fdm, NULL, fv, c, &numVals, &vals));
441           PetscCheck(numVals % Nc == 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "The number of components %" PetscInt_FMT " does not divide the number of values in the closure %" PetscInt_FMT, Nc, numVals);
442           switch (numVals / Nc) {
443           case 3: /* P1 Triangle */
444           case 4: /* P1 Quadrangle */
445             for (va = 0; va < numVals / Nc; ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp]), vbound[0], vbound[1]);
446             break;
447           case 6: /* P2 Triangle */
448           case 8: /* P2 Quadrangle */
449             for (va = 0; va < numVals / (Nc * 2); ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp + numVals / (Nc * 2)]), vbound[0], vbound[1]);
450             break;
451           default:
452             SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of values for cell closure %" PetscInt_FMT " cannot be handled", numVals / Nc);
453           }
454           PetscCall(DMPlexVecRestoreClosure(fdm, NULL, fv, c, &numVals, &vals));
455         }
456         PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
457         switch (numCoords) {
458         case 6:
459         case 12: /* Localized triangle */
460           PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), color[0], color[1], color[2]));
461           break;
462         case 8:
463         case 16: /* Localized quadrilateral */
464           PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), color[0], color[1], color[2]));
465           PetscCall(PetscDrawTriangle(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), color[2], color[3], color[0]));
466           break;
467         default:
468           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells with %" PetscInt_FMT " coordinates", numCoords);
469         }
470         PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
471       }
472       PetscCall(VecRestoreArrayRead(fv, &array));
473       PetscCall(PetscDrawFlush(draw));
474       PetscCall(PetscDrawPause(draw));
475       PetscCall(PetscDrawSave(draw));
476     }
477     if (Nf > 1) {
478       PetscCall(VecRestoreSubVector(v, fis, &fv));
479       PetscCall(ISDestroy(&fis));
480       PetscCall(DMDestroy(&fdm));
481     }
482   }
483   PetscFunctionReturn(PETSC_SUCCESS);
484 }
485 
486 static PetscErrorCode VecView_Plex_Local_Draw(Vec v, PetscViewer viewer)
487 {
488   DM        dm;
489   PetscDraw draw;
490   PetscInt  dim;
491   PetscBool isnull;
492 
493   PetscFunctionBegin;
494   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
495   PetscCall(PetscDrawIsNull(draw, &isnull));
496   if (isnull) PetscFunctionReturn(PETSC_SUCCESS);
497 
498   PetscCall(VecGetDM(v, &dm));
499   PetscCall(DMGetCoordinateDim(dm, &dim));
500   switch (dim) {
501   case 1:
502     PetscCall(VecView_Plex_Local_Draw_1D(v, viewer));
503     break;
504   case 2:
505     PetscCall(VecView_Plex_Local_Draw_2D(v, viewer));
506     break;
507   default:
508     SETERRQ(PetscObjectComm((PetscObject)v), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT ". Try PETSCVIEWERGLVIS", dim);
509   }
510   PetscFunctionReturn(PETSC_SUCCESS);
511 }
512 
513 static PetscErrorCode VecView_Plex_Local_VTK(Vec v, PetscViewer viewer)
514 {
515   DM                      dm;
516   Vec                     locv;
517   const char             *name;
518   PetscSection            section;
519   PetscInt                pStart, pEnd;
520   PetscInt                numFields;
521   PetscViewerVTKFieldType ft;
522 
523   PetscFunctionBegin;
524   PetscCall(VecGetDM(v, &dm));
525   PetscCall(DMCreateLocalVector(dm, &locv)); /* VTK viewer requires exclusive ownership of the vector */
526   PetscCall(PetscObjectGetName((PetscObject)v, &name));
527   PetscCall(PetscObjectSetName((PetscObject)locv, name));
528   PetscCall(VecCopy(v, locv));
529   PetscCall(DMGetLocalSection(dm, &section));
530   PetscCall(PetscSectionGetNumFields(section, &numFields));
531   if (!numFields) {
532     PetscCall(DMPlexGetFieldType_Internal(dm, section, PETSC_DETERMINE, &pStart, &pEnd, &ft));
533     PetscCall(PetscViewerVTKAddField(viewer, (PetscObject)dm, DMPlexVTKWriteAll, PETSC_DEFAULT, ft, PETSC_TRUE, (PetscObject)locv));
534   } else {
535     PetscInt f;
536 
537     for (f = 0; f < numFields; f++) {
538       PetscCall(DMPlexGetFieldType_Internal(dm, section, f, &pStart, &pEnd, &ft));
539       if (ft == PETSC_VTK_INVALID) continue;
540       PetscCall(PetscObjectReference((PetscObject)locv));
541       PetscCall(PetscViewerVTKAddField(viewer, (PetscObject)dm, DMPlexVTKWriteAll, f, ft, PETSC_TRUE, (PetscObject)locv));
542     }
543     PetscCall(VecDestroy(&locv));
544   }
545   PetscFunctionReturn(PETSC_SUCCESS);
546 }
547 
548 PetscErrorCode VecView_Plex_Local(Vec v, PetscViewer viewer)
549 {
550   DM        dm;
551   PetscBool isvtk, ishdf5, isdraw, isglvis, iscgns;
552 
553   PetscFunctionBegin;
554   PetscCall(VecGetDM(v, &dm));
555   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
556   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
557   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
558   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
559   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
560   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
561   if (isvtk || ishdf5 || isdraw || isglvis || iscgns) {
562     PetscInt    i, numFields;
563     PetscObject fe;
564     PetscBool   fem  = PETSC_FALSE;
565     Vec         locv = v;
566     const char *name;
567     PetscInt    step;
568     PetscReal   time;
569 
570     PetscCall(DMGetNumFields(dm, &numFields));
571     for (i = 0; i < numFields; i++) {
572       PetscCall(DMGetField(dm, i, NULL, &fe));
573       if (fe->classid == PETSCFE_CLASSID) {
574         fem = PETSC_TRUE;
575         break;
576       }
577     }
578     if (fem) {
579       PetscObject isZero;
580 
581       PetscCall(DMGetLocalVector(dm, &locv));
582       PetscCall(PetscObjectGetName((PetscObject)v, &name));
583       PetscCall(PetscObjectSetName((PetscObject)locv, name));
584       PetscCall(PetscObjectQuery((PetscObject)v, "__Vec_bc_zero__", &isZero));
585       PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", isZero));
586       PetscCall(VecCopy(v, locv));
587       PetscCall(DMGetOutputSequenceNumber(dm, NULL, &time));
588       PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_TRUE, locv, time, NULL, NULL, NULL));
589     }
590     if (isvtk) {
591       PetscCall(VecView_Plex_Local_VTK(locv, viewer));
592     } else if (ishdf5) {
593 #if defined(PETSC_HAVE_HDF5)
594       PetscCall(VecView_Plex_Local_HDF5_Internal(locv, viewer));
595 #else
596       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
597 #endif
598     } else if (isdraw) {
599       PetscCall(VecView_Plex_Local_Draw(locv, viewer));
600     } else if (isglvis) {
601       PetscCall(DMGetOutputSequenceNumber(dm, &step, NULL));
602       PetscCall(PetscViewerGLVisSetSnapId(viewer, step));
603       PetscCall(VecView_GLVis(locv, viewer));
604     } else if (iscgns) {
605 #if defined(PETSC_HAVE_CGNS)
606       PetscCall(VecView_Plex_Local_CGNS(locv, viewer));
607 #else
608       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "CGNS not supported in this build.\nPlease reconfigure using --download-cgns");
609 #endif
610     }
611     if (fem) {
612       PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", NULL));
613       PetscCall(DMRestoreLocalVector(dm, &locv));
614     }
615   } else {
616     PetscBool isseq;
617 
618     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
619     if (isseq) PetscCall(VecView_Seq(v, viewer));
620     else PetscCall(VecView_MPI(v, viewer));
621   }
622   PetscFunctionReturn(PETSC_SUCCESS);
623 }
624 
625 PetscErrorCode VecView_Plex(Vec v, PetscViewer viewer)
626 {
627   DM        dm;
628   PetscBool isvtk, ishdf5, isdraw, isglvis, isexodusii, iscgns;
629 
630   PetscFunctionBegin;
631   PetscCall(VecGetDM(v, &dm));
632   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
633   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
634   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
635   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
636   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
637   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
638   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodusii));
639   if (isvtk || isdraw || isglvis || iscgns) {
640     Vec         locv;
641     PetscObject isZero;
642     const char *name;
643 
644     PetscCall(DMGetLocalVector(dm, &locv));
645     PetscCall(PetscObjectGetName((PetscObject)v, &name));
646     PetscCall(PetscObjectSetName((PetscObject)locv, name));
647     PetscCall(DMGlobalToLocalBegin(dm, v, INSERT_VALUES, locv));
648     PetscCall(DMGlobalToLocalEnd(dm, v, INSERT_VALUES, locv));
649     PetscCall(PetscObjectQuery((PetscObject)v, "__Vec_bc_zero__", &isZero));
650     PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", isZero));
651     PetscCall(VecView_Plex_Local(locv, viewer));
652     PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", NULL));
653     PetscCall(DMRestoreLocalVector(dm, &locv));
654   } else if (ishdf5) {
655 #if defined(PETSC_HAVE_HDF5)
656     PetscCall(VecView_Plex_HDF5_Internal(v, viewer));
657 #else
658     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
659 #endif
660   } else if (isexodusii) {
661 #if defined(PETSC_HAVE_EXODUSII)
662     PetscCall(VecView_PlexExodusII_Internal(v, viewer));
663 #else
664     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
665 #endif
666   } else {
667     PetscBool isseq;
668 
669     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
670     if (isseq) PetscCall(VecView_Seq(v, viewer));
671     else PetscCall(VecView_MPI(v, viewer));
672   }
673   PetscFunctionReturn(PETSC_SUCCESS);
674 }
675 
676 PetscErrorCode VecView_Plex_Native(Vec originalv, PetscViewer viewer)
677 {
678   DM                dm;
679   MPI_Comm          comm;
680   PetscViewerFormat format;
681   Vec               v;
682   PetscBool         isvtk, ishdf5;
683 
684   PetscFunctionBegin;
685   PetscCall(VecGetDM(originalv, &dm));
686   PetscCall(PetscObjectGetComm((PetscObject)originalv, &comm));
687   PetscCheck(dm, comm, PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
688   PetscCall(PetscViewerGetFormat(viewer, &format));
689   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
690   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
691   if (format == PETSC_VIEWER_NATIVE) {
692     /* Natural ordering is the common case for DMDA, NATIVE means plain vector, for PLEX is the opposite */
693     /* this need a better fix */
694     if (dm->useNatural) {
695       if (dm->sfNatural) {
696         const char *vecname;
697         PetscInt    n, nroots;
698 
699         PetscCall(VecGetLocalSize(originalv, &n));
700         PetscCall(PetscSFGetGraph(dm->sfNatural, &nroots, NULL, NULL, NULL));
701         if (n == nroots) {
702           PetscCall(DMPlexCreateNaturalVector(dm, &v));
703           PetscCall(DMPlexGlobalToNaturalBegin(dm, originalv, v));
704           PetscCall(DMPlexGlobalToNaturalEnd(dm, originalv, v));
705           PetscCall(PetscObjectGetName((PetscObject)originalv, &vecname));
706           PetscCall(PetscObjectSetName((PetscObject)v, vecname));
707         } else SETERRQ(comm, PETSC_ERR_ARG_WRONG, "DM global to natural SF only handles global vectors");
708       } else SETERRQ(comm, PETSC_ERR_ARG_WRONGSTATE, "DM global to natural SF was not created");
709     } else v = originalv;
710   } else v = originalv;
711 
712   if (ishdf5) {
713 #if defined(PETSC_HAVE_HDF5)
714     PetscCall(VecView_Plex_HDF5_Native_Internal(v, viewer));
715 #else
716     SETERRQ(comm, PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
717 #endif
718   } else if (isvtk) {
719     SETERRQ(comm, PETSC_ERR_SUP, "VTK format does not support viewing in natural order. Please switch to HDF5.");
720   } else {
721     PetscBool isseq;
722 
723     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
724     if (isseq) PetscCall(VecView_Seq(v, viewer));
725     else PetscCall(VecView_MPI(v, viewer));
726   }
727   if (v != originalv) PetscCall(VecDestroy(&v));
728   PetscFunctionReturn(PETSC_SUCCESS);
729 }
730 
731 PetscErrorCode VecLoad_Plex_Local(Vec v, PetscViewer viewer)
732 {
733   DM        dm;
734   PetscBool ishdf5;
735 
736   PetscFunctionBegin;
737   PetscCall(VecGetDM(v, &dm));
738   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
739   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
740   if (ishdf5) {
741     DM          dmBC;
742     Vec         gv;
743     const char *name;
744 
745     PetscCall(DMGetOutputDM(dm, &dmBC));
746     PetscCall(DMGetGlobalVector(dmBC, &gv));
747     PetscCall(PetscObjectGetName((PetscObject)v, &name));
748     PetscCall(PetscObjectSetName((PetscObject)gv, name));
749     PetscCall(VecLoad_Default(gv, viewer));
750     PetscCall(DMGlobalToLocalBegin(dmBC, gv, INSERT_VALUES, v));
751     PetscCall(DMGlobalToLocalEnd(dmBC, gv, INSERT_VALUES, v));
752     PetscCall(DMRestoreGlobalVector(dmBC, &gv));
753   } else PetscCall(VecLoad_Default(v, viewer));
754   PetscFunctionReturn(PETSC_SUCCESS);
755 }
756 
757 PetscErrorCode VecLoad_Plex(Vec v, PetscViewer viewer)
758 {
759   DM        dm;
760   PetscBool ishdf5, isexodusii;
761 
762   PetscFunctionBegin;
763   PetscCall(VecGetDM(v, &dm));
764   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
765   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
766   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodusii));
767   if (ishdf5) {
768 #if defined(PETSC_HAVE_HDF5)
769     PetscCall(VecLoad_Plex_HDF5_Internal(v, viewer));
770 #else
771     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
772 #endif
773   } else if (isexodusii) {
774 #if defined(PETSC_HAVE_EXODUSII)
775     PetscCall(VecLoad_PlexExodusII_Internal(v, viewer));
776 #else
777     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
778 #endif
779   } else PetscCall(VecLoad_Default(v, viewer));
780   PetscFunctionReturn(PETSC_SUCCESS);
781 }
782 
783 PetscErrorCode VecLoad_Plex_Native(Vec originalv, PetscViewer viewer)
784 {
785   DM                dm;
786   PetscViewerFormat format;
787   PetscBool         ishdf5;
788 
789   PetscFunctionBegin;
790   PetscCall(VecGetDM(originalv, &dm));
791   PetscCheck(dm, PetscObjectComm((PetscObject)originalv), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
792   PetscCall(PetscViewerGetFormat(viewer, &format));
793   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
794   if (format == PETSC_VIEWER_NATIVE) {
795     if (dm->useNatural) {
796       if (dm->sfNatural) {
797         if (ishdf5) {
798 #if defined(PETSC_HAVE_HDF5)
799           Vec         v;
800           const char *vecname;
801 
802           PetscCall(DMPlexCreateNaturalVector(dm, &v));
803           PetscCall(PetscObjectGetName((PetscObject)originalv, &vecname));
804           PetscCall(PetscObjectSetName((PetscObject)v, vecname));
805           PetscCall(VecLoad_Plex_HDF5_Native_Internal(v, viewer));
806           PetscCall(DMPlexNaturalToGlobalBegin(dm, v, originalv));
807           PetscCall(DMPlexNaturalToGlobalEnd(dm, v, originalv));
808           PetscCall(VecDestroy(&v));
809 #else
810           SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
811 #endif
812         } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Reading in natural order is not supported for anything but HDF5.");
813       }
814     } else PetscCall(VecLoad_Default(originalv, viewer));
815   }
816   PetscFunctionReturn(PETSC_SUCCESS);
817 }
818 
819 PETSC_UNUSED static PetscErrorCode DMPlexView_Ascii_Geometry(DM dm, PetscViewer viewer)
820 {
821   PetscSection       coordSection;
822   Vec                coordinates;
823   DMLabel            depthLabel, celltypeLabel;
824   const char        *name[4];
825   const PetscScalar *a;
826   PetscInt           dim, pStart, pEnd, cStart, cEnd, c;
827 
828   PetscFunctionBegin;
829   PetscCall(DMGetDimension(dm, &dim));
830   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
831   PetscCall(DMGetCoordinateSection(dm, &coordSection));
832   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
833   PetscCall(DMPlexGetCellTypeLabel(dm, &celltypeLabel));
834   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
835   PetscCall(PetscSectionGetChart(coordSection, &pStart, &pEnd));
836   PetscCall(VecGetArrayRead(coordinates, &a));
837   name[0]       = "vertex";
838   name[1]       = "edge";
839   name[dim - 1] = "face";
840   name[dim]     = "cell";
841   for (c = cStart; c < cEnd; ++c) {
842     PetscInt *closure = NULL;
843     PetscInt  closureSize, cl, ct;
844 
845     PetscCall(DMLabelGetValue(celltypeLabel, c, &ct));
846     PetscCall(PetscViewerASCIIPrintf(viewer, "Geometry for cell %" PetscInt_FMT " polytope type %s:\n", c, DMPolytopeTypes[ct]));
847     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
848     PetscCall(PetscViewerASCIIPushTab(viewer));
849     for (cl = 0; cl < closureSize * 2; cl += 2) {
850       PetscInt point = closure[cl], depth, dof, off, d, p;
851 
852       if ((point < pStart) || (point >= pEnd)) continue;
853       PetscCall(PetscSectionGetDof(coordSection, point, &dof));
854       if (!dof) continue;
855       PetscCall(DMLabelGetValue(depthLabel, point, &depth));
856       PetscCall(PetscSectionGetOffset(coordSection, point, &off));
857       PetscCall(PetscViewerASCIIPrintf(viewer, "%s %" PetscInt_FMT " coords:", name[depth], point));
858       for (p = 0; p < dof / dim; ++p) {
859         PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
860         for (d = 0; d < dim; ++d) {
861           if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
862           PetscCall(PetscViewerASCIIPrintf(viewer, "%g", (double)PetscRealPart(a[off + p * dim + d])));
863         }
864         PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
865       }
866       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
867     }
868     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
869     PetscCall(PetscViewerASCIIPopTab(viewer));
870   }
871   PetscCall(VecRestoreArrayRead(coordinates, &a));
872   PetscFunctionReturn(PETSC_SUCCESS);
873 }
874 
875 typedef enum {
876   CS_CARTESIAN,
877   CS_POLAR,
878   CS_CYLINDRICAL,
879   CS_SPHERICAL
880 } CoordSystem;
881 const char *CoordSystems[] = {"cartesian", "polar", "cylindrical", "spherical", "CoordSystem", "CS_", NULL};
882 
883 static PetscErrorCode DMPlexView_Ascii_Coordinates(PetscViewer viewer, CoordSystem cs, PetscInt dim, const PetscScalar x[])
884 {
885   PetscInt i;
886 
887   PetscFunctionBegin;
888   if (dim > 3) {
889     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double)PetscRealPart(x[i])));
890   } else {
891     PetscReal coords[3], trcoords[3] = {0., 0., 0.};
892 
893     for (i = 0; i < dim; ++i) coords[i] = PetscRealPart(x[i]);
894     switch (cs) {
895     case CS_CARTESIAN:
896       for (i = 0; i < dim; ++i) trcoords[i] = coords[i];
897       break;
898     case CS_POLAR:
899       PetscCheck(dim == 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Polar coordinates are for 2 dimension, not %" PetscInt_FMT, dim);
900       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
901       trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
902       break;
903     case CS_CYLINDRICAL:
904       PetscCheck(dim == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cylindrical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
905       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
906       trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
907       trcoords[2] = coords[2];
908       break;
909     case CS_SPHERICAL:
910       PetscCheck(dim == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Spherical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
911       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]) + PetscSqr(coords[2]));
912       trcoords[1] = PetscAtan2Real(PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1])), coords[2]);
913       trcoords[2] = PetscAtan2Real(coords[1], coords[0]);
914       break;
915     }
916     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double)trcoords[i]));
917   }
918   PetscFunctionReturn(PETSC_SUCCESS);
919 }
920 
921 static PetscErrorCode DMPlexView_Ascii(DM dm, PetscViewer viewer)
922 {
923   DM_Plex          *mesh = (DM_Plex *)dm->data;
924   DM                cdm, cdmCell;
925   PetscSection      coordSection, coordSectionCell;
926   Vec               coordinates, coordinatesCell;
927   PetscViewerFormat format;
928 
929   PetscFunctionBegin;
930   PetscCall(PetscViewerGetFormat(viewer, &format));
931   if (format == PETSC_VIEWER_ASCII_INFO_DETAIL) {
932     const char *name;
933     PetscInt    dim, cellHeight, maxConeSize, maxSupportSize;
934     PetscInt    pStart, pEnd, p, numLabels, l;
935     PetscMPIInt rank, size;
936 
937     PetscCall(DMGetCoordinateDM(dm, &cdm));
938     PetscCall(DMGetCoordinateSection(dm, &coordSection));
939     PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
940     PetscCall(DMGetCellCoordinateDM(dm, &cdmCell));
941     PetscCall(DMGetCellCoordinateSection(dm, &coordSectionCell));
942     PetscCall(DMGetCellCoordinatesLocal(dm, &coordinatesCell));
943     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
944     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
945     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
946     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
947     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
948     PetscCall(DMGetDimension(dm, &dim));
949     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
950     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
951     else PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
952     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
953     PetscCall(PetscViewerASCIIPrintf(viewer, "Supports:\n"));
954     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
955     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max support size: %" PetscInt_FMT "\n", rank, maxSupportSize));
956     for (p = pStart; p < pEnd; ++p) {
957       PetscInt dof, off, s;
958 
959       PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
960       PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
961       for (s = off; s < off + dof; ++s) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d]: %" PetscInt_FMT " ----> %" PetscInt_FMT "\n", rank, p, mesh->supports[s]));
962     }
963     PetscCall(PetscViewerFlush(viewer));
964     PetscCall(PetscViewerASCIIPrintf(viewer, "Cones:\n"));
965     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max cone size: %" PetscInt_FMT "\n", rank, maxConeSize));
966     for (p = pStart; p < pEnd; ++p) {
967       PetscInt dof, off, c;
968 
969       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
970       PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
971       for (c = off; c < off + dof; ++c) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d]: %" PetscInt_FMT " <---- %" PetscInt_FMT " (%" PetscInt_FMT ")\n", rank, p, mesh->cones[c], mesh->coneOrientations[c]));
972     }
973     PetscCall(PetscViewerFlush(viewer));
974     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
975     if (coordSection && coordinates) {
976       CoordSystem        cs = CS_CARTESIAN;
977       const PetscScalar *array, *arrayCell = NULL;
978       PetscInt           Nf, Nc, pvStart, pvEnd, pcStart = PETSC_MAX_INT, pcEnd = PETSC_MIN_INT, pStart, pEnd, p;
979       PetscMPIInt        rank;
980       const char        *name;
981 
982       PetscCall(PetscOptionsGetEnum(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_coord_system", CoordSystems, (PetscEnum *)&cs, NULL));
983       PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)viewer), &rank));
984       PetscCall(PetscSectionGetNumFields(coordSection, &Nf));
985       PetscCheck(Nf == 1, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Coordinate section should have 1 field, not %" PetscInt_FMT, Nf);
986       PetscCall(PetscSectionGetFieldComponents(coordSection, 0, &Nc));
987       PetscCall(PetscSectionGetChart(coordSection, &pvStart, &pvEnd));
988       if (coordSectionCell) PetscCall(PetscSectionGetChart(coordSectionCell, &pcStart, &pcEnd));
989       pStart = PetscMin(pvStart, pcStart);
990       pEnd   = PetscMax(pvEnd, pcEnd);
991       PetscCall(PetscObjectGetName((PetscObject)coordinates, &name));
992       PetscCall(PetscViewerASCIIPrintf(viewer, "%s with %" PetscInt_FMT " fields\n", name, Nf));
993       PetscCall(PetscViewerASCIIPrintf(viewer, "  field 0 with %" PetscInt_FMT " components\n", Nc));
994       if (cs != CS_CARTESIAN) PetscCall(PetscViewerASCIIPrintf(viewer, "  output coordinate system: %s\n", CoordSystems[cs]));
995 
996       PetscCall(VecGetArrayRead(coordinates, &array));
997       if (coordinatesCell) PetscCall(VecGetArrayRead(coordinatesCell, &arrayCell));
998       PetscCall(PetscViewerASCIIPushSynchronized(viewer));
999       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "Process %d:\n", rank));
1000       for (p = pStart; p < pEnd; ++p) {
1001         PetscInt dof, off;
1002 
1003         if (p >= pvStart && p < pvEnd) {
1004           PetscCall(PetscSectionGetDof(coordSection, p, &dof));
1005           PetscCall(PetscSectionGetOffset(coordSection, p, &off));
1006           if (dof) {
1007             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dim %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
1008             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &array[off]));
1009             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
1010           }
1011         }
1012         if (cdmCell && p >= pcStart && p < pcEnd) {
1013           PetscCall(PetscSectionGetDof(coordSectionCell, p, &dof));
1014           PetscCall(PetscSectionGetOffset(coordSectionCell, p, &off));
1015           if (dof) {
1016             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dim %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
1017             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &arrayCell[off]));
1018             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
1019           }
1020         }
1021       }
1022       PetscCall(PetscViewerFlush(viewer));
1023       PetscCall(PetscViewerASCIIPopSynchronized(viewer));
1024       PetscCall(VecRestoreArrayRead(coordinates, &array));
1025       if (coordinatesCell) PetscCall(VecRestoreArrayRead(coordinatesCell, &arrayCell));
1026     }
1027     PetscCall(DMGetNumLabels(dm, &numLabels));
1028     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
1029     for (l = 0; l < numLabels; ++l) {
1030       DMLabel     label;
1031       PetscBool   isdepth;
1032       const char *name;
1033 
1034       PetscCall(DMGetLabelName(dm, l, &name));
1035       PetscCall(PetscStrcmp(name, "depth", &isdepth));
1036       if (isdepth) continue;
1037       PetscCall(DMGetLabel(dm, name, &label));
1038       PetscCall(DMLabelView(label, viewer));
1039     }
1040     if (size > 1) {
1041       PetscSF sf;
1042 
1043       PetscCall(DMGetPointSF(dm, &sf));
1044       PetscCall(PetscSFView(sf, viewer));
1045     }
1046     if (mesh->periodic.face_sf) PetscCall(PetscSFView(mesh->periodic.face_sf, viewer));
1047     PetscCall(PetscViewerFlush(viewer));
1048   } else if (format == PETSC_VIEWER_ASCII_LATEX) {
1049     const char  *name, *color;
1050     const char  *defcolors[3]  = {"gray", "orange", "green"};
1051     const char  *deflcolors[4] = {"blue", "cyan", "red", "magenta"};
1052     char         lname[PETSC_MAX_PATH_LEN];
1053     PetscReal    scale      = 2.0;
1054     PetscReal    tikzscale  = 1.0;
1055     PetscBool    useNumbers = PETSC_TRUE, drawNumbers[4], drawColors[4], useLabels, useColors, plotEdges, drawHasse = PETSC_FALSE;
1056     double       tcoords[3];
1057     PetscScalar *coords;
1058     PetscInt     numLabels, l, numColors, numLColors, dim, d, depth, cStart, cEnd, c, vStart, vEnd, v, eStart = 0, eEnd = 0, e, p, n;
1059     PetscMPIInt  rank, size;
1060     char       **names, **colors, **lcolors;
1061     PetscBool    flg, lflg;
1062     PetscBT      wp = NULL;
1063     PetscInt     pEnd, pStart;
1064 
1065     PetscCall(DMGetCoordinateDM(dm, &cdm));
1066     PetscCall(DMGetCoordinateSection(dm, &coordSection));
1067     PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1068     PetscCall(DMGetCellCoordinateDM(dm, &cdmCell));
1069     PetscCall(DMGetCellCoordinateSection(dm, &coordSectionCell));
1070     PetscCall(DMGetCellCoordinatesLocal(dm, &coordinatesCell));
1071     PetscCall(DMGetDimension(dm, &dim));
1072     PetscCall(DMPlexGetDepth(dm, &depth));
1073     PetscCall(DMGetNumLabels(dm, &numLabels));
1074     numLabels  = PetscMax(numLabels, 10);
1075     numColors  = 10;
1076     numLColors = 10;
1077     PetscCall(PetscCalloc3(numLabels, &names, numColors, &colors, numLColors, &lcolors));
1078     PetscCall(PetscOptionsGetReal(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_scale", &scale, NULL));
1079     PetscCall(PetscOptionsGetReal(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_tikzscale", &tikzscale, NULL));
1080     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_numbers", &useNumbers, NULL));
1081     for (d = 0; d < 4; ++d) drawNumbers[d] = useNumbers;
1082     for (d = 0; d < 4; ++d) drawColors[d] = PETSC_TRUE;
1083     n = 4;
1084     PetscCall(PetscOptionsGetBoolArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_numbers_depth", drawNumbers, &n, &flg));
1085     PetscCheck(!flg || n == dim + 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim + 1);
1086     n = 4;
1087     PetscCall(PetscOptionsGetBoolArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_colors_depth", drawColors, &n, &flg));
1088     PetscCheck(!flg || n == dim + 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim + 1);
1089     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_labels", names, &numLabels, &useLabels));
1090     if (!useLabels) numLabels = 0;
1091     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_colors", colors, &numColors, &useColors));
1092     if (!useColors) {
1093       numColors = 3;
1094       for (c = 0; c < numColors; ++c) PetscCall(PetscStrallocpy(defcolors[c], &colors[c]));
1095     }
1096     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_lcolors", lcolors, &numLColors, &useColors));
1097     if (!useColors) {
1098       numLColors = 4;
1099       for (c = 0; c < numLColors; ++c) PetscCall(PetscStrallocpy(deflcolors[c], &lcolors[c]));
1100     }
1101     PetscCall(PetscOptionsGetString(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_label_filter", lname, sizeof(lname), &lflg));
1102     plotEdges = (PetscBool)(depth > 1 && drawNumbers[1] && dim < 3);
1103     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_edges", &plotEdges, &flg));
1104     PetscCheck(!flg || !plotEdges || depth >= dim, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Mesh must be interpolated");
1105     if (depth < dim) plotEdges = PETSC_FALSE;
1106     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_hasse", &drawHasse, NULL));
1107 
1108     /* filter points with labelvalue != labeldefaultvalue */
1109     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
1110     PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1111     PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
1112     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1113     if (lflg) {
1114       DMLabel lbl;
1115 
1116       PetscCall(DMGetLabel(dm, lname, &lbl));
1117       if (lbl) {
1118         PetscInt val, defval;
1119 
1120         PetscCall(DMLabelGetDefaultValue(lbl, &defval));
1121         PetscCall(PetscBTCreate(pEnd - pStart, &wp));
1122         for (c = pStart; c < pEnd; c++) {
1123           PetscInt *closure = NULL;
1124           PetscInt  closureSize;
1125 
1126           PetscCall(DMLabelGetValue(lbl, c, &val));
1127           if (val == defval) continue;
1128 
1129           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1130           for (p = 0; p < closureSize * 2; p += 2) PetscCall(PetscBTSet(wp, closure[p] - pStart));
1131           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1132         }
1133       }
1134     }
1135 
1136     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1137     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
1138     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1139     PetscCall(PetscViewerASCIIPrintf(viewer, "\
1140 \\documentclass[tikz]{standalone}\n\n\
1141 \\usepackage{pgflibraryshapes}\n\
1142 \\usetikzlibrary{backgrounds}\n\
1143 \\usetikzlibrary{arrows}\n\
1144 \\begin{document}\n"));
1145     if (size > 1) {
1146       PetscCall(PetscViewerASCIIPrintf(viewer, "%s for process ", name));
1147       for (p = 0; p < size; ++p) {
1148         if (p) PetscCall(PetscViewerASCIIPrintf(viewer, (p == size - 1) ? ", and " : ", "));
1149         PetscCall(PetscViewerASCIIPrintf(viewer, "{\\textcolor{%s}%" PetscInt_FMT "}", colors[p % numColors], p));
1150       }
1151       PetscCall(PetscViewerASCIIPrintf(viewer, ".\n\n\n"));
1152     }
1153     if (drawHasse) {
1154       PetscInt maxStratum = PetscMax(vEnd - vStart, PetscMax(eEnd - eStart, cEnd - cStart));
1155 
1156       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vStart}{%" PetscInt_FMT "}\n", vStart));
1157       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vEnd}{%" PetscInt_FMT "}\n", vEnd - 1));
1158       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numVertices}{%" PetscInt_FMT "}\n", vEnd - vStart));
1159       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vShift}{%.2f}\n", 3 + (maxStratum - (vEnd - vStart)) / 2.));
1160       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eStart}{%" PetscInt_FMT "}\n", eStart));
1161       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eEnd}{%" PetscInt_FMT "}\n", eEnd - 1));
1162       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eShift}{%.2f}\n", 3 + (maxStratum - (eEnd - eStart)) / 2.));
1163       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numEdges}{%" PetscInt_FMT "}\n", eEnd - eStart));
1164       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cStart}{%" PetscInt_FMT "}\n", cStart));
1165       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cEnd}{%" PetscInt_FMT "}\n", cEnd - 1));
1166       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numCells}{%" PetscInt_FMT "}\n", cEnd - cStart));
1167       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cShift}{%.2f}\n", 3 + (maxStratum - (cEnd - cStart)) / 2.));
1168     }
1169     PetscCall(PetscViewerASCIIPrintf(viewer, "\\begin{tikzpicture}[scale = %g,font=\\fontsize{8}{8}\\selectfont]\n", (double)tikzscale));
1170 
1171     /* Plot vertices */
1172     PetscCall(VecGetArray(coordinates, &coords));
1173     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
1174     for (v = vStart; v < vEnd; ++v) {
1175       PetscInt  off, dof, d;
1176       PetscBool isLabeled = PETSC_FALSE;
1177 
1178       if (wp && !PetscBTLookup(wp, v - pStart)) continue;
1179       PetscCall(PetscSectionGetDof(coordSection, v, &dof));
1180       PetscCall(PetscSectionGetOffset(coordSection, v, &off));
1181       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1182       PetscCheck(dof <= 3, PETSC_COMM_SELF, PETSC_ERR_PLIB, "coordSection vertex %" PetscInt_FMT " has dof %" PetscInt_FMT " > 3", v, dof);
1183       for (d = 0; d < dof; ++d) {
1184         tcoords[d] = (double)(scale * PetscRealPart(coords[off + d]));
1185         tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1186       }
1187       /* Rotate coordinates since PGF makes z point out of the page instead of up */
1188       if (dim == 3) {
1189         PetscReal tmp = tcoords[1];
1190         tcoords[1]    = tcoords[2];
1191         tcoords[2]    = -tmp;
1192       }
1193       for (d = 0; d < dof; ++d) {
1194         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1195         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)tcoords[d]));
1196       }
1197       if (drawHasse) color = colors[0 % numColors];
1198       else color = colors[rank % numColors];
1199       for (l = 0; l < numLabels; ++l) {
1200         PetscInt val;
1201         PetscCall(DMGetLabelValue(dm, names[l], v, &val));
1202         if (val >= 0) {
1203           color     = lcolors[l % numLColors];
1204           isLabeled = PETSC_TRUE;
1205           break;
1206         }
1207       }
1208       if (drawNumbers[0]) {
1209         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", v, rank, color, v));
1210       } else if (drawColors[0]) {
1211         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", v, rank, !isLabeled ? 1 : 2, color));
1212       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", v, rank));
1213     }
1214     PetscCall(VecRestoreArray(coordinates, &coords));
1215     PetscCall(PetscViewerFlush(viewer));
1216     /* Plot edges */
1217     if (plotEdges) {
1218       PetscCall(VecGetArray(coordinates, &coords));
1219       PetscCall(PetscViewerASCIIPrintf(viewer, "\\path\n"));
1220       for (e = eStart; e < eEnd; ++e) {
1221         const PetscInt *cone;
1222         PetscInt        coneSize, offA, offB, dof, d;
1223 
1224         if (wp && !PetscBTLookup(wp, e - pStart)) continue;
1225         PetscCall(DMPlexGetConeSize(dm, e, &coneSize));
1226         PetscCheck(coneSize == 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Edge %" PetscInt_FMT " cone should have two vertices, not %" PetscInt_FMT, e, coneSize);
1227         PetscCall(DMPlexGetCone(dm, e, &cone));
1228         PetscCall(PetscSectionGetDof(coordSection, cone[0], &dof));
1229         PetscCall(PetscSectionGetOffset(coordSection, cone[0], &offA));
1230         PetscCall(PetscSectionGetOffset(coordSection, cone[1], &offB));
1231         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "("));
1232         for (d = 0; d < dof; ++d) {
1233           tcoords[d] = (double)(0.5 * scale * PetscRealPart(coords[offA + d] + coords[offB + d]));
1234           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1235         }
1236         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1237         if (dim == 3) {
1238           PetscReal tmp = tcoords[1];
1239           tcoords[1]    = tcoords[2];
1240           tcoords[2]    = -tmp;
1241         }
1242         for (d = 0; d < dof; ++d) {
1243           if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1244           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)tcoords[d]));
1245         }
1246         if (drawHasse) color = colors[1 % numColors];
1247         else color = colors[rank % numColors];
1248         for (l = 0; l < numLabels; ++l) {
1249           PetscInt val;
1250           PetscCall(DMGetLabelValue(dm, names[l], e, &val));
1251           if (val >= 0) {
1252             color = lcolors[l % numLColors];
1253             break;
1254           }
1255         }
1256         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "} --\n", e, rank, color, e));
1257       }
1258       PetscCall(VecRestoreArray(coordinates, &coords));
1259       PetscCall(PetscViewerFlush(viewer));
1260       PetscCall(PetscViewerASCIIPrintf(viewer, "(0,0);\n"));
1261     }
1262     /* Plot cells */
1263     if (dim == 3 || !drawNumbers[1]) {
1264       for (e = eStart; e < eEnd; ++e) {
1265         const PetscInt *cone;
1266 
1267         if (wp && !PetscBTLookup(wp, e - pStart)) continue;
1268         color = colors[rank % numColors];
1269         for (l = 0; l < numLabels; ++l) {
1270           PetscInt val;
1271           PetscCall(DMGetLabelValue(dm, names[l], e, &val));
1272           if (val >= 0) {
1273             color = lcolors[l % numLColors];
1274             break;
1275           }
1276         }
1277         PetscCall(DMPlexGetCone(dm, e, &cone));
1278         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", color, cone[0], rank, cone[1], rank));
1279       }
1280     } else {
1281       DMPolytopeType ct;
1282 
1283       /* Drawing a 2D polygon */
1284       for (c = cStart; c < cEnd; ++c) {
1285         if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1286         PetscCall(DMPlexGetCellType(dm, c, &ct));
1287         if (DMPolytopeTypeIsHybrid(ct)) {
1288           const PetscInt *cone;
1289           PetscInt        coneSize, e;
1290 
1291           PetscCall(DMPlexGetCone(dm, c, &cone));
1292           PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
1293           for (e = 0; e < coneSize; ++e) {
1294             const PetscInt *econe;
1295 
1296             PetscCall(DMPlexGetCone(dm, cone[e], &econe));
1297             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));
1298           }
1299         } else {
1300           PetscInt *closure = NULL;
1301           PetscInt  closureSize, Nv = 0, v;
1302 
1303           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1304           for (p = 0; p < closureSize * 2; p += 2) {
1305             const PetscInt point = closure[p];
1306 
1307             if ((point >= vStart) && (point < vEnd)) closure[Nv++] = point;
1308           }
1309           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] ", colors[rank % numColors]));
1310           for (v = 0; v <= Nv; ++v) {
1311             const PetscInt vertex = closure[v % Nv];
1312 
1313             if (v > 0) {
1314               if (plotEdges) {
1315                 const PetscInt *edge;
1316                 PetscInt        endpoints[2], ne;
1317 
1318                 endpoints[0] = closure[v - 1];
1319                 endpoints[1] = vertex;
1320                 PetscCall(DMPlexGetJoin(dm, 2, endpoints, &ne, &edge));
1321                 PetscCheck(ne == 1, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Could not find edge for vertices %" PetscInt_FMT ", %" PetscInt_FMT, endpoints[0], endpoints[1]);
1322                 PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- (%" PetscInt_FMT "_%d) -- ", edge[0], rank));
1323                 PetscCall(DMPlexRestoreJoin(dm, 2, endpoints, &ne, &edge));
1324               } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- "));
1325             }
1326             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "(%" PetscInt_FMT "_%d)", vertex, rank));
1327           }
1328           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ";\n"));
1329           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1330         }
1331       }
1332     }
1333     for (c = cStart; c < cEnd; ++c) {
1334       double             ccoords[3] = {0.0, 0.0, 0.0};
1335       PetscBool          isLabeled  = PETSC_FALSE;
1336       PetscScalar       *cellCoords = NULL;
1337       const PetscScalar *array;
1338       PetscInt           numCoords, cdim, d;
1339       PetscBool          isDG;
1340 
1341       if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1342       PetscCall(DMGetCoordinateDim(dm, &cdim));
1343       PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1344       PetscCheck(!(numCoords % cdim), PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "coordinate dim %" PetscInt_FMT " does not divide numCoords %" PetscInt_FMT, cdim, numCoords);
1345       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1346       for (p = 0; p < numCoords / cdim; ++p) {
1347         for (d = 0; d < cdim; ++d) {
1348           tcoords[d] = (double)(scale * PetscRealPart(cellCoords[p * cdim + d]));
1349           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1350         }
1351         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1352         if (cdim == 3) {
1353           PetscReal tmp = tcoords[1];
1354           tcoords[1]    = tcoords[2];
1355           tcoords[2]    = -tmp;
1356         }
1357         for (d = 0; d < dim; ++d) ccoords[d] += tcoords[d];
1358       }
1359       for (d = 0; d < cdim; ++d) ccoords[d] /= (numCoords / cdim);
1360       PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1361       for (d = 0; d < cdim; ++d) {
1362         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1363         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)ccoords[d]));
1364       }
1365       if (drawHasse) color = colors[depth % numColors];
1366       else color = colors[rank % numColors];
1367       for (l = 0; l < numLabels; ++l) {
1368         PetscInt val;
1369         PetscCall(DMGetLabelValue(dm, names[l], c, &val));
1370         if (val >= 0) {
1371           color     = lcolors[l % numLColors];
1372           isLabeled = PETSC_TRUE;
1373           break;
1374         }
1375       }
1376       if (drawNumbers[dim]) {
1377         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", c, rank, color, c));
1378       } else if (drawColors[dim]) {
1379         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", c, rank, !isLabeled ? 1 : 2, color));
1380       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", c, rank));
1381     }
1382     if (drawHasse) {
1383       color = colors[depth % numColors];
1384       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Cells\n"));
1385       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\c in {\\cStart,...,\\cEnd}\n"));
1386       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1387       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\c_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\cShift+\\c-\\cStart,0) {\\c};\n", rank, color));
1388       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1389 
1390       color = colors[1 % numColors];
1391       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Edges\n"));
1392       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\e in {\\eStart,...,\\eEnd}\n"));
1393       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1394       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\e_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\eShift+\\e-\\eStart,1) {\\e};\n", rank, color));
1395       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1396 
1397       color = colors[0 % numColors];
1398       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Vertices\n"));
1399       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\v in {\\vStart,...,\\vEnd}\n"));
1400       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1401       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\v_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\vShift+\\v-\\vStart,2) {\\v};\n", rank, color));
1402       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1403 
1404       for (p = pStart; p < pEnd; ++p) {
1405         const PetscInt *cone;
1406         PetscInt        coneSize, cp;
1407 
1408         PetscCall(DMPlexGetCone(dm, p, &cone));
1409         PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
1410         for (cp = 0; cp < coneSize; ++cp) PetscCall(PetscViewerASCIIPrintf(viewer, "\\draw[->, shorten >=1pt] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", cone[cp], rank, p, rank));
1411       }
1412     }
1413     PetscCall(PetscViewerFlush(viewer));
1414     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
1415     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{tikzpicture}\n"));
1416     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{document}\n"));
1417     for (l = 0; l < numLabels; ++l) PetscCall(PetscFree(names[l]));
1418     for (c = 0; c < numColors; ++c) PetscCall(PetscFree(colors[c]));
1419     for (c = 0; c < numLColors; ++c) PetscCall(PetscFree(lcolors[c]));
1420     PetscCall(PetscFree3(names, colors, lcolors));
1421     PetscCall(PetscBTDestroy(&wp));
1422   } else if (format == PETSC_VIEWER_LOAD_BALANCE) {
1423     Vec                    cown, acown;
1424     VecScatter             sct;
1425     ISLocalToGlobalMapping g2l;
1426     IS                     gid, acis;
1427     MPI_Comm               comm, ncomm = MPI_COMM_NULL;
1428     MPI_Group              ggroup, ngroup;
1429     PetscScalar           *array, nid;
1430     const PetscInt        *idxs;
1431     PetscInt              *idxs2, *start, *adjacency, *work;
1432     PetscInt64             lm[3], gm[3];
1433     PetscInt               i, c, cStart, cEnd, cum, numVertices, ect, ectn, cellHeight;
1434     PetscMPIInt            d1, d2, rank;
1435 
1436     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1437     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1438 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1439     PetscCallMPI(MPI_Comm_split_type(comm, MPI_COMM_TYPE_SHARED, rank, MPI_INFO_NULL, &ncomm));
1440 #endif
1441     if (ncomm != MPI_COMM_NULL) {
1442       PetscCallMPI(MPI_Comm_group(comm, &ggroup));
1443       PetscCallMPI(MPI_Comm_group(ncomm, &ngroup));
1444       d1 = 0;
1445       PetscCallMPI(MPI_Group_translate_ranks(ngroup, 1, &d1, ggroup, &d2));
1446       nid = d2;
1447       PetscCallMPI(MPI_Group_free(&ggroup));
1448       PetscCallMPI(MPI_Group_free(&ngroup));
1449       PetscCallMPI(MPI_Comm_free(&ncomm));
1450     } else nid = 0.0;
1451 
1452     /* Get connectivity */
1453     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1454     PetscCall(DMPlexCreatePartitionerGraph(dm, cellHeight, &numVertices, &start, &adjacency, &gid));
1455 
1456     /* filter overlapped local cells */
1457     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
1458     PetscCall(ISGetIndices(gid, &idxs));
1459     PetscCall(ISGetLocalSize(gid, &cum));
1460     PetscCall(PetscMalloc1(cum, &idxs2));
1461     for (c = cStart, cum = 0; c < cEnd; c++) {
1462       if (idxs[c - cStart] < 0) continue;
1463       idxs2[cum++] = idxs[c - cStart];
1464     }
1465     PetscCall(ISRestoreIndices(gid, &idxs));
1466     PetscCheck(numVertices == cum, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Unexpected %" PetscInt_FMT " != %" PetscInt_FMT, numVertices, cum);
1467     PetscCall(ISDestroy(&gid));
1468     PetscCall(ISCreateGeneral(comm, numVertices, idxs2, PETSC_OWN_POINTER, &gid));
1469 
1470     /* support for node-aware cell locality */
1471     PetscCall(ISCreateGeneral(comm, start[numVertices], adjacency, PETSC_USE_POINTER, &acis));
1472     PetscCall(VecCreateSeq(PETSC_COMM_SELF, start[numVertices], &acown));
1473     PetscCall(VecCreateMPI(comm, numVertices, PETSC_DECIDE, &cown));
1474     PetscCall(VecGetArray(cown, &array));
1475     for (c = 0; c < numVertices; c++) array[c] = nid;
1476     PetscCall(VecRestoreArray(cown, &array));
1477     PetscCall(VecScatterCreate(cown, acis, acown, NULL, &sct));
1478     PetscCall(VecScatterBegin(sct, cown, acown, INSERT_VALUES, SCATTER_FORWARD));
1479     PetscCall(VecScatterEnd(sct, cown, acown, INSERT_VALUES, SCATTER_FORWARD));
1480     PetscCall(ISDestroy(&acis));
1481     PetscCall(VecScatterDestroy(&sct));
1482     PetscCall(VecDestroy(&cown));
1483 
1484     /* compute edgeCut */
1485     for (c = 0, cum = 0; c < numVertices; c++) cum = PetscMax(cum, start[c + 1] - start[c]);
1486     PetscCall(PetscMalloc1(cum, &work));
1487     PetscCall(ISLocalToGlobalMappingCreateIS(gid, &g2l));
1488     PetscCall(ISLocalToGlobalMappingSetType(g2l, ISLOCALTOGLOBALMAPPINGHASH));
1489     PetscCall(ISDestroy(&gid));
1490     PetscCall(VecGetArray(acown, &array));
1491     for (c = 0, ect = 0, ectn = 0; c < numVertices; c++) {
1492       PetscInt totl;
1493 
1494       totl = start[c + 1] - start[c];
1495       PetscCall(ISGlobalToLocalMappingApply(g2l, IS_GTOLM_MASK, totl, adjacency + start[c], NULL, work));
1496       for (i = 0; i < totl; i++) {
1497         if (work[i] < 0) {
1498           ect += 1;
1499           ectn += (array[i + start[c]] != nid) ? 0 : 1;
1500         }
1501       }
1502     }
1503     PetscCall(PetscFree(work));
1504     PetscCall(VecRestoreArray(acown, &array));
1505     lm[0] = numVertices > 0 ? numVertices : PETSC_MAX_INT;
1506     lm[1] = -numVertices;
1507     PetscCall(MPIU_Allreduce(lm, gm, 2, MPIU_INT64, MPI_MIN, comm));
1508     PetscCall(PetscViewerASCIIPrintf(viewer, "  Cell balance: %.2f (max %" PetscInt_FMT ", min %" PetscInt_FMT, -((double)gm[1]) / ((double)gm[0]), -(PetscInt)gm[1], (PetscInt)gm[0]));
1509     lm[0] = ect;                     /* edgeCut */
1510     lm[1] = ectn;                    /* node-aware edgeCut */
1511     lm[2] = numVertices > 0 ? 0 : 1; /* empty processes */
1512     PetscCall(MPIU_Allreduce(lm, gm, 3, MPIU_INT64, MPI_SUM, comm));
1513     PetscCall(PetscViewerASCIIPrintf(viewer, ", empty %" PetscInt_FMT ")\n", (PetscInt)gm[2]));
1514 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1515     PetscCall(PetscViewerASCIIPrintf(viewer, "  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n", (PetscInt)(gm[0] / 2), gm[0] ? ((double)(gm[1])) / ((double)gm[0]) : 1.));
1516 #else
1517     PetscCall(PetscViewerASCIIPrintf(viewer, "  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n", (PetscInt)(gm[0] / 2), 0.0));
1518 #endif
1519     PetscCall(ISLocalToGlobalMappingDestroy(&g2l));
1520     PetscCall(PetscFree(start));
1521     PetscCall(PetscFree(adjacency));
1522     PetscCall(VecDestroy(&acown));
1523   } else {
1524     const char    *name;
1525     PetscInt      *sizes, *hybsizes, *ghostsizes;
1526     PetscInt       locDepth, depth, cellHeight, dim, d;
1527     PetscInt       pStart, pEnd, p, gcStart, gcEnd, gcNum;
1528     PetscInt       numLabels, l, maxSize = 17;
1529     DMPolytopeType ct0 = DM_POLYTOPE_UNKNOWN;
1530     MPI_Comm       comm;
1531     PetscMPIInt    size, rank;
1532 
1533     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1534     PetscCallMPI(MPI_Comm_size(comm, &size));
1535     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1536     PetscCall(DMGetDimension(dm, &dim));
1537     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1538     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1539     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
1540     else PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
1541     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
1542     PetscCall(DMPlexGetDepth(dm, &locDepth));
1543     PetscCall(MPIU_Allreduce(&locDepth, &depth, 1, MPIU_INT, MPI_MAX, comm));
1544     PetscCall(DMPlexGetCellTypeStratum(dm, DM_POLYTOPE_FV_GHOST, &gcStart, &gcEnd));
1545     gcNum = gcEnd - gcStart;
1546     if (size < maxSize) PetscCall(PetscCalloc3(size, &sizes, size, &hybsizes, size, &ghostsizes));
1547     else PetscCall(PetscCalloc3(3, &sizes, 3, &hybsizes, 3, &ghostsizes));
1548     for (d = 0; d <= depth; d++) {
1549       PetscInt Nc[2] = {0, 0}, ict;
1550 
1551       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
1552       if (pStart < pEnd) PetscCall(DMPlexGetCellType(dm, pStart, &ct0));
1553       ict = ct0;
1554       PetscCallMPI(MPI_Bcast(&ict, 1, MPIU_INT, 0, comm));
1555       ct0 = (DMPolytopeType)ict;
1556       for (p = pStart; p < pEnd; ++p) {
1557         DMPolytopeType ct;
1558 
1559         PetscCall(DMPlexGetCellType(dm, p, &ct));
1560         if (ct == ct0) ++Nc[0];
1561         else ++Nc[1];
1562       }
1563       if (size < maxSize) {
1564         PetscCallMPI(MPI_Gather(&Nc[0], 1, MPIU_INT, sizes, 1, MPIU_INT, 0, comm));
1565         PetscCallMPI(MPI_Gather(&Nc[1], 1, MPIU_INT, hybsizes, 1, MPIU_INT, 0, comm));
1566         if (d == depth) PetscCallMPI(MPI_Gather(&gcNum, 1, MPIU_INT, ghostsizes, 1, MPIU_INT, 0, comm));
1567         PetscCall(PetscViewerASCIIPrintf(viewer, "  Number of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1568         for (p = 0; p < size; ++p) {
1569           if (rank == 0) {
1570             PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT, sizes[p] + hybsizes[p]));
1571             if (hybsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT ")", hybsizes[p]));
1572             if (ghostsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "]", ghostsizes[p]));
1573           }
1574         }
1575       } else {
1576         PetscInt locMinMax[2];
1577 
1578         locMinMax[0] = Nc[0] + Nc[1];
1579         locMinMax[1] = Nc[0] + Nc[1];
1580         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, sizes));
1581         locMinMax[0] = Nc[1];
1582         locMinMax[1] = Nc[1];
1583         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, hybsizes));
1584         if (d == depth) {
1585           locMinMax[0] = gcNum;
1586           locMinMax[1] = gcNum;
1587           PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, ghostsizes));
1588         }
1589         PetscCall(PetscViewerASCIIPrintf(viewer, "  Min/Max of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1590         PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT "/%" PetscInt_FMT, sizes[0], sizes[1]));
1591         if (hybsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT "/%" PetscInt_FMT ")", hybsizes[0], hybsizes[1]));
1592         if (ghostsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "/%" PetscInt_FMT "]", ghostsizes[0], ghostsizes[1]));
1593       }
1594       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
1595     }
1596     PetscCall(PetscFree3(sizes, hybsizes, ghostsizes));
1597     {
1598       const PetscReal *maxCell;
1599       const PetscReal *L;
1600       PetscBool        localized;
1601 
1602       PetscCall(DMGetPeriodicity(dm, &maxCell, NULL, &L));
1603       PetscCall(DMGetCoordinatesLocalized(dm, &localized));
1604       if (L || localized) {
1605         PetscCall(PetscViewerASCIIPrintf(viewer, "Periodic mesh"));
1606         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1607         if (L) {
1608           PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
1609           for (d = 0; d < dim; ++d) {
1610             if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1611             PetscCall(PetscViewerASCIIPrintf(viewer, "%s", L[d] > 0.0 ? "PERIODIC" : "NONE"));
1612           }
1613           PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
1614         }
1615         PetscCall(PetscViewerASCIIPrintf(viewer, " coordinates %s\n", localized ? "localized" : "not localized"));
1616         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1617       }
1618     }
1619     PetscCall(DMGetNumLabels(dm, &numLabels));
1620     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
1621     for (l = 0; l < numLabels; ++l) {
1622       DMLabel         label;
1623       const char     *name;
1624       IS              valueIS;
1625       const PetscInt *values;
1626       PetscInt        numValues, v;
1627 
1628       PetscCall(DMGetLabelName(dm, l, &name));
1629       PetscCall(DMGetLabel(dm, name, &label));
1630       PetscCall(DMLabelGetNumValues(label, &numValues));
1631       PetscCall(PetscViewerASCIIPrintf(viewer, "  %s: %" PetscInt_FMT " strata with value/size (", name, numValues));
1632       PetscCall(DMLabelGetValueIS(label, &valueIS));
1633       PetscCall(ISGetIndices(valueIS, &values));
1634       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1635       for (v = 0; v < numValues; ++v) {
1636         PetscInt size;
1637 
1638         PetscCall(DMLabelGetStratumSize(label, values[v], &size));
1639         if (v > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1640         PetscCall(PetscViewerASCIIPrintf(viewer, "%" PetscInt_FMT " (%" PetscInt_FMT ")", values[v], size));
1641       }
1642       PetscCall(PetscViewerASCIIPrintf(viewer, ")\n"));
1643       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1644       PetscCall(ISRestoreIndices(valueIS, &values));
1645       PetscCall(ISDestroy(&valueIS));
1646     }
1647     {
1648       char    **labelNames;
1649       PetscInt  Nl = numLabels;
1650       PetscBool flg;
1651 
1652       PetscCall(PetscMalloc1(Nl, &labelNames));
1653       PetscCall(PetscOptionsGetStringArray(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_plex_view_labels", labelNames, &Nl, &flg));
1654       for (l = 0; l < Nl; ++l) {
1655         DMLabel label;
1656 
1657         PetscCall(DMHasLabel(dm, labelNames[l], &flg));
1658         if (flg) {
1659           PetscCall(DMGetLabel(dm, labelNames[l], &label));
1660           PetscCall(DMLabelView(label, viewer));
1661         }
1662         PetscCall(PetscFree(labelNames[l]));
1663       }
1664       PetscCall(PetscFree(labelNames));
1665     }
1666     /* If no fields are specified, people do not want to see adjacency */
1667     if (dm->Nf) {
1668       PetscInt f;
1669 
1670       for (f = 0; f < dm->Nf; ++f) {
1671         const char *name;
1672 
1673         PetscCall(PetscObjectGetName(dm->fields[f].disc, &name));
1674         if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Field %s:\n", name));
1675         PetscCall(PetscViewerASCIIPushTab(viewer));
1676         if (dm->fields[f].label) PetscCall(DMLabelView(dm->fields[f].label, viewer));
1677         if (dm->fields[f].adjacency[0]) {
1678           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM++\n"));
1679           else PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM\n"));
1680         } else {
1681           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FEM\n"));
1682           else PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FUNKY\n"));
1683         }
1684         PetscCall(PetscViewerASCIIPopTab(viewer));
1685       }
1686     }
1687     PetscCall(DMGetCoarseDM(dm, &cdm));
1688     if (cdm) {
1689       PetscCall(PetscViewerASCIIPushTab(viewer));
1690       PetscCall(PetscViewerASCIIPrintf(viewer, "Defined by transform from:\n"));
1691       PetscCall(DMPlexView_Ascii(cdm, viewer));
1692       PetscCall(PetscViewerASCIIPopTab(viewer));
1693     }
1694   }
1695   PetscFunctionReturn(PETSC_SUCCESS);
1696 }
1697 
1698 static PetscErrorCode DMPlexDrawCell(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[])
1699 {
1700   DMPolytopeType ct;
1701   PetscMPIInt    rank;
1702   PetscInt       cdim;
1703 
1704   PetscFunctionBegin;
1705   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1706   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1707   PetscCall(DMGetCoordinateDim(dm, &cdim));
1708   switch (ct) {
1709   case DM_POLYTOPE_SEGMENT:
1710   case DM_POLYTOPE_POINT_PRISM_TENSOR:
1711     switch (cdim) {
1712     case 1: {
1713       const PetscReal y  = 0.5;  /* TODO Put it in the middle of the viewport */
1714       const PetscReal dy = 0.05; /* TODO Make it a fraction of the total length */
1715 
1716       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y, PetscRealPart(coords[1]), y, PETSC_DRAW_BLACK));
1717       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y + dy, PetscRealPart(coords[0]), y - dy, PETSC_DRAW_BLACK));
1718       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[1]), y + dy, PetscRealPart(coords[1]), y - dy, PETSC_DRAW_BLACK));
1719     } break;
1720     case 2: {
1721       const PetscReal dx = (PetscRealPart(coords[3]) - PetscRealPart(coords[1]));
1722       const PetscReal dy = (PetscRealPart(coords[2]) - PetscRealPart(coords[0]));
1723       const PetscReal l  = 0.1 / PetscSqrtReal(dx * dx + dy * dy);
1724 
1725       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1726       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));
1727       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));
1728     } break;
1729     default:
1730       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of dimension %" PetscInt_FMT, cdim);
1731     }
1732     break;
1733   case DM_POLYTOPE_TRIANGLE:
1734     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));
1735     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1736     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1737     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1738     break;
1739   case DM_POLYTOPE_QUADRILATERAL:
1740     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));
1741     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));
1742     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1743     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1744     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_BLACK));
1745     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1746     break;
1747   case DM_POLYTOPE_SEG_PRISM_TENSOR:
1748     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));
1749     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));
1750     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1751     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_BLACK));
1752     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1753     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1754     break;
1755   case DM_POLYTOPE_FV_GHOST:
1756     break;
1757   default:
1758     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1759   }
1760   PetscFunctionReturn(PETSC_SUCCESS);
1761 }
1762 
1763 static PetscErrorCode DrawPolygon_Private(DM dm, PetscDraw draw, PetscInt cell, PetscInt Nv, const PetscReal refVertices[], const PetscScalar coords[], PetscInt edgeDiv, PetscReal refCoords[], PetscReal edgeCoords[])
1764 {
1765   PetscReal   centroid[2] = {0., 0.};
1766   PetscMPIInt rank;
1767   PetscInt    fillColor;
1768 
1769   PetscFunctionBegin;
1770   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1771   fillColor = PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2;
1772   for (PetscInt v = 0; v < Nv; ++v) {
1773     centroid[0] += PetscRealPart(coords[v * 2 + 0]) / Nv;
1774     centroid[1] += PetscRealPart(coords[v * 2 + 1]) / Nv;
1775   }
1776   for (PetscInt e = 0; e < Nv; ++e) {
1777     refCoords[0] = refVertices[e * 2 + 0];
1778     refCoords[1] = refVertices[e * 2 + 1];
1779     for (PetscInt d = 1; d <= edgeDiv; ++d) {
1780       refCoords[d * 2 + 0] = refCoords[0] + (refVertices[(e + 1) % Nv * 2 + 0] - refCoords[0]) * d / edgeDiv;
1781       refCoords[d * 2 + 1] = refCoords[1] + (refVertices[(e + 1) % Nv * 2 + 1] - refCoords[1]) * d / edgeDiv;
1782     }
1783     PetscCall(DMPlexReferenceToCoordinates(dm, cell, edgeDiv + 1, refCoords, edgeCoords));
1784     for (PetscInt d = 0; d < edgeDiv; ++d) {
1785       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));
1786       PetscCall(PetscDrawLine(draw, edgeCoords[d * 2 + 0], edgeCoords[d * 2 + 1], edgeCoords[(d + 1) * 2 + 0], edgeCoords[(d + 1) * 2 + 1], PETSC_DRAW_BLACK));
1787     }
1788   }
1789   PetscFunctionReturn(PETSC_SUCCESS);
1790 }
1791 
1792 static PetscErrorCode DMPlexDrawCellHighOrder(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[], PetscInt edgeDiv, PetscReal refCoords[], PetscReal edgeCoords[])
1793 {
1794   DMPolytopeType ct;
1795 
1796   PetscFunctionBegin;
1797   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1798   switch (ct) {
1799   case DM_POLYTOPE_TRIANGLE: {
1800     PetscReal refVertices[6] = {-1., -1., 1., -1., -1., 1.};
1801 
1802     PetscCall(DrawPolygon_Private(dm, draw, cell, 3, refVertices, coords, edgeDiv, refCoords, edgeCoords));
1803   } break;
1804   case DM_POLYTOPE_QUADRILATERAL: {
1805     PetscReal refVertices[8] = {-1., -1., 1., -1., 1., 1., -1., 1.};
1806 
1807     PetscCall(DrawPolygon_Private(dm, draw, cell, 4, refVertices, coords, edgeDiv, refCoords, edgeCoords));
1808   } break;
1809   default:
1810     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1811   }
1812   PetscFunctionReturn(PETSC_SUCCESS);
1813 }
1814 
1815 static PetscErrorCode DMPlexView_Draw(DM dm, PetscViewer viewer)
1816 {
1817   PetscDraw    draw;
1818   DM           cdm;
1819   PetscSection coordSection;
1820   Vec          coordinates;
1821   PetscReal    xyl[3], xyr[3];
1822   PetscReal   *refCoords, *edgeCoords;
1823   PetscBool    isnull, drawAffine;
1824   PetscInt     dim, vStart, vEnd, cStart, cEnd, c, cDegree, edgeDiv;
1825 
1826   PetscFunctionBegin;
1827   PetscCall(DMGetCoordinateDim(dm, &dim));
1828   PetscCheck(dim <= 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT, dim);
1829   PetscCall(DMGetCoordinateDegree_Internal(dm, &cDegree));
1830   drawAffine = cDegree > 1 ? PETSC_FALSE : PETSC_TRUE;
1831   edgeDiv    = cDegree + 1;
1832   PetscCall(PetscOptionsGetBool(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_view_draw_affine", &drawAffine, NULL));
1833   if (!drawAffine) PetscCall(PetscMalloc2((edgeDiv + 1) * dim, &refCoords, (edgeDiv + 1) * dim, &edgeCoords));
1834   PetscCall(DMGetCoordinateDM(dm, &cdm));
1835   PetscCall(DMGetLocalSection(cdm, &coordSection));
1836   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1837   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1838   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1839 
1840   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
1841   PetscCall(PetscDrawIsNull(draw, &isnull));
1842   if (isnull) PetscFunctionReturn(PETSC_SUCCESS);
1843   PetscCall(PetscDrawSetTitle(draw, "Mesh"));
1844 
1845   PetscCall(DMGetBoundingBox(dm, xyl, xyr));
1846   PetscCall(PetscDrawSetCoordinates(draw, xyl[0], xyl[1], xyr[0], xyr[1]));
1847   PetscCall(PetscDrawClear(draw));
1848 
1849   for (c = cStart; c < cEnd; ++c) {
1850     PetscScalar       *coords = NULL;
1851     const PetscScalar *coords_arr;
1852     PetscInt           numCoords;
1853     PetscBool          isDG;
1854 
1855     PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
1856     if (drawAffine) PetscCall(DMPlexDrawCell(dm, draw, c, coords));
1857     else PetscCall(DMPlexDrawCellHighOrder(dm, draw, c, coords, edgeDiv, refCoords, edgeCoords));
1858     PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
1859   }
1860   if (!drawAffine) PetscCall(PetscFree2(refCoords, edgeCoords));
1861   PetscCall(PetscDrawFlush(draw));
1862   PetscCall(PetscDrawPause(draw));
1863   PetscCall(PetscDrawSave(draw));
1864   PetscFunctionReturn(PETSC_SUCCESS);
1865 }
1866 
1867 static PetscErrorCode DMPlexCreateHighOrderSurrogate_Internal(DM dm, DM *hdm)
1868 {
1869   DM           odm = dm, rdm = dm, cdm;
1870   PetscFE      fe;
1871   PetscSpace   sp;
1872   PetscClassId id;
1873   PetscInt     degree;
1874   PetscBool    hoView = PETSC_TRUE;
1875 
1876   PetscFunctionBegin;
1877   PetscObjectOptionsBegin((PetscObject)dm);
1878   PetscCall(PetscOptionsBool("-dm_plex_high_order_view", "Subsample to view meshes with high order coordinates", "DMPlexCreateHighOrderSurrogate_Internal", hoView, &hoView, NULL));
1879   PetscOptionsEnd();
1880   PetscCall(PetscObjectReference((PetscObject)dm));
1881   *hdm = dm;
1882   if (!hoView) PetscFunctionReturn(PETSC_SUCCESS);
1883   PetscCall(DMGetCoordinateDM(dm, &cdm));
1884   PetscCall(DMGetField(cdm, 0, NULL, (PetscObject *)&fe));
1885   PetscCall(PetscObjectGetClassId((PetscObject)fe, &id));
1886   if (id != PETSCFE_CLASSID) PetscFunctionReturn(PETSC_SUCCESS);
1887   PetscCall(PetscFEGetBasisSpace(fe, &sp));
1888   PetscCall(PetscSpaceGetDegree(sp, &degree, NULL));
1889   for (PetscInt r = 0, rd = PetscCeilReal(((PetscReal)degree) / 2.); r < (PetscInt)PetscCeilReal(PetscLog2Real(degree)); ++r, rd = PetscCeilReal(((PetscReal)rd) / 2.)) {
1890     DM  cdm, rcdm;
1891     Mat In;
1892     Vec cl, rcl;
1893 
1894     PetscCall(DMRefine(odm, PetscObjectComm((PetscObject)odm), &rdm));
1895     PetscCall(DMPlexCreateCoordinateSpace(rdm, rd, PETSC_FALSE, NULL));
1896     PetscCall(PetscObjectSetName((PetscObject)rdm, "Refined Mesh with Linear Coordinates"));
1897     PetscCall(DMGetCoordinateDM(odm, &cdm));
1898     PetscCall(DMGetCoordinateDM(rdm, &rcdm));
1899     PetscCall(DMGetCoordinatesLocal(odm, &cl));
1900     PetscCall(DMGetCoordinatesLocal(rdm, &rcl));
1901     PetscCall(DMSetCoarseDM(rcdm, cdm));
1902     PetscCall(DMCreateInterpolation(cdm, rcdm, &In, NULL));
1903     PetscCall(MatMult(In, cl, rcl));
1904     PetscCall(MatDestroy(&In));
1905     PetscCall(DMSetCoordinatesLocal(rdm, rcl));
1906     PetscCall(DMDestroy(&odm));
1907     odm = rdm;
1908   }
1909   *hdm = rdm;
1910   PetscFunctionReturn(PETSC_SUCCESS);
1911 }
1912 
1913 #if defined(PETSC_HAVE_EXODUSII)
1914   #include <exodusII.h>
1915   #include <petscviewerexodusii.h>
1916 #endif
1917 
1918 PetscErrorCode DMView_Plex(DM dm, PetscViewer viewer)
1919 {
1920   PetscBool iascii, ishdf5, isvtk, isdraw, flg, isglvis, isexodus, iscgns;
1921   char      name[PETSC_MAX_PATH_LEN];
1922 
1923   PetscFunctionBegin;
1924   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1925   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1926   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &iascii));
1927   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
1928   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1929   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
1930   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
1931   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodus));
1932   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
1933   if (iascii) {
1934     PetscViewerFormat format;
1935     PetscCall(PetscViewerGetFormat(viewer, &format));
1936     if (format == PETSC_VIEWER_ASCII_GLVIS) PetscCall(DMPlexView_GLVis(dm, viewer));
1937     else PetscCall(DMPlexView_Ascii(dm, viewer));
1938   } else if (ishdf5) {
1939 #if defined(PETSC_HAVE_HDF5)
1940     PetscCall(DMPlexView_HDF5_Internal(dm, viewer));
1941 #else
1942     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1943 #endif
1944   } else if (isvtk) {
1945     PetscCall(DMPlexVTKWriteAll((PetscObject)dm, viewer));
1946   } else if (isdraw) {
1947     DM hdm;
1948 
1949     PetscCall(DMPlexCreateHighOrderSurrogate_Internal(dm, &hdm));
1950     PetscCall(DMPlexView_Draw(hdm, viewer));
1951     PetscCall(DMDestroy(&hdm));
1952   } else if (isglvis) {
1953     PetscCall(DMPlexView_GLVis(dm, viewer));
1954 #if defined(PETSC_HAVE_EXODUSII)
1955   } else if (isexodus) {
1956     /*
1957       exodusII requires that all sets be part of exactly one cell set.
1958       If the dm does not have a "Cell Sets" label defined, we create one
1959       with ID 1, containing all cells.
1960       Note that if the Cell Sets label is defined but does not cover all cells,
1961       we may still have a problem. This should probably be checked here or in the viewer;
1962     */
1963     PetscInt numCS;
1964     PetscCall(DMGetLabelSize(dm, "Cell Sets", &numCS));
1965     if (!numCS) {
1966       PetscInt cStart, cEnd, c;
1967       PetscCall(DMCreateLabel(dm, "Cell Sets"));
1968       PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1969       for (c = cStart; c < cEnd; ++c) PetscCall(DMSetLabelValue(dm, "Cell Sets", c, 1));
1970     }
1971     PetscCall(DMView_PlexExodusII(dm, viewer));
1972 #endif
1973 #if defined(PETSC_HAVE_CGNS)
1974   } else if (iscgns) {
1975     PetscCall(DMView_PlexCGNS(dm, viewer));
1976 #endif
1977   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex writing", ((PetscObject)viewer)->type_name);
1978   /* Optionally view the partition */
1979   PetscCall(PetscOptionsHasName(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_partition_view", &flg));
1980   if (flg) {
1981     Vec ranks;
1982     PetscCall(DMPlexCreateRankField(dm, &ranks));
1983     PetscCall(VecView(ranks, viewer));
1984     PetscCall(VecDestroy(&ranks));
1985   }
1986   /* Optionally view a label */
1987   PetscCall(PetscOptionsGetString(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_label_view", name, sizeof(name), &flg));
1988   if (flg) {
1989     DMLabel label;
1990     Vec     val;
1991 
1992     PetscCall(DMGetLabel(dm, name, &label));
1993     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Label %s provided to -dm_label_view does not exist in this DM", name);
1994     PetscCall(DMPlexCreateLabelField(dm, label, &val));
1995     PetscCall(VecView(val, viewer));
1996     PetscCall(VecDestroy(&val));
1997   }
1998   PetscFunctionReturn(PETSC_SUCCESS);
1999 }
2000 
2001 /*@
2002   DMPlexTopologyView - Saves a `DMPLEX` topology into a file
2003 
2004   Collective
2005 
2006   Input Parameters:
2007 + dm     - The `DM` whose topology is to be saved
2008 - viewer - The `PetscViewer` to save it in
2009 
2010   Level: advanced
2011 
2012 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexTopologyLoad()`, `PetscViewer`
2013 @*/
2014 PetscErrorCode DMPlexTopologyView(DM dm, PetscViewer viewer)
2015 {
2016   PetscBool ishdf5;
2017 
2018   PetscFunctionBegin;
2019   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2020   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2021   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2022   PetscCall(PetscLogEventBegin(DMPLEX_TopologyView, viewer, 0, 0, 0));
2023   if (ishdf5) {
2024 #if defined(PETSC_HAVE_HDF5)
2025     PetscViewerFormat format;
2026     PetscCall(PetscViewerGetFormat(viewer, &format));
2027     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2028       IS globalPointNumbering;
2029 
2030       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
2031       PetscCall(DMPlexTopologyView_HDF5_Internal(dm, globalPointNumbering, viewer));
2032       PetscCall(ISDestroy(&globalPointNumbering));
2033     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
2034 #else
2035     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2036 #endif
2037   }
2038   PetscCall(PetscLogEventEnd(DMPLEX_TopologyView, viewer, 0, 0, 0));
2039   PetscFunctionReturn(PETSC_SUCCESS);
2040 }
2041 
2042 /*@
2043   DMPlexCoordinatesView - Saves `DMPLEX` coordinates into a file
2044 
2045   Collective
2046 
2047   Input Parameters:
2048 + dm     - The `DM` whose coordinates are to be saved
2049 - viewer - The `PetscViewer` for saving
2050 
2051   Level: advanced
2052 
2053 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexLabelsView()`, `DMPlexCoordinatesLoad()`, `PetscViewer`
2054 @*/
2055 PetscErrorCode DMPlexCoordinatesView(DM dm, PetscViewer viewer)
2056 {
2057   PetscBool ishdf5;
2058 
2059   PetscFunctionBegin;
2060   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2061   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2062   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2063   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
2064   if (ishdf5) {
2065 #if defined(PETSC_HAVE_HDF5)
2066     PetscViewerFormat format;
2067     PetscCall(PetscViewerGetFormat(viewer, &format));
2068     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2069       PetscCall(DMPlexCoordinatesView_HDF5_Internal(dm, viewer));
2070     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
2071 #else
2072     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2073 #endif
2074   }
2075   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
2076   PetscFunctionReturn(PETSC_SUCCESS);
2077 }
2078 
2079 /*@
2080   DMPlexLabelsView - Saves `DMPLEX` labels into a file
2081 
2082   Collective
2083 
2084   Input Parameters:
2085 + dm     - The `DM` whose labels are to be saved
2086 - viewer - The `PetscViewer` for saving
2087 
2088   Level: advanced
2089 
2090 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsLoad()`, `PetscViewer`
2091 @*/
2092 PetscErrorCode DMPlexLabelsView(DM dm, PetscViewer viewer)
2093 {
2094   PetscBool ishdf5;
2095 
2096   PetscFunctionBegin;
2097   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2098   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2099   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2100   PetscCall(PetscLogEventBegin(DMPLEX_LabelsView, viewer, 0, 0, 0));
2101   if (ishdf5) {
2102 #if defined(PETSC_HAVE_HDF5)
2103     IS                globalPointNumbering;
2104     PetscViewerFormat format;
2105 
2106     PetscCall(PetscViewerGetFormat(viewer, &format));
2107     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2108       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
2109       PetscCall(DMPlexLabelsView_HDF5_Internal(dm, globalPointNumbering, viewer));
2110       PetscCall(ISDestroy(&globalPointNumbering));
2111     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2112 #else
2113     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2114 #endif
2115   }
2116   PetscCall(PetscLogEventEnd(DMPLEX_LabelsView, viewer, 0, 0, 0));
2117   PetscFunctionReturn(PETSC_SUCCESS);
2118 }
2119 
2120 /*@
2121   DMPlexSectionView - Saves a section associated with a `DMPLEX`
2122 
2123   Collective
2124 
2125   Input Parameters:
2126 + dm        - The `DM` that contains the topology on which the section to be saved is defined
2127 . viewer    - The `PetscViewer` for saving
2128 - sectiondm - The `DM` that contains the section to be saved, can be `NULL`
2129 
2130   Level: advanced
2131 
2132   Notes:
2133   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.
2134 
2135   In general `dm` and `sectiondm` are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object (or in case `sectiondm` is `NULL`) if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2136 
2137 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`, `PetscSectionView()`, `DMPlexSectionLoad()`, `PetscViewer`
2138 @*/
2139 PetscErrorCode DMPlexSectionView(DM dm, PetscViewer viewer, DM sectiondm)
2140 {
2141   PetscBool ishdf5;
2142 
2143   PetscFunctionBegin;
2144   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2145   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2146   if (!sectiondm) sectiondm = dm;
2147   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2148   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2149   PetscCall(PetscLogEventBegin(DMPLEX_SectionView, viewer, 0, 0, 0));
2150   if (ishdf5) {
2151 #if defined(PETSC_HAVE_HDF5)
2152     PetscCall(DMPlexSectionView_HDF5_Internal(dm, viewer, sectiondm));
2153 #else
2154     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2155 #endif
2156   }
2157   PetscCall(PetscLogEventEnd(DMPLEX_SectionView, viewer, 0, 0, 0));
2158   PetscFunctionReturn(PETSC_SUCCESS);
2159 }
2160 
2161 /*@
2162   DMPlexGlobalVectorView - Saves a global vector
2163 
2164   Collective
2165 
2166   Input Parameters:
2167 + dm        - The `DM` that represents the topology
2168 . viewer    - The `PetscViewer` to save data with
2169 . sectiondm - The `DM` that contains the global section on which vec is defined, can be `NULL`
2170 - vec       - The global vector to be saved
2171 
2172   Level: advanced
2173 
2174   Notes:
2175   In general `dm` and `sectiondm` are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object (or in case `sectiondm` is `NULL`) if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2176 
2177   Calling sequence:
2178 .vb
2179        DMCreate(PETSC_COMM_WORLD, &dm);
2180        DMSetType(dm, DMPLEX);
2181        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2182        DMClone(dm, &sectiondm);
2183        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2184        PetscSectionCreate(PETSC_COMM_WORLD, &section);
2185        DMPlexGetChart(sectiondm, &pStart, &pEnd);
2186        PetscSectionSetChart(section, pStart, pEnd);
2187        PetscSectionSetUp(section);
2188        DMSetLocalSection(sectiondm, section);
2189        PetscSectionDestroy(&section);
2190        DMGetGlobalVector(sectiondm, &vec);
2191        PetscObjectSetName((PetscObject)vec, "vec_name");
2192        DMPlexTopologyView(dm, viewer);
2193        DMPlexSectionView(dm, viewer, sectiondm);
2194        DMPlexGlobalVectorView(dm, viewer, sectiondm, vec);
2195        DMRestoreGlobalVector(sectiondm, &vec);
2196        DMDestroy(&sectiondm);
2197        DMDestroy(&dm);
2198 .ve
2199 
2200 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexLocalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2201 @*/
2202 PetscErrorCode DMPlexGlobalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2203 {
2204   PetscBool ishdf5;
2205 
2206   PetscFunctionBegin;
2207   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2208   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2209   if (!sectiondm) sectiondm = dm;
2210   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2211   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2212   /* Check consistency */
2213   {
2214     PetscSection section;
2215     PetscBool    includesConstraints;
2216     PetscInt     m, m1;
2217 
2218     PetscCall(VecGetLocalSize(vec, &m1));
2219     PetscCall(DMGetGlobalSection(sectiondm, &section));
2220     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2221     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2222     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2223     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2224   }
2225   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2226   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorView, viewer, 0, 0, 0));
2227   if (ishdf5) {
2228 #if defined(PETSC_HAVE_HDF5)
2229     PetscCall(DMPlexGlobalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2230 #else
2231     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2232 #endif
2233   }
2234   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorView, viewer, 0, 0, 0));
2235   PetscFunctionReturn(PETSC_SUCCESS);
2236 }
2237 
2238 /*@
2239   DMPlexLocalVectorView - Saves a local vector
2240 
2241   Collective
2242 
2243   Input Parameters:
2244 + dm        - The `DM` that represents the topology
2245 . viewer    - The `PetscViewer` to save data with
2246 . sectiondm - The `DM` that contains the local section on which `vec` is defined, can be `NULL`
2247 - vec       - The local vector to be saved
2248 
2249   Level: advanced
2250 
2251   Note:
2252   In general `dm` and `sectiondm` are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object (or in case `sectiondm` is `NULL`) if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2253 
2254   Calling sequence:
2255 .vb
2256        DMCreate(PETSC_COMM_WORLD, &dm);
2257        DMSetType(dm, DMPLEX);
2258        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2259        DMClone(dm, &sectiondm);
2260        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2261        PetscSectionCreate(PETSC_COMM_WORLD, &section);
2262        DMPlexGetChart(sectiondm, &pStart, &pEnd);
2263        PetscSectionSetChart(section, pStart, pEnd);
2264        PetscSectionSetUp(section);
2265        DMSetLocalSection(sectiondm, section);
2266        DMGetLocalVector(sectiondm, &vec);
2267        PetscObjectSetName((PetscObject)vec, "vec_name");
2268        DMPlexTopologyView(dm, viewer);
2269        DMPlexSectionView(dm, viewer, sectiondm);
2270        DMPlexLocalVectorView(dm, viewer, sectiondm, vec);
2271        DMRestoreLocalVector(sectiondm, &vec);
2272        DMDestroy(&sectiondm);
2273        DMDestroy(&dm);
2274 .ve
2275 
2276 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexGlobalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2277 @*/
2278 PetscErrorCode DMPlexLocalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2279 {
2280   PetscBool ishdf5;
2281 
2282   PetscFunctionBegin;
2283   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2284   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2285   if (!sectiondm) sectiondm = dm;
2286   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2287   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2288   /* Check consistency */
2289   {
2290     PetscSection section;
2291     PetscBool    includesConstraints;
2292     PetscInt     m, m1;
2293 
2294     PetscCall(VecGetLocalSize(vec, &m1));
2295     PetscCall(DMGetLocalSection(sectiondm, &section));
2296     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2297     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2298     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2299     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2300   }
2301   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2302   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorView, viewer, 0, 0, 0));
2303   if (ishdf5) {
2304 #if defined(PETSC_HAVE_HDF5)
2305     PetscCall(DMPlexLocalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2306 #else
2307     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2308 #endif
2309   }
2310   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorView, viewer, 0, 0, 0));
2311   PetscFunctionReturn(PETSC_SUCCESS);
2312 }
2313 
2314 PetscErrorCode DMLoad_Plex(DM dm, PetscViewer viewer)
2315 {
2316   PetscBool ishdf5;
2317 
2318   PetscFunctionBegin;
2319   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2320   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2321   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2322   if (ishdf5) {
2323 #if defined(PETSC_HAVE_HDF5)
2324     PetscViewerFormat format;
2325     PetscCall(PetscViewerGetFormat(viewer, &format));
2326     if (format == PETSC_VIEWER_HDF5_XDMF || format == PETSC_VIEWER_HDF5_VIZ) {
2327       PetscCall(DMPlexLoad_HDF5_Xdmf_Internal(dm, viewer));
2328     } else if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2329       PetscCall(DMPlexLoad_HDF5_Internal(dm, viewer));
2330     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2331     PetscFunctionReturn(PETSC_SUCCESS);
2332 #else
2333     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2334 #endif
2335   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex loading", ((PetscObject)viewer)->type_name);
2336 }
2337 
2338 /*@
2339   DMPlexTopologyLoad - Loads a topology into a `DMPLEX`
2340 
2341   Collective
2342 
2343   Input Parameters:
2344 + dm     - The `DM` into which the topology is loaded
2345 - viewer - The `PetscViewer` for the saved topology
2346 
2347   Output Parameter:
2348 . 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
2349 
2350   Level: advanced
2351 
2352 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2353           `PetscViewer`, `PetscSF`
2354 @*/
2355 PetscErrorCode DMPlexTopologyLoad(DM dm, PetscViewer viewer, PetscSF *globalToLocalPointSF)
2356 {
2357   PetscBool ishdf5;
2358 
2359   PetscFunctionBegin;
2360   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2361   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2362   if (globalToLocalPointSF) PetscAssertPointer(globalToLocalPointSF, 3);
2363   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2364   PetscCall(PetscLogEventBegin(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2365   if (ishdf5) {
2366 #if defined(PETSC_HAVE_HDF5)
2367     PetscViewerFormat format;
2368     PetscCall(PetscViewerGetFormat(viewer, &format));
2369     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2370       PetscCall(DMPlexTopologyLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2371     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2372 #else
2373     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2374 #endif
2375   }
2376   PetscCall(PetscLogEventEnd(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2377   PetscFunctionReturn(PETSC_SUCCESS);
2378 }
2379 
2380 /*@
2381   DMPlexCoordinatesLoad - Loads coordinates into a `DMPLEX`
2382 
2383   Collective
2384 
2385   Input Parameters:
2386 + dm                   - The `DM` into which the coordinates are loaded
2387 . viewer               - The `PetscViewer` for the saved coordinates
2388 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad()` when loading dm from viewer
2389 
2390   Level: advanced
2391 
2392 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2393           `PetscSF`, `PetscViewer`
2394 @*/
2395 PetscErrorCode DMPlexCoordinatesLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2396 {
2397   PetscBool ishdf5;
2398 
2399   PetscFunctionBegin;
2400   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2401   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2402   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2403   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2404   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesLoad, viewer, 0, 0, 0));
2405   if (ishdf5) {
2406 #if defined(PETSC_HAVE_HDF5)
2407     PetscViewerFormat format;
2408     PetscCall(PetscViewerGetFormat(viewer, &format));
2409     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2410       PetscCall(DMPlexCoordinatesLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2411     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2412 #else
2413     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2414 #endif
2415   }
2416   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesLoad, viewer, 0, 0, 0));
2417   PetscFunctionReturn(PETSC_SUCCESS);
2418 }
2419 
2420 /*@
2421   DMPlexLabelsLoad - Loads labels into a `DMPLEX`
2422 
2423   Collective
2424 
2425   Input Parameters:
2426 + dm                   - The `DM` into which the labels are loaded
2427 . viewer               - The `PetscViewer` for the saved labels
2428 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad()` when loading `dm` from viewer
2429 
2430   Level: advanced
2431 
2432   Note:
2433   The `PetscSF` argument must not be NULL if the `DM` is distributed, otherwise an error occurs.
2434 
2435 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2436           `PetscSF`, `PetscViewer`
2437 @*/
2438 PetscErrorCode DMPlexLabelsLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2439 {
2440   PetscBool ishdf5;
2441 
2442   PetscFunctionBegin;
2443   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2444   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2445   if (globalToLocalPointSF) PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2446   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2447   PetscCall(PetscLogEventBegin(DMPLEX_LabelsLoad, viewer, 0, 0, 0));
2448   if (ishdf5) {
2449 #if defined(PETSC_HAVE_HDF5)
2450     PetscViewerFormat format;
2451 
2452     PetscCall(PetscViewerGetFormat(viewer, &format));
2453     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2454       PetscCall(DMPlexLabelsLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2455     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2456 #else
2457     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2458 #endif
2459   }
2460   PetscCall(PetscLogEventEnd(DMPLEX_LabelsLoad, viewer, 0, 0, 0));
2461   PetscFunctionReturn(PETSC_SUCCESS);
2462 }
2463 
2464 /*@
2465   DMPlexSectionLoad - Loads section into a `DMPLEX`
2466 
2467   Collective
2468 
2469   Input Parameters:
2470 + dm                   - The `DM` that represents the topology
2471 . viewer               - The `PetscViewer` that represents the on-disk section (sectionA)
2472 . sectiondm            - The `DM` into which the on-disk section (sectionA) is migrated, can be `NULL`
2473 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad(`) when loading dm from viewer
2474 
2475   Output Parameters:
2476 + 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)
2477 - 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)
2478 
2479   Level: advanced
2480 
2481   Notes:
2482   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.
2483 
2484   In general `dm` and `sectiondm` are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object (or in case `sectiondm` is `NULL`) if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2485 
2486   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.
2487 
2488   Example using 2 processes:
2489 .vb
2490   NX (number of points on dm): 4
2491   sectionA                   : the on-disk section
2492   vecA                       : a vector associated with sectionA
2493   sectionB                   : sectiondm's local section constructed in this function
2494   vecB (local)               : a vector associated with sectiondm's local section
2495   vecB (global)              : a vector associated with sectiondm's global section
2496 
2497                                      rank 0    rank 1
2498   vecA (global)                  : [.0 .4 .1 | .2 .3]        <- to be loaded in DMPlexGlobalVectorLoad() or DMPlexLocalVectorLoad()
2499   sectionA->atlasOff             :       0 2 | 1             <- loaded in PetscSectionLoad()
2500   sectionA->atlasDof             :       1 3 | 1             <- loaded in PetscSectionLoad()
2501   sectionA's global point numbers:       0 2 | 3             <- loaded in DMPlexSectionLoad()
2502   [0, NX)                        :       0 1 | 2 3           <- conceptual partition used in globalToLocalPointSF
2503   sectionB's global point numbers:     0 1 3 | 3 2           <- associated with [0, NX) by globalToLocalPointSF
2504   sectionB->atlasDof             :     1 0 1 | 1 3
2505   sectionB->atlasOff (no perm)   :     0 1 1 | 0 1
2506   vecB (local)                   :   [.0 .4] | [.4 .1 .2 .3] <- to be constructed by calling DMPlexLocalVectorLoad() with localDofSF
2507   vecB (global)                  :    [.0 .4 | .1 .2 .3]     <- to be constructed by calling DMPlexGlobalVectorLoad() with globalDofSF
2508 .ve
2509   where "|" represents a partition of loaded data, and global point 3 is assumed to be owned by rank 0.
2510 
2511 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`, `PetscSectionLoad()`, `DMPlexSectionView()`, `PetscSF`, `PetscViewer`
2512 @*/
2513 PetscErrorCode DMPlexSectionLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF globalToLocalPointSF, PetscSF *globalDofSF, PetscSF *localDofSF)
2514 {
2515   PetscBool ishdf5;
2516 
2517   PetscFunctionBegin;
2518   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2519   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2520   if (!sectiondm) sectiondm = dm;
2521   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2522   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 4);
2523   if (globalDofSF) PetscAssertPointer(globalDofSF, 5);
2524   if (localDofSF) PetscAssertPointer(localDofSF, 6);
2525   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2526   PetscCall(PetscLogEventBegin(DMPLEX_SectionLoad, viewer, 0, 0, 0));
2527   if (ishdf5) {
2528 #if defined(PETSC_HAVE_HDF5)
2529     PetscCall(DMPlexSectionLoad_HDF5_Internal(dm, viewer, sectiondm, globalToLocalPointSF, globalDofSF, localDofSF));
2530 #else
2531     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2532 #endif
2533   }
2534   PetscCall(PetscLogEventEnd(DMPLEX_SectionLoad, viewer, 0, 0, 0));
2535   PetscFunctionReturn(PETSC_SUCCESS);
2536 }
2537 
2538 /*@
2539   DMPlexGlobalVectorLoad - Loads on-disk vector data into a global vector
2540 
2541   Collective
2542 
2543   Input Parameters:
2544 + dm        - The `DM` that represents the topology
2545 . viewer    - The `PetscViewer` that represents the on-disk vector data
2546 . sectiondm - The `DM` that contains the global section on which vec is defined, can be `NULL`
2547 . sf        - The `PetscSF` that migrates the on-disk vector data into vec
2548 - vec       - The global vector to set values of
2549 
2550   Level: advanced
2551 
2552   Notes:
2553   In general dm and sectiondm are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object (or in case `sectiondm` is `NULL`) if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2554 
2555   Calling sequence:
2556 .vb
2557        DMCreate(PETSC_COMM_WORLD, &dm);
2558        DMSetType(dm, DMPLEX);
2559        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2560        DMPlexTopologyLoad(dm, viewer, &sfX);
2561        DMClone(dm, &sectiondm);
2562        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2563        DMPlexSectionLoad(dm, viewer, sectiondm, sfX, &gsf, NULL);
2564        DMGetGlobalVector(sectiondm, &vec);
2565        PetscObjectSetName((PetscObject)vec, "vec_name");
2566        DMPlexGlobalVectorLoad(dm, viewer, sectiondm, gsf, vec);
2567        DMRestoreGlobalVector(sectiondm, &vec);
2568        PetscSFDestroy(&gsf);
2569        PetscSFDestroy(&sfX);
2570        DMDestroy(&sectiondm);
2571        DMDestroy(&dm);
2572 .ve
2573 
2574 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexLocalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`,
2575           `PetscSF`, `PetscViewer`
2576 @*/
2577 PetscErrorCode DMPlexGlobalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2578 {
2579   PetscBool ishdf5;
2580 
2581   PetscFunctionBegin;
2582   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2583   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2584   if (!sectiondm) sectiondm = dm;
2585   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2586   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2587   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2588   /* Check consistency */
2589   {
2590     PetscSection section;
2591     PetscBool    includesConstraints;
2592     PetscInt     m, m1;
2593 
2594     PetscCall(VecGetLocalSize(vec, &m1));
2595     PetscCall(DMGetGlobalSection(sectiondm, &section));
2596     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2597     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2598     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2599     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2600   }
2601   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2602   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorLoad, viewer, 0, 0, 0));
2603   if (ishdf5) {
2604 #if defined(PETSC_HAVE_HDF5)
2605     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2606 #else
2607     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2608 #endif
2609   }
2610   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorLoad, viewer, 0, 0, 0));
2611   PetscFunctionReturn(PETSC_SUCCESS);
2612 }
2613 
2614 /*@
2615   DMPlexLocalVectorLoad - Loads on-disk vector data into a local vector
2616 
2617   Collective
2618 
2619   Input Parameters:
2620 + dm        - The `DM` that represents the topology
2621 . viewer    - The `PetscViewer` that represents the on-disk vector data
2622 . sectiondm - The `DM` that contains the local section on which vec is defined, can be `NULL`
2623 . sf        - The `PetscSF` that migrates the on-disk vector data into vec
2624 - vec       - The local vector to set values of
2625 
2626   Level: advanced
2627 
2628   Notes:
2629   In general `dm` and `sectiondm` are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object (or in case `sectiondm` is `NULL`) if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2630 
2631   Calling sequence:
2632 .vb
2633        DMCreate(PETSC_COMM_WORLD, &dm);
2634        DMSetType(dm, DMPLEX);
2635        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2636        DMPlexTopologyLoad(dm, viewer, &sfX);
2637        DMClone(dm, &sectiondm);
2638        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2639        DMPlexSectionLoad(dm, viewer, sectiondm, sfX, NULL, &lsf);
2640        DMGetLocalVector(sectiondm, &vec);
2641        PetscObjectSetName((PetscObject)vec, "vec_name");
2642        DMPlexLocalVectorLoad(dm, viewer, sectiondm, lsf, vec);
2643        DMRestoreLocalVector(sectiondm, &vec);
2644        PetscSFDestroy(&lsf);
2645        PetscSFDestroy(&sfX);
2646        DMDestroy(&sectiondm);
2647        DMDestroy(&dm);
2648 .ve
2649 
2650 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`,
2651           `PetscSF`, `PetscViewer`
2652 @*/
2653 PetscErrorCode DMPlexLocalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2654 {
2655   PetscBool ishdf5;
2656 
2657   PetscFunctionBegin;
2658   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2659   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2660   if (!sectiondm) sectiondm = dm;
2661   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2662   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2663   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2664   /* Check consistency */
2665   {
2666     PetscSection section;
2667     PetscBool    includesConstraints;
2668     PetscInt     m, m1;
2669 
2670     PetscCall(VecGetLocalSize(vec, &m1));
2671     PetscCall(DMGetLocalSection(sectiondm, &section));
2672     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2673     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2674     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2675     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2676   }
2677   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2678   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorLoad, viewer, 0, 0, 0));
2679   if (ishdf5) {
2680 #if defined(PETSC_HAVE_HDF5)
2681     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2682 #else
2683     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2684 #endif
2685   }
2686   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorLoad, viewer, 0, 0, 0));
2687   PetscFunctionReturn(PETSC_SUCCESS);
2688 }
2689 
2690 PetscErrorCode DMDestroy_Plex(DM dm)
2691 {
2692   DM_Plex *mesh = (DM_Plex *)dm->data;
2693 
2694   PetscFunctionBegin;
2695   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMSetUpGLVisViewer_C", NULL));
2696   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertBoundaryValues_C", NULL));
2697   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMCreateNeumannOverlap_C", NULL));
2698   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMInterpolateSolution_C", NULL));
2699   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertTimeDerivativeBoundaryValues_C", NULL));
2700   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", NULL));
2701   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeGetDefault_C", NULL));
2702   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeSetDefault_C", NULL));
2703   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "MatComputeNeumannOverlap_C", NULL));
2704   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderGetDefault_C", NULL));
2705   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderSetDefault_C", NULL));
2706   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderSectionGetDefault_C", NULL));
2707   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderSectionSetDefault_C", NULL));
2708   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", NULL));
2709   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexSetOverlap_C", NULL));
2710   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetUseCeed_C", NULL));
2711   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexSetUseCeed_C", NULL));
2712   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMGetIsoperiodicPointSF_C", NULL));
2713   if (--mesh->refct > 0) PetscFunctionReturn(PETSC_SUCCESS);
2714   PetscCall(PetscSectionDestroy(&mesh->coneSection));
2715   PetscCall(PetscFree(mesh->cones));
2716   PetscCall(PetscFree(mesh->coneOrientations));
2717   PetscCall(PetscSectionDestroy(&mesh->supportSection));
2718   PetscCall(PetscSectionDestroy(&mesh->subdomainSection));
2719   PetscCall(PetscFree(mesh->supports));
2720   PetscCall(PetscFree(mesh->cellTypes));
2721   PetscCall(DMPlexTransformDestroy(&mesh->tr));
2722   PetscCall(PetscFree(mesh->tetgenOpts));
2723   PetscCall(PetscFree(mesh->triangleOpts));
2724   PetscCall(PetscFree(mesh->transformType));
2725   PetscCall(PetscFree(mesh->distributionName));
2726   PetscCall(PetscPartitionerDestroy(&mesh->partitioner));
2727   PetscCall(DMLabelDestroy(&mesh->subpointMap));
2728   PetscCall(ISDestroy(&mesh->subpointIS));
2729   PetscCall(ISDestroy(&mesh->globalVertexNumbers));
2730   PetscCall(ISDestroy(&mesh->globalCellNumbers));
2731   PetscCall(PetscSFDestroy(&mesh->periodic.face_sf));
2732   PetscCall(PetscSFDestroy(&mesh->periodic.composed_sf));
2733   PetscCall(ISDestroy(&mesh->periodic.periodic_points));
2734   PetscCall(PetscSectionDestroy(&mesh->anchorSection));
2735   PetscCall(ISDestroy(&mesh->anchorIS));
2736   PetscCall(PetscSectionDestroy(&mesh->parentSection));
2737   PetscCall(PetscFree(mesh->parents));
2738   PetscCall(PetscFree(mesh->childIDs));
2739   PetscCall(PetscSectionDestroy(&mesh->childSection));
2740   PetscCall(PetscFree(mesh->children));
2741   PetscCall(DMDestroy(&mesh->referenceTree));
2742   PetscCall(PetscGridHashDestroy(&mesh->lbox));
2743   PetscCall(PetscFree(mesh->neighbors));
2744   if (mesh->metricCtx) PetscCall(PetscFree(mesh->metricCtx));
2745   /* This was originally freed in DMDestroy(), but that prevents reference counting of backend objects */
2746   PetscCall(PetscFree(mesh));
2747   PetscFunctionReturn(PETSC_SUCCESS);
2748 }
2749 
2750 PetscErrorCode DMCreateMatrix_Plex(DM dm, Mat *J)
2751 {
2752   PetscSection           sectionGlobal, sectionLocal;
2753   PetscInt               bs = -1, mbs;
2754   PetscInt               localSize, localStart = 0;
2755   PetscBool              isShell, isBlock, isSeqBlock, isMPIBlock, isSymBlock, isSymSeqBlock, isSymMPIBlock, isMatIS;
2756   MatType                mtype;
2757   ISLocalToGlobalMapping ltog;
2758 
2759   PetscFunctionBegin;
2760   PetscCall(MatInitializePackage());
2761   mtype = dm->mattype;
2762   PetscCall(DMGetLocalSection(dm, &sectionLocal));
2763   PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
2764   /* PetscCall(PetscSectionGetStorageSize(sectionGlobal, &localSize)); */
2765   PetscCall(PetscSectionGetConstrainedStorageSize(sectionGlobal, &localSize));
2766   PetscCallMPI(MPI_Exscan(&localSize, &localStart, 1, MPIU_INT, MPI_SUM, PetscObjectComm((PetscObject)dm)));
2767   PetscCall(MatCreate(PetscObjectComm((PetscObject)dm), J));
2768   PetscCall(MatSetSizes(*J, localSize, localSize, PETSC_DETERMINE, PETSC_DETERMINE));
2769   PetscCall(MatSetType(*J, mtype));
2770   PetscCall(MatSetFromOptions(*J));
2771   PetscCall(MatGetBlockSize(*J, &mbs));
2772   if (mbs > 1) bs = mbs;
2773   PetscCall(PetscStrcmp(mtype, MATSHELL, &isShell));
2774   PetscCall(PetscStrcmp(mtype, MATBAIJ, &isBlock));
2775   PetscCall(PetscStrcmp(mtype, MATSEQBAIJ, &isSeqBlock));
2776   PetscCall(PetscStrcmp(mtype, MATMPIBAIJ, &isMPIBlock));
2777   PetscCall(PetscStrcmp(mtype, MATSBAIJ, &isSymBlock));
2778   PetscCall(PetscStrcmp(mtype, MATSEQSBAIJ, &isSymSeqBlock));
2779   PetscCall(PetscStrcmp(mtype, MATMPISBAIJ, &isSymMPIBlock));
2780   PetscCall(PetscStrcmp(mtype, MATIS, &isMatIS));
2781   if (!isShell) {
2782     PetscBool fillMatrix = (PetscBool)(!dm->prealloc_only && !isMatIS);
2783     PetscInt *dnz, *onz, *dnzu, *onzu, bsLocal[2], bsMinMax[2], *pblocks;
2784     PetscInt  pStart, pEnd, p, dof, cdof, num_fields;
2785 
2786     PetscCall(DMGetLocalToGlobalMapping(dm, &ltog));
2787 
2788     PetscCall(PetscCalloc1(localSize, &pblocks));
2789     PetscCall(PetscSectionGetChart(sectionGlobal, &pStart, &pEnd));
2790     PetscCall(PetscSectionGetNumFields(sectionGlobal, &num_fields));
2791     for (p = pStart; p < pEnd; ++p) {
2792       switch (dm->blocking_type) {
2793       case DM_BLOCKING_TOPOLOGICAL_POINT: { // One block per topological point
2794         PetscInt bdof, offset;
2795 
2796         PetscCall(PetscSectionGetDof(sectionGlobal, p, &dof));
2797         PetscCall(PetscSectionGetOffset(sectionGlobal, p, &offset));
2798         PetscCall(PetscSectionGetConstraintDof(sectionGlobal, p, &cdof));
2799         for (PetscInt i = 0; i < dof - cdof; ++i) pblocks[offset - localStart + i] = dof - cdof;
2800         // Signal block concatenation
2801         if (dof - cdof && sectionLocal->blockStarts && !PetscBTLookup(sectionLocal->blockStarts, p)) pblocks[offset - localStart] = -(dof - cdof);
2802         dof  = dof < 0 ? -(dof + 1) : dof;
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       } break;
2812       case DM_BLOCKING_FIELD_NODE: {
2813         for (PetscInt field = 0; field < num_fields; field++) {
2814           PetscInt num_comp, bdof, offset;
2815           PetscCall(PetscSectionGetFieldComponents(sectionGlobal, field, &num_comp));
2816           PetscCall(PetscSectionGetFieldDof(sectionGlobal, p, field, &dof));
2817           if (dof < 0) continue;
2818           PetscCall(PetscSectionGetFieldOffset(sectionGlobal, p, field, &offset));
2819           PetscCall(PetscSectionGetFieldConstraintDof(sectionGlobal, p, field, &cdof));
2820           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);
2821           PetscInt num_nodes = dof / num_comp;
2822           for (PetscInt i = 0; i < dof - cdof; i++) pblocks[offset - localStart + i] = (dof - cdof) / num_nodes;
2823           // Handle possibly constant block size (unlikely)
2824           bdof = cdof && (dof - cdof) ? 1 : dof;
2825           if (dof) {
2826             if (bs < 0) {
2827               bs = bdof;
2828             } else if (bs != bdof) {
2829               bs = 1;
2830             }
2831           }
2832         }
2833       } break;
2834       }
2835     }
2836     /* Must have same blocksize on all procs (some might have no points) */
2837     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs;
2838     bsLocal[1] = bs;
2839     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
2840     if (bsMinMax[0] != bsMinMax[1]) bs = 1;
2841     else bs = bsMinMax[0];
2842     bs = PetscMax(1, bs);
2843     PetscCall(MatSetLocalToGlobalMapping(*J, ltog, ltog));
2844     if (dm->prealloc_skip) { // User will likely use MatSetPreallocationCOO(), but still set structural parameters
2845       PetscCall(MatSetBlockSize(*J, bs));
2846       PetscCall(MatSetUp(*J));
2847     } else {
2848       PetscCall(PetscCalloc4(localSize / bs, &dnz, localSize / bs, &onz, localSize / bs, &dnzu, localSize / bs, &onzu));
2849       PetscCall(DMPlexPreallocateOperator(dm, bs, dnz, onz, dnzu, onzu, *J, fillMatrix));
2850       PetscCall(PetscFree4(dnz, onz, dnzu, onzu));
2851     }
2852     { // Consolidate blocks
2853       PetscInt nblocks = 0;
2854       for (PetscInt i = 0; i < localSize; i += PetscMax(1, pblocks[i])) {
2855         if (pblocks[i] == 0) continue;
2856         // Negative block size indicates the blocks should be concatenated
2857         if (pblocks[i] < 0) {
2858           pblocks[i] = -pblocks[i];
2859           pblocks[nblocks - 1] += pblocks[i];
2860         } else {
2861           pblocks[nblocks++] = pblocks[i]; // nblocks always <= i
2862         }
2863         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]);
2864       }
2865       PetscCall(MatSetVariableBlockSizes(*J, nblocks, pblocks));
2866     }
2867     PetscCall(PetscFree(pblocks));
2868   }
2869   PetscCall(MatSetDM(*J, dm));
2870   PetscFunctionReturn(PETSC_SUCCESS);
2871 }
2872 
2873 /*@
2874   DMPlexGetSubdomainSection - Returns the section associated with the subdomain
2875 
2876   Not Collective
2877 
2878   Input Parameter:
2879 . dm - The `DMPLEX`
2880 
2881   Output Parameter:
2882 . subsection - The subdomain section
2883 
2884   Level: developer
2885 
2886 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `PetscSection`
2887 @*/
2888 PetscErrorCode DMPlexGetSubdomainSection(DM dm, PetscSection *subsection)
2889 {
2890   DM_Plex *mesh = (DM_Plex *)dm->data;
2891 
2892   PetscFunctionBegin;
2893   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2894   if (!mesh->subdomainSection) {
2895     PetscSection section;
2896     PetscSF      sf;
2897 
2898     PetscCall(PetscSFCreate(PETSC_COMM_SELF, &sf));
2899     PetscCall(DMGetLocalSection(dm, &section));
2900     PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_FALSE, PETSC_TRUE, &mesh->subdomainSection));
2901     PetscCall(PetscSFDestroy(&sf));
2902   }
2903   *subsection = mesh->subdomainSection;
2904   PetscFunctionReturn(PETSC_SUCCESS);
2905 }
2906 
2907 /*@
2908   DMPlexGetChart - Return the interval for all mesh points [`pStart`, `pEnd`)
2909 
2910   Not Collective
2911 
2912   Input Parameter:
2913 . dm - The `DMPLEX`
2914 
2915   Output Parameters:
2916 + pStart - The first mesh point
2917 - pEnd   - The upper bound for mesh points
2918 
2919   Level: beginner
2920 
2921 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetChart()`
2922 @*/
2923 PetscErrorCode DMPlexGetChart(DM dm, PetscInt *pStart, PetscInt *pEnd)
2924 {
2925   DM_Plex *mesh = (DM_Plex *)dm->data;
2926 
2927   PetscFunctionBegin;
2928   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2929   if (mesh->tr) PetscCall(DMPlexTransformGetChart(mesh->tr, pStart, pEnd));
2930   else PetscCall(PetscSectionGetChart(mesh->coneSection, pStart, pEnd));
2931   PetscFunctionReturn(PETSC_SUCCESS);
2932 }
2933 
2934 /*@
2935   DMPlexSetChart - Set the interval for all mesh points [`pStart`, `pEnd`)
2936 
2937   Not Collective
2938 
2939   Input Parameters:
2940 + dm     - The `DMPLEX`
2941 . pStart - The first mesh point
2942 - pEnd   - The upper bound for mesh points
2943 
2944   Level: beginner
2945 
2946 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetChart()`
2947 @*/
2948 PetscErrorCode DMPlexSetChart(DM dm, PetscInt pStart, PetscInt pEnd)
2949 {
2950   DM_Plex *mesh = (DM_Plex *)dm->data;
2951 
2952   PetscFunctionBegin;
2953   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2954   PetscCall(PetscSectionSetChart(mesh->coneSection, pStart, pEnd));
2955   PetscCall(PetscSectionSetChart(mesh->supportSection, pStart, pEnd));
2956   PetscCall(PetscFree(mesh->cellTypes));
2957   PetscFunctionReturn(PETSC_SUCCESS);
2958 }
2959 
2960 /*@
2961   DMPlexGetConeSize - Return the number of in-edges for this point in the DAG
2962 
2963   Not Collective
2964 
2965   Input Parameters:
2966 + dm - The `DMPLEX`
2967 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
2968 
2969   Output Parameter:
2970 . size - The cone size for point `p`
2971 
2972   Level: beginner
2973 
2974 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
2975 @*/
2976 PetscErrorCode DMPlexGetConeSize(DM dm, PetscInt p, PetscInt *size)
2977 {
2978   DM_Plex *mesh = (DM_Plex *)dm->data;
2979 
2980   PetscFunctionBegin;
2981   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2982   PetscAssertPointer(size, 3);
2983   if (mesh->tr) PetscCall(DMPlexTransformGetConeSize(mesh->tr, p, size));
2984   else PetscCall(PetscSectionGetDof(mesh->coneSection, p, size));
2985   PetscFunctionReturn(PETSC_SUCCESS);
2986 }
2987 
2988 /*@
2989   DMPlexSetConeSize - Set the number of in-edges for this point in the DAG
2990 
2991   Not Collective
2992 
2993   Input Parameters:
2994 + dm   - The `DMPLEX`
2995 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
2996 - size - The cone size for point `p`
2997 
2998   Level: beginner
2999 
3000   Note:
3001   This should be called after `DMPlexSetChart()`.
3002 
3003 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetConeSize()`, `DMPlexSetChart()`
3004 @*/
3005 PetscErrorCode DMPlexSetConeSize(DM dm, PetscInt p, PetscInt size)
3006 {
3007   DM_Plex *mesh = (DM_Plex *)dm->data;
3008 
3009   PetscFunctionBegin;
3010   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3011   PetscCheck(!mesh->tr, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Cannot call DMPlexSetConeSize() on a mesh with a transform defined.");
3012   PetscCall(PetscSectionSetDof(mesh->coneSection, p, size));
3013   PetscFunctionReturn(PETSC_SUCCESS);
3014 }
3015 
3016 /*@C
3017   DMPlexGetCone - Return the points on the in-edges for this point in the DAG
3018 
3019   Not Collective
3020 
3021   Input Parameters:
3022 + dm - The `DMPLEX`
3023 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3024 
3025   Output Parameter:
3026 . cone - An array of points which are on the in-edges for point `p`
3027 
3028   Level: beginner
3029 
3030   Fortran Notes:
3031   You must also call `DMPlexRestoreCone()` after you finish using the returned array.
3032   `DMPlexRestoreCone()` is not needed/available in C.
3033 
3034 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSize()`, `DMPlexSetCone()`, `DMPlexGetConeTuple()`, `DMPlexSetChart()`, `DMPlexRestoreCone()`
3035 @*/
3036 PetscErrorCode DMPlexGetCone(DM dm, PetscInt p, const PetscInt *cone[])
3037 {
3038   DM_Plex *mesh = (DM_Plex *)dm->data;
3039   PetscInt off;
3040 
3041   PetscFunctionBegin;
3042   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3043   PetscAssertPointer(cone, 3);
3044   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3045   *cone = PetscSafePointerPlusOffset(mesh->cones, off);
3046   PetscFunctionReturn(PETSC_SUCCESS);
3047 }
3048 
3049 /*@C
3050   DMPlexGetConeTuple - Return the points on the in-edges of several points in the DAG
3051 
3052   Not Collective
3053 
3054   Input Parameters:
3055 + dm - The `DMPLEX`
3056 - p  - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3057 
3058   Output Parameters:
3059 + pConesSection - `PetscSection` describing the layout of `pCones`
3060 - pCones        - An array of points which are on the in-edges for the point set `p`
3061 
3062   Level: intermediate
3063 
3064 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeRecursive()`, `DMPlexSetChart()`, `PetscSection`, `IS`
3065 @*/
3066 PetscErrorCode DMPlexGetConeTuple(DM dm, IS p, PetscSection *pConesSection, IS *pCones)
3067 {
3068   PetscSection cs, newcs;
3069   PetscInt    *cones;
3070   PetscInt    *newarr = NULL;
3071   PetscInt     n;
3072 
3073   PetscFunctionBegin;
3074   PetscCall(DMPlexGetCones(dm, &cones));
3075   PetscCall(DMPlexGetConeSection(dm, &cs));
3076   PetscCall(PetscSectionExtractDofsFromArray(cs, MPIU_INT, cones, p, &newcs, pCones ? ((void **)&newarr) : NULL));
3077   if (pConesSection) *pConesSection = newcs;
3078   if (pCones) {
3079     PetscCall(PetscSectionGetStorageSize(newcs, &n));
3080     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)p), n, newarr, PETSC_OWN_POINTER, pCones));
3081   }
3082   PetscFunctionReturn(PETSC_SUCCESS);
3083 }
3084 
3085 /*@
3086   DMPlexGetConeRecursiveVertices - Expand each given point into its cone points and do that recursively until we end up just with vertices.
3087 
3088   Not Collective
3089 
3090   Input Parameters:
3091 + dm     - The `DMPLEX`
3092 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3093 
3094   Output Parameter:
3095 . expandedPoints - An array of vertices recursively expanded from input points
3096 
3097   Level: advanced
3098 
3099   Notes:
3100   Like `DMPlexGetConeRecursive()` but returns only the 0-depth `IS` (i.e. vertices only) and no sections.
3101 
3102   There is no corresponding Restore function, just call `ISDestroy()` on the returned `IS` to deallocate.
3103 
3104 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexRestoreConeRecursive()`,
3105           `DMPlexGetDepth()`, `IS`
3106 @*/
3107 PetscErrorCode DMPlexGetConeRecursiveVertices(DM dm, IS points, IS *expandedPoints)
3108 {
3109   IS      *expandedPointsAll;
3110   PetscInt depth;
3111 
3112   PetscFunctionBegin;
3113   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3114   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
3115   PetscAssertPointer(expandedPoints, 3);
3116   PetscCall(DMPlexGetConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
3117   *expandedPoints = expandedPointsAll[0];
3118   PetscCall(PetscObjectReference((PetscObject)expandedPointsAll[0]));
3119   PetscCall(DMPlexRestoreConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
3120   PetscFunctionReturn(PETSC_SUCCESS);
3121 }
3122 
3123 /*@
3124   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).
3125 
3126   Not Collective
3127 
3128   Input Parameters:
3129 + dm     - The `DMPLEX`
3130 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3131 
3132   Output Parameters:
3133 + depth          - (optional) Size of the output arrays, equal to `DMPLEX` depth, returned by `DMPlexGetDepth()`
3134 . expandedPoints - (optional) An array of index sets with recursively expanded cones
3135 - sections       - (optional) An array of sections which describe mappings from points to their cone points
3136 
3137   Level: advanced
3138 
3139   Notes:
3140   Like `DMPlexGetConeTuple()` but recursive.
3141 
3142   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.
3143   For example, for d=0 it contains only vertices, for d=1 it can contain vertices and edges, etc.
3144 
3145   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\:
3146   (1) DAG points in `expandedPoints`[d+1] with `depth` d+1 to their cone points in `expandedPoints`[d];
3147   (2) DAG points in `expandedPoints`[d+1] with `depth` in [0,d] to the same points in `expandedPoints`[d].
3148 
3149 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexRestoreConeRecursive()`, `DMPlexGetConeRecursiveVertices()`,
3150           `DMPlexGetDepth()`, `PetscSection`, `IS`
3151 @*/
3152 PetscErrorCode DMPlexGetConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
3153 {
3154   const PetscInt *arr0 = NULL, *cone = NULL;
3155   PetscInt       *arr = NULL, *newarr = NULL;
3156   PetscInt        d, depth_, i, n, newn, cn, co, start, end;
3157   IS             *expandedPoints_;
3158   PetscSection   *sections_;
3159 
3160   PetscFunctionBegin;
3161   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3162   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
3163   if (depth) PetscAssertPointer(depth, 3);
3164   if (expandedPoints) PetscAssertPointer(expandedPoints, 4);
3165   if (sections) PetscAssertPointer(sections, 5);
3166   PetscCall(ISGetLocalSize(points, &n));
3167   PetscCall(ISGetIndices(points, &arr0));
3168   PetscCall(DMPlexGetDepth(dm, &depth_));
3169   PetscCall(PetscCalloc1(depth_, &expandedPoints_));
3170   PetscCall(PetscCalloc1(depth_, &sections_));
3171   arr = (PetscInt *)arr0; /* this is ok because first generation of arr is not modified */
3172   for (d = depth_ - 1; d >= 0; d--) {
3173     PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &sections_[d]));
3174     PetscCall(PetscSectionSetChart(sections_[d], 0, n));
3175     for (i = 0; i < n; i++) {
3176       PetscCall(DMPlexGetDepthStratum(dm, d + 1, &start, &end));
3177       if (arr[i] >= start && arr[i] < end) {
3178         PetscCall(DMPlexGetConeSize(dm, arr[i], &cn));
3179         PetscCall(PetscSectionSetDof(sections_[d], i, cn));
3180       } else {
3181         PetscCall(PetscSectionSetDof(sections_[d], i, 1));
3182       }
3183     }
3184     PetscCall(PetscSectionSetUp(sections_[d]));
3185     PetscCall(PetscSectionGetStorageSize(sections_[d], &newn));
3186     PetscCall(PetscMalloc1(newn, &newarr));
3187     for (i = 0; i < n; i++) {
3188       PetscCall(PetscSectionGetDof(sections_[d], i, &cn));
3189       PetscCall(PetscSectionGetOffset(sections_[d], i, &co));
3190       if (cn > 1) {
3191         PetscCall(DMPlexGetCone(dm, arr[i], &cone));
3192         PetscCall(PetscMemcpy(&newarr[co], cone, cn * sizeof(PetscInt)));
3193       } else {
3194         newarr[co] = arr[i];
3195       }
3196     }
3197     PetscCall(ISCreateGeneral(PETSC_COMM_SELF, newn, newarr, PETSC_OWN_POINTER, &expandedPoints_[d]));
3198     arr = newarr;
3199     n   = newn;
3200   }
3201   PetscCall(ISRestoreIndices(points, &arr0));
3202   *depth = depth_;
3203   if (expandedPoints) *expandedPoints = expandedPoints_;
3204   else {
3205     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&expandedPoints_[d]));
3206     PetscCall(PetscFree(expandedPoints_));
3207   }
3208   if (sections) *sections = sections_;
3209   else {
3210     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&sections_[d]));
3211     PetscCall(PetscFree(sections_));
3212   }
3213   PetscFunctionReturn(PETSC_SUCCESS);
3214 }
3215 
3216 /*@
3217   DMPlexRestoreConeRecursive - Deallocates arrays created by `DMPlexGetConeRecursive()`
3218 
3219   Not Collective
3220 
3221   Input Parameters:
3222 + dm     - The `DMPLEX`
3223 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3224 
3225   Output Parameters:
3226 + depth          - (optional) Size of the output arrays, equal to `DMPLEX` depth, returned by `DMPlexGetDepth()`
3227 . expandedPoints - (optional) An array of recursively expanded cones
3228 - sections       - (optional) An array of sections which describe mappings from points to their cone points
3229 
3230   Level: advanced
3231 
3232   Note:
3233   See `DMPlexGetConeRecursive()`
3234 
3235 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexGetConeRecursiveVertices()`,
3236           `DMPlexGetDepth()`, `IS`, `PetscSection`
3237 @*/
3238 PetscErrorCode DMPlexRestoreConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
3239 {
3240   PetscInt d, depth_;
3241 
3242   PetscFunctionBegin;
3243   PetscCall(DMPlexGetDepth(dm, &depth_));
3244   PetscCheck(!depth || *depth == depth_, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "depth changed since last call to DMPlexGetConeRecursive");
3245   if (depth) *depth = 0;
3246   if (expandedPoints) {
3247     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&((*expandedPoints)[d])));
3248     PetscCall(PetscFree(*expandedPoints));
3249   }
3250   if (sections) {
3251     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&((*sections)[d])));
3252     PetscCall(PetscFree(*sections));
3253   }
3254   PetscFunctionReturn(PETSC_SUCCESS);
3255 }
3256 
3257 /*@
3258   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
3259 
3260   Not Collective
3261 
3262   Input Parameters:
3263 + dm   - The `DMPLEX`
3264 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3265 - cone - An array of points which are on the in-edges for point `p`
3266 
3267   Level: beginner
3268 
3269   Note:
3270   This should be called after all calls to `DMPlexSetConeSize()` and `DMSetUp()`.
3271 
3272 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`, `DMPlexSetSupport()`, `DMPlexSetSupportSize()`
3273 @*/
3274 PetscErrorCode DMPlexSetCone(DM dm, PetscInt p, const PetscInt cone[])
3275 {
3276   DM_Plex *mesh = (DM_Plex *)dm->data;
3277   PetscInt dof, off, c;
3278 
3279   PetscFunctionBegin;
3280   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3281   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3282   if (dof) PetscAssertPointer(cone, 3);
3283   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3284   if (PetscDefined(USE_DEBUG)) {
3285     PetscInt pStart, pEnd;
3286     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3287     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);
3288     for (c = 0; c < dof; ++c) {
3289       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);
3290       mesh->cones[off + c] = cone[c];
3291     }
3292   } else {
3293     for (c = 0; c < dof; ++c) mesh->cones[off + c] = cone[c];
3294   }
3295   PetscFunctionReturn(PETSC_SUCCESS);
3296 }
3297 
3298 /*@C
3299   DMPlexGetConeOrientation - Return the orientations on the in-edges for this point in the DAG
3300 
3301   Not Collective
3302 
3303   Input Parameters:
3304 + dm - The `DMPLEX`
3305 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3306 
3307   Output Parameter:
3308 . coneOrientation - An array of orientations which are on the in-edges for point `p`. An orientation is an
3309                     integer giving the prescription for cone traversal.
3310 
3311   Level: beginner
3312 
3313   Note:
3314   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3315   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3316   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3317   with the identity.
3318 
3319   Fortran Notes:
3320   You must also call `DMPlexRestoreConeOrientation()` after you finish using the returned array.
3321   `DMPlexRestoreConeOrientation()` is not needed/available in C.
3322 
3323 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPolytopeTypeComposeOrientation()`, `DMPolytopeTypeComposeOrientationInv()`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetCone()`, `DMPlexSetChart()`
3324 @*/
3325 PetscErrorCode DMPlexGetConeOrientation(DM dm, PetscInt p, const PetscInt *coneOrientation[])
3326 {
3327   DM_Plex *mesh = (DM_Plex *)dm->data;
3328   PetscInt off;
3329 
3330   PetscFunctionBegin;
3331   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3332   if (PetscDefined(USE_DEBUG)) {
3333     PetscInt dof;
3334     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3335     if (dof) PetscAssertPointer(coneOrientation, 3);
3336   }
3337   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3338 
3339   *coneOrientation = &mesh->coneOrientations[off];
3340   PetscFunctionReturn(PETSC_SUCCESS);
3341 }
3342 
3343 /*@
3344   DMPlexSetConeOrientation - Set the orientations on the in-edges for this point in the DAG
3345 
3346   Not Collective
3347 
3348   Input Parameters:
3349 + dm              - The `DMPLEX`
3350 . p               - The point, which must lie in the chart set with `DMPlexSetChart()`
3351 - coneOrientation - An array of orientations
3352 
3353   Level: beginner
3354 
3355   Notes:
3356   This should be called after all calls to `DMPlexSetConeSize()` and `DMSetUp()`.
3357 
3358   The meaning of coneOrientation is detailed in `DMPlexGetConeOrientation()`.
3359 
3360 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetConeOrientation()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3361 @*/
3362 PetscErrorCode DMPlexSetConeOrientation(DM dm, PetscInt p, const PetscInt coneOrientation[])
3363 {
3364   DM_Plex *mesh = (DM_Plex *)dm->data;
3365   PetscInt pStart, pEnd;
3366   PetscInt dof, off, c;
3367 
3368   PetscFunctionBegin;
3369   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3370   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3371   if (dof) PetscAssertPointer(coneOrientation, 3);
3372   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3373   if (PetscDefined(USE_DEBUG)) {
3374     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3375     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);
3376     for (c = 0; c < dof; ++c) {
3377       PetscInt cdof, o = coneOrientation[c];
3378 
3379       PetscCall(PetscSectionGetDof(mesh->coneSection, mesh->cones[off + c], &cdof));
3380       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);
3381       mesh->coneOrientations[off + c] = o;
3382     }
3383   } else {
3384     for (c = 0; c < dof; ++c) mesh->coneOrientations[off + c] = coneOrientation[c];
3385   }
3386   PetscFunctionReturn(PETSC_SUCCESS);
3387 }
3388 
3389 /*@
3390   DMPlexInsertCone - Insert a point into the in-edges for the point p in the DAG
3391 
3392   Not Collective
3393 
3394   Input Parameters:
3395 + dm        - The `DMPLEX`
3396 . p         - The point, which must lie in the chart set with `DMPlexSetChart()`
3397 . conePos   - The local index in the cone where the point should be put
3398 - conePoint - The mesh point to insert
3399 
3400   Level: beginner
3401 
3402 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3403 @*/
3404 PetscErrorCode DMPlexInsertCone(DM dm, PetscInt p, PetscInt conePos, PetscInt conePoint)
3405 {
3406   DM_Plex *mesh = (DM_Plex *)dm->data;
3407   PetscInt pStart, pEnd;
3408   PetscInt dof, off;
3409 
3410   PetscFunctionBegin;
3411   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3412   if (PetscDefined(USE_DEBUG)) {
3413     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3414     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);
3415     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);
3416     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3417     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);
3418   }
3419   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3420   mesh->cones[off + conePos] = conePoint;
3421   PetscFunctionReturn(PETSC_SUCCESS);
3422 }
3423 
3424 /*@
3425   DMPlexInsertConeOrientation - Insert a point orientation for the in-edge for the point p in the DAG
3426 
3427   Not Collective
3428 
3429   Input Parameters:
3430 + dm              - The `DMPLEX`
3431 . p               - The point, which must lie in the chart set with `DMPlexSetChart()`
3432 . conePos         - The local index in the cone where the point should be put
3433 - coneOrientation - The point orientation to insert
3434 
3435   Level: beginner
3436 
3437   Note:
3438   The meaning of coneOrientation values is detailed in `DMPlexGetConeOrientation()`.
3439 
3440 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3441 @*/
3442 PetscErrorCode DMPlexInsertConeOrientation(DM dm, PetscInt p, PetscInt conePos, PetscInt coneOrientation)
3443 {
3444   DM_Plex *mesh = (DM_Plex *)dm->data;
3445   PetscInt pStart, pEnd;
3446   PetscInt dof, off;
3447 
3448   PetscFunctionBegin;
3449   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3450   if (PetscDefined(USE_DEBUG)) {
3451     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3452     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);
3453     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3454     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);
3455   }
3456   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3457   mesh->coneOrientations[off + conePos] = coneOrientation;
3458   PetscFunctionReturn(PETSC_SUCCESS);
3459 }
3460 
3461 /*@C
3462   DMPlexGetOrientedCone - Return the points and orientations on the in-edges for this point in the DAG
3463 
3464   Not collective
3465 
3466   Input Parameters:
3467 + dm - The DMPlex
3468 - p  - The point, which must lie in the chart set with DMPlexSetChart()
3469 
3470   Output Parameters:
3471 + cone - An array of points which are on the in-edges for point `p`
3472 - ornt - An array of orientations which are on the in-edges for point `p`. An orientation is an
3473         integer giving the prescription for cone traversal.
3474 
3475   Level: beginner
3476 
3477   Notes:
3478   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3479   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3480   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3481   with the identity.
3482 
3483   Fortran Notes:
3484   You must also call `DMPlexRestoreCone()` after you finish using the returned array.
3485   `DMPlexRestoreCone()` is not needed/available in C.
3486 
3487 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreOrientedCone()`, `DMPlexGetConeSize()`, `DMPlexGetCone()`, `DMPlexGetChart()`
3488 @*/
3489 PetscErrorCode DMPlexGetOrientedCone(DM dm, PetscInt p, const PetscInt *cone[], const PetscInt *ornt[])
3490 {
3491   DM_Plex *mesh = (DM_Plex *)dm->data;
3492 
3493   PetscFunctionBegin;
3494   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3495   if (mesh->tr) {
3496     PetscCall(DMPlexTransformGetCone(mesh->tr, p, cone, ornt));
3497   } else {
3498     PetscInt off;
3499     if (PetscDefined(USE_DEBUG)) {
3500       PetscInt dof;
3501       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3502       if (dof) {
3503         if (cone) PetscAssertPointer(cone, 3);
3504         if (ornt) PetscAssertPointer(ornt, 4);
3505       }
3506     }
3507     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3508     if (cone) *cone = PetscSafePointerPlusOffset(mesh->cones, off);
3509     if (ornt) *ornt = PetscSafePointerPlusOffset(mesh->coneOrientations, off);
3510   }
3511   PetscFunctionReturn(PETSC_SUCCESS);
3512 }
3513 
3514 /*@C
3515   DMPlexRestoreOrientedCone - Restore the points and orientations on the in-edges for this point in the DAG
3516 
3517   Not Collective
3518 
3519   Input Parameters:
3520 + dm   - The DMPlex
3521 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3522 . cone - An array of points which are on the in-edges for point p
3523 - ornt - An array of orientations which are on the in-edges for point `p`. An orientation is an
3524         integer giving the prescription for cone traversal.
3525 
3526   Level: beginner
3527 
3528   Notes:
3529   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3530   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3531   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3532   with the identity.
3533 
3534   Fortran Notes:
3535   You must also call `DMPlexRestoreCone()` after you finish using the returned array.
3536   `DMPlexRestoreCone()` is not needed/available in C.
3537 
3538 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetOrientedCone()`, `DMPlexGetConeSize()`, `DMPlexGetCone()`, `DMPlexGetChart()`
3539 @*/
3540 PetscErrorCode DMPlexRestoreOrientedCone(DM dm, PetscInt p, const PetscInt *cone[], const PetscInt *ornt[])
3541 {
3542   DM_Plex *mesh = (DM_Plex *)dm->data;
3543 
3544   PetscFunctionBegin;
3545   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3546   if (mesh->tr) PetscCall(DMPlexTransformRestoreCone(mesh->tr, p, cone, ornt));
3547   PetscFunctionReturn(PETSC_SUCCESS);
3548 }
3549 
3550 /*@
3551   DMPlexGetSupportSize - Return the number of out-edges for this point in the DAG
3552 
3553   Not Collective
3554 
3555   Input Parameters:
3556 + dm - The `DMPLEX`
3557 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3558 
3559   Output Parameter:
3560 . size - The support size for point `p`
3561 
3562   Level: beginner
3563 
3564 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`, `DMPlexGetConeSize()`
3565 @*/
3566 PetscErrorCode DMPlexGetSupportSize(DM dm, PetscInt p, PetscInt *size)
3567 {
3568   DM_Plex *mesh = (DM_Plex *)dm->data;
3569 
3570   PetscFunctionBegin;
3571   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3572   PetscAssertPointer(size, 3);
3573   PetscCall(PetscSectionGetDof(mesh->supportSection, p, size));
3574   PetscFunctionReturn(PETSC_SUCCESS);
3575 }
3576 
3577 /*@
3578   DMPlexSetSupportSize - Set the number of out-edges for this point in the DAG
3579 
3580   Not Collective
3581 
3582   Input Parameters:
3583 + dm   - The `DMPLEX`
3584 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3585 - size - The support size for point `p`
3586 
3587   Level: beginner
3588 
3589   Note:
3590   This should be called after `DMPlexSetChart()`.
3591 
3592 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetSupportSize()`, `DMPlexSetChart()`
3593 @*/
3594 PetscErrorCode DMPlexSetSupportSize(DM dm, PetscInt p, PetscInt size)
3595 {
3596   DM_Plex *mesh = (DM_Plex *)dm->data;
3597 
3598   PetscFunctionBegin;
3599   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3600   PetscCall(PetscSectionSetDof(mesh->supportSection, p, size));
3601   PetscFunctionReturn(PETSC_SUCCESS);
3602 }
3603 
3604 /*@C
3605   DMPlexGetSupport - Return the points on the out-edges for this point in the DAG
3606 
3607   Not Collective
3608 
3609   Input Parameters:
3610 + dm - The `DMPLEX`
3611 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3612 
3613   Output Parameter:
3614 . support - An array of points which are on the out-edges for point `p`
3615 
3616   Level: beginner
3617 
3618   Fortran Notes:
3619   You must also call `DMPlexRestoreSupport()` after you finish using the returned array.
3620   `DMPlexRestoreSupport()` is not needed/available in C.
3621 
3622 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSupportSize()`, `DMPlexSetSupport()`, `DMPlexGetCone()`, `DMPlexSetChart()`
3623 @*/
3624 PetscErrorCode DMPlexGetSupport(DM dm, PetscInt p, const PetscInt *support[])
3625 {
3626   DM_Plex *mesh = (DM_Plex *)dm->data;
3627   PetscInt off;
3628 
3629   PetscFunctionBegin;
3630   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3631   PetscAssertPointer(support, 3);
3632   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3633   *support = PetscSafePointerPlusOffset(mesh->supports, off);
3634   PetscFunctionReturn(PETSC_SUCCESS);
3635 }
3636 
3637 /*@
3638   DMPlexSetSupport - Set the points on the out-edges for this point in the DAG, that is the list of points that this point covers
3639 
3640   Not Collective
3641 
3642   Input Parameters:
3643 + dm      - The `DMPLEX`
3644 . p       - The point, which must lie in the chart set with `DMPlexSetChart()`
3645 - support - An array of points which are on the out-edges for point `p`
3646 
3647   Level: beginner
3648 
3649   Note:
3650   This should be called after all calls to `DMPlexSetSupportSize()` and `DMSetUp()`.
3651 
3652 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetCone()`, `DMPlexSetConeSize()`, `DMPlexCreate()`, `DMPlexGetSupport()`, `DMPlexSetChart()`, `DMPlexSetSupportSize()`, `DMSetUp()`
3653 @*/
3654 PetscErrorCode DMPlexSetSupport(DM dm, PetscInt p, const PetscInt support[])
3655 {
3656   DM_Plex *mesh = (DM_Plex *)dm->data;
3657   PetscInt pStart, pEnd;
3658   PetscInt dof, off, c;
3659 
3660   PetscFunctionBegin;
3661   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3662   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3663   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3664   if (dof) PetscAssertPointer(support, 3);
3665   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3666   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);
3667   for (c = 0; c < dof; ++c) {
3668     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);
3669     mesh->supports[off + c] = support[c];
3670   }
3671   PetscFunctionReturn(PETSC_SUCCESS);
3672 }
3673 
3674 /*@
3675   DMPlexInsertSupport - Insert a point into the out-edges for the point p in the DAG
3676 
3677   Not Collective
3678 
3679   Input Parameters:
3680 + dm           - The `DMPLEX`
3681 . p            - The point, which must lie in the chart set with `DMPlexSetChart()`
3682 . supportPos   - The local index in the cone where the point should be put
3683 - supportPoint - The mesh point to insert
3684 
3685   Level: beginner
3686 
3687 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3688 @*/
3689 PetscErrorCode DMPlexInsertSupport(DM dm, PetscInt p, PetscInt supportPos, PetscInt supportPoint)
3690 {
3691   DM_Plex *mesh = (DM_Plex *)dm->data;
3692   PetscInt pStart, pEnd;
3693   PetscInt dof, off;
3694 
3695   PetscFunctionBegin;
3696   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3697   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3698   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3699   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3700   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);
3701   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);
3702   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);
3703   mesh->supports[off + supportPos] = supportPoint;
3704   PetscFunctionReturn(PETSC_SUCCESS);
3705 }
3706 
3707 /* Converts an orientation o in the current numbering to the previous scheme used in Plex */
3708 PetscInt DMPolytopeConvertNewOrientation_Internal(DMPolytopeType ct, PetscInt o)
3709 {
3710   switch (ct) {
3711   case DM_POLYTOPE_SEGMENT:
3712     if (o == -1) return -2;
3713     break;
3714   case DM_POLYTOPE_TRIANGLE:
3715     if (o == -3) return -1;
3716     if (o == -2) return -3;
3717     if (o == -1) return -2;
3718     break;
3719   case DM_POLYTOPE_QUADRILATERAL:
3720     if (o == -4) return -2;
3721     if (o == -3) return -1;
3722     if (o == -2) return -4;
3723     if (o == -1) return -3;
3724     break;
3725   default:
3726     return o;
3727   }
3728   return o;
3729 }
3730 
3731 /* Converts an orientation o in the previous scheme used in Plex to the current numbering */
3732 PetscInt DMPolytopeConvertOldOrientation_Internal(DMPolytopeType ct, PetscInt o)
3733 {
3734   switch (ct) {
3735   case DM_POLYTOPE_SEGMENT:
3736     if ((o == -2) || (o == 1)) return -1;
3737     if (o == -1) return 0;
3738     break;
3739   case DM_POLYTOPE_TRIANGLE:
3740     if (o == -3) return -2;
3741     if (o == -2) return -1;
3742     if (o == -1) return -3;
3743     break;
3744   case DM_POLYTOPE_QUADRILATERAL:
3745     if (o == -4) return -2;
3746     if (o == -3) return -1;
3747     if (o == -2) return -4;
3748     if (o == -1) return -3;
3749     break;
3750   default:
3751     return o;
3752   }
3753   return o;
3754 }
3755 
3756 /* Takes in a mesh whose orientations are in the previous scheme and converts them all to the current numbering */
3757 PetscErrorCode DMPlexConvertOldOrientations_Internal(DM dm)
3758 {
3759   PetscInt pStart, pEnd, p;
3760 
3761   PetscFunctionBegin;
3762   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3763   for (p = pStart; p < pEnd; ++p) {
3764     const PetscInt *cone, *ornt;
3765     PetscInt        coneSize, c;
3766 
3767     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
3768     PetscCall(DMPlexGetCone(dm, p, &cone));
3769     PetscCall(DMPlexGetConeOrientation(dm, p, &ornt));
3770     for (c = 0; c < coneSize; ++c) {
3771       DMPolytopeType ct;
3772       const PetscInt o = ornt[c];
3773 
3774       PetscCall(DMPlexGetCellType(dm, cone[c], &ct));
3775       switch (ct) {
3776       case DM_POLYTOPE_SEGMENT:
3777         if ((o == -2) || (o == 1)) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3778         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, 0));
3779         break;
3780       case DM_POLYTOPE_TRIANGLE:
3781         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3782         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3783         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3784         break;
3785       case DM_POLYTOPE_QUADRILATERAL:
3786         if (o == -4) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3787         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3788         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -4));
3789         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3790         break;
3791       default:
3792         break;
3793       }
3794     }
3795   }
3796   PetscFunctionReturn(PETSC_SUCCESS);
3797 }
3798 
3799 static inline PetscErrorCode DMPlexGetTransitiveClosure_Hot_Private(DM dm, PetscInt p, PetscBool useCone, PetscInt *size, const PetscInt *arr[], const PetscInt *ornt[])
3800 {
3801   DM_Plex *mesh = (DM_Plex *)dm->data;
3802 
3803   PetscFunctionBeginHot;
3804   if (PetscDefined(USE_DEBUG) || mesh->tr) {
3805     if (useCone) {
3806       PetscCall(DMPlexGetConeSize(dm, p, size));
3807       PetscCall(DMPlexGetOrientedCone(dm, p, arr, ornt));
3808     } else {
3809       PetscCall(DMPlexGetSupportSize(dm, p, size));
3810       PetscCall(DMPlexGetSupport(dm, p, arr));
3811     }
3812   } else {
3813     if (useCone) {
3814       const PetscSection s   = mesh->coneSection;
3815       const PetscInt     ps  = p - s->pStart;
3816       const PetscInt     off = s->atlasOff[ps];
3817 
3818       *size = s->atlasDof[ps];
3819       *arr  = mesh->cones + off;
3820       *ornt = mesh->coneOrientations + off;
3821     } else {
3822       const PetscSection s   = mesh->supportSection;
3823       const PetscInt     ps  = p - s->pStart;
3824       const PetscInt     off = s->atlasOff[ps];
3825 
3826       *size = s->atlasDof[ps];
3827       *arr  = mesh->supports + off;
3828     }
3829   }
3830   PetscFunctionReturn(PETSC_SUCCESS);
3831 }
3832 
3833 static inline PetscErrorCode DMPlexRestoreTransitiveClosure_Hot_Private(DM dm, PetscInt p, PetscBool useCone, PetscInt *size, const PetscInt *arr[], const PetscInt *ornt[])
3834 {
3835   DM_Plex *mesh = (DM_Plex *)dm->data;
3836 
3837   PetscFunctionBeginHot;
3838   if (PetscDefined(USE_DEBUG) || mesh->tr) {
3839     if (useCone) PetscCall(DMPlexRestoreOrientedCone(dm, p, arr, ornt));
3840   }
3841   PetscFunctionReturn(PETSC_SUCCESS);
3842 }
3843 
3844 static PetscErrorCode DMPlexGetTransitiveClosure_Depth1_Private(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3845 {
3846   DMPolytopeType  ct = DM_POLYTOPE_UNKNOWN;
3847   PetscInt       *closure;
3848   const PetscInt *tmp = NULL, *tmpO = NULL;
3849   PetscInt        off = 0, tmpSize, t;
3850 
3851   PetscFunctionBeginHot;
3852   if (ornt) {
3853     PetscCall(DMPlexGetCellType(dm, p, &ct));
3854     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;
3855   }
3856   if (*points) {
3857     closure = *points;
3858   } else {
3859     PetscInt maxConeSize, maxSupportSize;
3860     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3861     PetscCall(DMGetWorkArray(dm, 2 * (PetscMax(maxConeSize, maxSupportSize) + 1), MPIU_INT, &closure));
3862   }
3863   PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, p, useCone, &tmpSize, &tmp, &tmpO));
3864   if (ct == DM_POLYTOPE_UNKNOWN) {
3865     closure[off++] = p;
3866     closure[off++] = 0;
3867     for (t = 0; t < tmpSize; ++t) {
3868       closure[off++] = tmp[t];
3869       closure[off++] = tmpO ? tmpO[t] : 0;
3870     }
3871   } else {
3872     const PetscInt *arr = DMPolytopeTypeGetArrangement(ct, ornt);
3873 
3874     /* We assume that cells with a valid type have faces with a valid type */
3875     closure[off++] = p;
3876     closure[off++] = ornt;
3877     for (t = 0; t < tmpSize; ++t) {
3878       DMPolytopeType ft;
3879 
3880       PetscCall(DMPlexGetCellType(dm, tmp[t], &ft));
3881       closure[off++] = tmp[arr[t]];
3882       closure[off++] = tmpO ? DMPolytopeTypeComposeOrientation(ft, ornt, tmpO[t]) : 0;
3883     }
3884   }
3885   PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, p, useCone, &tmpSize, &tmp, &tmpO));
3886   if (numPoints) *numPoints = tmpSize + 1;
3887   if (points) *points = closure;
3888   PetscFunctionReturn(PETSC_SUCCESS);
3889 }
3890 
3891 /* We need a special tensor version because we want to allow duplicate points in the endcaps for hybrid cells */
3892 static PetscErrorCode DMPlexTransitiveClosure_Tensor_Internal(DM dm, PetscInt point, DMPolytopeType ct, PetscInt o, PetscBool useCone, PetscInt *numPoints, PetscInt **points)
3893 {
3894   const PetscInt *arr = DMPolytopeTypeGetArrangement(ct, o);
3895   const PetscInt *cone, *ornt;
3896   PetscInt       *pts, *closure = NULL;
3897   DMPolytopeType  ft;
3898   PetscInt        maxConeSize, maxSupportSize, coneSeries, supportSeries, maxSize;
3899   PetscInt        dim, coneSize, c, d, clSize, cl;
3900 
3901   PetscFunctionBeginHot;
3902   PetscCall(DMGetDimension(dm, &dim));
3903   PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, point, PETSC_TRUE, &coneSize, &cone, &ornt));
3904   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3905   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, dim + 1) - 1) / (maxConeSize - 1)) : dim + 1;
3906   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, dim + 1) - 1) / (maxSupportSize - 1)) : dim + 1;
3907   maxSize       = PetscMax(coneSeries, supportSeries);
3908   if (*points) {
3909     pts = *points;
3910   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &pts));
3911   c        = 0;
3912   pts[c++] = point;
3913   pts[c++] = o;
3914   PetscCall(DMPlexGetCellType(dm, cone[arr[0 * 2 + 0]], &ft));
3915   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[0 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[0 * 2 + 1], ornt[0]), useCone, &clSize, &closure));
3916   for (cl = 0; cl < clSize * 2; cl += 2) {
3917     pts[c++] = closure[cl];
3918     pts[c++] = closure[cl + 1];
3919   }
3920   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[1 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[1 * 2 + 1], ornt[1]), useCone, &clSize, &closure));
3921   for (cl = 0; cl < clSize * 2; cl += 2) {
3922     pts[c++] = closure[cl];
3923     pts[c++] = closure[cl + 1];
3924   }
3925   PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[0], useCone, &clSize, &closure));
3926   for (d = 2; d < coneSize; ++d) {
3927     PetscCall(DMPlexGetCellType(dm, cone[arr[d * 2 + 0]], &ft));
3928     pts[c++] = cone[arr[d * 2 + 0]];
3929     pts[c++] = DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]);
3930   }
3931   PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, point, PETSC_TRUE, &coneSize, &cone, &ornt));
3932   if (dim >= 3) {
3933     for (d = 2; d < coneSize; ++d) {
3934       const PetscInt  fpoint = cone[arr[d * 2 + 0]];
3935       const PetscInt *fcone, *fornt;
3936       PetscInt        fconeSize, fc, i;
3937 
3938       PetscCall(DMPlexGetCellType(dm, fpoint, &ft));
3939       const PetscInt *farr = DMPolytopeTypeGetArrangement(ft, DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]));
3940       PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, fpoint, PETSC_TRUE, &fconeSize, &fcone, &fornt));
3941       for (fc = 0; fc < fconeSize; ++fc) {
3942         const PetscInt cp = fcone[farr[fc * 2 + 0]];
3943         const PetscInt co = farr[fc * 2 + 1];
3944 
3945         for (i = 0; i < c; i += 2)
3946           if (pts[i] == cp) break;
3947         if (i == c) {
3948           PetscCall(DMPlexGetCellType(dm, cp, &ft));
3949           pts[c++] = cp;
3950           pts[c++] = DMPolytopeTypeComposeOrientation(ft, co, fornt[farr[fc * 2 + 0]]);
3951         }
3952       }
3953       PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, fpoint, PETSC_TRUE, &fconeSize, &fcone, &fornt));
3954     }
3955   }
3956   *numPoints = c / 2;
3957   *points    = pts;
3958   PetscFunctionReturn(PETSC_SUCCESS);
3959 }
3960 
3961 PetscErrorCode DMPlexGetTransitiveClosure_Internal(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3962 {
3963   DMPolytopeType ct;
3964   PetscInt      *closure, *fifo;
3965   PetscInt       closureSize = 0, fifoStart = 0, fifoSize = 0;
3966   PetscInt       maxConeSize, maxSupportSize, coneSeries, supportSeries;
3967   PetscInt       depth, maxSize;
3968 
3969   PetscFunctionBeginHot;
3970   PetscCall(DMPlexGetDepth(dm, &depth));
3971   if (depth == 1) {
3972     PetscCall(DMPlexGetTransitiveClosure_Depth1_Private(dm, p, ornt, useCone, numPoints, points));
3973     PetscFunctionReturn(PETSC_SUCCESS);
3974   }
3975   PetscCall(DMPlexGetCellType(dm, p, &ct));
3976   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;
3977   if (DMPolytopeTypeIsHybrid(ct) && ct != DM_POLYTOPE_POINT_PRISM_TENSOR) {
3978     PetscCall(DMPlexTransitiveClosure_Tensor_Internal(dm, p, ct, ornt, useCone, numPoints, points));
3979     PetscFunctionReturn(PETSC_SUCCESS);
3980   }
3981   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3982   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, depth + 1) - 1) / (maxConeSize - 1)) : depth + 1;
3983   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, depth + 1) - 1) / (maxSupportSize - 1)) : depth + 1;
3984   maxSize       = PetscMax(coneSeries, supportSeries);
3985   PetscCall(DMGetWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
3986   if (*points) {
3987     closure = *points;
3988   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &closure));
3989   closure[closureSize++] = p;
3990   closure[closureSize++] = ornt;
3991   fifo[fifoSize++]       = p;
3992   fifo[fifoSize++]       = ornt;
3993   fifo[fifoSize++]       = ct;
3994   /* Should kick out early when depth is reached, rather than checking all vertices for empty cones */
3995   while (fifoSize - fifoStart) {
3996     const PetscInt       q    = fifo[fifoStart++];
3997     const PetscInt       o    = fifo[fifoStart++];
3998     const DMPolytopeType qt   = (DMPolytopeType)fifo[fifoStart++];
3999     const PetscInt      *qarr = DMPolytopeTypeGetArrangement(qt, o);
4000     const PetscInt      *tmp, *tmpO = NULL;
4001     PetscInt             tmpSize, t;
4002 
4003     if (PetscDefined(USE_DEBUG)) {
4004       PetscInt nO = DMPolytopeTypeGetNumArrangements(qt) / 2;
4005       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);
4006     }
4007     PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, q, useCone, &tmpSize, &tmp, &tmpO));
4008     for (t = 0; t < tmpSize; ++t) {
4009       const PetscInt ip = useCone && qarr ? qarr[t * 2] : t;
4010       const PetscInt io = useCone && qarr ? qarr[t * 2 + 1] : 0;
4011       const PetscInt cp = tmp[ip];
4012       PetscCall(DMPlexGetCellType(dm, cp, &ct));
4013       const PetscInt co = tmpO ? DMPolytopeTypeComposeOrientation(ct, io, tmpO[ip]) : 0;
4014       PetscInt       c;
4015 
4016       /* Check for duplicate */
4017       for (c = 0; c < closureSize; c += 2) {
4018         if (closure[c] == cp) break;
4019       }
4020       if (c == closureSize) {
4021         closure[closureSize++] = cp;
4022         closure[closureSize++] = co;
4023         fifo[fifoSize++]       = cp;
4024         fifo[fifoSize++]       = co;
4025         fifo[fifoSize++]       = ct;
4026       }
4027     }
4028     PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, q, useCone, &tmpSize, &tmp, &tmpO));
4029   }
4030   PetscCall(DMRestoreWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
4031   if (numPoints) *numPoints = closureSize / 2;
4032   if (points) *points = closure;
4033   PetscFunctionReturn(PETSC_SUCCESS);
4034 }
4035 
4036 /*@C
4037   DMPlexGetTransitiveClosure - Return the points on the transitive closure of the in-edges or out-edges for this point in the DAG
4038 
4039   Not Collective
4040 
4041   Input Parameters:
4042 + dm      - The `DMPLEX`
4043 . p       - The mesh point
4044 - useCone - `PETSC_TRUE` for the closure, otherwise return the star
4045 
4046   Input/Output Parameter:
4047 . points - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...];
4048            if `NULL` on input, internal storage will be returned, otherwise the provided array is used
4049 
4050   Output Parameter:
4051 . numPoints - The number of points in the closure, so points[] is of size 2*`numPoints`
4052 
4053   Level: beginner
4054 
4055   Note:
4056   If using internal storage (points is `NULL` on input), each call overwrites the last output.
4057 
4058   Fortran Notes:
4059   The `numPoints` argument is not present in the Fortran binding since it is internal to the array.
4060 
4061 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
4062 @*/
4063 PetscErrorCode DMPlexGetTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
4064 {
4065   PetscFunctionBeginHot;
4066   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4067   if (numPoints) PetscAssertPointer(numPoints, 4);
4068   if (points) PetscAssertPointer(points, 5);
4069   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, p, 0, useCone, numPoints, points));
4070   PetscFunctionReturn(PETSC_SUCCESS);
4071 }
4072 
4073 /*@C
4074   DMPlexRestoreTransitiveClosure - Restore the array of points on the transitive closure of the in-edges or out-edges for this point in the DAG
4075 
4076   Not Collective
4077 
4078   Input Parameters:
4079 + dm        - The `DMPLEX`
4080 . p         - The mesh point
4081 . useCone   - `PETSC_TRUE` for the closure, otherwise return the star
4082 . numPoints - The number of points in the closure, so points[] is of size 2*`numPoints`
4083 - points    - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...]
4084 
4085   Level: beginner
4086 
4087   Note:
4088   If not using internal storage (points is not `NULL` on input), this call is unnecessary
4089 
4090 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
4091 @*/
4092 PetscErrorCode DMPlexRestoreTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
4093 {
4094   PetscFunctionBeginHot;
4095   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4096   if (numPoints) *numPoints = 0;
4097   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, points));
4098   PetscFunctionReturn(PETSC_SUCCESS);
4099 }
4100 
4101 /*@
4102   DMPlexGetMaxSizes - Return the maximum number of in-edges (cone) and out-edges (support) for any point in the DAG
4103 
4104   Not Collective
4105 
4106   Input Parameter:
4107 . dm - The `DMPLEX`
4108 
4109   Output Parameters:
4110 + maxConeSize    - The maximum number of in-edges
4111 - maxSupportSize - The maximum number of out-edges
4112 
4113   Level: beginner
4114 
4115 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
4116 @*/
4117 PetscErrorCode DMPlexGetMaxSizes(DM dm, PetscInt *maxConeSize, PetscInt *maxSupportSize)
4118 {
4119   DM_Plex *mesh = (DM_Plex *)dm->data;
4120 
4121   PetscFunctionBegin;
4122   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4123   if (maxConeSize) PetscCall(PetscSectionGetMaxDof(mesh->coneSection, maxConeSize));
4124   if (maxSupportSize) PetscCall(PetscSectionGetMaxDof(mesh->supportSection, maxSupportSize));
4125   PetscFunctionReturn(PETSC_SUCCESS);
4126 }
4127 
4128 PetscErrorCode DMSetUp_Plex(DM dm)
4129 {
4130   DM_Plex *mesh = (DM_Plex *)dm->data;
4131   PetscInt size, maxSupportSize;
4132 
4133   PetscFunctionBegin;
4134   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4135   PetscCall(PetscSectionSetUp(mesh->coneSection));
4136   PetscCall(PetscSectionGetStorageSize(mesh->coneSection, &size));
4137   PetscCall(PetscMalloc1(size, &mesh->cones));
4138   PetscCall(PetscCalloc1(size, &mesh->coneOrientations));
4139   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
4140   if (maxSupportSize) {
4141     PetscCall(PetscSectionSetUp(mesh->supportSection));
4142     PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &size));
4143     PetscCall(PetscMalloc1(size, &mesh->supports));
4144   }
4145   PetscFunctionReturn(PETSC_SUCCESS);
4146 }
4147 
4148 PetscErrorCode DMCreateSubDM_Plex(DM dm, PetscInt numFields, const PetscInt fields[], IS *is, DM *subdm)
4149 {
4150   PetscFunctionBegin;
4151   if (subdm) PetscCall(DMClone(dm, subdm));
4152   PetscCall(DMCreateSectionSubDM(dm, numFields, fields, is, subdm));
4153   if (subdm) (*subdm)->useNatural = dm->useNatural;
4154   if (dm->useNatural && dm->sfMigration) {
4155     PetscSF sfNatural;
4156 
4157     (*subdm)->sfMigration = dm->sfMigration;
4158     PetscCall(PetscObjectReference((PetscObject)dm->sfMigration));
4159     PetscCall(DMPlexCreateGlobalToNaturalSF(*subdm, NULL, (*subdm)->sfMigration, &sfNatural));
4160     (*subdm)->sfNatural = sfNatural;
4161   }
4162   PetscFunctionReturn(PETSC_SUCCESS);
4163 }
4164 
4165 PetscErrorCode DMCreateSuperDM_Plex(DM dms[], PetscInt len, IS **is, DM *superdm)
4166 {
4167   PetscInt i = 0;
4168 
4169   PetscFunctionBegin;
4170   PetscCall(DMClone(dms[0], superdm));
4171   PetscCall(DMCreateSectionSuperDM(dms, len, is, superdm));
4172   (*superdm)->useNatural = PETSC_FALSE;
4173   for (i = 0; i < len; i++) {
4174     if (dms[i]->useNatural && dms[i]->sfMigration) {
4175       PetscSF sfNatural;
4176 
4177       (*superdm)->sfMigration = dms[i]->sfMigration;
4178       PetscCall(PetscObjectReference((PetscObject)dms[i]->sfMigration));
4179       (*superdm)->useNatural = PETSC_TRUE;
4180       PetscCall(DMPlexCreateGlobalToNaturalSF(*superdm, NULL, (*superdm)->sfMigration, &sfNatural));
4181       (*superdm)->sfNatural = sfNatural;
4182       break;
4183     }
4184   }
4185   PetscFunctionReturn(PETSC_SUCCESS);
4186 }
4187 
4188 /*@
4189   DMPlexSymmetrize - Create support (out-edge) information from cone (in-edge) information
4190 
4191   Not Collective
4192 
4193   Input Parameter:
4194 . dm - The `DMPLEX`
4195 
4196   Level: beginner
4197 
4198   Note:
4199   This should be called after all calls to `DMPlexSetCone()`
4200 
4201 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMPlexSetCone()`
4202 @*/
4203 PetscErrorCode DMPlexSymmetrize(DM dm)
4204 {
4205   DM_Plex  *mesh = (DM_Plex *)dm->data;
4206   PetscInt *offsets;
4207   PetscInt  supportSize;
4208   PetscInt  pStart, pEnd, p;
4209 
4210   PetscFunctionBegin;
4211   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4212   PetscCheck(!mesh->supports, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONGSTATE, "Supports were already setup in this DMPlex");
4213   PetscCall(PetscLogEventBegin(DMPLEX_Symmetrize, dm, 0, 0, 0));
4214   /* Calculate support sizes */
4215   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4216   for (p = pStart; p < pEnd; ++p) {
4217     PetscInt dof, off, c;
4218 
4219     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
4220     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
4221     for (c = off; c < off + dof; ++c) PetscCall(PetscSectionAddDof(mesh->supportSection, mesh->cones[c], 1));
4222   }
4223   PetscCall(PetscSectionSetUp(mesh->supportSection));
4224   /* Calculate supports */
4225   PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &supportSize));
4226   PetscCall(PetscMalloc1(supportSize, &mesh->supports));
4227   PetscCall(PetscCalloc1(pEnd - pStart, &offsets));
4228   for (p = pStart; p < pEnd; ++p) {
4229     PetscInt dof, off, c;
4230 
4231     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
4232     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
4233     for (c = off; c < off + dof; ++c) {
4234       const PetscInt q = mesh->cones[c];
4235       PetscInt       offS;
4236 
4237       PetscCall(PetscSectionGetOffset(mesh->supportSection, q, &offS));
4238 
4239       mesh->supports[offS + offsets[q]] = p;
4240       ++offsets[q];
4241     }
4242   }
4243   PetscCall(PetscFree(offsets));
4244   PetscCall(PetscLogEventEnd(DMPLEX_Symmetrize, dm, 0, 0, 0));
4245   PetscFunctionReturn(PETSC_SUCCESS);
4246 }
4247 
4248 static PetscErrorCode DMPlexCreateDepthStratum(DM dm, DMLabel label, PetscInt depth, PetscInt pStart, PetscInt pEnd)
4249 {
4250   IS stratumIS;
4251 
4252   PetscFunctionBegin;
4253   if (pStart >= pEnd) PetscFunctionReturn(PETSC_SUCCESS);
4254   if (PetscDefined(USE_DEBUG)) {
4255     PetscInt  qStart, qEnd, numLevels, level;
4256     PetscBool overlap = PETSC_FALSE;
4257     PetscCall(DMLabelGetNumValues(label, &numLevels));
4258     for (level = 0; level < numLevels; level++) {
4259       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4260       if ((pStart >= qStart && pStart < qEnd) || (pEnd > qStart && pEnd <= qEnd)) {
4261         overlap = PETSC_TRUE;
4262         break;
4263       }
4264     }
4265     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);
4266   }
4267   PetscCall(ISCreateStride(PETSC_COMM_SELF, pEnd - pStart, pStart, 1, &stratumIS));
4268   PetscCall(DMLabelSetStratumIS(label, depth, stratumIS));
4269   PetscCall(ISDestroy(&stratumIS));
4270   PetscFunctionReturn(PETSC_SUCCESS);
4271 }
4272 
4273 static PetscErrorCode DMPlexStratify_CellType_Private(DM dm, DMLabel label)
4274 {
4275   PetscInt *pMin, *pMax;
4276   PetscInt  pStart, pEnd;
4277   PetscInt  dmin = PETSC_MAX_INT, dmax = PETSC_MIN_INT;
4278 
4279   PetscFunctionBegin;
4280   {
4281     DMLabel label2;
4282 
4283     PetscCall(DMPlexGetCellTypeLabel(dm, &label2));
4284     PetscCall(PetscObjectViewFromOptions((PetscObject)label2, NULL, "-ct_view"));
4285   }
4286   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4287   for (PetscInt p = pStart; p < pEnd; ++p) {
4288     DMPolytopeType ct;
4289 
4290     PetscCall(DMPlexGetCellType(dm, p, &ct));
4291     dmin = PetscMin(DMPolytopeTypeGetDim(ct), dmin);
4292     dmax = PetscMax(DMPolytopeTypeGetDim(ct), dmax);
4293   }
4294   PetscCall(PetscMalloc2(dmax + 1, &pMin, dmax + 1, &pMax));
4295   for (PetscInt d = dmin; d <= dmax; ++d) {
4296     pMin[d] = PETSC_MAX_INT;
4297     pMax[d] = PETSC_MIN_INT;
4298   }
4299   for (PetscInt p = pStart; p < pEnd; ++p) {
4300     DMPolytopeType ct;
4301     PetscInt       d;
4302 
4303     PetscCall(DMPlexGetCellType(dm, p, &ct));
4304     d       = DMPolytopeTypeGetDim(ct);
4305     pMin[d] = PetscMin(p, pMin[d]);
4306     pMax[d] = PetscMax(p, pMax[d]);
4307   }
4308   for (PetscInt d = dmin; d <= dmax; ++d) {
4309     if (pMin[d] > pMax[d]) continue;
4310     PetscCall(DMPlexCreateDepthStratum(dm, label, d, pMin[d], pMax[d] + 1));
4311   }
4312   PetscCall(PetscFree2(pMin, pMax));
4313   PetscFunctionReturn(PETSC_SUCCESS);
4314 }
4315 
4316 static PetscErrorCode DMPlexStratify_Topological_Private(DM dm, DMLabel label)
4317 {
4318   PetscInt pStart, pEnd;
4319   PetscInt numRoots = 0, numLeaves = 0;
4320 
4321   PetscFunctionBegin;
4322   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4323   {
4324     /* Initialize roots and count leaves */
4325     PetscInt sMin = PETSC_MAX_INT;
4326     PetscInt sMax = PETSC_MIN_INT;
4327     PetscInt coneSize, supportSize;
4328 
4329     for (PetscInt p = pStart; p < pEnd; ++p) {
4330       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4331       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
4332       if (!coneSize && supportSize) {
4333         sMin = PetscMin(p, sMin);
4334         sMax = PetscMax(p, sMax);
4335         ++numRoots;
4336       } else if (!supportSize && coneSize) {
4337         ++numLeaves;
4338       } else if (!supportSize && !coneSize) {
4339         /* Isolated points */
4340         sMin = PetscMin(p, sMin);
4341         sMax = PetscMax(p, sMax);
4342       }
4343     }
4344     PetscCall(DMPlexCreateDepthStratum(dm, label, 0, sMin, sMax + 1));
4345   }
4346 
4347   if (numRoots + numLeaves == (pEnd - pStart)) {
4348     PetscInt sMin = PETSC_MAX_INT;
4349     PetscInt sMax = PETSC_MIN_INT;
4350     PetscInt coneSize, supportSize;
4351 
4352     for (PetscInt p = pStart; p < pEnd; ++p) {
4353       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4354       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
4355       if (!supportSize && coneSize) {
4356         sMin = PetscMin(p, sMin);
4357         sMax = PetscMax(p, sMax);
4358       }
4359     }
4360     PetscCall(DMPlexCreateDepthStratum(dm, label, 1, sMin, sMax + 1));
4361   } else {
4362     PetscInt level = 0;
4363     PetscInt qStart, qEnd;
4364 
4365     PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4366     while (qEnd > qStart) {
4367       PetscInt sMin = PETSC_MAX_INT;
4368       PetscInt sMax = PETSC_MIN_INT;
4369 
4370       for (PetscInt q = qStart; q < qEnd; ++q) {
4371         const PetscInt *support;
4372         PetscInt        supportSize;
4373 
4374         PetscCall(DMPlexGetSupportSize(dm, q, &supportSize));
4375         PetscCall(DMPlexGetSupport(dm, q, &support));
4376         for (PetscInt s = 0; s < supportSize; ++s) {
4377           sMin = PetscMin(support[s], sMin);
4378           sMax = PetscMax(support[s], sMax);
4379         }
4380       }
4381       PetscCall(DMLabelGetNumValues(label, &level));
4382       PetscCall(DMPlexCreateDepthStratum(dm, label, level, sMin, sMax + 1));
4383       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4384     }
4385   }
4386   PetscFunctionReturn(PETSC_SUCCESS);
4387 }
4388 
4389 /*@
4390   DMPlexStratify - Computes the strata for all points in the `DMPLEX`
4391 
4392   Collective
4393 
4394   Input Parameter:
4395 . dm - The `DMPLEX`
4396 
4397   Level: beginner
4398 
4399   Notes:
4400   The strata group all points of the same grade, and this function calculates the strata. This
4401   grade can be seen as the height (or depth) of the point in the DAG.
4402 
4403   The DAG for most topologies is a graded poset (https://en.wikipedia.org/wiki/Graded_poset), and
4404   can be illustrated by a Hasse Diagram (https://en.wikipedia.org/wiki/Hasse_diagram).
4405   Concretely, `DMPlexStratify()` creates a new label named "depth" containing the depth in the DAG of each point. For cell-vertex
4406   meshes, vertices are depth 0 and cells are depth 1. For fully interpolated meshes, depth 0 for vertices, 1 for edges, and so on
4407   until cells have depth equal to the dimension of the mesh. The depth label can be accessed through `DMPlexGetDepthLabel()` or `DMPlexGetDepthStratum()`, or
4408   manually via `DMGetLabel()`.  The height is defined implicitly by height = maxDimension - depth, and can be accessed
4409   via `DMPlexGetHeightStratum()`.  For example, cells have height 0 and faces have height 1.
4410 
4411   The depth of a point is calculated by executing a breadth-first search (BFS) on the DAG. This could produce surprising results
4412   if run on a partially interpolated mesh, meaning one that had some edges and faces, but not others. For example, suppose that
4413   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
4414   to interpolate only that one (e0), so that
4415 .vb
4416   cone(c0) = {e0, v2}
4417   cone(e0) = {v0, v1}
4418 .ve
4419   If `DMPlexStratify()` is run on this mesh, it will give depths
4420 .vb
4421    depth 0 = {v0, v1, v2}
4422    depth 1 = {e0, c0}
4423 .ve
4424   where the triangle has been given depth 1, instead of 2, because it is reachable from vertex v2.
4425 
4426   `DMPlexStratify()` should be called after all calls to `DMPlexSymmetrize()`
4427 
4428 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexComputeCellTypes()`
4429 @*/
4430 PetscErrorCode DMPlexStratify(DM dm)
4431 {
4432   DM_Plex  *mesh = (DM_Plex *)dm->data;
4433   DMLabel   label;
4434   PetscBool flg = PETSC_FALSE;
4435 
4436   PetscFunctionBegin;
4437   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4438   PetscCall(PetscLogEventBegin(DMPLEX_Stratify, dm, 0, 0, 0));
4439 
4440   // Create depth label
4441   PetscCall(DMCreateLabel(dm, "depth"));
4442   PetscCall(DMPlexGetDepthLabel(dm, &label));
4443 
4444   PetscCall(PetscOptionsGetBool(NULL, dm->hdr.prefix, "-dm_plex_stratify_celltype", &flg, NULL));
4445   if (flg) PetscCall(DMPlexStratify_CellType_Private(dm, label));
4446   else PetscCall(DMPlexStratify_Topological_Private(dm, label));
4447 
4448   { /* just in case there is an empty process */
4449     PetscInt numValues, maxValues = 0, v;
4450 
4451     PetscCall(DMLabelGetNumValues(label, &numValues));
4452     PetscCall(MPIU_Allreduce(&numValues, &maxValues, 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
4453     for (v = numValues; v < maxValues; v++) PetscCall(DMLabelAddStratum(label, v));
4454   }
4455   PetscCall(PetscObjectStateGet((PetscObject)label, &mesh->depthState));
4456   PetscCall(PetscLogEventEnd(DMPLEX_Stratify, dm, 0, 0, 0));
4457   PetscFunctionReturn(PETSC_SUCCESS);
4458 }
4459 
4460 PetscErrorCode DMPlexComputeCellType_Internal(DM dm, PetscInt p, PetscInt pdepth, DMPolytopeType *pt)
4461 {
4462   DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4463   PetscInt       dim, depth, pheight, coneSize;
4464 
4465   PetscFunctionBeginHot;
4466   PetscCall(DMGetDimension(dm, &dim));
4467   PetscCall(DMPlexGetDepth(dm, &depth));
4468   PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4469   pheight = depth - pdepth;
4470   if (depth <= 1) {
4471     switch (pdepth) {
4472     case 0:
4473       ct = DM_POLYTOPE_POINT;
4474       break;
4475     case 1:
4476       switch (coneSize) {
4477       case 2:
4478         ct = DM_POLYTOPE_SEGMENT;
4479         break;
4480       case 3:
4481         ct = DM_POLYTOPE_TRIANGLE;
4482         break;
4483       case 4:
4484         switch (dim) {
4485         case 2:
4486           ct = DM_POLYTOPE_QUADRILATERAL;
4487           break;
4488         case 3:
4489           ct = DM_POLYTOPE_TETRAHEDRON;
4490           break;
4491         default:
4492           break;
4493         }
4494         break;
4495       case 5:
4496         ct = DM_POLYTOPE_PYRAMID;
4497         break;
4498       case 6:
4499         ct = DM_POLYTOPE_TRI_PRISM_TENSOR;
4500         break;
4501       case 8:
4502         ct = DM_POLYTOPE_HEXAHEDRON;
4503         break;
4504       default:
4505         break;
4506       }
4507     }
4508   } else {
4509     if (pdepth == 0) {
4510       ct = DM_POLYTOPE_POINT;
4511     } else if (pheight == 0) {
4512       switch (dim) {
4513       case 1:
4514         switch (coneSize) {
4515         case 2:
4516           ct = DM_POLYTOPE_SEGMENT;
4517           break;
4518         default:
4519           break;
4520         }
4521         break;
4522       case 2:
4523         switch (coneSize) {
4524         case 3:
4525           ct = DM_POLYTOPE_TRIANGLE;
4526           break;
4527         case 4:
4528           ct = DM_POLYTOPE_QUADRILATERAL;
4529           break;
4530         default:
4531           break;
4532         }
4533         break;
4534       case 3:
4535         switch (coneSize) {
4536         case 4:
4537           ct = DM_POLYTOPE_TETRAHEDRON;
4538           break;
4539         case 5: {
4540           const PetscInt *cone;
4541           PetscInt        faceConeSize;
4542 
4543           PetscCall(DMPlexGetCone(dm, p, &cone));
4544           PetscCall(DMPlexGetConeSize(dm, cone[0], &faceConeSize));
4545           switch (faceConeSize) {
4546           case 3:
4547             ct = DM_POLYTOPE_TRI_PRISM_TENSOR;
4548             break;
4549           case 4:
4550             ct = DM_POLYTOPE_PYRAMID;
4551             break;
4552           }
4553         } break;
4554         case 6:
4555           ct = DM_POLYTOPE_HEXAHEDRON;
4556           break;
4557         default:
4558           break;
4559         }
4560         break;
4561       default:
4562         break;
4563       }
4564     } else if (pheight > 0) {
4565       switch (coneSize) {
4566       case 2:
4567         ct = DM_POLYTOPE_SEGMENT;
4568         break;
4569       case 3:
4570         ct = DM_POLYTOPE_TRIANGLE;
4571         break;
4572       case 4:
4573         ct = DM_POLYTOPE_QUADRILATERAL;
4574         break;
4575       default:
4576         break;
4577       }
4578     }
4579   }
4580   *pt = ct;
4581   PetscFunctionReturn(PETSC_SUCCESS);
4582 }
4583 
4584 /*@
4585   DMPlexComputeCellTypes - Infer the polytope type of every cell using its dimension and cone size.
4586 
4587   Collective
4588 
4589   Input Parameter:
4590 . dm - The `DMPLEX`
4591 
4592   Level: developer
4593 
4594   Note:
4595   This function is normally called automatically when a cell type is requested. It creates an
4596   internal `DMLabel` named "celltype" which can be directly accessed using `DMGetLabel()`. A user may disable
4597   automatic creation by creating the label manually, using `DMCreateLabel`(dm, "celltype").
4598 
4599   `DMPlexComputeCellTypes()` should be called after all calls to `DMPlexSymmetrize()` and `DMPlexStratify()`
4600 
4601 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexStratify()`, `DMGetLabel()`, `DMCreateLabel()`
4602 @*/
4603 PetscErrorCode DMPlexComputeCellTypes(DM dm)
4604 {
4605   DM_Plex *mesh;
4606   DMLabel  ctLabel;
4607   PetscInt pStart, pEnd, p;
4608 
4609   PetscFunctionBegin;
4610   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4611   mesh = (DM_Plex *)dm->data;
4612   PetscCall(DMCreateLabel(dm, "celltype"));
4613   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
4614   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4615   PetscCall(PetscFree(mesh->cellTypes));
4616   PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
4617   for (p = pStart; p < pEnd; ++p) {
4618     DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4619     PetscInt       pdepth;
4620 
4621     PetscCall(DMPlexGetPointDepth(dm, p, &pdepth));
4622     PetscCall(DMPlexComputeCellType_Internal(dm, p, pdepth, &ct));
4623     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);
4624     PetscCall(DMLabelSetValue(ctLabel, p, ct));
4625     mesh->cellTypes[p - pStart].value_as_uint8 = ct;
4626   }
4627   PetscCall(PetscObjectStateGet((PetscObject)ctLabel, &mesh->celltypeState));
4628   PetscCall(PetscObjectViewFromOptions((PetscObject)ctLabel, NULL, "-dm_plex_celltypes_view"));
4629   PetscFunctionReturn(PETSC_SUCCESS);
4630 }
4631 
4632 /*@C
4633   DMPlexGetJoin - Get an array for the join of the set of points
4634 
4635   Not Collective
4636 
4637   Input Parameters:
4638 + dm        - The `DMPLEX` object
4639 . numPoints - The number of input points for the join
4640 - points    - The input points
4641 
4642   Output Parameters:
4643 + numCoveredPoints - The number of points in the join
4644 - coveredPoints    - The points in the join
4645 
4646   Level: intermediate
4647 
4648   Note:
4649   Currently, this is restricted to a single level join
4650 
4651   Fortran Notes:
4652   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4653 
4654 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4655 @*/
4656 PetscErrorCode DMPlexGetJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4657 {
4658   DM_Plex  *mesh = (DM_Plex *)dm->data;
4659   PetscInt *join[2];
4660   PetscInt  joinSize, i = 0;
4661   PetscInt  dof, off, p, c, m;
4662   PetscInt  maxSupportSize;
4663 
4664   PetscFunctionBegin;
4665   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4666   PetscAssertPointer(points, 3);
4667   PetscAssertPointer(numCoveredPoints, 4);
4668   PetscAssertPointer(coveredPoints, 5);
4669   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
4670   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[0]));
4671   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[1]));
4672   /* Copy in support of first point */
4673   PetscCall(PetscSectionGetDof(mesh->supportSection, points[0], &dof));
4674   PetscCall(PetscSectionGetOffset(mesh->supportSection, points[0], &off));
4675   for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = mesh->supports[off + joinSize];
4676   /* Check each successive support */
4677   for (p = 1; p < numPoints; ++p) {
4678     PetscInt newJoinSize = 0;
4679 
4680     PetscCall(PetscSectionGetDof(mesh->supportSection, points[p], &dof));
4681     PetscCall(PetscSectionGetOffset(mesh->supportSection, points[p], &off));
4682     for (c = 0; c < dof; ++c) {
4683       const PetscInt point = mesh->supports[off + c];
4684 
4685       for (m = 0; m < joinSize; ++m) {
4686         if (point == join[i][m]) {
4687           join[1 - i][newJoinSize++] = point;
4688           break;
4689         }
4690       }
4691     }
4692     joinSize = newJoinSize;
4693     i        = 1 - i;
4694   }
4695   *numCoveredPoints = joinSize;
4696   *coveredPoints    = join[i];
4697   PetscCall(DMRestoreWorkArray(dm, maxSupportSize, MPIU_INT, &join[1 - i]));
4698   PetscFunctionReturn(PETSC_SUCCESS);
4699 }
4700 
4701 /*@C
4702   DMPlexRestoreJoin - Restore an array for the join of the set of points
4703 
4704   Not Collective
4705 
4706   Input Parameters:
4707 + dm        - The `DMPLEX` object
4708 . numPoints - The number of input points for the join
4709 - points    - The input points
4710 
4711   Output Parameters:
4712 + numCoveredPoints - The number of points in the join
4713 - coveredPoints    - The points in the join
4714 
4715   Level: intermediate
4716 
4717   Fortran Notes:
4718   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4719 
4720 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetJoin()`, `DMPlexGetFullJoin()`, `DMPlexGetMeet()`
4721 @*/
4722 PetscErrorCode DMPlexRestoreJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4723 {
4724   PetscFunctionBegin;
4725   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4726   if (points) PetscAssertPointer(points, 3);
4727   if (numCoveredPoints) PetscAssertPointer(numCoveredPoints, 4);
4728   PetscAssertPointer(coveredPoints, 5);
4729   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
4730   if (numCoveredPoints) *numCoveredPoints = 0;
4731   PetscFunctionReturn(PETSC_SUCCESS);
4732 }
4733 
4734 /*@C
4735   DMPlexGetFullJoin - Get an array for the join of the set of points
4736 
4737   Not Collective
4738 
4739   Input Parameters:
4740 + dm        - The `DMPLEX` object
4741 . numPoints - The number of input points for the join
4742 - points    - The input points
4743 
4744   Output Parameters:
4745 + numCoveredPoints - The number of points in the join
4746 - coveredPoints    - The points in the join
4747 
4748   Level: intermediate
4749 
4750   Fortran Notes:
4751   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4752 
4753 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetJoin()`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4754 @*/
4755 PetscErrorCode DMPlexGetFullJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4756 {
4757   PetscInt *offsets, **closures;
4758   PetscInt *join[2];
4759   PetscInt  depth = 0, maxSize, joinSize = 0, i = 0;
4760   PetscInt  p, d, c, m, ms;
4761 
4762   PetscFunctionBegin;
4763   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4764   PetscAssertPointer(points, 3);
4765   PetscAssertPointer(numCoveredPoints, 4);
4766   PetscAssertPointer(coveredPoints, 5);
4767 
4768   PetscCall(DMPlexGetDepth(dm, &depth));
4769   PetscCall(PetscCalloc1(numPoints, &closures));
4770   PetscCall(DMGetWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
4771   PetscCall(DMPlexGetMaxSizes(dm, NULL, &ms));
4772   maxSize = (ms > 1) ? ((PetscPowInt(ms, depth + 1) - 1) / (ms - 1)) : depth + 1;
4773   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[0]));
4774   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[1]));
4775 
4776   for (p = 0; p < numPoints; ++p) {
4777     PetscInt closureSize;
4778 
4779     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_FALSE, &closureSize, &closures[p]));
4780 
4781     offsets[p * (depth + 2) + 0] = 0;
4782     for (d = 0; d < depth + 1; ++d) {
4783       PetscInt pStart, pEnd, i;
4784 
4785       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
4786       for (i = offsets[p * (depth + 2) + d]; i < closureSize; ++i) {
4787         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
4788           offsets[p * (depth + 2) + d + 1] = i;
4789           break;
4790         }
4791       }
4792       if (i == closureSize) offsets[p * (depth + 2) + d + 1] = i;
4793     }
4794     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);
4795   }
4796   for (d = 0; d < depth + 1; ++d) {
4797     PetscInt dof;
4798 
4799     /* Copy in support of first point */
4800     dof = offsets[d + 1] - offsets[d];
4801     for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = closures[0][(offsets[d] + joinSize) * 2];
4802     /* Check each successive cone */
4803     for (p = 1; p < numPoints && joinSize; ++p) {
4804       PetscInt newJoinSize = 0;
4805 
4806       dof = offsets[p * (depth + 2) + d + 1] - offsets[p * (depth + 2) + d];
4807       for (c = 0; c < dof; ++c) {
4808         const PetscInt point = closures[p][(offsets[p * (depth + 2) + d] + c) * 2];
4809 
4810         for (m = 0; m < joinSize; ++m) {
4811           if (point == join[i][m]) {
4812             join[1 - i][newJoinSize++] = point;
4813             break;
4814           }
4815         }
4816       }
4817       joinSize = newJoinSize;
4818       i        = 1 - i;
4819     }
4820     if (joinSize) break;
4821   }
4822   *numCoveredPoints = joinSize;
4823   *coveredPoints    = join[i];
4824   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_FALSE, NULL, &closures[p]));
4825   PetscCall(PetscFree(closures));
4826   PetscCall(DMRestoreWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
4827   PetscCall(DMRestoreWorkArray(dm, ms, MPIU_INT, &join[1 - i]));
4828   PetscFunctionReturn(PETSC_SUCCESS);
4829 }
4830 
4831 /*@C
4832   DMPlexGetMeet - Get an array for the meet of the set of points
4833 
4834   Not Collective
4835 
4836   Input Parameters:
4837 + dm        - The `DMPLEX` object
4838 . numPoints - The number of input points for the meet
4839 - points    - The input points
4840 
4841   Output Parameters:
4842 + numCoveringPoints - The number of points in the meet
4843 - coveringPoints    - The points in the meet
4844 
4845   Level: intermediate
4846 
4847   Note:
4848   Currently, this is restricted to a single level meet
4849 
4850   Fortran Notes:
4851   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4852 
4853 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
4854 @*/
4855 PetscErrorCode DMPlexGetMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveringPoints, const PetscInt **coveringPoints)
4856 {
4857   DM_Plex  *mesh = (DM_Plex *)dm->data;
4858   PetscInt *meet[2];
4859   PetscInt  meetSize, i = 0;
4860   PetscInt  dof, off, p, c, m;
4861   PetscInt  maxConeSize;
4862 
4863   PetscFunctionBegin;
4864   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4865   PetscAssertPointer(points, 3);
4866   PetscAssertPointer(numCoveringPoints, 4);
4867   PetscAssertPointer(coveringPoints, 5);
4868   PetscCall(PetscSectionGetMaxDof(mesh->coneSection, &maxConeSize));
4869   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[0]));
4870   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[1]));
4871   /* Copy in cone of first point */
4872   PetscCall(PetscSectionGetDof(mesh->coneSection, points[0], &dof));
4873   PetscCall(PetscSectionGetOffset(mesh->coneSection, points[0], &off));
4874   for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = mesh->cones[off + meetSize];
4875   /* Check each successive cone */
4876   for (p = 1; p < numPoints; ++p) {
4877     PetscInt newMeetSize = 0;
4878 
4879     PetscCall(PetscSectionGetDof(mesh->coneSection, points[p], &dof));
4880     PetscCall(PetscSectionGetOffset(mesh->coneSection, points[p], &off));
4881     for (c = 0; c < dof; ++c) {
4882       const PetscInt point = mesh->cones[off + c];
4883 
4884       for (m = 0; m < meetSize; ++m) {
4885         if (point == meet[i][m]) {
4886           meet[1 - i][newMeetSize++] = point;
4887           break;
4888         }
4889       }
4890     }
4891     meetSize = newMeetSize;
4892     i        = 1 - i;
4893   }
4894   *numCoveringPoints = meetSize;
4895   *coveringPoints    = meet[i];
4896   PetscCall(DMRestoreWorkArray(dm, maxConeSize, MPIU_INT, &meet[1 - i]));
4897   PetscFunctionReturn(PETSC_SUCCESS);
4898 }
4899 
4900 /*@C
4901   DMPlexRestoreMeet - Restore an array for the meet of the set of points
4902 
4903   Not Collective
4904 
4905   Input Parameters:
4906 + dm        - The `DMPLEX` object
4907 . numPoints - The number of input points for the meet
4908 - points    - The input points
4909 
4910   Output Parameters:
4911 + numCoveredPoints - The number of points in the meet
4912 - coveredPoints    - The points in the meet
4913 
4914   Level: intermediate
4915 
4916   Fortran Notes:
4917   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4918 
4919 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetMeet()`, `DMPlexGetFullMeet()`, `DMPlexGetJoin()`
4920 @*/
4921 PetscErrorCode DMPlexRestoreMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4922 {
4923   PetscFunctionBegin;
4924   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4925   if (points) PetscAssertPointer(points, 3);
4926   if (numCoveredPoints) PetscAssertPointer(numCoveredPoints, 4);
4927   PetscAssertPointer(coveredPoints, 5);
4928   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
4929   if (numCoveredPoints) *numCoveredPoints = 0;
4930   PetscFunctionReturn(PETSC_SUCCESS);
4931 }
4932 
4933 /*@C
4934   DMPlexGetFullMeet - Get an array for the meet of the set of points
4935 
4936   Not Collective
4937 
4938   Input Parameters:
4939 + dm        - The `DMPLEX` object
4940 . numPoints - The number of input points for the meet
4941 - points    - The input points
4942 
4943   Output Parameters:
4944 + numCoveredPoints - The number of points in the meet
4945 - coveredPoints    - The points in the meet
4946 
4947   Level: intermediate
4948 
4949   Fortran Notes:
4950   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4951 
4952 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetMeet()`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
4953 @*/
4954 PetscErrorCode DMPlexGetFullMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4955 {
4956   PetscInt *offsets, **closures;
4957   PetscInt *meet[2];
4958   PetscInt  height = 0, maxSize, meetSize = 0, i = 0;
4959   PetscInt  p, h, c, m, mc;
4960 
4961   PetscFunctionBegin;
4962   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4963   PetscAssertPointer(points, 3);
4964   PetscAssertPointer(numCoveredPoints, 4);
4965   PetscAssertPointer(coveredPoints, 5);
4966 
4967   PetscCall(DMPlexGetDepth(dm, &height));
4968   PetscCall(PetscMalloc1(numPoints, &closures));
4969   PetscCall(DMGetWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
4970   PetscCall(DMPlexGetMaxSizes(dm, &mc, NULL));
4971   maxSize = (mc > 1) ? ((PetscPowInt(mc, height + 1) - 1) / (mc - 1)) : height + 1;
4972   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[0]));
4973   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[1]));
4974 
4975   for (p = 0; p < numPoints; ++p) {
4976     PetscInt closureSize;
4977 
4978     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_TRUE, &closureSize, &closures[p]));
4979 
4980     offsets[p * (height + 2) + 0] = 0;
4981     for (h = 0; h < height + 1; ++h) {
4982       PetscInt pStart, pEnd, i;
4983 
4984       PetscCall(DMPlexGetHeightStratum(dm, h, &pStart, &pEnd));
4985       for (i = offsets[p * (height + 2) + h]; i < closureSize; ++i) {
4986         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
4987           offsets[p * (height + 2) + h + 1] = i;
4988           break;
4989         }
4990       }
4991       if (i == closureSize) offsets[p * (height + 2) + h + 1] = i;
4992     }
4993     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);
4994   }
4995   for (h = 0; h < height + 1; ++h) {
4996     PetscInt dof;
4997 
4998     /* Copy in cone of first point */
4999     dof = offsets[h + 1] - offsets[h];
5000     for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = closures[0][(offsets[h] + meetSize) * 2];
5001     /* Check each successive cone */
5002     for (p = 1; p < numPoints && meetSize; ++p) {
5003       PetscInt newMeetSize = 0;
5004 
5005       dof = offsets[p * (height + 2) + h + 1] - offsets[p * (height + 2) + h];
5006       for (c = 0; c < dof; ++c) {
5007         const PetscInt point = closures[p][(offsets[p * (height + 2) + h] + c) * 2];
5008 
5009         for (m = 0; m < meetSize; ++m) {
5010           if (point == meet[i][m]) {
5011             meet[1 - i][newMeetSize++] = point;
5012             break;
5013           }
5014         }
5015       }
5016       meetSize = newMeetSize;
5017       i        = 1 - i;
5018     }
5019     if (meetSize) break;
5020   }
5021   *numCoveredPoints = meetSize;
5022   *coveredPoints    = meet[i];
5023   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_TRUE, NULL, &closures[p]));
5024   PetscCall(PetscFree(closures));
5025   PetscCall(DMRestoreWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
5026   PetscCall(DMRestoreWorkArray(dm, mc, MPIU_INT, &meet[1 - i]));
5027   PetscFunctionReturn(PETSC_SUCCESS);
5028 }
5029 
5030 /*@C
5031   DMPlexEqual - Determine if two `DM` have the same topology
5032 
5033   Not Collective
5034 
5035   Input Parameters:
5036 + dmA - A `DMPLEX` object
5037 - dmB - A `DMPLEX` object
5038 
5039   Output Parameter:
5040 . equal - `PETSC_TRUE` if the topologies are identical
5041 
5042   Level: intermediate
5043 
5044   Note:
5045   We are not solving graph isomorphism, so we do not permute.
5046 
5047 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCone()`
5048 @*/
5049 PetscErrorCode DMPlexEqual(DM dmA, DM dmB, PetscBool *equal)
5050 {
5051   PetscInt depth, depthB, pStart, pEnd, pStartB, pEndB, p;
5052 
5053   PetscFunctionBegin;
5054   PetscValidHeaderSpecific(dmA, DM_CLASSID, 1);
5055   PetscValidHeaderSpecific(dmB, DM_CLASSID, 2);
5056   PetscAssertPointer(equal, 3);
5057 
5058   *equal = PETSC_FALSE;
5059   PetscCall(DMPlexGetDepth(dmA, &depth));
5060   PetscCall(DMPlexGetDepth(dmB, &depthB));
5061   if (depth != depthB) PetscFunctionReturn(PETSC_SUCCESS);
5062   PetscCall(DMPlexGetChart(dmA, &pStart, &pEnd));
5063   PetscCall(DMPlexGetChart(dmB, &pStartB, &pEndB));
5064   if ((pStart != pStartB) || (pEnd != pEndB)) PetscFunctionReturn(PETSC_SUCCESS);
5065   for (p = pStart; p < pEnd; ++p) {
5066     const PetscInt *cone, *coneB, *ornt, *orntB, *support, *supportB;
5067     PetscInt        coneSize, coneSizeB, c, supportSize, supportSizeB, s;
5068 
5069     PetscCall(DMPlexGetConeSize(dmA, p, &coneSize));
5070     PetscCall(DMPlexGetCone(dmA, p, &cone));
5071     PetscCall(DMPlexGetConeOrientation(dmA, p, &ornt));
5072     PetscCall(DMPlexGetConeSize(dmB, p, &coneSizeB));
5073     PetscCall(DMPlexGetCone(dmB, p, &coneB));
5074     PetscCall(DMPlexGetConeOrientation(dmB, p, &orntB));
5075     if (coneSize != coneSizeB) PetscFunctionReturn(PETSC_SUCCESS);
5076     for (c = 0; c < coneSize; ++c) {
5077       if (cone[c] != coneB[c]) PetscFunctionReturn(PETSC_SUCCESS);
5078       if (ornt[c] != orntB[c]) PetscFunctionReturn(PETSC_SUCCESS);
5079     }
5080     PetscCall(DMPlexGetSupportSize(dmA, p, &supportSize));
5081     PetscCall(DMPlexGetSupport(dmA, p, &support));
5082     PetscCall(DMPlexGetSupportSize(dmB, p, &supportSizeB));
5083     PetscCall(DMPlexGetSupport(dmB, p, &supportB));
5084     if (supportSize != supportSizeB) PetscFunctionReturn(PETSC_SUCCESS);
5085     for (s = 0; s < supportSize; ++s) {
5086       if (support[s] != supportB[s]) PetscFunctionReturn(PETSC_SUCCESS);
5087     }
5088   }
5089   *equal = PETSC_TRUE;
5090   PetscFunctionReturn(PETSC_SUCCESS);
5091 }
5092 
5093 /*@C
5094   DMPlexGetNumFaceVertices - Returns the number of vertices on a face
5095 
5096   Not Collective
5097 
5098   Input Parameters:
5099 + dm         - The `DMPLEX`
5100 . cellDim    - The cell dimension
5101 - numCorners - The number of vertices on a cell
5102 
5103   Output Parameter:
5104 . numFaceVertices - The number of vertices on a face
5105 
5106   Level: developer
5107 
5108   Note:
5109   Of course this can only work for a restricted set of symmetric shapes
5110 
5111 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCone()`
5112 @*/
5113 PetscErrorCode DMPlexGetNumFaceVertices(DM dm, PetscInt cellDim, PetscInt numCorners, PetscInt *numFaceVertices)
5114 {
5115   MPI_Comm comm;
5116 
5117   PetscFunctionBegin;
5118   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
5119   PetscAssertPointer(numFaceVertices, 4);
5120   switch (cellDim) {
5121   case 0:
5122     *numFaceVertices = 0;
5123     break;
5124   case 1:
5125     *numFaceVertices = 1;
5126     break;
5127   case 2:
5128     switch (numCorners) {
5129     case 3:                 /* triangle */
5130       *numFaceVertices = 2; /* Edge has 2 vertices */
5131       break;
5132     case 4:                 /* quadrilateral */
5133       *numFaceVertices = 2; /* Edge has 2 vertices */
5134       break;
5135     case 6:                 /* quadratic triangle, tri and quad cohesive Lagrange cells */
5136       *numFaceVertices = 3; /* Edge has 3 vertices */
5137       break;
5138     case 9:                 /* quadratic quadrilateral, quadratic quad cohesive Lagrange cells */
5139       *numFaceVertices = 3; /* Edge has 3 vertices */
5140       break;
5141     default:
5142       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
5143     }
5144     break;
5145   case 3:
5146     switch (numCorners) {
5147     case 4:                 /* tetradehdron */
5148       *numFaceVertices = 3; /* Face has 3 vertices */
5149       break;
5150     case 6:                 /* tet cohesive cells */
5151       *numFaceVertices = 4; /* Face has 4 vertices */
5152       break;
5153     case 8:                 /* hexahedron */
5154       *numFaceVertices = 4; /* Face has 4 vertices */
5155       break;
5156     case 9:                 /* tet cohesive Lagrange cells */
5157       *numFaceVertices = 6; /* Face has 6 vertices */
5158       break;
5159     case 10:                /* quadratic tetrahedron */
5160       *numFaceVertices = 6; /* Face has 6 vertices */
5161       break;
5162     case 12:                /* hex cohesive Lagrange cells */
5163       *numFaceVertices = 6; /* Face has 6 vertices */
5164       break;
5165     case 18:                /* quadratic tet cohesive Lagrange cells */
5166       *numFaceVertices = 6; /* Face has 6 vertices */
5167       break;
5168     case 27:                /* quadratic hexahedron, quadratic hex cohesive Lagrange cells */
5169       *numFaceVertices = 9; /* Face has 9 vertices */
5170       break;
5171     default:
5172       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
5173     }
5174     break;
5175   default:
5176     SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid cell dimension %" PetscInt_FMT, cellDim);
5177   }
5178   PetscFunctionReturn(PETSC_SUCCESS);
5179 }
5180 
5181 /*@
5182   DMPlexGetDepthLabel - Get the `DMLabel` recording the depth of each point
5183 
5184   Not Collective
5185 
5186   Input Parameter:
5187 . dm - The `DMPLEX` object
5188 
5189   Output Parameter:
5190 . depthLabel - The `DMLabel` recording point depth
5191 
5192   Level: developer
5193 
5194 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepth()`, `DMPlexGetHeightStratum()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`,
5195 @*/
5196 PetscErrorCode DMPlexGetDepthLabel(DM dm, DMLabel *depthLabel)
5197 {
5198   PetscFunctionBegin;
5199   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5200   PetscAssertPointer(depthLabel, 2);
5201   *depthLabel = dm->depthLabel;
5202   PetscFunctionReturn(PETSC_SUCCESS);
5203 }
5204 
5205 /*@
5206   DMPlexGetDepth - Get the depth of the DAG representing this mesh
5207 
5208   Not Collective
5209 
5210   Input Parameter:
5211 . dm - The `DMPLEX` object
5212 
5213   Output Parameter:
5214 . depth - The number of strata (breadth first levels) in the DAG
5215 
5216   Level: developer
5217 
5218   Notes:
5219   This returns maximum of point depths over all points, i.e. maximum value of the label returned by `DMPlexGetDepthLabel()`.
5220 
5221   The point depth is described more in detail in `DMPlexGetDepthStratum()`.
5222 
5223   An empty mesh gives -1.
5224 
5225 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepthLabel()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`
5226 @*/
5227 PetscErrorCode DMPlexGetDepth(DM dm, PetscInt *depth)
5228 {
5229   DM_Plex *mesh = (DM_Plex *)dm->data;
5230   DMLabel  label;
5231   PetscInt d = 0;
5232 
5233   PetscFunctionBegin;
5234   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5235   PetscAssertPointer(depth, 2);
5236   if (mesh->tr) {
5237     PetscCall(DMPlexTransformGetDepth(mesh->tr, depth));
5238   } else {
5239     PetscCall(DMPlexGetDepthLabel(dm, &label));
5240     if (label) PetscCall(DMLabelGetNumValues(label, &d));
5241     *depth = d - 1;
5242   }
5243   PetscFunctionReturn(PETSC_SUCCESS);
5244 }
5245 
5246 /*@
5247   DMPlexGetDepthStratum - Get the bounds [`start`, `end`) for all points at a certain depth.
5248 
5249   Not Collective
5250 
5251   Input Parameters:
5252 + dm    - The `DMPLEX` object
5253 - depth - The requested depth
5254 
5255   Output Parameters:
5256 + start - The first point at this `depth`
5257 - end   - One beyond the last point at this `depth`
5258 
5259   Level: developer
5260 
5261   Notes:
5262   Depth indexing is related to topological dimension.  Depth stratum 0 contains the lowest topological dimension points,
5263   often "vertices".  If the mesh is "interpolated" (see `DMPlexInterpolate()`), then depth stratum 1 contains the next
5264   higher dimension, e.g., "edges".
5265 
5266 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetHeightStratum()`, `DMPlexGetCellTypeStratum()`, `DMPlexGetDepth()`, `DMPlexGetDepthLabel()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`, `DMPlexInterpolate()`
5267 @*/
5268 PetscErrorCode DMPlexGetDepthStratum(DM dm, PetscInt depth, PetscInt *start, PetscInt *end)
5269 {
5270   DM_Plex *mesh = (DM_Plex *)dm->data;
5271   DMLabel  label;
5272   PetscInt pStart, pEnd;
5273 
5274   PetscFunctionBegin;
5275   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5276   if (start) {
5277     PetscAssertPointer(start, 3);
5278     *start = 0;
5279   }
5280   if (end) {
5281     PetscAssertPointer(end, 4);
5282     *end = 0;
5283   }
5284   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
5285   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
5286   if (depth < 0) {
5287     if (start) *start = pStart;
5288     if (end) *end = pEnd;
5289     PetscFunctionReturn(PETSC_SUCCESS);
5290   }
5291   if (mesh->tr) {
5292     PetscCall(DMPlexTransformGetDepthStratum(mesh->tr, depth, start, end));
5293   } else {
5294     PetscCall(DMPlexGetDepthLabel(dm, &label));
5295     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named depth was found");
5296     PetscCall(DMLabelGetStratumBounds(label, depth, start, end));
5297   }
5298   PetscFunctionReturn(PETSC_SUCCESS);
5299 }
5300 
5301 /*@
5302   DMPlexGetHeightStratum - Get the bounds [`start`, `end`) for all points at a certain height.
5303 
5304   Not Collective
5305 
5306   Input Parameters:
5307 + dm     - The `DMPLEX` object
5308 - height - The requested height
5309 
5310   Output Parameters:
5311 + start - The first point at this `height`
5312 - end   - One beyond the last point at this `height`
5313 
5314   Level: developer
5315 
5316   Notes:
5317   Height indexing is related to topological codimension.  Height stratum 0 contains the highest topological dimension
5318   points, often called "cells" or "elements".  If the mesh is "interpolated" (see `DMPlexInterpolate()`), then height
5319   stratum 1 contains the boundary of these "cells", often called "faces" or "facets".
5320 
5321 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepthStratum()`, `DMPlexGetCellTypeStratum()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
5322 @*/
5323 PetscErrorCode DMPlexGetHeightStratum(DM dm, PetscInt height, PetscInt *start, PetscInt *end)
5324 {
5325   DMLabel  label;
5326   PetscInt depth, pStart, pEnd;
5327 
5328   PetscFunctionBegin;
5329   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5330   if (start) {
5331     PetscAssertPointer(start, 3);
5332     *start = 0;
5333   }
5334   if (end) {
5335     PetscAssertPointer(end, 4);
5336     *end = 0;
5337   }
5338   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
5339   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
5340   if (height < 0) {
5341     if (start) *start = pStart;
5342     if (end) *end = pEnd;
5343     PetscFunctionReturn(PETSC_SUCCESS);
5344   }
5345   PetscCall(DMPlexGetDepthLabel(dm, &label));
5346   if (label) PetscCall(DMLabelGetNumValues(label, &depth));
5347   else PetscCall(DMGetDimension(dm, &depth));
5348   PetscCheck(depth >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Depth not yet computed");
5349   PetscCall(DMPlexGetDepthStratum(dm, depth - 1 - height, start, end));
5350   PetscFunctionReturn(PETSC_SUCCESS);
5351 }
5352 
5353 /*@
5354   DMPlexGetPointDepth - Get the `depth` of a given point
5355 
5356   Not Collective
5357 
5358   Input Parameters:
5359 + dm    - The `DMPLEX` object
5360 - point - The point
5361 
5362   Output Parameter:
5363 . depth - The depth of the `point`
5364 
5365   Level: intermediate
5366 
5367 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
5368 @*/
5369 PetscErrorCode DMPlexGetPointDepth(DM dm, PetscInt point, PetscInt *depth)
5370 {
5371   PetscFunctionBegin;
5372   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5373   PetscAssertPointer(depth, 3);
5374   PetscCall(DMLabelGetValue(dm->depthLabel, point, depth));
5375   PetscFunctionReturn(PETSC_SUCCESS);
5376 }
5377 
5378 /*@
5379   DMPlexGetPointHeight - Get the `height` of a given point
5380 
5381   Not Collective
5382 
5383   Input Parameters:
5384 + dm    - The `DMPLEX` object
5385 - point - The point
5386 
5387   Output Parameter:
5388 . height - The height of the `point`
5389 
5390   Level: intermediate
5391 
5392 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointDepth()`
5393 @*/
5394 PetscErrorCode DMPlexGetPointHeight(DM dm, PetscInt point, PetscInt *height)
5395 {
5396   PetscInt n, pDepth;
5397 
5398   PetscFunctionBegin;
5399   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5400   PetscAssertPointer(height, 3);
5401   PetscCall(DMLabelGetNumValues(dm->depthLabel, &n));
5402   PetscCall(DMLabelGetValue(dm->depthLabel, point, &pDepth));
5403   *height = n - 1 - pDepth; /* DAG depth is n-1 */
5404   PetscFunctionReturn(PETSC_SUCCESS);
5405 }
5406 
5407 /*@
5408   DMPlexGetCellTypeLabel - Get the `DMLabel` recording the polytope type of each cell
5409 
5410   Not Collective
5411 
5412   Input Parameter:
5413 . dm - The `DMPLEX` object
5414 
5415   Output Parameter:
5416 . celltypeLabel - The `DMLabel` recording cell polytope type
5417 
5418   Level: developer
5419 
5420   Note:
5421   This function will trigger automatica computation of cell types. This can be disabled by calling
5422   `DMCreateLabel`(dm, "celltype") beforehand.
5423 
5424 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMCreateLabel()`
5425 @*/
5426 PetscErrorCode DMPlexGetCellTypeLabel(DM dm, DMLabel *celltypeLabel)
5427 {
5428   PetscFunctionBegin;
5429   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5430   PetscAssertPointer(celltypeLabel, 2);
5431   if (!dm->celltypeLabel) PetscCall(DMPlexComputeCellTypes(dm));
5432   *celltypeLabel = dm->celltypeLabel;
5433   PetscFunctionReturn(PETSC_SUCCESS);
5434 }
5435 
5436 /*@
5437   DMPlexGetCellType - Get the polytope type of a given cell
5438 
5439   Not Collective
5440 
5441   Input Parameters:
5442 + dm   - The `DMPLEX` object
5443 - cell - The cell
5444 
5445   Output Parameter:
5446 . celltype - The polytope type of the cell
5447 
5448   Level: intermediate
5449 
5450 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPolytopeType`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`
5451 @*/
5452 PetscErrorCode DMPlexGetCellType(DM dm, PetscInt cell, DMPolytopeType *celltype)
5453 {
5454   DM_Plex *mesh = (DM_Plex *)dm->data;
5455   DMLabel  label;
5456   PetscInt ct;
5457 
5458   PetscFunctionBegin;
5459   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5460   PetscAssertPointer(celltype, 3);
5461   if (mesh->tr) {
5462     PetscCall(DMPlexTransformGetCellType(mesh->tr, cell, celltype));
5463   } else {
5464     PetscInt pStart, pEnd;
5465 
5466     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, NULL));
5467     if (!mesh->cellTypes) { /* XXX remove? optimize? */
5468       PetscCall(PetscSectionGetChart(mesh->coneSection, NULL, &pEnd));
5469       PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
5470       PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5471       for (PetscInt p = pStart; p < pEnd; p++) {
5472         PetscCall(DMLabelGetValue(label, p, &ct));
5473         mesh->cellTypes[p - pStart].value_as_uint8 = (DMPolytopeType)ct;
5474       }
5475     }
5476     *celltype = (DMPolytopeType)mesh->cellTypes[cell - pStart].value_as_uint8;
5477     if (PetscDefined(USE_DEBUG)) {
5478       PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5479       PetscCall(DMLabelGetValue(label, cell, &ct));
5480       PetscCheck(ct >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Cell %" PetscInt_FMT " has not been assigned a cell type", cell);
5481       PetscCheck(ct == (PetscInt)*celltype, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid cellType for %" PetscInt_FMT ": %d != %" PetscInt_FMT, cell, (int)*celltype, ct);
5482     }
5483   }
5484   PetscFunctionReturn(PETSC_SUCCESS);
5485 }
5486 
5487 /*@
5488   DMPlexSetCellType - Set the polytope type of a given cell
5489 
5490   Not Collective
5491 
5492   Input Parameters:
5493 + dm       - The `DMPLEX` object
5494 . cell     - The cell
5495 - celltype - The polytope type of the cell
5496 
5497   Level: advanced
5498 
5499   Note:
5500   By default, cell types will be automatically computed using `DMPlexComputeCellTypes()` before this function
5501   is executed. This function will override the computed type. However, if automatic classification will not succeed
5502   and a user wants to manually specify all types, the classification must be disabled by calling
5503   DMCreateLabel(dm, "celltype") before getting or setting any cell types.
5504 
5505 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexComputeCellTypes()`, `DMCreateLabel()`
5506 @*/
5507 PetscErrorCode DMPlexSetCellType(DM dm, PetscInt cell, DMPolytopeType celltype)
5508 {
5509   DM_Plex *mesh = (DM_Plex *)dm->data;
5510   DMLabel  label;
5511   PetscInt pStart, pEnd;
5512 
5513   PetscFunctionBegin;
5514   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5515   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
5516   PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5517   PetscCall(DMLabelSetValue(label, cell, celltype));
5518   if (!mesh->cellTypes) PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
5519   mesh->cellTypes[cell - pStart].value_as_uint8 = celltype;
5520   PetscFunctionReturn(PETSC_SUCCESS);
5521 }
5522 
5523 PetscErrorCode DMCreateCoordinateDM_Plex(DM dm, DM *cdm)
5524 {
5525   PetscSection section, s;
5526   Mat          m;
5527   PetscInt     maxHeight;
5528   const char  *prefix;
5529 
5530   PetscFunctionBegin;
5531   PetscCall(DMClone(dm, cdm));
5532   PetscCall(PetscObjectGetOptionsPrefix((PetscObject)dm, &prefix));
5533   PetscCall(PetscObjectSetOptionsPrefix((PetscObject)*cdm, prefix));
5534   PetscCall(PetscObjectAppendOptionsPrefix((PetscObject)*cdm, "cdm_"));
5535   PetscCall(DMPlexGetMaxProjectionHeight(dm, &maxHeight));
5536   PetscCall(DMPlexSetMaxProjectionHeight(*cdm, maxHeight));
5537   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
5538   PetscCall(DMSetLocalSection(*cdm, section));
5539   PetscCall(PetscSectionDestroy(&section));
5540   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &s));
5541   PetscCall(MatCreate(PETSC_COMM_SELF, &m));
5542   PetscCall(DMSetDefaultConstraints(*cdm, s, m, NULL));
5543   PetscCall(PetscSectionDestroy(&s));
5544   PetscCall(MatDestroy(&m));
5545 
5546   PetscCall(DMSetNumFields(*cdm, 1));
5547   PetscCall(DMCreateDS(*cdm));
5548   (*cdm)->cloneOpts = PETSC_TRUE;
5549   if (dm->setfromoptionscalled) PetscCall(DMSetFromOptions(*cdm));
5550   PetscFunctionReturn(PETSC_SUCCESS);
5551 }
5552 
5553 PetscErrorCode DMCreateCoordinateField_Plex(DM dm, DMField *field)
5554 {
5555   Vec coordsLocal, cellCoordsLocal;
5556   DM  coordsDM, cellCoordsDM;
5557 
5558   PetscFunctionBegin;
5559   *field = NULL;
5560   PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
5561   PetscCall(DMGetCoordinateDM(dm, &coordsDM));
5562   PetscCall(DMGetCellCoordinatesLocal(dm, &cellCoordsLocal));
5563   PetscCall(DMGetCellCoordinateDM(dm, &cellCoordsDM));
5564   if (coordsLocal && coordsDM) {
5565     if (cellCoordsLocal && cellCoordsDM) PetscCall(DMFieldCreateDSWithDG(coordsDM, cellCoordsDM, 0, coordsLocal, cellCoordsLocal, field));
5566     else PetscCall(DMFieldCreateDS(coordsDM, 0, coordsLocal, field));
5567   }
5568   PetscFunctionReturn(PETSC_SUCCESS);
5569 }
5570 
5571 /*@C
5572   DMPlexGetConeSection - Return a section which describes the layout of cone data
5573 
5574   Not Collective
5575 
5576   Input Parameter:
5577 . dm - The `DMPLEX` object
5578 
5579   Output Parameter:
5580 . section - The `PetscSection` object
5581 
5582   Level: developer
5583 
5584 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSupportSection()`, `DMPlexGetCones()`, `DMPlexGetConeOrientations()`, `PetscSection`
5585 @*/
5586 PetscErrorCode DMPlexGetConeSection(DM dm, PetscSection *section)
5587 {
5588   DM_Plex *mesh = (DM_Plex *)dm->data;
5589 
5590   PetscFunctionBegin;
5591   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5592   if (section) *section = mesh->coneSection;
5593   PetscFunctionReturn(PETSC_SUCCESS);
5594 }
5595 
5596 /*@C
5597   DMPlexGetSupportSection - Return a section which describes the layout of support data
5598 
5599   Not Collective
5600 
5601   Input Parameter:
5602 . dm - The `DMPLEX` object
5603 
5604   Output Parameter:
5605 . section - The `PetscSection` object
5606 
5607   Level: developer
5608 
5609 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `PetscSection`
5610 @*/
5611 PetscErrorCode DMPlexGetSupportSection(DM dm, PetscSection *section)
5612 {
5613   DM_Plex *mesh = (DM_Plex *)dm->data;
5614 
5615   PetscFunctionBegin;
5616   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5617   if (section) *section = mesh->supportSection;
5618   PetscFunctionReturn(PETSC_SUCCESS);
5619 }
5620 
5621 /*@C
5622   DMPlexGetCones - Return cone data
5623 
5624   Not Collective
5625 
5626   Input Parameter:
5627 . dm - The `DMPLEX` object
5628 
5629   Output Parameter:
5630 . cones - The cone for each point
5631 
5632   Level: developer
5633 
5634 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`
5635 @*/
5636 PetscErrorCode DMPlexGetCones(DM dm, PetscInt *cones[])
5637 {
5638   DM_Plex *mesh = (DM_Plex *)dm->data;
5639 
5640   PetscFunctionBegin;
5641   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5642   if (cones) *cones = mesh->cones;
5643   PetscFunctionReturn(PETSC_SUCCESS);
5644 }
5645 
5646 /*@C
5647   DMPlexGetConeOrientations - Return cone orientation data
5648 
5649   Not Collective
5650 
5651   Input Parameter:
5652 . dm - The `DMPLEX` object
5653 
5654   Output Parameter:
5655 . coneOrientations - The array of cone orientations for all points
5656 
5657   Level: developer
5658 
5659   Notes:
5660   The `PetscSection` returned by `DMPlexGetConeSection()` partitions coneOrientations into cone orientations of particular points as returned by `DMPlexGetConeOrientation()`.
5661 
5662   The meaning of coneOrientations values is detailed in `DMPlexGetConeOrientation()`.
5663 
5664 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `DMPlexGetConeOrientation()`, `PetscSection`
5665 @*/
5666 PetscErrorCode DMPlexGetConeOrientations(DM dm, PetscInt *coneOrientations[])
5667 {
5668   DM_Plex *mesh = (DM_Plex *)dm->data;
5669 
5670   PetscFunctionBegin;
5671   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5672   if (coneOrientations) *coneOrientations = mesh->coneOrientations;
5673   PetscFunctionReturn(PETSC_SUCCESS);
5674 }
5675 
5676 /******************************** FEM Support **********************************/
5677 
5678 PetscErrorCode DMPlexGetAllCells_Internal(DM plex, IS *cellIS)
5679 {
5680   PetscInt depth;
5681 
5682   PetscFunctionBegin;
5683   PetscCall(DMPlexGetDepth(plex, &depth));
5684   PetscCall(DMGetStratumIS(plex, "dim", depth, cellIS));
5685   if (!*cellIS) PetscCall(DMGetStratumIS(plex, "depth", depth, cellIS));
5686   PetscFunctionReturn(PETSC_SUCCESS);
5687 }
5688 
5689 PetscErrorCode DMPlexGetAllFaces_Internal(DM plex, IS *faceIS)
5690 {
5691   PetscInt depth;
5692 
5693   PetscFunctionBegin;
5694   PetscCall(DMPlexGetDepth(plex, &depth));
5695   PetscCall(DMGetStratumIS(plex, "dim", depth - 1, faceIS));
5696   if (!*faceIS) PetscCall(DMGetStratumIS(plex, "depth", depth - 1, faceIS));
5697   PetscFunctionReturn(PETSC_SUCCESS);
5698 }
5699 
5700 /*
5701  Returns number of components and tensor degree for the field.  For interpolated meshes, line should be a point
5702  representing a line in the section.
5703 */
5704 static PetscErrorCode PetscSectionFieldGetTensorDegree_Private(DM dm, PetscSection section, PetscInt field, PetscInt line, PetscInt *Nc, PetscInt *k, PetscBool *continuous, PetscBool *tensor)
5705 {
5706   PetscObject  obj;
5707   PetscClassId id;
5708   PetscFE      fe = NULL;
5709 
5710   PetscFunctionBeginHot;
5711   PetscCall(PetscSectionGetFieldComponents(section, field, Nc));
5712   PetscCall(DMGetField(dm, field, NULL, &obj));
5713   PetscCall(PetscObjectGetClassId(obj, &id));
5714   if (id == PETSCFE_CLASSID) fe = (PetscFE)obj;
5715 
5716   if (!fe) {
5717     /* Assume the full interpolated mesh is in the chart; lines in particular */
5718     /* An order k SEM disc has k-1 dofs on an edge */
5719     PetscCall(PetscSectionGetFieldDof(section, line, field, k));
5720     *k = *k / *Nc + 1;
5721   } else {
5722     PetscInt       dual_space_size, dim;
5723     PetscDualSpace dsp;
5724 
5725     PetscCall(DMGetDimension(dm, &dim));
5726     PetscCall(PetscFEGetDualSpace(fe, &dsp));
5727     PetscCall(PetscDualSpaceGetDimension(dsp, &dual_space_size));
5728     *k = (PetscInt)PetscCeilReal(PetscPowReal(dual_space_size / *Nc, 1.0 / dim)) - 1;
5729     PetscCall(PetscDualSpaceLagrangeGetContinuity(dsp, continuous));
5730     PetscCall(PetscDualSpaceLagrangeGetTensor(dsp, tensor));
5731   }
5732   PetscFunctionReturn(PETSC_SUCCESS);
5733 }
5734 
5735 static PetscErrorCode GetFieldSize_Private(PetscInt dim, PetscInt k, PetscBool tensor, PetscInt *dof)
5736 {
5737   PetscFunctionBeginHot;
5738   if (tensor) {
5739     *dof = PetscPowInt(k + 1, dim);
5740   } else {
5741     switch (dim) {
5742     case 1:
5743       *dof = k + 1;
5744       break;
5745     case 2:
5746       *dof = ((k + 1) * (k + 2)) / 2;
5747       break;
5748     case 3:
5749       *dof = ((k + 1) * (k + 2) * (k + 3)) / 6;
5750       break;
5751     default:
5752       *dof = 0;
5753     }
5754   }
5755   PetscFunctionReturn(PETSC_SUCCESS);
5756 }
5757 
5758 /*@
5759 
5760   DMPlexSetClosurePermutationTensor - Create a permutation from the default (BFS) point ordering in the closure, to a
5761   lexicographic ordering over the tensor product cell (i.e., line, quad, hex, etc.), and set this permutation in the
5762   section provided (or the section of the `DM`).
5763 
5764   Input Parameters:
5765 + dm      - The `DM`
5766 . point   - Either a cell (highest dim point) or an edge (dim 1 point), or `PETSC_DETERMINE`
5767 - section - The `PetscSection` to reorder, or `NULL` for the default section
5768 
5769   Example:
5770   A typical interpolated single-quad mesh might order points as
5771 .vb
5772   [c0, v1, v2, v3, v4, e5, e6, e7, e8]
5773 
5774   v4 -- e6 -- v3
5775   |           |
5776   e7    c0    e8
5777   |           |
5778   v1 -- e5 -- v2
5779 .ve
5780 
5781   (There is no significance to the ordering described here.)  The default section for a Q3 quad might typically assign
5782   dofs in the order of points, e.g.,
5783 .vb
5784     c0 -> [0,1,2,3]
5785     v1 -> [4]
5786     ...
5787     e5 -> [8, 9]
5788 .ve
5789 
5790   which corresponds to the dofs
5791 .vb
5792     6   10  11  7
5793     13  2   3   15
5794     12  0   1   14
5795     4   8   9   5
5796 .ve
5797 
5798   The closure in BFS ordering works through height strata (cells, edges, vertices) to produce the ordering
5799 .vb
5800   0 1 2 3 8 9 14 15 11 10 13 12 4 5 7 6
5801 .ve
5802 
5803   After calling DMPlexSetClosurePermutationTensor(), the closure will be ordered lexicographically,
5804 .vb
5805    4 8 9 5 12 0 1 14 13 2 3 15 6 10 11 7
5806 .ve
5807 
5808   Level: developer
5809 
5810   Notes:
5811   The point is used to determine the number of dofs/field on an edge. For SEM, this is related to the polynomial
5812   degree of the basis.
5813 
5814   This is required to run with libCEED.
5815 
5816 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMGetLocalSection()`, `PetscSectionSetClosurePermutation()`, `DMSetGlobalSection()`
5817 @*/
5818 PetscErrorCode DMPlexSetClosurePermutationTensor(DM dm, PetscInt point, PetscSection section)
5819 {
5820   DMLabel   label;
5821   PetscInt  dim, depth = -1, eStart = -1, Nf;
5822   PetscBool continuous = PETSC_TRUE, tensor = PETSC_TRUE;
5823 
5824   PetscFunctionBegin;
5825   PetscCall(DMGetDimension(dm, &dim));
5826   if (dim < 1) PetscFunctionReturn(PETSC_SUCCESS);
5827   if (point < 0) {
5828     PetscInt sStart, sEnd;
5829 
5830     PetscCall(DMPlexGetDepthStratum(dm, 1, &sStart, &sEnd));
5831     point = sEnd - sStart ? sStart : point;
5832   }
5833   PetscCall(DMPlexGetDepthLabel(dm, &label));
5834   if (point >= 0) PetscCall(DMLabelGetValue(label, point, &depth));
5835   if (!section) PetscCall(DMGetLocalSection(dm, &section));
5836   if (depth == 1) {
5837     eStart = point;
5838   } else if (depth == dim) {
5839     const PetscInt *cone;
5840 
5841     PetscCall(DMPlexGetCone(dm, point, &cone));
5842     if (dim == 2) eStart = cone[0];
5843     else if (dim == 3) {
5844       const PetscInt *cone2;
5845       PetscCall(DMPlexGetCone(dm, cone[0], &cone2));
5846       eStart = cone2[0];
5847     } 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);
5848   } 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);
5849 
5850   PetscCall(PetscSectionGetNumFields(section, &Nf));
5851   for (PetscInt d = 1; d <= dim; d++) {
5852     PetscInt  k, f, Nc, c, i, j, size = 0, offset = 0, foffset = 0;
5853     PetscInt *perm;
5854 
5855     for (f = 0; f < Nf; ++f) {
5856       PetscInt dof;
5857 
5858       PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
5859       PetscCheck(dim == 1 || tensor || !continuous, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Continuous field %" PetscInt_FMT " must have a tensor product discretization", f);
5860       if (!continuous && d < dim) continue;
5861       PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
5862       size += dof * Nc;
5863     }
5864     PetscCall(PetscMalloc1(size, &perm));
5865     for (f = 0; f < Nf; ++f) {
5866       switch (d) {
5867       case 1:
5868         PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
5869         if (!continuous && d < dim) continue;
5870         /*
5871          Original ordering is [ edge of length k-1; vtx0; vtx1 ]
5872          We want              [ vtx0; edge of length k-1; vtx1 ]
5873          */
5874         if (continuous) {
5875           for (c = 0; c < Nc; c++, offset++) perm[offset] = (k - 1) * Nc + c + foffset;
5876           for (i = 0; i < k - 1; i++)
5877             for (c = 0; c < Nc; c++, offset++) perm[offset] = i * Nc + c + foffset;
5878           for (c = 0; c < Nc; c++, offset++) perm[offset] = k * Nc + c + foffset;
5879           foffset = offset;
5880         } else {
5881           PetscInt dof;
5882 
5883           PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
5884           for (i = 0; i < dof * Nc; ++i, ++offset) perm[offset] = i + foffset;
5885           foffset = offset;
5886         }
5887         break;
5888       case 2:
5889         /* The original quad closure is oriented clockwise, {f, e_b, e_r, e_t, e_l, v_lb, v_rb, v_tr, v_tl} */
5890         PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
5891         if (!continuous && d < dim) continue;
5892         /* The SEM order is
5893 
5894          v_lb, {e_b}, v_rb,
5895          e^{(k-1)-i}_l, {f^{i*(k-1)}}, e^i_r,
5896          v_lt, reverse {e_t}, v_rt
5897          */
5898         if (continuous) {
5899           const PetscInt of   = 0;
5900           const PetscInt oeb  = of + PetscSqr(k - 1);
5901           const PetscInt oer  = oeb + (k - 1);
5902           const PetscInt oet  = oer + (k - 1);
5903           const PetscInt oel  = oet + (k - 1);
5904           const PetscInt ovlb = oel + (k - 1);
5905           const PetscInt ovrb = ovlb + 1;
5906           const PetscInt ovrt = ovrb + 1;
5907           const PetscInt ovlt = ovrt + 1;
5908           PetscInt       o;
5909 
5910           /* bottom */
5911           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlb * Nc + c + foffset;
5912           for (o = oeb; o < oer; ++o)
5913             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5914           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrb * Nc + c + foffset;
5915           /* middle */
5916           for (i = 0; i < k - 1; ++i) {
5917             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oel + (k - 2) - i) * Nc + c + foffset;
5918             for (o = of + (k - 1) * i; o < of + (k - 1) * (i + 1); ++o)
5919               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5920             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oer + i) * Nc + c + foffset;
5921           }
5922           /* top */
5923           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlt * Nc + c + foffset;
5924           for (o = oel - 1; o >= oet; --o)
5925             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5926           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrt * Nc + c + foffset;
5927           foffset = offset;
5928         } else {
5929           PetscInt dof;
5930 
5931           PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
5932           for (i = 0; i < dof * Nc; ++i, ++offset) perm[offset] = i + foffset;
5933           foffset = offset;
5934         }
5935         break;
5936       case 3:
5937         /* The original hex closure is
5938 
5939          {c,
5940          f_b, f_t, f_f, f_b, f_r, f_l,
5941          e_bl, e_bb, e_br, e_bf,  e_tf, e_tr, e_tb, e_tl,  e_rf, e_lf, e_lb, e_rb,
5942          v_blf, v_blb, v_brb, v_brf, v_tlf, v_trf, v_trb, v_tlb}
5943          */
5944         PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
5945         if (!continuous && d < dim) continue;
5946         /* The SEM order is
5947          Bottom Slice
5948          v_blf, {e^{(k-1)-n}_bf}, v_brf,
5949          e^{i}_bl, f^{n*(k-1)+(k-1)-i}_b, e^{(k-1)-i}_br,
5950          v_blb, {e_bb}, v_brb,
5951 
5952          Middle Slice (j)
5953          {e^{(k-1)-j}_lf}, {f^{j*(k-1)+n}_f}, e^j_rf,
5954          f^{i*(k-1)+j}_l, {c^{(j*(k-1) + i)*(k-1)+n}_t}, f^{j*(k-1)+i}_r,
5955          e^j_lb, {f^{j*(k-1)+(k-1)-n}_b}, e^{(k-1)-j}_rb,
5956 
5957          Top Slice
5958          v_tlf, {e_tf}, v_trf,
5959          e^{(k-1)-i}_tl, {f^{i*(k-1)}_t}, e^{i}_tr,
5960          v_tlb, {e^{(k-1)-n}_tb}, v_trb,
5961          */
5962         if (continuous) {
5963           const PetscInt oc    = 0;
5964           const PetscInt ofb   = oc + PetscSqr(k - 1) * (k - 1);
5965           const PetscInt oft   = ofb + PetscSqr(k - 1);
5966           const PetscInt off   = oft + PetscSqr(k - 1);
5967           const PetscInt ofk   = off + PetscSqr(k - 1);
5968           const PetscInt ofr   = ofk + PetscSqr(k - 1);
5969           const PetscInt ofl   = ofr + PetscSqr(k - 1);
5970           const PetscInt oebl  = ofl + PetscSqr(k - 1);
5971           const PetscInt oebb  = oebl + (k - 1);
5972           const PetscInt oebr  = oebb + (k - 1);
5973           const PetscInt oebf  = oebr + (k - 1);
5974           const PetscInt oetf  = oebf + (k - 1);
5975           const PetscInt oetr  = oetf + (k - 1);
5976           const PetscInt oetb  = oetr + (k - 1);
5977           const PetscInt oetl  = oetb + (k - 1);
5978           const PetscInt oerf  = oetl + (k - 1);
5979           const PetscInt oelf  = oerf + (k - 1);
5980           const PetscInt oelb  = oelf + (k - 1);
5981           const PetscInt oerb  = oelb + (k - 1);
5982           const PetscInt ovblf = oerb + (k - 1);
5983           const PetscInt ovblb = ovblf + 1;
5984           const PetscInt ovbrb = ovblb + 1;
5985           const PetscInt ovbrf = ovbrb + 1;
5986           const PetscInt ovtlf = ovbrf + 1;
5987           const PetscInt ovtrf = ovtlf + 1;
5988           const PetscInt ovtrb = ovtrf + 1;
5989           const PetscInt ovtlb = ovtrb + 1;
5990           PetscInt       o, n;
5991 
5992           /* Bottom Slice */
5993           /*   bottom */
5994           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblf * Nc + c + foffset;
5995           for (o = oetf - 1; o >= oebf; --o)
5996             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5997           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrf * Nc + c + foffset;
5998           /*   middle */
5999           for (i = 0; i < k - 1; ++i) {
6000             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebl + i) * Nc + c + foffset;
6001             for (n = 0; n < k - 1; ++n) {
6002               o = ofb + n * (k - 1) + i;
6003               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6004             }
6005             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebr + (k - 2) - i) * Nc + c + foffset;
6006           }
6007           /*   top */
6008           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblb * Nc + c + foffset;
6009           for (o = oebb; o < oebr; ++o)
6010             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6011           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrb * Nc + c + foffset;
6012 
6013           /* Middle Slice */
6014           for (j = 0; j < k - 1; ++j) {
6015             /*   bottom */
6016             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelf + (k - 2) - j) * Nc + c + foffset;
6017             for (o = off + j * (k - 1); o < off + (j + 1) * (k - 1); ++o)
6018               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6019             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerf + j) * Nc + c + foffset;
6020             /*   middle */
6021             for (i = 0; i < k - 1; ++i) {
6022               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofl + i * (k - 1) + j) * Nc + c + foffset;
6023               for (n = 0; n < k - 1; ++n)
6024                 for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oc + (j * (k - 1) + i) * (k - 1) + n) * Nc + c + foffset;
6025               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofr + j * (k - 1) + i) * Nc + c + foffset;
6026             }
6027             /*   top */
6028             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelb + j) * Nc + c + foffset;
6029             for (o = ofk + j * (k - 1) + (k - 2); o >= ofk + j * (k - 1); --o)
6030               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6031             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerb + (k - 2) - j) * Nc + c + foffset;
6032           }
6033 
6034           /* Top Slice */
6035           /*   bottom */
6036           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlf * Nc + c + foffset;
6037           for (o = oetf; o < oetr; ++o)
6038             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6039           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrf * Nc + c + foffset;
6040           /*   middle */
6041           for (i = 0; i < k - 1; ++i) {
6042             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetl + (k - 2) - i) * Nc + c + foffset;
6043             for (n = 0; n < k - 1; ++n)
6044               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oft + i * (k - 1) + n) * Nc + c + foffset;
6045             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetr + i) * Nc + c + foffset;
6046           }
6047           /*   top */
6048           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlb * Nc + c + foffset;
6049           for (o = oetl - 1; o >= oetb; --o)
6050             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6051           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrb * Nc + c + foffset;
6052 
6053           foffset = offset;
6054         } else {
6055           PetscInt dof;
6056 
6057           PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
6058           for (i = 0; i < dof * Nc; ++i, ++offset) perm[offset] = i + foffset;
6059           foffset = offset;
6060         }
6061         break;
6062       default:
6063         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "No spectral ordering for dimension %" PetscInt_FMT, d);
6064       }
6065     }
6066     PetscCheck(offset == size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Number of permutation entries %" PetscInt_FMT " != %" PetscInt_FMT, offset, size);
6067     /* Check permutation */
6068     {
6069       PetscInt *check;
6070 
6071       PetscCall(PetscMalloc1(size, &check));
6072       for (i = 0; i < size; ++i) {
6073         check[i] = -1;
6074         PetscCheck(perm[i] >= 0 && perm[i] < size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid permutation index p[%" PetscInt_FMT "] = %" PetscInt_FMT, i, perm[i]);
6075       }
6076       for (i = 0; i < size; ++i) check[perm[i]] = i;
6077       for (i = 0; i < size; ++i) PetscCheck(check[i] >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Missing permutation index %" PetscInt_FMT, i);
6078       PetscCall(PetscFree(check));
6079     }
6080     PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size, PETSC_OWN_POINTER, perm));
6081     if (d == dim) { // Add permutation for localized (in case this is a coordinate DM)
6082       PetscInt *loc_perm;
6083       PetscCall(PetscMalloc1(size * 2, &loc_perm));
6084       for (PetscInt i = 0; i < size; i++) {
6085         loc_perm[i]        = perm[i];
6086         loc_perm[size + i] = size + perm[i];
6087       }
6088       PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size * 2, PETSC_OWN_POINTER, loc_perm));
6089     }
6090   }
6091   PetscFunctionReturn(PETSC_SUCCESS);
6092 }
6093 
6094 PetscErrorCode DMPlexGetPointDualSpaceFEM(DM dm, PetscInt point, PetscInt field, PetscDualSpace *dspace)
6095 {
6096   PetscDS  prob;
6097   PetscInt depth, Nf, h;
6098   DMLabel  label;
6099 
6100   PetscFunctionBeginHot;
6101   PetscCall(DMGetDS(dm, &prob));
6102   Nf      = prob->Nf;
6103   label   = dm->depthLabel;
6104   *dspace = NULL;
6105   if (field < Nf) {
6106     PetscObject disc = prob->disc[field];
6107 
6108     if (disc->classid == PETSCFE_CLASSID) {
6109       PetscDualSpace dsp;
6110 
6111       PetscCall(PetscFEGetDualSpace((PetscFE)disc, &dsp));
6112       PetscCall(DMLabelGetNumValues(label, &depth));
6113       PetscCall(DMLabelGetValue(label, point, &h));
6114       h = depth - 1 - h;
6115       if (h) {
6116         PetscCall(PetscDualSpaceGetHeightSubspace(dsp, h, dspace));
6117       } else {
6118         *dspace = dsp;
6119       }
6120     }
6121   }
6122   PetscFunctionReturn(PETSC_SUCCESS);
6123 }
6124 
6125 static inline PetscErrorCode DMPlexVecGetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6126 {
6127   PetscScalar       *array;
6128   const PetscScalar *vArray;
6129   const PetscInt    *cone, *coneO;
6130   PetscInt           pStart, pEnd, p, numPoints, size = 0, offset = 0;
6131 
6132   PetscFunctionBeginHot;
6133   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6134   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
6135   PetscCall(DMPlexGetCone(dm, point, &cone));
6136   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
6137   if (!values || !*values) {
6138     if ((point >= pStart) && (point < pEnd)) {
6139       PetscInt dof;
6140 
6141       PetscCall(PetscSectionGetDof(section, point, &dof));
6142       size += dof;
6143     }
6144     for (p = 0; p < numPoints; ++p) {
6145       const PetscInt cp = cone[p];
6146       PetscInt       dof;
6147 
6148       if ((cp < pStart) || (cp >= pEnd)) continue;
6149       PetscCall(PetscSectionGetDof(section, cp, &dof));
6150       size += dof;
6151     }
6152     if (!values) {
6153       if (csize) *csize = size;
6154       PetscFunctionReturn(PETSC_SUCCESS);
6155     }
6156     PetscCall(DMGetWorkArray(dm, size, MPIU_SCALAR, &array));
6157   } else {
6158     array = *values;
6159   }
6160   size = 0;
6161   PetscCall(VecGetArrayRead(v, &vArray));
6162   if ((point >= pStart) && (point < pEnd)) {
6163     PetscInt           dof, off, d;
6164     const PetscScalar *varr;
6165 
6166     PetscCall(PetscSectionGetDof(section, point, &dof));
6167     PetscCall(PetscSectionGetOffset(section, point, &off));
6168     varr = PetscSafePointerPlusOffset(vArray, off);
6169     for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
6170     size += dof;
6171   }
6172   for (p = 0; p < numPoints; ++p) {
6173     const PetscInt     cp = cone[p];
6174     PetscInt           o  = coneO[p];
6175     PetscInt           dof, off, d;
6176     const PetscScalar *varr;
6177 
6178     if ((cp < pStart) || (cp >= pEnd)) continue;
6179     PetscCall(PetscSectionGetDof(section, cp, &dof));
6180     PetscCall(PetscSectionGetOffset(section, cp, &off));
6181     varr = PetscSafePointerPlusOffset(vArray, off);
6182     if (o >= 0) {
6183       for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
6184     } else {
6185       for (d = dof - 1; d >= 0; --d, ++offset) array[offset] = varr[d];
6186     }
6187     size += dof;
6188   }
6189   PetscCall(VecRestoreArrayRead(v, &vArray));
6190   if (!*values) {
6191     if (csize) *csize = size;
6192     *values = array;
6193   } else {
6194     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
6195     *csize = size;
6196   }
6197   PetscFunctionReturn(PETSC_SUCCESS);
6198 }
6199 
6200 /* Compress out points not in the section */
6201 static inline PetscErrorCode CompressPoints_Private(PetscSection section, PetscInt *numPoints, PetscInt points[])
6202 {
6203   const PetscInt np = *numPoints;
6204   PetscInt       pStart, pEnd, p, q;
6205 
6206   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6207   for (p = 0, q = 0; p < np; ++p) {
6208     const PetscInt r = points[p * 2];
6209     if ((r >= pStart) && (r < pEnd)) {
6210       points[q * 2]     = r;
6211       points[q * 2 + 1] = points[p * 2 + 1];
6212       ++q;
6213     }
6214   }
6215   *numPoints = q;
6216   return PETSC_SUCCESS;
6217 }
6218 
6219 /* Compressed closure does not apply closure permutation */
6220 PetscErrorCode DMPlexGetCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt ornt, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
6221 {
6222   const PetscInt *cla = NULL;
6223   PetscInt        np, *pts = NULL;
6224 
6225   PetscFunctionBeginHot;
6226   PetscCall(PetscSectionGetClosureIndex(section, (PetscObject)dm, clSec, clPoints));
6227   if (!ornt && *clPoints) {
6228     PetscInt dof, off;
6229 
6230     PetscCall(PetscSectionGetDof(*clSec, point, &dof));
6231     PetscCall(PetscSectionGetOffset(*clSec, point, &off));
6232     PetscCall(ISGetIndices(*clPoints, &cla));
6233     np  = dof / 2;
6234     pts = PetscSafePointerPlusOffset((PetscInt *)cla, off);
6235   } else {
6236     PetscCall(DMPlexGetTransitiveClosure_Internal(dm, point, ornt, PETSC_TRUE, &np, &pts));
6237     PetscCall(CompressPoints_Private(section, &np, pts));
6238   }
6239   *numPoints = np;
6240   *points    = pts;
6241   *clp       = cla;
6242   PetscFunctionReturn(PETSC_SUCCESS);
6243 }
6244 
6245 PetscErrorCode DMPlexRestoreCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
6246 {
6247   PetscFunctionBeginHot;
6248   if (!*clPoints) {
6249     PetscCall(DMPlexRestoreTransitiveClosure(dm, point, PETSC_TRUE, numPoints, points));
6250   } else {
6251     PetscCall(ISRestoreIndices(*clPoints, clp));
6252   }
6253   *numPoints = 0;
6254   *points    = NULL;
6255   *clSec     = NULL;
6256   *clPoints  = NULL;
6257   *clp       = NULL;
6258   PetscFunctionReturn(PETSC_SUCCESS);
6259 }
6260 
6261 static inline PetscErrorCode DMPlexVecGetClosure_Static(DM dm, PetscSection section, PetscInt numPoints, const PetscInt points[], const PetscInt clperm[], const PetscScalar vArray[], PetscInt *size, PetscScalar array[])
6262 {
6263   PetscInt            offset = 0, p;
6264   const PetscInt    **perms  = NULL;
6265   const PetscScalar **flips  = NULL;
6266 
6267   PetscFunctionBeginHot;
6268   *size = 0;
6269   PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
6270   for (p = 0; p < numPoints; p++) {
6271     const PetscInt     point = points[2 * p];
6272     const PetscInt    *perm  = perms ? perms[p] : NULL;
6273     const PetscScalar *flip  = flips ? flips[p] : NULL;
6274     PetscInt           dof, off, d;
6275     const PetscScalar *varr;
6276 
6277     PetscCall(PetscSectionGetDof(section, point, &dof));
6278     PetscCall(PetscSectionGetOffset(section, point, &off));
6279     varr = PetscSafePointerPlusOffset(vArray, off);
6280     if (clperm) {
6281       if (perm) {
6282         for (d = 0; d < dof; d++) array[clperm[offset + perm[d]]] = varr[d];
6283       } else {
6284         for (d = 0; d < dof; d++) array[clperm[offset + d]] = varr[d];
6285       }
6286       if (flip) {
6287         for (d = 0; d < dof; d++) array[clperm[offset + d]] *= flip[d];
6288       }
6289     } else {
6290       if (perm) {
6291         for (d = 0; d < dof; d++) array[offset + perm[d]] = varr[d];
6292       } else {
6293         for (d = 0; d < dof; d++) array[offset + d] = varr[d];
6294       }
6295       if (flip) {
6296         for (d = 0; d < dof; d++) array[offset + d] *= flip[d];
6297       }
6298     }
6299     offset += dof;
6300   }
6301   PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
6302   *size = offset;
6303   PetscFunctionReturn(PETSC_SUCCESS);
6304 }
6305 
6306 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[])
6307 {
6308   PetscInt offset = 0, f;
6309 
6310   PetscFunctionBeginHot;
6311   *size = 0;
6312   for (f = 0; f < numFields; ++f) {
6313     PetscInt            p;
6314     const PetscInt    **perms = NULL;
6315     const PetscScalar **flips = NULL;
6316 
6317     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6318     for (p = 0; p < numPoints; p++) {
6319       const PetscInt     point = points[2 * p];
6320       PetscInt           fdof, foff, b;
6321       const PetscScalar *varr;
6322       const PetscInt    *perm = perms ? perms[p] : NULL;
6323       const PetscScalar *flip = flips ? flips[p] : NULL;
6324 
6325       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6326       PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6327       varr = &vArray[foff];
6328       if (clperm) {
6329         if (perm) {
6330           for (b = 0; b < fdof; b++) array[clperm[offset + perm[b]]] = varr[b];
6331         } else {
6332           for (b = 0; b < fdof; b++) array[clperm[offset + b]] = varr[b];
6333         }
6334         if (flip) {
6335           for (b = 0; b < fdof; b++) array[clperm[offset + b]] *= flip[b];
6336         }
6337       } else {
6338         if (perm) {
6339           for (b = 0; b < fdof; b++) array[offset + perm[b]] = varr[b];
6340         } else {
6341           for (b = 0; b < fdof; b++) array[offset + b] = varr[b];
6342         }
6343         if (flip) {
6344           for (b = 0; b < fdof; b++) array[offset + b] *= flip[b];
6345         }
6346       }
6347       offset += fdof;
6348     }
6349     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6350   }
6351   *size = offset;
6352   PetscFunctionReturn(PETSC_SUCCESS);
6353 }
6354 
6355 PetscErrorCode DMPlexVecGetOrientedClosure_Internal(DM dm, PetscSection section, PetscBool useClPerm, Vec v, PetscInt point, PetscInt ornt, PetscInt *csize, PetscScalar *values[])
6356 {
6357   PetscSection    clSection;
6358   IS              clPoints;
6359   PetscInt       *points = NULL;
6360   const PetscInt *clp, *perm = NULL;
6361   PetscInt        depth, numFields, numPoints, asize;
6362 
6363   PetscFunctionBeginHot;
6364   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6365   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6366   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6367   PetscValidHeaderSpecific(v, VEC_CLASSID, 4);
6368   PetscCall(DMPlexGetDepth(dm, &depth));
6369   PetscCall(PetscSectionGetNumFields(section, &numFields));
6370   if (depth == 1 && numFields < 2) {
6371     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6372     PetscFunctionReturn(PETSC_SUCCESS);
6373   }
6374   /* Get points */
6375   PetscCall(DMPlexGetCompressedClosure(dm, section, point, ornt, &numPoints, &points, &clSection, &clPoints, &clp));
6376   /* Get sizes */
6377   asize = 0;
6378   for (PetscInt p = 0; p < numPoints * 2; p += 2) {
6379     PetscInt dof;
6380     PetscCall(PetscSectionGetDof(section, points[p], &dof));
6381     asize += dof;
6382   }
6383   if (values) {
6384     const PetscScalar *vArray;
6385     PetscInt           size;
6386 
6387     if (*values) {
6388       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);
6389     } else PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, values));
6390     if (useClPerm) PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, asize, &perm));
6391     PetscCall(VecGetArrayRead(v, &vArray));
6392     /* Get values */
6393     if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, numPoints, points, numFields, perm, vArray, &size, *values));
6394     else PetscCall(DMPlexVecGetClosure_Static(dm, section, numPoints, points, perm, vArray, &size, *values));
6395     PetscCheck(asize == size, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Section size %" PetscInt_FMT " does not match Vec closure size %" PetscInt_FMT, asize, size);
6396     /* Cleanup array */
6397     PetscCall(VecRestoreArrayRead(v, &vArray));
6398   }
6399   if (csize) *csize = asize;
6400   /* Cleanup points */
6401   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6402   PetscFunctionReturn(PETSC_SUCCESS);
6403 }
6404 
6405 /*@C
6406   DMPlexVecGetClosure - Get an array of the values on the closure of 'point'
6407 
6408   Not collective
6409 
6410   Input Parameters:
6411 + dm      - The `DM`
6412 . section - The section describing the layout in `v`, or `NULL` to use the default section
6413 . v       - The local vector
6414 - point   - The point in the `DM`
6415 
6416   Input/Output Parameters:
6417 + csize  - The size of the input values array, or `NULL`; on output the number of values in the closure
6418 - values - An array to use for the values, or `NULL` to have it allocated automatically;
6419            if the user provided `NULL`, it is a borrowed array and should not be freed
6420 
6421   Level: intermediate
6422 
6423   Notes:
6424   `DMPlexVecGetClosure()`/`DMPlexVecRestoreClosure()` only allocates the values array if it set to `NULL` in the
6425   calling function. This is because `DMPlexVecGetClosure()` is typically called in the inner loop of a `Vec` or `Mat`
6426   assembly function, and a user may already have allocated storage for this operation.
6427 
6428   A typical use could be
6429 .vb
6430    values = NULL;
6431    PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
6432    for (cl = 0; cl < clSize; ++cl) {
6433      <Compute on closure>
6434    }
6435    PetscCall(DMPlexVecRestoreClosure(dm, NULL, v, p, &clSize, &values));
6436 .ve
6437   or
6438 .vb
6439    PetscMalloc1(clMaxSize, &values);
6440    for (p = pStart; p < pEnd; ++p) {
6441      clSize = clMaxSize;
6442      PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
6443      for (cl = 0; cl < clSize; ++cl) {
6444        <Compute on closure>
6445      }
6446    }
6447    PetscFree(values);
6448 .ve
6449 
6450   Fortran Notes:
6451   The `csize` argument is not present in the Fortran binding since it is internal to the array.
6452 
6453 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecRestoreClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6454 @*/
6455 PetscErrorCode DMPlexVecGetClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6456 {
6457   PetscFunctionBeginHot;
6458   PetscCall(DMPlexVecGetOrientedClosure_Internal(dm, section, PETSC_TRUE, v, point, 0, csize, values));
6459   PetscFunctionReturn(PETSC_SUCCESS);
6460 }
6461 
6462 PetscErrorCode DMPlexVecGetClosureAtDepth_Internal(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt depth, PetscInt *csize, PetscScalar *values[])
6463 {
6464   DMLabel            depthLabel;
6465   PetscSection       clSection;
6466   IS                 clPoints;
6467   PetscScalar       *array;
6468   const PetscScalar *vArray;
6469   PetscInt          *points = NULL;
6470   const PetscInt    *clp, *perm = NULL;
6471   PetscInt           mdepth, numFields, numPoints, Np = 0, p, clsize, size;
6472 
6473   PetscFunctionBeginHot;
6474   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6475   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6476   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6477   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6478   PetscCall(DMPlexGetDepth(dm, &mdepth));
6479   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
6480   PetscCall(PetscSectionGetNumFields(section, &numFields));
6481   if (mdepth == 1 && numFields < 2) {
6482     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6483     PetscFunctionReturn(PETSC_SUCCESS);
6484   }
6485   /* Get points */
6486   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
6487   for (clsize = 0, p = 0; p < Np; p++) {
6488     PetscInt dof;
6489     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
6490     clsize += dof;
6491   }
6492   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &perm));
6493   /* Filter points */
6494   for (p = 0; p < numPoints * 2; p += 2) {
6495     PetscInt dep;
6496 
6497     PetscCall(DMLabelGetValue(depthLabel, points[p], &dep));
6498     if (dep != depth) continue;
6499     points[Np * 2 + 0] = points[p];
6500     points[Np * 2 + 1] = points[p + 1];
6501     ++Np;
6502   }
6503   /* Get array */
6504   if (!values || !*values) {
6505     PetscInt asize = 0, dof;
6506 
6507     for (p = 0; p < Np * 2; p += 2) {
6508       PetscCall(PetscSectionGetDof(section, points[p], &dof));
6509       asize += dof;
6510     }
6511     if (!values) {
6512       PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6513       if (csize) *csize = asize;
6514       PetscFunctionReturn(PETSC_SUCCESS);
6515     }
6516     PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, &array));
6517   } else {
6518     array = *values;
6519   }
6520   PetscCall(VecGetArrayRead(v, &vArray));
6521   /* Get values */
6522   if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, Np, points, numFields, perm, vArray, &size, array));
6523   else PetscCall(DMPlexVecGetClosure_Static(dm, section, Np, points, perm, vArray, &size, array));
6524   /* Cleanup points */
6525   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6526   /* Cleanup array */
6527   PetscCall(VecRestoreArrayRead(v, &vArray));
6528   if (!*values) {
6529     if (csize) *csize = size;
6530     *values = array;
6531   } else {
6532     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
6533     *csize = size;
6534   }
6535   PetscFunctionReturn(PETSC_SUCCESS);
6536 }
6537 
6538 /*@C
6539   DMPlexVecRestoreClosure - Restore the array of the values on the closure of 'point'
6540 
6541   Not collective
6542 
6543   Input Parameters:
6544 + dm      - The `DM`
6545 . section - The section describing the layout in `v`, or `NULL` to use the default section
6546 . v       - The local vector
6547 . point   - The point in the `DM`
6548 . csize   - The number of values in the closure, or `NULL`
6549 - values  - The array of values, which is a borrowed array and should not be freed
6550 
6551   Level: intermediate
6552 
6553   Note:
6554   The array values are discarded and not copied back into `v`. In order to copy values back to `v`, use `DMPlexVecSetClosure()`
6555 
6556   Fortran Notes:
6557   The `csize` argument is not present in the Fortran binding since it is internal to the array.
6558 
6559 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6560 @*/
6561 PetscErrorCode DMPlexVecRestoreClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6562 {
6563   PetscInt size = 0;
6564 
6565   PetscFunctionBegin;
6566   /* Should work without recalculating size */
6567   PetscCall(DMRestoreWorkArray(dm, size, MPIU_SCALAR, (void *)values));
6568   *values = NULL;
6569   PetscFunctionReturn(PETSC_SUCCESS);
6570 }
6571 
6572 static inline void add(PetscScalar *x, PetscScalar y)
6573 {
6574   *x += y;
6575 }
6576 static inline void insert(PetscScalar *x, PetscScalar y)
6577 {
6578   *x = y;
6579 }
6580 
6581 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[])
6582 {
6583   PetscInt        cdof;  /* The number of constraints on this point */
6584   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6585   PetscScalar    *a;
6586   PetscInt        off, cind = 0, k;
6587 
6588   PetscFunctionBegin;
6589   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6590   PetscCall(PetscSectionGetOffset(section, point, &off));
6591   a = &array[off];
6592   if (!cdof || setBC) {
6593     if (clperm) {
6594       if (perm) {
6595         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6596       } else {
6597         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6598       }
6599     } else {
6600       if (perm) {
6601         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6602       } else {
6603         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6604       }
6605     }
6606   } else {
6607     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6608     if (clperm) {
6609       if (perm) {
6610         for (k = 0; k < dof; ++k) {
6611           if ((cind < cdof) && (k == cdofs[cind])) {
6612             ++cind;
6613             continue;
6614           }
6615           fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6616         }
6617       } else {
6618         for (k = 0; k < dof; ++k) {
6619           if ((cind < cdof) && (k == cdofs[cind])) {
6620             ++cind;
6621             continue;
6622           }
6623           fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6624         }
6625       }
6626     } else {
6627       if (perm) {
6628         for (k = 0; k < dof; ++k) {
6629           if ((cind < cdof) && (k == cdofs[cind])) {
6630             ++cind;
6631             continue;
6632           }
6633           fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6634         }
6635       } else {
6636         for (k = 0; k < dof; ++k) {
6637           if ((cind < cdof) && (k == cdofs[cind])) {
6638             ++cind;
6639             continue;
6640           }
6641           fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6642         }
6643       }
6644     }
6645   }
6646   PetscFunctionReturn(PETSC_SUCCESS);
6647 }
6648 
6649 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[])
6650 {
6651   PetscInt        cdof;  /* The number of constraints on this point */
6652   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6653   PetscScalar    *a;
6654   PetscInt        off, cind = 0, k;
6655 
6656   PetscFunctionBegin;
6657   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6658   PetscCall(PetscSectionGetOffset(section, point, &off));
6659   a = &array[off];
6660   if (cdof) {
6661     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6662     if (clperm) {
6663       if (perm) {
6664         for (k = 0; k < dof; ++k) {
6665           if ((cind < cdof) && (k == cdofs[cind])) {
6666             fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6667             cind++;
6668           }
6669         }
6670       } else {
6671         for (k = 0; k < dof; ++k) {
6672           if ((cind < cdof) && (k == cdofs[cind])) {
6673             fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6674             cind++;
6675           }
6676         }
6677       }
6678     } else {
6679       if (perm) {
6680         for (k = 0; k < dof; ++k) {
6681           if ((cind < cdof) && (k == cdofs[cind])) {
6682             fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6683             cind++;
6684           }
6685         }
6686       } else {
6687         for (k = 0; k < dof; ++k) {
6688           if ((cind < cdof) && (k == cdofs[cind])) {
6689             fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6690             cind++;
6691           }
6692         }
6693       }
6694     }
6695   }
6696   PetscFunctionReturn(PETSC_SUCCESS);
6697 }
6698 
6699 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[])
6700 {
6701   PetscScalar    *a;
6702   PetscInt        fdof, foff, fcdof, foffset = *offset;
6703   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6704   PetscInt        cind = 0, b;
6705 
6706   PetscFunctionBegin;
6707   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6708   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6709   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6710   a = &array[foff];
6711   if (!fcdof || setBC) {
6712     if (clperm) {
6713       if (perm) {
6714         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6715       } else {
6716         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6717       }
6718     } else {
6719       if (perm) {
6720         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6721       } else {
6722         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6723       }
6724     }
6725   } else {
6726     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6727     if (clperm) {
6728       if (perm) {
6729         for (b = 0; b < fdof; b++) {
6730           if ((cind < fcdof) && (b == fcdofs[cind])) {
6731             ++cind;
6732             continue;
6733           }
6734           fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6735         }
6736       } else {
6737         for (b = 0; b < fdof; b++) {
6738           if ((cind < fcdof) && (b == fcdofs[cind])) {
6739             ++cind;
6740             continue;
6741           }
6742           fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6743         }
6744       }
6745     } else {
6746       if (perm) {
6747         for (b = 0; b < fdof; b++) {
6748           if ((cind < fcdof) && (b == fcdofs[cind])) {
6749             ++cind;
6750             continue;
6751           }
6752           fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6753         }
6754       } else {
6755         for (b = 0; b < fdof; b++) {
6756           if ((cind < fcdof) && (b == fcdofs[cind])) {
6757             ++cind;
6758             continue;
6759           }
6760           fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6761         }
6762       }
6763     }
6764   }
6765   *offset += fdof;
6766   PetscFunctionReturn(PETSC_SUCCESS);
6767 }
6768 
6769 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[])
6770 {
6771   PetscScalar    *a;
6772   PetscInt        fdof, foff, fcdof, foffset = *offset;
6773   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6774   PetscInt        Nc, cind = 0, ncind = 0, b;
6775   PetscBool       ncSet, fcSet;
6776 
6777   PetscFunctionBegin;
6778   PetscCall(PetscSectionGetFieldComponents(section, f, &Nc));
6779   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6780   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6781   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6782   a = &array[foff];
6783   if (fcdof) {
6784     /* We just override fcdof and fcdofs with Ncc and comps */
6785     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6786     if (clperm) {
6787       if (perm) {
6788         if (comps) {
6789           for (b = 0; b < fdof; b++) {
6790             ncSet = fcSet = PETSC_FALSE;
6791             if (b % Nc == comps[ncind]) {
6792               ncind = (ncind + 1) % Ncc;
6793               ncSet = PETSC_TRUE;
6794             }
6795             if ((cind < fcdof) && (b == fcdofs[cind])) {
6796               ++cind;
6797               fcSet = PETSC_TRUE;
6798             }
6799             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6800           }
6801         } else {
6802           for (b = 0; b < fdof; b++) {
6803             if ((cind < fcdof) && (b == fcdofs[cind])) {
6804               fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6805               ++cind;
6806             }
6807           }
6808         }
6809       } else {
6810         if (comps) {
6811           for (b = 0; b < fdof; b++) {
6812             ncSet = fcSet = PETSC_FALSE;
6813             if (b % Nc == comps[ncind]) {
6814               ncind = (ncind + 1) % Ncc;
6815               ncSet = PETSC_TRUE;
6816             }
6817             if ((cind < fcdof) && (b == fcdofs[cind])) {
6818               ++cind;
6819               fcSet = PETSC_TRUE;
6820             }
6821             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6822           }
6823         } else {
6824           for (b = 0; b < fdof; b++) {
6825             if ((cind < fcdof) && (b == fcdofs[cind])) {
6826               fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6827               ++cind;
6828             }
6829           }
6830         }
6831       }
6832     } else {
6833       if (perm) {
6834         if (comps) {
6835           for (b = 0; b < fdof; b++) {
6836             ncSet = fcSet = PETSC_FALSE;
6837             if (b % Nc == comps[ncind]) {
6838               ncind = (ncind + 1) % Ncc;
6839               ncSet = PETSC_TRUE;
6840             }
6841             if ((cind < fcdof) && (b == fcdofs[cind])) {
6842               ++cind;
6843               fcSet = PETSC_TRUE;
6844             }
6845             if (ncSet && fcSet) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6846           }
6847         } else {
6848           for (b = 0; b < fdof; b++) {
6849             if ((cind < fcdof) && (b == fcdofs[cind])) {
6850               fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6851               ++cind;
6852             }
6853           }
6854         }
6855       } else {
6856         if (comps) {
6857           for (b = 0; b < fdof; b++) {
6858             ncSet = fcSet = PETSC_FALSE;
6859             if (b % Nc == comps[ncind]) {
6860               ncind = (ncind + 1) % Ncc;
6861               ncSet = PETSC_TRUE;
6862             }
6863             if ((cind < fcdof) && (b == fcdofs[cind])) {
6864               ++cind;
6865               fcSet = PETSC_TRUE;
6866             }
6867             if (ncSet && fcSet) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6868           }
6869         } else {
6870           for (b = 0; b < fdof; b++) {
6871             if ((cind < fcdof) && (b == fcdofs[cind])) {
6872               fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6873               ++cind;
6874             }
6875           }
6876         }
6877       }
6878     }
6879   }
6880   *offset += fdof;
6881   PetscFunctionReturn(PETSC_SUCCESS);
6882 }
6883 
6884 static inline PetscErrorCode DMPlexVecSetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6885 {
6886   PetscScalar    *array;
6887   const PetscInt *cone, *coneO;
6888   PetscInt        pStart, pEnd, p, numPoints, off, dof;
6889 
6890   PetscFunctionBeginHot;
6891   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6892   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
6893   PetscCall(DMPlexGetCone(dm, point, &cone));
6894   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
6895   PetscCall(VecGetArray(v, &array));
6896   for (p = 0, off = 0; p <= numPoints; ++p, off += dof) {
6897     const PetscInt cp = !p ? point : cone[p - 1];
6898     const PetscInt o  = !p ? 0 : coneO[p - 1];
6899 
6900     if ((cp < pStart) || (cp >= pEnd)) {
6901       dof = 0;
6902       continue;
6903     }
6904     PetscCall(PetscSectionGetDof(section, cp, &dof));
6905     /* ADD_VALUES */
6906     {
6907       const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6908       PetscScalar    *a;
6909       PetscInt        cdof, coff, cind = 0, k;
6910 
6911       PetscCall(PetscSectionGetConstraintDof(section, cp, &cdof));
6912       PetscCall(PetscSectionGetOffset(section, cp, &coff));
6913       a = &array[coff];
6914       if (!cdof) {
6915         if (o >= 0) {
6916           for (k = 0; k < dof; ++k) a[k] += values[off + k];
6917         } else {
6918           for (k = 0; k < dof; ++k) a[k] += values[off + dof - k - 1];
6919         }
6920       } else {
6921         PetscCall(PetscSectionGetConstraintIndices(section, cp, &cdofs));
6922         if (o >= 0) {
6923           for (k = 0; k < dof; ++k) {
6924             if ((cind < cdof) && (k == cdofs[cind])) {
6925               ++cind;
6926               continue;
6927             }
6928             a[k] += values[off + k];
6929           }
6930         } else {
6931           for (k = 0; k < dof; ++k) {
6932             if ((cind < cdof) && (k == cdofs[cind])) {
6933               ++cind;
6934               continue;
6935             }
6936             a[k] += values[off + dof - k - 1];
6937           }
6938         }
6939       }
6940     }
6941   }
6942   PetscCall(VecRestoreArray(v, &array));
6943   PetscFunctionReturn(PETSC_SUCCESS);
6944 }
6945 
6946 /*@C
6947   DMPlexVecSetClosure - Set an array of the values on the closure of `point`
6948 
6949   Not collective
6950 
6951   Input Parameters:
6952 + dm      - The `DM`
6953 . section - The section describing the layout in `v`, or `NULL` to use the default section
6954 . v       - The local vector
6955 . point   - The point in the `DM`
6956 . values  - The array of values
6957 - mode    - The insert mode. One of `INSERT_ALL_VALUES`, `ADD_ALL_VALUES`, `INSERT_VALUES`, `ADD_VALUES`, `INSERT_BC_VALUES`, and `ADD_BC_VALUES`,
6958          where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions.
6959 
6960   Level: intermediate
6961 
6962 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`
6963 @*/
6964 PetscErrorCode DMPlexVecSetClosure(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6965 {
6966   PetscSection    clSection;
6967   IS              clPoints;
6968   PetscScalar    *array;
6969   PetscInt       *points = NULL;
6970   const PetscInt *clp, *clperm = NULL;
6971   PetscInt        depth, numFields, numPoints, p, clsize;
6972 
6973   PetscFunctionBeginHot;
6974   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6975   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6976   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6977   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6978   PetscCall(DMPlexGetDepth(dm, &depth));
6979   PetscCall(PetscSectionGetNumFields(section, &numFields));
6980   if (depth == 1 && numFields < 2 && mode == ADD_VALUES) {
6981     PetscCall(DMPlexVecSetClosure_Depth1_Static(dm, section, v, point, values, mode));
6982     PetscFunctionReturn(PETSC_SUCCESS);
6983   }
6984   /* Get points */
6985   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
6986   for (clsize = 0, p = 0; p < numPoints; p++) {
6987     PetscInt dof;
6988     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
6989     clsize += dof;
6990   }
6991   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
6992   /* Get array */
6993   PetscCall(VecGetArray(v, &array));
6994   /* Get values */
6995   if (numFields > 0) {
6996     PetscInt offset = 0, f;
6997     for (f = 0; f < numFields; ++f) {
6998       const PetscInt    **perms = NULL;
6999       const PetscScalar **flips = NULL;
7000 
7001       PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7002       switch (mode) {
7003       case INSERT_VALUES:
7004         for (p = 0; p < numPoints; p++) {
7005           const PetscInt     point = points[2 * p];
7006           const PetscInt    *perm  = perms ? perms[p] : NULL;
7007           const PetscScalar *flip  = flips ? flips[p] : NULL;
7008           PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, clperm, values, &offset, array));
7009         }
7010         break;
7011       case INSERT_ALL_VALUES:
7012         for (p = 0; p < numPoints; p++) {
7013           const PetscInt     point = points[2 * p];
7014           const PetscInt    *perm  = perms ? perms[p] : NULL;
7015           const PetscScalar *flip  = flips ? flips[p] : NULL;
7016           PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, clperm, values, &offset, array));
7017         }
7018         break;
7019       case INSERT_BC_VALUES:
7020         for (p = 0; p < numPoints; p++) {
7021           const PetscInt     point = points[2 * p];
7022           const PetscInt    *perm  = perms ? perms[p] : NULL;
7023           const PetscScalar *flip  = flips ? flips[p] : NULL;
7024           PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, insert, clperm, values, &offset, array));
7025         }
7026         break;
7027       case ADD_VALUES:
7028         for (p = 0; p < numPoints; p++) {
7029           const PetscInt     point = points[2 * p];
7030           const PetscInt    *perm  = perms ? perms[p] : NULL;
7031           const PetscScalar *flip  = flips ? flips[p] : NULL;
7032           PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, clperm, values, &offset, array));
7033         }
7034         break;
7035       case ADD_ALL_VALUES:
7036         for (p = 0; p < numPoints; p++) {
7037           const PetscInt     point = points[2 * p];
7038           const PetscInt    *perm  = perms ? perms[p] : NULL;
7039           const PetscScalar *flip  = flips ? flips[p] : NULL;
7040           PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, clperm, values, &offset, array));
7041         }
7042         break;
7043       case ADD_BC_VALUES:
7044         for (p = 0; p < numPoints; p++) {
7045           const PetscInt     point = points[2 * p];
7046           const PetscInt    *perm  = perms ? perms[p] : NULL;
7047           const PetscScalar *flip  = flips ? flips[p] : NULL;
7048           PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, add, clperm, values, &offset, array));
7049         }
7050         break;
7051       default:
7052         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
7053       }
7054       PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7055     }
7056   } else {
7057     PetscInt            dof, off;
7058     const PetscInt    **perms = NULL;
7059     const PetscScalar **flips = NULL;
7060 
7061     PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
7062     switch (mode) {
7063     case INSERT_VALUES:
7064       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7065         const PetscInt     point = points[2 * p];
7066         const PetscInt    *perm  = perms ? perms[p] : NULL;
7067         const PetscScalar *flip  = flips ? flips[p] : NULL;
7068         PetscCall(PetscSectionGetDof(section, point, &dof));
7069         PetscCall(updatePoint_private(section, point, dof, insert, PETSC_FALSE, perm, flip, clperm, values, off, array));
7070       }
7071       break;
7072     case INSERT_ALL_VALUES:
7073       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7074         const PetscInt     point = points[2 * p];
7075         const PetscInt    *perm  = perms ? perms[p] : NULL;
7076         const PetscScalar *flip  = flips ? flips[p] : NULL;
7077         PetscCall(PetscSectionGetDof(section, point, &dof));
7078         PetscCall(updatePoint_private(section, point, dof, insert, PETSC_TRUE, perm, flip, clperm, values, off, array));
7079       }
7080       break;
7081     case INSERT_BC_VALUES:
7082       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7083         const PetscInt     point = points[2 * p];
7084         const PetscInt    *perm  = perms ? perms[p] : NULL;
7085         const PetscScalar *flip  = flips ? flips[p] : NULL;
7086         PetscCall(PetscSectionGetDof(section, point, &dof));
7087         PetscCall(updatePointBC_private(section, point, dof, insert, perm, flip, clperm, values, off, array));
7088       }
7089       break;
7090     case ADD_VALUES:
7091       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7092         const PetscInt     point = points[2 * p];
7093         const PetscInt    *perm  = perms ? perms[p] : NULL;
7094         const PetscScalar *flip  = flips ? flips[p] : NULL;
7095         PetscCall(PetscSectionGetDof(section, point, &dof));
7096         PetscCall(updatePoint_private(section, point, dof, add, PETSC_FALSE, perm, flip, clperm, values, off, array));
7097       }
7098       break;
7099     case ADD_ALL_VALUES:
7100       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7101         const PetscInt     point = points[2 * p];
7102         const PetscInt    *perm  = perms ? perms[p] : NULL;
7103         const PetscScalar *flip  = flips ? flips[p] : NULL;
7104         PetscCall(PetscSectionGetDof(section, point, &dof));
7105         PetscCall(updatePoint_private(section, point, dof, add, PETSC_TRUE, perm, flip, clperm, values, off, array));
7106       }
7107       break;
7108     case ADD_BC_VALUES:
7109       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7110         const PetscInt     point = points[2 * p];
7111         const PetscInt    *perm  = perms ? perms[p] : NULL;
7112         const PetscScalar *flip  = flips ? flips[p] : NULL;
7113         PetscCall(PetscSectionGetDof(section, point, &dof));
7114         PetscCall(updatePointBC_private(section, point, dof, add, perm, flip, clperm, values, off, array));
7115       }
7116       break;
7117     default:
7118       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
7119     }
7120     PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
7121   }
7122   /* Cleanup points */
7123   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
7124   /* Cleanup array */
7125   PetscCall(VecRestoreArray(v, &array));
7126   PetscFunctionReturn(PETSC_SUCCESS);
7127 }
7128 
7129 /* Check whether the given point is in the label. If not, update the offset to skip this point */
7130 static inline PetscErrorCode CheckPoint_Private(DMLabel label, PetscInt labelId, PetscSection section, PetscInt point, PetscInt f, PetscInt *offset, PetscBool *contains)
7131 {
7132   PetscFunctionBegin;
7133   *contains = PETSC_TRUE;
7134   if (label) {
7135     PetscInt fdof;
7136 
7137     PetscCall(DMLabelStratumHasPoint(label, labelId, point, contains));
7138     if (!*contains) {
7139       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7140       *offset += fdof;
7141       PetscFunctionReturn(PETSC_SUCCESS);
7142     }
7143   }
7144   PetscFunctionReturn(PETSC_SUCCESS);
7145 }
7146 
7147 /* Unlike DMPlexVecSetClosure(), this uses plex-native closure permutation, not a user-specified permutation such as DMPlexSetClosurePermutationTensor(). */
7148 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)
7149 {
7150   PetscSection    clSection;
7151   IS              clPoints;
7152   PetscScalar    *array;
7153   PetscInt       *points = NULL;
7154   const PetscInt *clp;
7155   PetscInt        numFields, numPoints, p;
7156   PetscInt        offset = 0, f;
7157 
7158   PetscFunctionBeginHot;
7159   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7160   if (!section) PetscCall(DMGetLocalSection(dm, &section));
7161   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7162   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
7163   PetscCall(PetscSectionGetNumFields(section, &numFields));
7164   /* Get points */
7165   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
7166   /* Get array */
7167   PetscCall(VecGetArray(v, &array));
7168   /* Get values */
7169   for (f = 0; f < numFields; ++f) {
7170     const PetscInt    **perms = NULL;
7171     const PetscScalar **flips = NULL;
7172     PetscBool           contains;
7173 
7174     if (!fieldActive[f]) {
7175       for (p = 0; p < numPoints * 2; p += 2) {
7176         PetscInt fdof;
7177         PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
7178         offset += fdof;
7179       }
7180       continue;
7181     }
7182     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7183     switch (mode) {
7184     case INSERT_VALUES:
7185       for (p = 0; p < numPoints; p++) {
7186         const PetscInt     point = points[2 * p];
7187         const PetscInt    *perm  = perms ? perms[p] : NULL;
7188         const PetscScalar *flip  = flips ? flips[p] : NULL;
7189         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7190         if (!contains) continue;
7191         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, NULL, values, &offset, array));
7192       }
7193       break;
7194     case INSERT_ALL_VALUES:
7195       for (p = 0; p < numPoints; p++) {
7196         const PetscInt     point = points[2 * p];
7197         const PetscInt    *perm  = perms ? perms[p] : NULL;
7198         const PetscScalar *flip  = flips ? flips[p] : NULL;
7199         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7200         if (!contains) continue;
7201         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, NULL, values, &offset, array));
7202       }
7203       break;
7204     case INSERT_BC_VALUES:
7205       for (p = 0; p < numPoints; p++) {
7206         const PetscInt     point = points[2 * p];
7207         const PetscInt    *perm  = perms ? perms[p] : NULL;
7208         const PetscScalar *flip  = flips ? flips[p] : NULL;
7209         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7210         if (!contains) continue;
7211         PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, Ncc, comps, insert, NULL, values, &offset, array));
7212       }
7213       break;
7214     case ADD_VALUES:
7215       for (p = 0; p < numPoints; p++) {
7216         const PetscInt     point = points[2 * p];
7217         const PetscInt    *perm  = perms ? perms[p] : NULL;
7218         const PetscScalar *flip  = flips ? flips[p] : NULL;
7219         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7220         if (!contains) continue;
7221         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, NULL, values, &offset, array));
7222       }
7223       break;
7224     case ADD_ALL_VALUES:
7225       for (p = 0; p < numPoints; p++) {
7226         const PetscInt     point = points[2 * p];
7227         const PetscInt    *perm  = perms ? perms[p] : NULL;
7228         const PetscScalar *flip  = flips ? flips[p] : NULL;
7229         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7230         if (!contains) continue;
7231         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, NULL, values, &offset, array));
7232       }
7233       break;
7234     default:
7235       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
7236     }
7237     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7238   }
7239   /* Cleanup points */
7240   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
7241   /* Cleanup array */
7242   PetscCall(VecRestoreArray(v, &array));
7243   PetscFunctionReturn(PETSC_SUCCESS);
7244 }
7245 
7246 static PetscErrorCode DMPlexPrintMatSetValues(PetscViewer viewer, Mat A, PetscInt point, PetscInt numRIndices, const PetscInt rindices[], PetscInt numCIndices, const PetscInt cindices[], const PetscScalar values[])
7247 {
7248   PetscMPIInt rank;
7249   PetscInt    i, j;
7250 
7251   PetscFunctionBegin;
7252   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7253   PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat for point %" PetscInt_FMT "\n", rank, point));
7254   for (i = 0; i < numRIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat row indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, rindices[i]));
7255   for (i = 0; i < numCIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat col indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, cindices[i]));
7256   numCIndices = numCIndices ? numCIndices : numRIndices;
7257   if (!values) PetscFunctionReturn(PETSC_SUCCESS);
7258   for (i = 0; i < numRIndices; i++) {
7259     PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]", rank));
7260     for (j = 0; j < numCIndices; j++) {
7261 #if defined(PETSC_USE_COMPLEX)
7262       PetscCall(PetscViewerASCIIPrintf(viewer, " (%g,%g)", (double)PetscRealPart(values[i * numCIndices + j]), (double)PetscImaginaryPart(values[i * numCIndices + j])));
7263 #else
7264       PetscCall(PetscViewerASCIIPrintf(viewer, " %g", (double)values[i * numCIndices + j]));
7265 #endif
7266     }
7267     PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
7268   }
7269   PetscFunctionReturn(PETSC_SUCCESS);
7270 }
7271 
7272 /*
7273   DMPlexGetIndicesPoint_Internal - Add the indices for dofs on a point to an index array
7274 
7275   Input Parameters:
7276 + section - The section for this data layout
7277 . islocal - Is the section (and thus indices being requested) local or global?
7278 . point   - The point contributing dofs with these indices
7279 . off     - The global offset of this point
7280 . loff    - The local offset of each field
7281 . setBC   - The flag determining whether to include indices of boundary values
7282 . perm    - A permutation of the dofs on this point, or NULL
7283 - indperm - A permutation of the entire indices array, or NULL
7284 
7285   Output Parameter:
7286 . indices - Indices for dofs on this point
7287 
7288   Level: developer
7289 
7290   Note: The indices could be local or global, depending on the value of 'off'.
7291 */
7292 PetscErrorCode DMPlexGetIndicesPoint_Internal(PetscSection section, PetscBool islocal, PetscInt point, PetscInt off, PetscInt *loff, PetscBool setBC, const PetscInt perm[], const PetscInt indperm[], PetscInt indices[])
7293 {
7294   PetscInt        dof;   /* The number of unknowns on this point */
7295   PetscInt        cdof;  /* The number of constraints on this point */
7296   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
7297   PetscInt        cind = 0, k;
7298 
7299   PetscFunctionBegin;
7300   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
7301   PetscCall(PetscSectionGetDof(section, point, &dof));
7302   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
7303   if (!cdof || setBC) {
7304     for (k = 0; k < dof; ++k) {
7305       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
7306       const PetscInt ind    = indperm ? indperm[preind] : preind;
7307 
7308       indices[ind] = off + k;
7309     }
7310   } else {
7311     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
7312     for (k = 0; k < dof; ++k) {
7313       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
7314       const PetscInt ind    = indperm ? indperm[preind] : preind;
7315 
7316       if ((cind < cdof) && (k == cdofs[cind])) {
7317         /* Insert check for returning constrained indices */
7318         indices[ind] = -(off + k + 1);
7319         ++cind;
7320       } else {
7321         indices[ind] = off + k - (islocal ? 0 : cind);
7322       }
7323     }
7324   }
7325   *loff += dof;
7326   PetscFunctionReturn(PETSC_SUCCESS);
7327 }
7328 
7329 /*
7330  DMPlexGetIndicesPointFields_Internal - gets section indices for a point in its canonical ordering.
7331 
7332  Input Parameters:
7333 + section - a section (global or local)
7334 - islocal - `PETSC_TRUE` if requesting local indices (i.e., section is local); `PETSC_FALSE` for global
7335 . point - point within section
7336 . off - The offset of this point in the (local or global) indexed space - should match islocal and (usually) the section
7337 . foffs - array of length numFields containing the offset in canonical point ordering (the location in indices) of each field
7338 . setBC - identify constrained (boundary condition) points via involution.
7339 . perms - perms[f][permsoff][:] is a permutation of dofs within each field
7340 . permsoff - offset
7341 - indperm - index permutation
7342 
7343  Output Parameter:
7344 . foffs - each entry is incremented by the number of (unconstrained if setBC=FALSE) dofs in that field
7345 . indices - array to hold indices (as defined by section) of each dof associated with point
7346 
7347  Notes:
7348  If section is local and setBC=true, there is no distinction between constrained and unconstrained dofs.
7349  If section is local and setBC=false, the indices for constrained points are the involution -(i+1) of their position
7350  in the local vector.
7351 
7352  If section is global and setBC=false, the indices for constrained points are negative (and their value is not
7353  significant).  It is invalid to call with a global section and setBC=true.
7354 
7355  Developer Note:
7356  The section is only used for field layout, so islocal is technically a statement about the offset (off).  At some point
7357  in the future, global sections may have fields set, in which case we could pass the global section and obtain the
7358  offset could be obtained from the section instead of passing it explicitly as we do now.
7359 
7360  Example:
7361  Suppose a point contains one field with three components, and for which the unconstrained indices are {10, 11, 12}.
7362  When the middle component is constrained, we get the array {10, -12, 12} for (islocal=TRUE, setBC=FALSE).
7363  Note that -12 is the involution of 11, so the user can involute negative indices to recover local indices.
7364  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.
7365 
7366  Level: developer
7367 */
7368 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[])
7369 {
7370   PetscInt numFields, foff, f;
7371 
7372   PetscFunctionBegin;
7373   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
7374   PetscCall(PetscSectionGetNumFields(section, &numFields));
7375   for (f = 0, foff = 0; f < numFields; ++f) {
7376     PetscInt        fdof, cfdof;
7377     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7378     PetscInt        cind = 0, b;
7379     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7380 
7381     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7382     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7383     if (!cfdof || setBC) {
7384       for (b = 0; b < fdof; ++b) {
7385         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7386         const PetscInt ind    = indperm ? indperm[preind] : preind;
7387 
7388         indices[ind] = off + foff + b;
7389       }
7390     } else {
7391       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7392       for (b = 0; b < fdof; ++b) {
7393         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7394         const PetscInt ind    = indperm ? indperm[preind] : preind;
7395 
7396         if ((cind < cfdof) && (b == fcdofs[cind])) {
7397           indices[ind] = -(off + foff + b + 1);
7398           ++cind;
7399         } else {
7400           indices[ind] = off + foff + b - (islocal ? 0 : cind);
7401         }
7402       }
7403     }
7404     foff += (setBC || islocal ? fdof : (fdof - cfdof));
7405     foffs[f] += fdof;
7406   }
7407   PetscFunctionReturn(PETSC_SUCCESS);
7408 }
7409 
7410 /*
7411   This version believes the globalSection offsets for each field, rather than just the point offset
7412 
7413  . foffs - The offset into 'indices' for each field, since it is segregated by field
7414 
7415  Notes:
7416  The semantics of this function relate to that of setBC=FALSE in DMPlexGetIndicesPointFields_Internal.
7417  Since this function uses global indices, setBC=TRUE would be invalid, so no such argument exists.
7418 */
7419 static PetscErrorCode DMPlexGetIndicesPointFieldsSplit_Internal(PetscSection section, PetscSection globalSection, PetscInt point, PetscInt foffs[], const PetscInt ***perms, PetscInt permsoff, const PetscInt indperm[], PetscInt indices[])
7420 {
7421   PetscInt numFields, foff, f;
7422 
7423   PetscFunctionBegin;
7424   PetscCall(PetscSectionGetNumFields(section, &numFields));
7425   for (f = 0; f < numFields; ++f) {
7426     PetscInt        fdof, cfdof;
7427     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7428     PetscInt        cind = 0, b;
7429     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7430 
7431     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7432     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7433     PetscCall(PetscSectionGetFieldOffset(globalSection, point, f, &foff));
7434     if (!cfdof) {
7435       for (b = 0; b < fdof; ++b) {
7436         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7437         const PetscInt ind    = indperm ? indperm[preind] : preind;
7438 
7439         indices[ind] = foff + b;
7440       }
7441     } else {
7442       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7443       for (b = 0; b < fdof; ++b) {
7444         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7445         const PetscInt ind    = indperm ? indperm[preind] : preind;
7446 
7447         if ((cind < cfdof) && (b == fcdofs[cind])) {
7448           indices[ind] = -(foff + b + 1);
7449           ++cind;
7450         } else {
7451           indices[ind] = foff + b - cind;
7452         }
7453       }
7454     }
7455     foffs[f] += fdof;
7456   }
7457   PetscFunctionReturn(PETSC_SUCCESS);
7458 }
7459 
7460 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)
7461 {
7462   Mat             cMat;
7463   PetscSection    aSec, cSec;
7464   IS              aIS;
7465   PetscInt        aStart = -1, aEnd = -1;
7466   const PetscInt *anchors;
7467   PetscInt        numFields, f, p, q, newP = 0;
7468   PetscInt        newNumPoints = 0, newNumIndices = 0;
7469   PetscInt       *newPoints, *indices, *newIndices;
7470   PetscInt        maxAnchor, maxDof;
7471   PetscInt        newOffsets[32];
7472   PetscInt       *pointMatOffsets[32];
7473   PetscInt       *newPointOffsets[32];
7474   PetscScalar    *pointMat[32];
7475   PetscScalar    *newValues      = NULL, *tmpValues;
7476   PetscBool       anyConstrained = PETSC_FALSE;
7477 
7478   PetscFunctionBegin;
7479   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7480   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7481   PetscCall(PetscSectionGetNumFields(section, &numFields));
7482 
7483   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
7484   /* if there are point-to-point constraints */
7485   if (aSec) {
7486     PetscCall(PetscArrayzero(newOffsets, 32));
7487     PetscCall(ISGetIndices(aIS, &anchors));
7488     PetscCall(PetscSectionGetChart(aSec, &aStart, &aEnd));
7489     /* figure out how many points are going to be in the new element matrix
7490      * (we allow double counting, because it's all just going to be summed
7491      * into the global matrix anyway) */
7492     for (p = 0; p < 2 * numPoints; p += 2) {
7493       PetscInt b    = points[p];
7494       PetscInt bDof = 0, bSecDof;
7495 
7496       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7497       if (!bSecDof) continue;
7498       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7499       if (bDof) {
7500         /* this point is constrained */
7501         /* it is going to be replaced by its anchors */
7502         PetscInt bOff, q;
7503 
7504         anyConstrained = PETSC_TRUE;
7505         newNumPoints += bDof;
7506         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7507         for (q = 0; q < bDof; q++) {
7508           PetscInt a = anchors[bOff + q];
7509           PetscInt aDof;
7510 
7511           PetscCall(PetscSectionGetDof(section, a, &aDof));
7512           newNumIndices += aDof;
7513           for (f = 0; f < numFields; ++f) {
7514             PetscInt fDof;
7515 
7516             PetscCall(PetscSectionGetFieldDof(section, a, f, &fDof));
7517             newOffsets[f + 1] += fDof;
7518           }
7519         }
7520       } else {
7521         /* this point is not constrained */
7522         newNumPoints++;
7523         newNumIndices += bSecDof;
7524         for (f = 0; f < numFields; ++f) {
7525           PetscInt fDof;
7526 
7527           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7528           newOffsets[f + 1] += fDof;
7529         }
7530       }
7531     }
7532   }
7533   if (!anyConstrained) {
7534     if (outNumPoints) *outNumPoints = 0;
7535     if (outNumIndices) *outNumIndices = 0;
7536     if (outPoints) *outPoints = NULL;
7537     if (outValues) *outValues = NULL;
7538     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7539     PetscFunctionReturn(PETSC_SUCCESS);
7540   }
7541 
7542   if (outNumPoints) *outNumPoints = newNumPoints;
7543   if (outNumIndices) *outNumIndices = newNumIndices;
7544 
7545   for (f = 0; f < numFields; ++f) newOffsets[f + 1] += newOffsets[f];
7546 
7547   if (!outPoints && !outValues) {
7548     if (offsets) {
7549       for (f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7550     }
7551     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7552     PetscFunctionReturn(PETSC_SUCCESS);
7553   }
7554 
7555   PetscCheck(!numFields || newOffsets[numFields] == newNumIndices, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, newOffsets[numFields], newNumIndices);
7556 
7557   PetscCall(DMGetDefaultConstraints(dm, &cSec, &cMat, NULL));
7558 
7559   /* workspaces */
7560   if (numFields) {
7561     for (f = 0; f < numFields; f++) {
7562       PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[f]));
7563       PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[f]));
7564     }
7565   } else {
7566     PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[0]));
7567     PetscCall(DMGetWorkArray(dm, numPoints, MPIU_INT, &newPointOffsets[0]));
7568   }
7569 
7570   /* get workspaces for the point-to-point matrices */
7571   if (numFields) {
7572     PetscInt totalOffset, totalMatOffset;
7573 
7574     for (p = 0; p < numPoints; p++) {
7575       PetscInt b    = points[2 * p];
7576       PetscInt bDof = 0, bSecDof;
7577 
7578       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7579       if (!bSecDof) {
7580         for (f = 0; f < numFields; f++) {
7581           newPointOffsets[f][p + 1] = 0;
7582           pointMatOffsets[f][p + 1] = 0;
7583         }
7584         continue;
7585       }
7586       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7587       if (bDof) {
7588         for (f = 0; f < numFields; f++) {
7589           PetscInt fDof, q, bOff, allFDof = 0;
7590 
7591           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7592           PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7593           for (q = 0; q < bDof; q++) {
7594             PetscInt a = anchors[bOff + q];
7595             PetscInt aFDof;
7596 
7597             PetscCall(PetscSectionGetFieldDof(section, a, f, &aFDof));
7598             allFDof += aFDof;
7599           }
7600           newPointOffsets[f][p + 1] = allFDof;
7601           pointMatOffsets[f][p + 1] = fDof * allFDof;
7602         }
7603       } else {
7604         for (f = 0; f < numFields; f++) {
7605           PetscInt fDof;
7606 
7607           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7608           newPointOffsets[f][p + 1] = fDof;
7609           pointMatOffsets[f][p + 1] = 0;
7610         }
7611       }
7612     }
7613     for (f = 0, totalOffset = 0, totalMatOffset = 0; f < numFields; f++) {
7614       newPointOffsets[f][0] = totalOffset;
7615       pointMatOffsets[f][0] = totalMatOffset;
7616       for (p = 0; p < numPoints; p++) {
7617         newPointOffsets[f][p + 1] += newPointOffsets[f][p];
7618         pointMatOffsets[f][p + 1] += pointMatOffsets[f][p];
7619       }
7620       totalOffset    = newPointOffsets[f][numPoints];
7621       totalMatOffset = pointMatOffsets[f][numPoints];
7622       PetscCall(DMGetWorkArray(dm, pointMatOffsets[f][numPoints], MPIU_SCALAR, &pointMat[f]));
7623     }
7624   } else {
7625     for (p = 0; p < numPoints; p++) {
7626       PetscInt b    = points[2 * p];
7627       PetscInt bDof = 0, bSecDof;
7628 
7629       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7630       if (!bSecDof) {
7631         newPointOffsets[0][p + 1] = 0;
7632         pointMatOffsets[0][p + 1] = 0;
7633         continue;
7634       }
7635       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7636       if (bDof) {
7637         PetscInt bOff, q, allDof = 0;
7638 
7639         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7640         for (q = 0; q < bDof; q++) {
7641           PetscInt a = anchors[bOff + q], aDof;
7642 
7643           PetscCall(PetscSectionGetDof(section, a, &aDof));
7644           allDof += aDof;
7645         }
7646         newPointOffsets[0][p + 1] = allDof;
7647         pointMatOffsets[0][p + 1] = bSecDof * allDof;
7648       } else {
7649         newPointOffsets[0][p + 1] = bSecDof;
7650         pointMatOffsets[0][p + 1] = 0;
7651       }
7652     }
7653     newPointOffsets[0][0] = 0;
7654     pointMatOffsets[0][0] = 0;
7655     for (p = 0; p < numPoints; p++) {
7656       newPointOffsets[0][p + 1] += newPointOffsets[0][p];
7657       pointMatOffsets[0][p + 1] += pointMatOffsets[0][p];
7658     }
7659     PetscCall(DMGetWorkArray(dm, pointMatOffsets[0][numPoints], MPIU_SCALAR, &pointMat[0]));
7660   }
7661 
7662   /* output arrays */
7663   PetscCall(DMGetWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7664 
7665   /* get the point-to-point matrices; construct newPoints */
7666   PetscCall(PetscSectionGetMaxDof(aSec, &maxAnchor));
7667   PetscCall(PetscSectionGetMaxDof(section, &maxDof));
7668   PetscCall(DMGetWorkArray(dm, maxDof, MPIU_INT, &indices));
7669   PetscCall(DMGetWorkArray(dm, maxAnchor * maxDof, MPIU_INT, &newIndices));
7670   if (numFields) {
7671     for (p = 0, newP = 0; p < numPoints; p++) {
7672       PetscInt b    = points[2 * p];
7673       PetscInt o    = points[2 * p + 1];
7674       PetscInt bDof = 0, bSecDof;
7675 
7676       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7677       if (!bSecDof) continue;
7678       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7679       if (bDof) {
7680         PetscInt fStart[32], fEnd[32], fAnchorStart[32], fAnchorEnd[32], bOff, q;
7681 
7682         fStart[0] = 0;
7683         fEnd[0]   = 0;
7684         for (f = 0; f < numFields; f++) {
7685           PetscInt fDof;
7686 
7687           PetscCall(PetscSectionGetFieldDof(cSec, b, f, &fDof));
7688           fStart[f + 1] = fStart[f] + fDof;
7689           fEnd[f + 1]   = fStart[f + 1];
7690         }
7691         PetscCall(PetscSectionGetOffset(cSec, b, &bOff));
7692         PetscCall(DMPlexGetIndicesPointFields_Internal(cSec, PETSC_TRUE, b, bOff, fEnd, PETSC_TRUE, perms, p, NULL, indices));
7693 
7694         fAnchorStart[0] = 0;
7695         fAnchorEnd[0]   = 0;
7696         for (f = 0; f < numFields; f++) {
7697           PetscInt fDof = newPointOffsets[f][p + 1] - newPointOffsets[f][p];
7698 
7699           fAnchorStart[f + 1] = fAnchorStart[f] + fDof;
7700           fAnchorEnd[f + 1]   = fAnchorStart[f + 1];
7701         }
7702         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7703         for (q = 0; q < bDof; q++) {
7704           PetscInt a = anchors[bOff + q], aOff;
7705 
7706           /* we take the orientation of ap into account in the order that we constructed the indices above: the newly added points have no orientation */
7707           newPoints[2 * (newP + q)]     = a;
7708           newPoints[2 * (newP + q) + 1] = 0;
7709           PetscCall(PetscSectionGetOffset(section, a, &aOff));
7710           PetscCall(DMPlexGetIndicesPointFields_Internal(section, PETSC_TRUE, a, aOff, fAnchorEnd, PETSC_TRUE, NULL, -1, NULL, newIndices));
7711         }
7712         newP += bDof;
7713 
7714         if (outValues) {
7715           /* get the point-to-point submatrix */
7716           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]));
7717         }
7718       } else {
7719         newPoints[2 * newP]     = b;
7720         newPoints[2 * newP + 1] = o;
7721         newP++;
7722       }
7723     }
7724   } else {
7725     for (p = 0; p < numPoints; p++) {
7726       PetscInt b    = points[2 * p];
7727       PetscInt o    = points[2 * p + 1];
7728       PetscInt bDof = 0, bSecDof;
7729 
7730       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7731       if (!bSecDof) continue;
7732       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7733       if (bDof) {
7734         PetscInt bEnd = 0, bAnchorEnd = 0, bOff;
7735 
7736         PetscCall(PetscSectionGetOffset(cSec, b, &bOff));
7737         PetscCall(DMPlexGetIndicesPoint_Internal(cSec, PETSC_TRUE, b, bOff, &bEnd, PETSC_TRUE, (perms && perms[0]) ? perms[0][p] : NULL, NULL, indices));
7738 
7739         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7740         for (q = 0; q < bDof; q++) {
7741           PetscInt a = anchors[bOff + q], aOff;
7742 
7743           /* we take the orientation of ap into account in the order that we constructed the indices above: the newly added points have no orientation */
7744 
7745           newPoints[2 * (newP + q)]     = a;
7746           newPoints[2 * (newP + q) + 1] = 0;
7747           PetscCall(PetscSectionGetOffset(section, a, &aOff));
7748           PetscCall(DMPlexGetIndicesPoint_Internal(section, PETSC_TRUE, a, aOff, &bAnchorEnd, PETSC_TRUE, NULL, NULL, newIndices));
7749         }
7750         newP += bDof;
7751 
7752         /* get the point-to-point submatrix */
7753         if (outValues) PetscCall(MatGetValues(cMat, bEnd, indices, bAnchorEnd, newIndices, pointMat[0] + pointMatOffsets[0][p]));
7754       } else {
7755         newPoints[2 * newP]     = b;
7756         newPoints[2 * newP + 1] = o;
7757         newP++;
7758       }
7759     }
7760   }
7761 
7762   if (outValues) {
7763     PetscCall(DMGetWorkArray(dm, newNumIndices * numIndices, MPIU_SCALAR, &tmpValues));
7764     PetscCall(PetscArrayzero(tmpValues, newNumIndices * numIndices));
7765     /* multiply constraints on the right */
7766     if (numFields) {
7767       for (f = 0; f < numFields; f++) {
7768         PetscInt oldOff = offsets[f];
7769 
7770         for (p = 0; p < numPoints; p++) {
7771           PetscInt cStart = newPointOffsets[f][p];
7772           PetscInt b      = points[2 * p];
7773           PetscInt c, r, k;
7774           PetscInt dof;
7775 
7776           PetscCall(PetscSectionGetFieldDof(section, b, f, &dof));
7777           if (!dof) continue;
7778           if (pointMatOffsets[f][p] < pointMatOffsets[f][p + 1]) {
7779             PetscInt           nCols = newPointOffsets[f][p + 1] - cStart;
7780             const PetscScalar *mat   = pointMat[f] + pointMatOffsets[f][p];
7781 
7782             for (r = 0; r < numIndices; r++) {
7783               for (c = 0; c < nCols; c++) {
7784                 for (k = 0; k < dof; k++) tmpValues[r * newNumIndices + cStart + c] += values[r * numIndices + oldOff + k] * mat[k * nCols + c];
7785               }
7786             }
7787           } else {
7788             /* copy this column as is */
7789             for (r = 0; r < numIndices; r++) {
7790               for (c = 0; c < dof; c++) tmpValues[r * newNumIndices + cStart + c] = values[r * numIndices + oldOff + c];
7791             }
7792           }
7793           oldOff += dof;
7794         }
7795       }
7796     } else {
7797       PetscInt oldOff = 0;
7798       for (p = 0; p < numPoints; p++) {
7799         PetscInt cStart = newPointOffsets[0][p];
7800         PetscInt b      = points[2 * p];
7801         PetscInt c, r, k;
7802         PetscInt dof;
7803 
7804         PetscCall(PetscSectionGetDof(section, b, &dof));
7805         if (!dof) continue;
7806         if (pointMatOffsets[0][p] < pointMatOffsets[0][p + 1]) {
7807           PetscInt           nCols = newPointOffsets[0][p + 1] - cStart;
7808           const PetscScalar *mat   = pointMat[0] + pointMatOffsets[0][p];
7809 
7810           for (r = 0; r < numIndices; r++) {
7811             for (c = 0; c < nCols; c++) {
7812               for (k = 0; k < dof; k++) tmpValues[r * newNumIndices + cStart + c] += mat[k * nCols + c] * values[r * numIndices + oldOff + k];
7813             }
7814           }
7815         } else {
7816           /* copy this column as is */
7817           for (r = 0; r < numIndices; r++) {
7818             for (c = 0; c < dof; c++) tmpValues[r * newNumIndices + cStart + c] = values[r * numIndices + oldOff + c];
7819           }
7820         }
7821         oldOff += dof;
7822       }
7823     }
7824 
7825     if (multiplyLeft) {
7826       PetscCall(DMGetWorkArray(dm, newNumIndices * newNumIndices, MPIU_SCALAR, &newValues));
7827       PetscCall(PetscArrayzero(newValues, newNumIndices * newNumIndices));
7828       /* multiply constraints transpose on the left */
7829       if (numFields) {
7830         for (f = 0; f < numFields; f++) {
7831           PetscInt oldOff = offsets[f];
7832 
7833           for (p = 0; p < numPoints; p++) {
7834             PetscInt rStart = newPointOffsets[f][p];
7835             PetscInt b      = points[2 * p];
7836             PetscInt c, r, k;
7837             PetscInt dof;
7838 
7839             PetscCall(PetscSectionGetFieldDof(section, b, f, &dof));
7840             if (pointMatOffsets[f][p] < pointMatOffsets[f][p + 1]) {
7841               PetscInt                          nRows = newPointOffsets[f][p + 1] - rStart;
7842               const PetscScalar *PETSC_RESTRICT mat   = pointMat[f] + pointMatOffsets[f][p];
7843 
7844               for (r = 0; r < nRows; r++) {
7845                 for (c = 0; c < newNumIndices; c++) {
7846                   for (k = 0; k < dof; k++) newValues[(rStart + r) * newNumIndices + c] += mat[k * nRows + r] * tmpValues[(oldOff + k) * newNumIndices + c];
7847                 }
7848               }
7849             } else {
7850               /* copy this row as is */
7851               for (r = 0; r < dof; r++) {
7852                 for (c = 0; c < newNumIndices; c++) newValues[(rStart + r) * newNumIndices + c] = tmpValues[(oldOff + r) * newNumIndices + c];
7853               }
7854             }
7855             oldOff += dof;
7856           }
7857         }
7858       } else {
7859         PetscInt oldOff = 0;
7860 
7861         for (p = 0; p < numPoints; p++) {
7862           PetscInt rStart = newPointOffsets[0][p];
7863           PetscInt b      = points[2 * p];
7864           PetscInt c, r, k;
7865           PetscInt dof;
7866 
7867           PetscCall(PetscSectionGetDof(section, b, &dof));
7868           if (pointMatOffsets[0][p] < pointMatOffsets[0][p + 1]) {
7869             PetscInt                          nRows = newPointOffsets[0][p + 1] - rStart;
7870             const PetscScalar *PETSC_RESTRICT mat   = pointMat[0] + pointMatOffsets[0][p];
7871 
7872             for (r = 0; r < nRows; r++) {
7873               for (c = 0; c < newNumIndices; c++) {
7874                 for (k = 0; k < dof; k++) newValues[(rStart + r) * newNumIndices + c] += mat[k * nRows + r] * tmpValues[(oldOff + k) * newNumIndices + c];
7875               }
7876             }
7877           } else {
7878             /* copy this row as is */
7879             for (r = 0; r < dof; r++) {
7880               for (c = 0; c < newNumIndices; c++) newValues[(rStart + r) * newNumIndices + c] = tmpValues[(oldOff + r) * newNumIndices + c];
7881             }
7882           }
7883           oldOff += dof;
7884         }
7885       }
7886 
7887       PetscCall(DMRestoreWorkArray(dm, newNumIndices * numIndices, MPIU_SCALAR, &tmpValues));
7888     } else {
7889       newValues = tmpValues;
7890     }
7891   }
7892 
7893   /* clean up */
7894   PetscCall(DMRestoreWorkArray(dm, maxDof, MPIU_INT, &indices));
7895   PetscCall(DMRestoreWorkArray(dm, maxAnchor * maxDof, MPIU_INT, &newIndices));
7896 
7897   if (numFields) {
7898     for (f = 0; f < numFields; f++) {
7899       PetscCall(DMRestoreWorkArray(dm, pointMatOffsets[f][numPoints], MPIU_SCALAR, &pointMat[f]));
7900       PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[f]));
7901       PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[f]));
7902     }
7903   } else {
7904     PetscCall(DMRestoreWorkArray(dm, pointMatOffsets[0][numPoints], MPIU_SCALAR, &pointMat[0]));
7905     PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[0]));
7906     PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[0]));
7907   }
7908   PetscCall(ISRestoreIndices(aIS, &anchors));
7909 
7910   /* output */
7911   if (outPoints) {
7912     *outPoints = newPoints;
7913   } else {
7914     PetscCall(DMRestoreWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7915   }
7916   if (outValues) *outValues = newValues;
7917   for (f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7918   PetscFunctionReturn(PETSC_SUCCESS);
7919 }
7920 
7921 /*@C
7922   DMPlexGetClosureIndices - Gets the global dof indices associated with the closure of the given point within the provided sections.
7923 
7924   Not collective
7925 
7926   Input Parameters:
7927 + dm         - The `DM`
7928 . section    - The `PetscSection` describing the points (a local section)
7929 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
7930 . point      - The point defining the closure
7931 - useClPerm  - Use the closure point permutation if available
7932 
7933   Output Parameters:
7934 + numIndices - The number of dof indices in the closure of point with the input sections
7935 . indices    - The dof indices
7936 . outOffsets - Array to write the field offsets into, or `NULL`
7937 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or `NULL`
7938 
7939   Level: advanced
7940 
7941   Notes:
7942   Must call `DMPlexRestoreClosureIndices()` to free allocated memory
7943 
7944   If `idxSection` is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
7945   of those indices is not significant.  If `idxSection` is local, the constrained dofs will yield the involution -(idx+1)
7946   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
7947   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when `idxSection` == section, otherwise global
7948   indices (with the above semantics) are implied.
7949 
7950 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`,
7951           `PetscSection`, `DMGetGlobalSection()`
7952 @*/
7953 PetscErrorCode DMPlexGetClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
7954 {
7955   /* Closure ordering */
7956   PetscSection    clSection;
7957   IS              clPoints;
7958   const PetscInt *clp;
7959   PetscInt       *points;
7960   const PetscInt *clperm = NULL;
7961   /* Dof permutation and sign flips */
7962   const PetscInt    **perms[32] = {NULL};
7963   const PetscScalar **flips[32] = {NULL};
7964   PetscScalar        *valCopy   = NULL;
7965   /* Hanging node constraints */
7966   PetscInt    *pointsC = NULL;
7967   PetscScalar *valuesC = NULL;
7968   PetscInt     NclC, NiC;
7969 
7970   PetscInt *idx;
7971   PetscInt  Nf, Ncl, Ni = 0, offsets[32], p, f;
7972   PetscBool isLocal = (section == idxSection) ? PETSC_TRUE : PETSC_FALSE;
7973 
7974   PetscFunctionBeginHot;
7975   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7976   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7977   PetscValidHeaderSpecific(idxSection, PETSC_SECTION_CLASSID, 3);
7978   if (numIndices) PetscAssertPointer(numIndices, 6);
7979   if (indices) PetscAssertPointer(indices, 7);
7980   if (outOffsets) PetscAssertPointer(outOffsets, 8);
7981   if (values) PetscAssertPointer(values, 9);
7982   PetscCall(PetscSectionGetNumFields(section, &Nf));
7983   PetscCheck(Nf <= 31, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", Nf);
7984   PetscCall(PetscArrayzero(offsets, 32));
7985   /* 1) Get points in closure */
7986   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &Ncl, &points, &clSection, &clPoints, &clp));
7987   if (useClPerm) {
7988     PetscInt depth, clsize;
7989     PetscCall(DMPlexGetPointDepth(dm, point, &depth));
7990     for (clsize = 0, p = 0; p < Ncl; p++) {
7991       PetscInt dof;
7992       PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
7993       clsize += dof;
7994     }
7995     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
7996   }
7997   /* 2) Get number of indices on these points and field offsets from section */
7998   for (p = 0; p < Ncl * 2; p += 2) {
7999     PetscInt dof, fdof;
8000 
8001     PetscCall(PetscSectionGetDof(section, points[p], &dof));
8002     for (f = 0; f < Nf; ++f) {
8003       PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
8004       offsets[f + 1] += fdof;
8005     }
8006     Ni += dof;
8007   }
8008   for (f = 1; f < Nf; ++f) offsets[f + 1] += offsets[f];
8009   PetscCheck(!Nf || offsets[Nf] == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, offsets[Nf], Ni);
8010   /* 3) Get symmetries and sign flips. Apply sign flips to values if passed in (only works for square values matrix) */
8011   for (f = 0; f < PetscMax(1, Nf); ++f) {
8012     if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
8013     else PetscCall(PetscSectionGetPointSyms(section, Ncl, points, &perms[f], &flips[f]));
8014     /* may need to apply sign changes to the element matrix */
8015     if (values && flips[f]) {
8016       PetscInt foffset = offsets[f];
8017 
8018       for (p = 0; p < Ncl; ++p) {
8019         PetscInt           pnt  = points[2 * p], fdof;
8020         const PetscScalar *flip = flips[f] ? flips[f][p] : NULL;
8021 
8022         if (!Nf) PetscCall(PetscSectionGetDof(section, pnt, &fdof));
8023         else PetscCall(PetscSectionGetFieldDof(section, pnt, f, &fdof));
8024         if (flip) {
8025           PetscInt i, j, k;
8026 
8027           if (!valCopy) {
8028             PetscCall(DMGetWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
8029             for (j = 0; j < Ni * Ni; ++j) valCopy[j] = (*values)[j];
8030             *values = valCopy;
8031           }
8032           for (i = 0; i < fdof; ++i) {
8033             PetscScalar fval = flip[i];
8034 
8035             for (k = 0; k < Ni; ++k) {
8036               valCopy[Ni * (foffset + i) + k] *= fval;
8037               valCopy[Ni * k + (foffset + i)] *= fval;
8038             }
8039           }
8040         }
8041         foffset += fdof;
8042       }
8043     }
8044   }
8045   /* 4) Apply hanging node constraints. Get new symmetries and replace all storage with constrained storage */
8046   PetscCall(DMPlexAnchorsModifyMat(dm, section, Ncl, Ni, points, perms, values ? *values : NULL, &NclC, &NiC, &pointsC, values ? &valuesC : NULL, offsets, PETSC_TRUE));
8047   if (NclC) {
8048     if (valCopy) PetscCall(DMRestoreWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
8049     for (f = 0; f < PetscMax(1, Nf); ++f) {
8050       if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
8051       else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
8052     }
8053     for (f = 0; f < PetscMax(1, Nf); ++f) {
8054       if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, NclC, pointsC, &perms[f], &flips[f]));
8055       else PetscCall(PetscSectionGetPointSyms(section, NclC, pointsC, &perms[f], &flips[f]));
8056     }
8057     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
8058     Ncl    = NclC;
8059     Ni     = NiC;
8060     points = pointsC;
8061     if (values) *values = valuesC;
8062   }
8063   /* 5) Calculate indices */
8064   PetscCall(DMGetWorkArray(dm, Ni, MPIU_INT, &idx));
8065   if (Nf) {
8066     PetscInt  idxOff;
8067     PetscBool useFieldOffsets;
8068 
8069     if (outOffsets) {
8070       for (f = 0; f <= Nf; f++) outOffsets[f] = offsets[f];
8071     }
8072     PetscCall(PetscSectionGetUseFieldOffsets(idxSection, &useFieldOffsets));
8073     if (useFieldOffsets) {
8074       for (p = 0; p < Ncl; ++p) {
8075         const PetscInt pnt = points[p * 2];
8076 
8077         PetscCall(DMPlexGetIndicesPointFieldsSplit_Internal(section, idxSection, pnt, offsets, perms, p, clperm, idx));
8078       }
8079     } else {
8080       for (p = 0; p < Ncl; ++p) {
8081         const PetscInt pnt = points[p * 2];
8082 
8083         PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
8084         /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
8085          * not (at the time of this writing) have fields set. They probably should, in which case we would pass the
8086          * global section. */
8087         PetscCall(DMPlexGetIndicesPointFields_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, offsets, PETSC_FALSE, perms, p, clperm, idx));
8088       }
8089     }
8090   } else {
8091     PetscInt off = 0, idxOff;
8092 
8093     for (p = 0; p < Ncl; ++p) {
8094       const PetscInt  pnt  = points[p * 2];
8095       const PetscInt *perm = perms[0] ? perms[0][p] : NULL;
8096 
8097       PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
8098       /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
8099        * not (at the time of this writing) have fields set. They probably should, in which case we would pass the global section. */
8100       PetscCall(DMPlexGetIndicesPoint_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, &off, PETSC_FALSE, perm, clperm, idx));
8101     }
8102   }
8103   /* 6) Cleanup */
8104   for (f = 0; f < PetscMax(1, Nf); ++f) {
8105     if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
8106     else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
8107   }
8108   if (NclC) {
8109     PetscCall(DMRestoreWorkArray(dm, NclC * 2, MPIU_INT, &pointsC));
8110   } else {
8111     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
8112   }
8113 
8114   if (numIndices) *numIndices = Ni;
8115   if (indices) *indices = idx;
8116   PetscFunctionReturn(PETSC_SUCCESS);
8117 }
8118 
8119 /*@C
8120   DMPlexRestoreClosureIndices - Restores the global dof indices associated with the closure of the given point within the provided sections.
8121 
8122   Not collective
8123 
8124   Input Parameters:
8125 + dm         - The `DM`
8126 . section    - The `PetscSection` describing the points (a local section)
8127 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
8128 . point      - The point defining the closure
8129 - useClPerm  - Use the closure point permutation if available
8130 
8131   Output Parameters:
8132 + numIndices - The number of dof indices in the closure of point with the input sections
8133 . indices    - The dof indices
8134 . outOffsets - Array to write the field offsets into, or `NULL`
8135 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or `NULL`
8136 
8137   Level: advanced
8138 
8139   Notes:
8140   If values were modified, the user is responsible for calling `DMRestoreWorkArray`(dm, 0, `MPIU_SCALAR`, &values).
8141 
8142   If idxSection is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
8143   of those indices is not significant.  If idxSection is local, the constrained dofs will yield the involution -(idx+1)
8144   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
8145   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when idxSection == section, otherwise global
8146   indices (with the above semantics) are implied.
8147 
8148 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`, `DMGetGlobalSection()`
8149 @*/
8150 PetscErrorCode DMPlexRestoreClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
8151 {
8152   PetscFunctionBegin;
8153   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8154   PetscAssertPointer(indices, 7);
8155   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, indices));
8156   PetscFunctionReturn(PETSC_SUCCESS);
8157 }
8158 
8159 PetscErrorCode DMPlexMatSetClosure_Internal(DM dm, PetscSection section, PetscSection globalSection, PetscBool useClPerm, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8160 {
8161   DM_Plex           *mesh = (DM_Plex *)dm->data;
8162   PetscInt          *indices;
8163   PetscInt           numIndices;
8164   const PetscScalar *valuesOrig = values;
8165   PetscErrorCode     ierr;
8166 
8167   PetscFunctionBegin;
8168   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8169   if (!section) PetscCall(DMGetLocalSection(dm, &section));
8170   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
8171   if (!globalSection) PetscCall(DMGetGlobalSection(dm, &globalSection));
8172   PetscValidHeaderSpecific(globalSection, PETSC_SECTION_CLASSID, 3);
8173   PetscValidHeaderSpecific(A, MAT_CLASSID, 5);
8174 
8175   PetscCall(DMPlexGetClosureIndices(dm, section, globalSection, point, useClPerm, &numIndices, &indices, NULL, (PetscScalar **)&values));
8176 
8177   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndices, indices, 0, NULL, values));
8178   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8179   ierr = MatSetValues(A, numIndices, indices, numIndices, indices, values, mode);
8180   if (ierr) {
8181     PetscMPIInt rank;
8182 
8183     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8184     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8185     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndices, indices, 0, NULL, values));
8186     PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
8187     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
8188     SETERRQ(PetscObjectComm((PetscObject)dm), ierr, "Not possible to set matrix values");
8189   }
8190   if (mesh->printFEM > 1) {
8191     PetscInt i;
8192     PetscCall(PetscPrintf(PETSC_COMM_SELF, "  Indices:"));
8193     for (i = 0; i < numIndices; ++i) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, indices[i]));
8194     PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8195   }
8196 
8197   PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
8198   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
8199   PetscFunctionReturn(PETSC_SUCCESS);
8200 }
8201 
8202 /*@C
8203   DMPlexMatSetClosure - Set an array of the values on the closure of 'point'
8204 
8205   Not collective
8206 
8207   Input Parameters:
8208 + dm            - The `DM`
8209 . section       - The section describing the layout in `v`, or `NULL` to use the default section
8210 . globalSection - The section describing the layout in `v`, or `NULL` to use the default global section
8211 . A             - The matrix
8212 . point         - The point in the `DM`
8213 . values        - The array of values
8214 - mode          - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
8215 
8216   Level: intermediate
8217 
8218 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosureGeneral()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
8219 @*/
8220 PetscErrorCode DMPlexMatSetClosure(DM dm, PetscSection section, PetscSection globalSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8221 {
8222   PetscFunctionBegin;
8223   PetscCall(DMPlexMatSetClosure_Internal(dm, section, globalSection, PETSC_TRUE, A, point, values, mode));
8224   PetscFunctionReturn(PETSC_SUCCESS);
8225 }
8226 
8227 /*@C
8228   DMPlexMatSetClosureGeneral - Set an array of the values on the closure of 'point' using a different row and column section
8229 
8230   Not collective
8231 
8232   Input Parameters:
8233 + dmRow            - The `DM` for the row fields
8234 . sectionRow       - The section describing the layout, or `NULL` to use the default section in `dmRow`
8235 . useRowPerm       - The flag to use the closure permutation of the `dmRow` if available
8236 . globalSectionRow - The section describing the layout, or `NULL` to use the default global section in `dmRow`
8237 . dmCol            - The `DM` for the column fields
8238 . sectionCol       - The section describing the layout, or `NULL` to use the default section in `dmCol`
8239 . useColPerm       - The flag to use the closure permutation of the `dmCol` if available
8240 . globalSectionCol - The section describing the layout, or `NULL` to use the default global section in `dmCol`
8241 . A                - The matrix
8242 . point            - The point in the `DM`
8243 . values           - The array of values
8244 - mode             - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
8245 
8246   Level: intermediate
8247 
8248 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosure()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
8249 @*/
8250 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)
8251 {
8252   DM_Plex           *mesh = (DM_Plex *)dmRow->data;
8253   PetscInt          *indicesRow, *indicesCol;
8254   PetscInt           numIndicesRow, numIndicesCol;
8255   const PetscScalar *valuesOrig = values;
8256   PetscErrorCode     ierr;
8257 
8258   PetscFunctionBegin;
8259   PetscValidHeaderSpecific(dmRow, DM_CLASSID, 1);
8260   if (!sectionRow) PetscCall(DMGetLocalSection(dmRow, &sectionRow));
8261   PetscValidHeaderSpecific(sectionRow, PETSC_SECTION_CLASSID, 2);
8262   if (!globalSectionRow) PetscCall(DMGetGlobalSection(dmRow, &globalSectionRow));
8263   PetscValidHeaderSpecific(globalSectionRow, PETSC_SECTION_CLASSID, 3);
8264   PetscValidHeaderSpecific(dmCol, DM_CLASSID, 5);
8265   if (!sectionCol) PetscCall(DMGetLocalSection(dmCol, &sectionCol));
8266   PetscValidHeaderSpecific(sectionCol, PETSC_SECTION_CLASSID, 6);
8267   if (!globalSectionCol) PetscCall(DMGetGlobalSection(dmCol, &globalSectionCol));
8268   PetscValidHeaderSpecific(globalSectionCol, PETSC_SECTION_CLASSID, 7);
8269   PetscValidHeaderSpecific(A, MAT_CLASSID, 9);
8270 
8271   PetscCall(DMPlexGetClosureIndices(dmRow, sectionRow, globalSectionRow, point, useRowPerm, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&values));
8272   PetscCall(DMPlexGetClosureIndices(dmCol, sectionCol, globalSectionCol, point, useColPerm, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&values));
8273 
8274   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
8275   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8276   ierr = MatSetValues(A, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values, mode);
8277   if (ierr) {
8278     PetscMPIInt rank;
8279 
8280     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8281     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8282     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
8283     PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&values));
8284     PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesRow, NULL, (PetscScalar **)&values));
8285     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &values));
8286   }
8287 
8288   PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, useRowPerm, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&values));
8289   PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, useColPerm, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&values));
8290   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &values));
8291   PetscFunctionReturn(PETSC_SUCCESS);
8292 }
8293 
8294 PetscErrorCode DMPlexMatSetClosureRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8295 {
8296   DM_Plex        *mesh    = (DM_Plex *)dmf->data;
8297   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
8298   PetscInt       *cpoints = NULL;
8299   PetscInt       *findices, *cindices;
8300   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
8301   PetscInt        foffsets[32], coffsets[32];
8302   DMPolytopeType  ct;
8303   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
8304   PetscErrorCode  ierr;
8305 
8306   PetscFunctionBegin;
8307   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
8308   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
8309   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
8310   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
8311   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
8312   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
8313   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
8314   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
8315   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
8316   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
8317   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
8318   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
8319   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
8320   PetscCall(PetscArrayzero(foffsets, 32));
8321   PetscCall(PetscArrayzero(coffsets, 32));
8322   /* Column indices */
8323   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8324   maxFPoints = numCPoints;
8325   /* Compress out points not in the section */
8326   /*   TODO: Squeeze out points with 0 dof as well */
8327   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
8328   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
8329     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
8330       cpoints[q * 2]     = cpoints[p];
8331       cpoints[q * 2 + 1] = cpoints[p + 1];
8332       ++q;
8333     }
8334   }
8335   numCPoints = q;
8336   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
8337     PetscInt fdof;
8338 
8339     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
8340     if (!dof) continue;
8341     for (f = 0; f < numFields; ++f) {
8342       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
8343       coffsets[f + 1] += fdof;
8344     }
8345     numCIndices += dof;
8346   }
8347   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
8348   /* Row indices */
8349   PetscCall(DMPlexGetCellType(dmc, point, &ct));
8350   {
8351     DMPlexTransform tr;
8352     DMPolytopeType *rct;
8353     PetscInt       *rsize, *rcone, *rornt, Nt;
8354 
8355     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8356     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8357     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8358     numSubcells = rsize[Nt - 1];
8359     PetscCall(DMPlexTransformDestroy(&tr));
8360   }
8361   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8362   for (r = 0, q = 0; r < numSubcells; ++r) {
8363     /* TODO Map from coarse to fine cells */
8364     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8365     /* Compress out points not in the section */
8366     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8367     for (p = 0; p < numFPoints * 2; p += 2) {
8368       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8369         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8370         if (!dof) continue;
8371         for (s = 0; s < q; ++s)
8372           if (fpoints[p] == ftotpoints[s * 2]) break;
8373         if (s < q) continue;
8374         ftotpoints[q * 2]     = fpoints[p];
8375         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8376         ++q;
8377       }
8378     }
8379     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8380   }
8381   numFPoints = q;
8382   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8383     PetscInt fdof;
8384 
8385     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8386     if (!dof) continue;
8387     for (f = 0; f < numFields; ++f) {
8388       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8389       foffsets[f + 1] += fdof;
8390     }
8391     numFIndices += dof;
8392   }
8393   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8394 
8395   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8396   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8397   PetscCall(DMGetWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8398   PetscCall(DMGetWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8399   if (numFields) {
8400     const PetscInt **permsF[32] = {NULL};
8401     const PetscInt **permsC[32] = {NULL};
8402 
8403     for (f = 0; f < numFields; f++) {
8404       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8405       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8406     }
8407     for (p = 0; p < numFPoints; p++) {
8408       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8409       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8410     }
8411     for (p = 0; p < numCPoints; p++) {
8412       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8413       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8414     }
8415     for (f = 0; f < numFields; f++) {
8416       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8417       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8418     }
8419   } else {
8420     const PetscInt **permsF = NULL;
8421     const PetscInt **permsC = NULL;
8422 
8423     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8424     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8425     for (p = 0, off = 0; p < numFPoints; p++) {
8426       const PetscInt *perm = permsF ? permsF[p] : NULL;
8427 
8428       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8429       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8430     }
8431     for (p = 0, off = 0; p < numCPoints; p++) {
8432       const PetscInt *perm = permsC ? permsC[p] : NULL;
8433 
8434       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8435       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8436     }
8437     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8438     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8439   }
8440   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8441   /* TODO: flips */
8442   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8443   ierr = MatSetValues(A, numFIndices, findices, numCIndices, cindices, values, mode);
8444   if (ierr) {
8445     PetscMPIInt rank;
8446 
8447     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8448     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8449     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8450     PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8451     PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8452   }
8453   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8454   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8455   PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8456   PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8457   PetscFunctionReturn(PETSC_SUCCESS);
8458 }
8459 
8460 PetscErrorCode DMPlexMatGetClosureIndicesRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, PetscInt point, PetscInt cindices[], PetscInt findices[])
8461 {
8462   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
8463   PetscInt       *cpoints      = NULL;
8464   PetscInt        foffsets[32] = {0}, coffsets[32] = {0};
8465   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
8466   DMPolytopeType  ct;
8467   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
8468 
8469   PetscFunctionBegin;
8470   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
8471   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
8472   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
8473   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
8474   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
8475   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
8476   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
8477   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
8478   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
8479   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
8480   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
8481   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
8482   /* Column indices */
8483   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8484   maxFPoints = numCPoints;
8485   /* Compress out points not in the section */
8486   /*   TODO: Squeeze out points with 0 dof as well */
8487   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
8488   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
8489     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
8490       cpoints[q * 2]     = cpoints[p];
8491       cpoints[q * 2 + 1] = cpoints[p + 1];
8492       ++q;
8493     }
8494   }
8495   numCPoints = q;
8496   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
8497     PetscInt fdof;
8498 
8499     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
8500     if (!dof) continue;
8501     for (f = 0; f < numFields; ++f) {
8502       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
8503       coffsets[f + 1] += fdof;
8504     }
8505     numCIndices += dof;
8506   }
8507   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
8508   /* Row indices */
8509   PetscCall(DMPlexGetCellType(dmc, point, &ct));
8510   {
8511     DMPlexTransform tr;
8512     DMPolytopeType *rct;
8513     PetscInt       *rsize, *rcone, *rornt, Nt;
8514 
8515     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8516     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8517     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8518     numSubcells = rsize[Nt - 1];
8519     PetscCall(DMPlexTransformDestroy(&tr));
8520   }
8521   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8522   for (r = 0, q = 0; r < numSubcells; ++r) {
8523     /* TODO Map from coarse to fine cells */
8524     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8525     /* Compress out points not in the section */
8526     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8527     for (p = 0; p < numFPoints * 2; p += 2) {
8528       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8529         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8530         if (!dof) continue;
8531         for (s = 0; s < q; ++s)
8532           if (fpoints[p] == ftotpoints[s * 2]) break;
8533         if (s < q) continue;
8534         ftotpoints[q * 2]     = fpoints[p];
8535         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8536         ++q;
8537       }
8538     }
8539     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8540   }
8541   numFPoints = q;
8542   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8543     PetscInt fdof;
8544 
8545     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8546     if (!dof) continue;
8547     for (f = 0; f < numFields; ++f) {
8548       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8549       foffsets[f + 1] += fdof;
8550     }
8551     numFIndices += dof;
8552   }
8553   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8554 
8555   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8556   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8557   if (numFields) {
8558     const PetscInt **permsF[32] = {NULL};
8559     const PetscInt **permsC[32] = {NULL};
8560 
8561     for (f = 0; f < numFields; f++) {
8562       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8563       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8564     }
8565     for (p = 0; p < numFPoints; p++) {
8566       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8567       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8568     }
8569     for (p = 0; p < numCPoints; p++) {
8570       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8571       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8572     }
8573     for (f = 0; f < numFields; f++) {
8574       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8575       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8576     }
8577   } else {
8578     const PetscInt **permsF = NULL;
8579     const PetscInt **permsC = NULL;
8580 
8581     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8582     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8583     for (p = 0, off = 0; p < numFPoints; p++) {
8584       const PetscInt *perm = permsF ? permsF[p] : NULL;
8585 
8586       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8587       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8588     }
8589     for (p = 0, off = 0; p < numCPoints; p++) {
8590       const PetscInt *perm = permsC ? permsC[p] : NULL;
8591 
8592       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8593       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8594     }
8595     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8596     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8597   }
8598   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8599   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8600   PetscFunctionReturn(PETSC_SUCCESS);
8601 }
8602 
8603 /*@C
8604   DMPlexGetVTKCellHeight - Returns the height in the DAG used to determine which points are cells (normally 0)
8605 
8606   Input Parameter:
8607 . dm - The `DMPLEX` object
8608 
8609   Output Parameter:
8610 . cellHeight - The height of a cell
8611 
8612   Level: developer
8613 
8614 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetVTKCellHeight()`
8615 @*/
8616 PetscErrorCode DMPlexGetVTKCellHeight(DM dm, PetscInt *cellHeight)
8617 {
8618   DM_Plex *mesh = (DM_Plex *)dm->data;
8619 
8620   PetscFunctionBegin;
8621   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8622   PetscAssertPointer(cellHeight, 2);
8623   *cellHeight = mesh->vtkCellHeight;
8624   PetscFunctionReturn(PETSC_SUCCESS);
8625 }
8626 
8627 /*@C
8628   DMPlexSetVTKCellHeight - Sets the height in the DAG used to determine which points are cells (normally 0)
8629 
8630   Input Parameters:
8631 + dm         - The `DMPLEX` object
8632 - cellHeight - The height of a cell
8633 
8634   Level: developer
8635 
8636 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetVTKCellHeight()`
8637 @*/
8638 PetscErrorCode DMPlexSetVTKCellHeight(DM dm, PetscInt cellHeight)
8639 {
8640   DM_Plex *mesh = (DM_Plex *)dm->data;
8641 
8642   PetscFunctionBegin;
8643   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8644   mesh->vtkCellHeight = cellHeight;
8645   PetscFunctionReturn(PETSC_SUCCESS);
8646 }
8647 
8648 /*@
8649   DMPlexGetCellTypeStratum - Get the range of cells of a given celltype
8650 
8651   Input Parameters:
8652 + dm - The `DMPLEX` object
8653 - ct - The `DMPolytopeType` of the cell
8654 
8655   Output Parameters:
8656 + start - The first cell of this type, or `NULL`
8657 - end   - The upper bound on this celltype, or `NULL`
8658 
8659   Level: advanced
8660 
8661 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexConstructGhostCells()`, `DMPlexGetDepthStratum()`, `DMPlexGetHeightStratum()`
8662 @*/
8663 PetscErrorCode DMPlexGetCellTypeStratum(DM dm, DMPolytopeType ct, PetscInt *start, PetscInt *end)
8664 {
8665   DM_Plex *mesh = (DM_Plex *)dm->data;
8666   DMLabel  label;
8667   PetscInt pStart, pEnd;
8668 
8669   PetscFunctionBegin;
8670   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8671   if (start) {
8672     PetscAssertPointer(start, 3);
8673     *start = 0;
8674   }
8675   if (end) {
8676     PetscAssertPointer(end, 4);
8677     *end = 0;
8678   }
8679   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8680   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
8681   if (mesh->tr) {
8682     PetscCall(DMPlexTransformGetCellTypeStratum(mesh->tr, ct, start, end));
8683   } else {
8684     PetscCall(DMPlexGetCellTypeLabel(dm, &label));
8685     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named celltype was found");
8686     PetscCall(DMLabelGetStratumBounds(label, ct, start, end));
8687   }
8688   PetscFunctionReturn(PETSC_SUCCESS);
8689 }
8690 
8691 PetscErrorCode DMPlexCreateNumbering_Plex(DM dm, PetscInt pStart, PetscInt pEnd, PetscInt shift, PetscInt *globalSize, PetscSF sf, IS *numbering)
8692 {
8693   PetscSection section, globalSection;
8694   PetscInt    *numbers, p;
8695 
8696   PetscFunctionBegin;
8697   if (PetscDefined(USE_DEBUG)) PetscCall(DMPlexCheckPointSF(dm, sf, PETSC_TRUE));
8698   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
8699   PetscCall(PetscSectionSetChart(section, pStart, pEnd));
8700   for (p = pStart; p < pEnd; ++p) PetscCall(PetscSectionSetDof(section, p, 1));
8701   PetscCall(PetscSectionSetUp(section));
8702   PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_FALSE, PETSC_FALSE, &globalSection));
8703   PetscCall(PetscMalloc1(pEnd - pStart, &numbers));
8704   for (p = pStart; p < pEnd; ++p) {
8705     PetscCall(PetscSectionGetOffset(globalSection, p, &numbers[p - pStart]));
8706     if (numbers[p - pStart] < 0) numbers[p - pStart] -= shift;
8707     else numbers[p - pStart] += shift;
8708   }
8709   PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), pEnd - pStart, numbers, PETSC_OWN_POINTER, numbering));
8710   if (globalSize) {
8711     PetscLayout layout;
8712     PetscCall(PetscSectionGetPointLayout(PetscObjectComm((PetscObject)dm), globalSection, &layout));
8713     PetscCall(PetscLayoutGetSize(layout, globalSize));
8714     PetscCall(PetscLayoutDestroy(&layout));
8715   }
8716   PetscCall(PetscSectionDestroy(&section));
8717   PetscCall(PetscSectionDestroy(&globalSection));
8718   PetscFunctionReturn(PETSC_SUCCESS);
8719 }
8720 
8721 PetscErrorCode DMPlexCreateCellNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalCellNumbers)
8722 {
8723   PetscInt cellHeight, cStart, cEnd;
8724 
8725   PetscFunctionBegin;
8726   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8727   if (includeHybrid) PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8728   else PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd));
8729   PetscCall(DMPlexCreateNumbering_Plex(dm, cStart, cEnd, 0, NULL, dm->sf, globalCellNumbers));
8730   PetscFunctionReturn(PETSC_SUCCESS);
8731 }
8732 
8733 /*@
8734   DMPlexGetCellNumbering - Get a global cell numbering for all cells on this process
8735 
8736   Input Parameter:
8737 . dm - The `DMPLEX` object
8738 
8739   Output Parameter:
8740 . globalCellNumbers - Global cell numbers for all cells on this process
8741 
8742   Level: developer
8743 
8744 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetVertexNumbering()`
8745 @*/
8746 PetscErrorCode DMPlexGetCellNumbering(DM dm, IS *globalCellNumbers)
8747 {
8748   DM_Plex *mesh = (DM_Plex *)dm->data;
8749 
8750   PetscFunctionBegin;
8751   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8752   if (!mesh->globalCellNumbers) PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_FALSE, &mesh->globalCellNumbers));
8753   *globalCellNumbers = mesh->globalCellNumbers;
8754   PetscFunctionReturn(PETSC_SUCCESS);
8755 }
8756 
8757 PetscErrorCode DMPlexCreateVertexNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalVertexNumbers)
8758 {
8759   PetscInt vStart, vEnd;
8760 
8761   PetscFunctionBegin;
8762   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8763   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8764   PetscCall(DMPlexCreateNumbering_Plex(dm, vStart, vEnd, 0, NULL, dm->sf, globalVertexNumbers));
8765   PetscFunctionReturn(PETSC_SUCCESS);
8766 }
8767 
8768 /*@
8769   DMPlexGetVertexNumbering - Get a global vertex numbering for all vertices on this process
8770 
8771   Input Parameter:
8772 . dm - The `DMPLEX` object
8773 
8774   Output Parameter:
8775 . globalVertexNumbers - Global vertex numbers for all vertices on this process
8776 
8777   Level: developer
8778 
8779 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
8780 @*/
8781 PetscErrorCode DMPlexGetVertexNumbering(DM dm, IS *globalVertexNumbers)
8782 {
8783   DM_Plex *mesh = (DM_Plex *)dm->data;
8784 
8785   PetscFunctionBegin;
8786   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8787   if (!mesh->globalVertexNumbers) PetscCall(DMPlexCreateVertexNumbering_Internal(dm, PETSC_FALSE, &mesh->globalVertexNumbers));
8788   *globalVertexNumbers = mesh->globalVertexNumbers;
8789   PetscFunctionReturn(PETSC_SUCCESS);
8790 }
8791 
8792 /*@
8793   DMPlexCreatePointNumbering - Create a global numbering for all points.
8794 
8795   Collective
8796 
8797   Input Parameter:
8798 . dm - The `DMPLEX` object
8799 
8800   Output Parameter:
8801 . globalPointNumbers - Global numbers for all points on this process
8802 
8803   Level: developer
8804 
8805   Notes:
8806   The point numbering `IS` is parallel, with local portion indexed by local points (see `DMGetLocalSection()`). The global
8807   points are taken as stratified, with each MPI rank owning a contiguous subset of each stratum. In the IS, owned points
8808   will have their non-negative value while points owned by different ranks will be involuted -(idx+1). As an example,
8809   consider a parallel mesh in which the first two elements and first two vertices are owned by rank 0.
8810 
8811   The partitioned mesh is
8812   ```
8813   (2)--0--(3)--1--(4)    (1)--0--(2)
8814   ```
8815   and its global numbering is
8816   ```
8817   (3)--0--(4)--1--(5)--2--(6)
8818   ```
8819   Then the global numbering is provided as
8820   ```
8821   [0] Number of indices in set 5
8822   [0] 0 0
8823   [0] 1 1
8824   [0] 2 3
8825   [0] 3 4
8826   [0] 4 -6
8827   [1] Number of indices in set 3
8828   [1] 0 2
8829   [1] 1 5
8830   [1] 2 6
8831   ```
8832 
8833 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
8834 @*/
8835 PetscErrorCode DMPlexCreatePointNumbering(DM dm, IS *globalPointNumbers)
8836 {
8837   IS        nums[4];
8838   PetscInt  depths[4], gdepths[4], starts[4];
8839   PetscInt  depth, d, shift = 0;
8840   PetscBool empty = PETSC_FALSE;
8841 
8842   PetscFunctionBegin;
8843   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8844   PetscCall(DMPlexGetDepth(dm, &depth));
8845   // For unstratified meshes use dim instead of depth
8846   if (depth < 0) PetscCall(DMGetDimension(dm, &depth));
8847   // If any stratum is empty, we must mark all empty
8848   for (d = 0; d <= depth; ++d) {
8849     PetscInt end;
8850 
8851     depths[d] = depth - d;
8852     PetscCall(DMPlexGetDepthStratum(dm, depths[d], &starts[d], &end));
8853     if (!(starts[d] - end)) empty = PETSC_TRUE;
8854   }
8855   if (empty)
8856     for (d = 0; d <= depth; ++d) {
8857       depths[d] = -1;
8858       starts[d] = -1;
8859     }
8860   else PetscCall(PetscSortIntWithArray(depth + 1, starts, depths));
8861   PetscCall(MPIU_Allreduce(depths, gdepths, depth + 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
8862   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]);
8863   // Note here that 'shift' is collective, so that the numbering is stratified by depth
8864   for (d = 0; d <= depth; ++d) {
8865     PetscInt pStart, pEnd, gsize;
8866 
8867     PetscCall(DMPlexGetDepthStratum(dm, gdepths[d], &pStart, &pEnd));
8868     PetscCall(DMPlexCreateNumbering_Plex(dm, pStart, pEnd, shift, &gsize, dm->sf, &nums[d]));
8869     shift += gsize;
8870   }
8871   PetscCall(ISConcatenate(PETSC_COMM_SELF, depth + 1, nums, globalPointNumbers));
8872   for (d = 0; d <= depth; ++d) PetscCall(ISDestroy(&nums[d]));
8873   PetscFunctionReturn(PETSC_SUCCESS);
8874 }
8875 
8876 /*@
8877   DMPlexCreateRankField - Create a cell field whose value is the rank of the owner
8878 
8879   Input Parameter:
8880 . dm - The `DMPLEX` object
8881 
8882   Output Parameter:
8883 . ranks - The rank field
8884 
8885   Options Database Key:
8886 . -dm_partition_view - Adds the rank field into the `DM` output from `-dm_view` using the same viewer
8887 
8888   Level: intermediate
8889 
8890 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`
8891 @*/
8892 PetscErrorCode DMPlexCreateRankField(DM dm, Vec *ranks)
8893 {
8894   DM             rdm;
8895   PetscFE        fe;
8896   PetscScalar   *r;
8897   PetscMPIInt    rank;
8898   DMPolytopeType ct;
8899   PetscInt       dim, cStart, cEnd, c;
8900   PetscBool      simplex;
8901 
8902   PetscFunctionBeginUser;
8903   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8904   PetscAssertPointer(ranks, 2);
8905   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
8906   PetscCall(DMClone(dm, &rdm));
8907   PetscCall(DMGetDimension(rdm, &dim));
8908   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
8909   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
8910   simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct) + 1 ? PETSC_TRUE : PETSC_FALSE;
8911   PetscCall(PetscFECreateDefault(PETSC_COMM_SELF, dim, 1, simplex, "PETSc___rank_", -1, &fe));
8912   PetscCall(PetscObjectSetName((PetscObject)fe, "rank"));
8913   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
8914   PetscCall(PetscFEDestroy(&fe));
8915   PetscCall(DMCreateDS(rdm));
8916   PetscCall(DMCreateGlobalVector(rdm, ranks));
8917   PetscCall(PetscObjectSetName((PetscObject)*ranks, "partition"));
8918   PetscCall(VecGetArray(*ranks, &r));
8919   for (c = cStart; c < cEnd; ++c) {
8920     PetscScalar *lr;
8921 
8922     PetscCall(DMPlexPointGlobalRef(rdm, c, r, &lr));
8923     if (lr) *lr = rank;
8924   }
8925   PetscCall(VecRestoreArray(*ranks, &r));
8926   PetscCall(DMDestroy(&rdm));
8927   PetscFunctionReturn(PETSC_SUCCESS);
8928 }
8929 
8930 /*@
8931   DMPlexCreateLabelField - Create a field whose value is the label value for that point
8932 
8933   Input Parameters:
8934 + dm    - The `DMPLEX`
8935 - label - The `DMLabel`
8936 
8937   Output Parameter:
8938 . val - The label value field
8939 
8940   Options Database Key:
8941 . -dm_label_view - Adds the label value field into the `DM` output from `-dm_view` using the same viewer
8942 
8943   Level: intermediate
8944 
8945 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`
8946 @*/
8947 PetscErrorCode DMPlexCreateLabelField(DM dm, DMLabel label, Vec *val)
8948 {
8949   DM             rdm, plex;
8950   Vec            lval;
8951   PetscSection   section;
8952   PetscFE        fe;
8953   PetscScalar   *v;
8954   PetscInt       dim, pStart, pEnd, p, cStart;
8955   DMPolytopeType ct;
8956   char           name[PETSC_MAX_PATH_LEN];
8957   const char    *lname, *prefix;
8958 
8959   PetscFunctionBeginUser;
8960   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8961   PetscAssertPointer(label, 2);
8962   PetscAssertPointer(val, 3);
8963   PetscCall(DMClone(dm, &rdm));
8964   PetscCall(DMConvert(rdm, DMPLEX, &plex));
8965   PetscCall(DMPlexGetHeightStratum(plex, 0, &cStart, NULL));
8966   PetscCall(DMPlexGetCellType(plex, cStart, &ct));
8967   PetscCall(DMDestroy(&plex));
8968   PetscCall(DMGetDimension(rdm, &dim));
8969   PetscCall(DMGetOptionsPrefix(dm, &prefix));
8970   PetscCall(PetscObjectGetName((PetscObject)label, &lname));
8971   PetscCall(PetscSNPrintf(name, sizeof(name), "%s%s_", prefix ? prefix : "", lname));
8972   PetscCall(PetscFECreateByCell(PETSC_COMM_SELF, dim, 1, ct, name, -1, &fe));
8973   PetscCall(PetscObjectSetName((PetscObject)fe, ""));
8974   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
8975   PetscCall(PetscFEDestroy(&fe));
8976   PetscCall(DMCreateDS(rdm));
8977   PetscCall(DMCreateGlobalVector(rdm, val));
8978   PetscCall(DMCreateLocalVector(rdm, &lval));
8979   PetscCall(PetscObjectSetName((PetscObject)*val, lname));
8980   PetscCall(DMGetLocalSection(rdm, &section));
8981   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
8982   PetscCall(VecGetArray(lval, &v));
8983   for (p = pStart; p < pEnd; ++p) {
8984     PetscInt cval, dof, off;
8985 
8986     PetscCall(PetscSectionGetDof(section, p, &dof));
8987     if (!dof) continue;
8988     PetscCall(DMLabelGetValue(label, p, &cval));
8989     PetscCall(PetscSectionGetOffset(section, p, &off));
8990     for (PetscInt d = 0; d < dof; d++) v[off + d] = cval;
8991   }
8992   PetscCall(VecRestoreArray(lval, &v));
8993   PetscCall(DMLocalToGlobal(rdm, lval, INSERT_VALUES, *val));
8994   PetscCall(VecDestroy(&lval));
8995   PetscCall(DMDestroy(&rdm));
8996   PetscFunctionReturn(PETSC_SUCCESS);
8997 }
8998 
8999 /*@
9000   DMPlexCheckSymmetry - Check that the adjacency information in the mesh is symmetric.
9001 
9002   Input Parameter:
9003 . dm - The `DMPLEX` object
9004 
9005   Level: developer
9006 
9007   Notes:
9008   This is a useful diagnostic when creating meshes programmatically.
9009 
9010   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9011 
9012 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9013 @*/
9014 PetscErrorCode DMPlexCheckSymmetry(DM dm)
9015 {
9016   PetscSection    coneSection, supportSection;
9017   const PetscInt *cone, *support;
9018   PetscInt        coneSize, c, supportSize, s;
9019   PetscInt        pStart, pEnd, p, pp, csize, ssize;
9020   PetscBool       storagecheck = PETSC_TRUE;
9021 
9022   PetscFunctionBegin;
9023   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9024   PetscCall(DMViewFromOptions(dm, NULL, "-sym_dm_view"));
9025   PetscCall(DMPlexGetConeSection(dm, &coneSection));
9026   PetscCall(DMPlexGetSupportSection(dm, &supportSection));
9027   /* Check that point p is found in the support of its cone points, and vice versa */
9028   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
9029   for (p = pStart; p < pEnd; ++p) {
9030     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
9031     PetscCall(DMPlexGetCone(dm, p, &cone));
9032     for (c = 0; c < coneSize; ++c) {
9033       PetscBool dup = PETSC_FALSE;
9034       PetscInt  d;
9035       for (d = c - 1; d >= 0; --d) {
9036         if (cone[c] == cone[d]) {
9037           dup = PETSC_TRUE;
9038           break;
9039         }
9040       }
9041       PetscCall(DMPlexGetSupportSize(dm, cone[c], &supportSize));
9042       PetscCall(DMPlexGetSupport(dm, cone[c], &support));
9043       for (s = 0; s < supportSize; ++s) {
9044         if (support[s] == p) break;
9045       }
9046       if ((s >= supportSize) || (dup && (support[s + 1] != p))) {
9047         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", p));
9048         for (s = 0; s < coneSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[s]));
9049         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9050         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", cone[c]));
9051         for (s = 0; s < supportSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[s]));
9052         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9053         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]);
9054         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in support of cone point %" PetscInt_FMT, p, cone[c]);
9055       }
9056     }
9057     PetscCall(DMPlexGetTreeParent(dm, p, &pp, NULL));
9058     if (p != pp) {
9059       storagecheck = PETSC_FALSE;
9060       continue;
9061     }
9062     PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
9063     PetscCall(DMPlexGetSupport(dm, p, &support));
9064     for (s = 0; s < supportSize; ++s) {
9065       PetscCall(DMPlexGetConeSize(dm, support[s], &coneSize));
9066       PetscCall(DMPlexGetCone(dm, support[s], &cone));
9067       for (c = 0; c < coneSize; ++c) {
9068         PetscCall(DMPlexGetTreeParent(dm, cone[c], &pp, NULL));
9069         if (cone[c] != pp) {
9070           c = 0;
9071           break;
9072         }
9073         if (cone[c] == p) break;
9074       }
9075       if (c >= coneSize) {
9076         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", p));
9077         for (c = 0; c < supportSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[c]));
9078         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9079         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", support[s]));
9080         for (c = 0; c < coneSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[c]));
9081         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9082         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in cone of support point %" PetscInt_FMT, p, support[s]);
9083       }
9084     }
9085   }
9086   if (storagecheck) {
9087     PetscCall(PetscSectionGetStorageSize(coneSection, &csize));
9088     PetscCall(PetscSectionGetStorageSize(supportSection, &ssize));
9089     PetscCheck(csize == ssize, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Total cone size %" PetscInt_FMT " != Total support size %" PetscInt_FMT, csize, ssize);
9090   }
9091   PetscFunctionReturn(PETSC_SUCCESS);
9092 }
9093 
9094 /*
9095   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.
9096 */
9097 static PetscErrorCode DMPlexCellUnsplitVertices_Private(DM dm, PetscInt c, DMPolytopeType ct, PetscInt *unsplit)
9098 {
9099   DMPolytopeType  cct;
9100   PetscInt        ptpoints[4];
9101   const PetscInt *cone, *ccone, *ptcone;
9102   PetscInt        coneSize, cp, cconeSize, ccp, npt = 0, pt;
9103 
9104   PetscFunctionBegin;
9105   *unsplit = 0;
9106   switch (ct) {
9107   case DM_POLYTOPE_POINT_PRISM_TENSOR:
9108     ptpoints[npt++] = c;
9109     break;
9110   case DM_POLYTOPE_SEG_PRISM_TENSOR:
9111     PetscCall(DMPlexGetCone(dm, c, &cone));
9112     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9113     for (cp = 0; cp < coneSize; ++cp) {
9114       PetscCall(DMPlexGetCellType(dm, cone[cp], &cct));
9115       if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) ptpoints[npt++] = cone[cp];
9116     }
9117     break;
9118   case DM_POLYTOPE_TRI_PRISM_TENSOR:
9119   case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9120     PetscCall(DMPlexGetCone(dm, c, &cone));
9121     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9122     for (cp = 0; cp < coneSize; ++cp) {
9123       PetscCall(DMPlexGetCone(dm, cone[cp], &ccone));
9124       PetscCall(DMPlexGetConeSize(dm, cone[cp], &cconeSize));
9125       for (ccp = 0; ccp < cconeSize; ++ccp) {
9126         PetscCall(DMPlexGetCellType(dm, ccone[ccp], &cct));
9127         if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) {
9128           PetscInt p;
9129           for (p = 0; p < npt; ++p)
9130             if (ptpoints[p] == ccone[ccp]) break;
9131           if (p == npt) ptpoints[npt++] = ccone[ccp];
9132         }
9133       }
9134     }
9135     break;
9136   default:
9137     break;
9138   }
9139   for (pt = 0; pt < npt; ++pt) {
9140     PetscCall(DMPlexGetCone(dm, ptpoints[pt], &ptcone));
9141     if (ptcone[0] == ptcone[1]) ++(*unsplit);
9142   }
9143   PetscFunctionReturn(PETSC_SUCCESS);
9144 }
9145 
9146 /*@
9147   DMPlexCheckSkeleton - Check that each cell has the correct number of vertices
9148 
9149   Input Parameters:
9150 + dm         - The `DMPLEX` object
9151 - cellHeight - Normally 0
9152 
9153   Level: developer
9154 
9155   Notes:
9156   This is a useful diagnostic when creating meshes programmatically.
9157   Currently applicable only to homogeneous simplex or tensor meshes.
9158 
9159   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9160 
9161 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9162 @*/
9163 PetscErrorCode DMPlexCheckSkeleton(DM dm, PetscInt cellHeight)
9164 {
9165   DMPlexInterpolatedFlag interp;
9166   DMPolytopeType         ct;
9167   PetscInt               vStart, vEnd, cStart, cEnd, c;
9168 
9169   PetscFunctionBegin;
9170   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9171   PetscCall(DMPlexIsInterpolated(dm, &interp));
9172   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9173   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
9174   for (c = cStart; c < cEnd; ++c) {
9175     PetscInt *closure = NULL;
9176     PetscInt  coneSize, closureSize, cl, Nv = 0;
9177 
9178     PetscCall(DMPlexGetCellType(dm, c, &ct));
9179     PetscCheck((PetscInt)ct >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell %" PetscInt_FMT " has no cell type", c);
9180     if (ct == DM_POLYTOPE_UNKNOWN) continue;
9181     if (interp == DMPLEX_INTERPOLATED_FULL) {
9182       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9183       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));
9184     }
9185     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9186     for (cl = 0; cl < closureSize * 2; cl += 2) {
9187       const PetscInt p = closure[cl];
9188       if ((p >= vStart) && (p < vEnd)) ++Nv;
9189     }
9190     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9191     /* Special Case: Tensor faces with identified vertices */
9192     if (Nv < DMPolytopeTypeGetNumVertices(ct)) {
9193       PetscInt unsplit;
9194 
9195       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9196       if (Nv + unsplit == DMPolytopeTypeGetNumVertices(ct)) continue;
9197     }
9198     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));
9199   }
9200   PetscFunctionReturn(PETSC_SUCCESS);
9201 }
9202 
9203 /*@
9204   DMPlexCheckFaces - Check that the faces of each cell give a vertex order this is consistent with what we expect from the cell type
9205 
9206   Collective
9207 
9208   Input Parameters:
9209 + dm         - The `DMPLEX` object
9210 - cellHeight - Normally 0
9211 
9212   Level: developer
9213 
9214   Notes:
9215   This is a useful diagnostic when creating meshes programmatically.
9216   This routine is only relevant for meshes that are fully interpolated across all ranks.
9217   It will error out if a partially interpolated mesh is given on some rank.
9218   It will do nothing for locally uninterpolated mesh (as there is nothing to check).
9219 
9220   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9221 
9222 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMPlexGetVTKCellHeight()`, `DMSetFromOptions()`
9223 @*/
9224 PetscErrorCode DMPlexCheckFaces(DM dm, PetscInt cellHeight)
9225 {
9226   PetscInt               dim, depth, vStart, vEnd, cStart, cEnd, c, h;
9227   DMPlexInterpolatedFlag interpEnum;
9228 
9229   PetscFunctionBegin;
9230   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9231   PetscCall(DMPlexIsInterpolatedCollective(dm, &interpEnum));
9232   if (interpEnum == DMPLEX_INTERPOLATED_NONE) PetscFunctionReturn(PETSC_SUCCESS);
9233   if (interpEnum != DMPLEX_INTERPOLATED_FULL) {
9234     PetscCall(PetscPrintf(PetscObjectComm((PetscObject)dm), "DMPlexCheckFaces() warning: Mesh is only partially interpolated, this is currently not supported"));
9235     PetscFunctionReturn(PETSC_SUCCESS);
9236   }
9237 
9238   PetscCall(DMGetDimension(dm, &dim));
9239   PetscCall(DMPlexGetDepth(dm, &depth));
9240   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
9241   for (h = cellHeight; h < PetscMin(depth, dim); ++h) {
9242     PetscCall(DMPlexGetHeightStratum(dm, h, &cStart, &cEnd));
9243     for (c = cStart; c < cEnd; ++c) {
9244       const PetscInt       *cone, *ornt, *faceSizes, *faces;
9245       const DMPolytopeType *faceTypes;
9246       DMPolytopeType        ct;
9247       PetscInt              numFaces, coneSize, f;
9248       PetscInt             *closure = NULL, closureSize, cl, numCorners = 0, fOff = 0, unsplit;
9249 
9250       PetscCall(DMPlexGetCellType(dm, c, &ct));
9251       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9252       if (unsplit) continue;
9253       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9254       PetscCall(DMPlexGetCone(dm, c, &cone));
9255       PetscCall(DMPlexGetConeOrientation(dm, c, &ornt));
9256       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9257       for (cl = 0; cl < closureSize * 2; cl += 2) {
9258         const PetscInt p = closure[cl];
9259         if ((p >= vStart) && (p < vEnd)) closure[numCorners++] = p;
9260       }
9261       PetscCall(DMPlexGetRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
9262       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);
9263       for (f = 0; f < numFaces; ++f) {
9264         DMPolytopeType fct;
9265         PetscInt      *fclosure = NULL, fclosureSize, cl, fnumCorners = 0, v;
9266 
9267         PetscCall(DMPlexGetCellType(dm, cone[f], &fct));
9268         PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[f], ornt[f], PETSC_TRUE, &fclosureSize, &fclosure));
9269         for (cl = 0; cl < fclosureSize * 2; cl += 2) {
9270           const PetscInt p = fclosure[cl];
9271           if ((p >= vStart) && (p < vEnd)) fclosure[fnumCorners++] = p;
9272         }
9273         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]);
9274         for (v = 0; v < fnumCorners; ++v) {
9275           if (fclosure[v] != faces[fOff + v]) {
9276             PetscInt v1;
9277 
9278             PetscCall(PetscPrintf(PETSC_COMM_SELF, "face closure:"));
9279             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, fclosure[v1]));
9280             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\ncell face:"));
9281             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, faces[fOff + v1]));
9282             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9283             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]);
9284           }
9285         }
9286         PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[f], PETSC_TRUE, &fclosureSize, &fclosure));
9287         fOff += faceSizes[f];
9288       }
9289       PetscCall(DMPlexRestoreRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
9290       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9291     }
9292   }
9293   PetscFunctionReturn(PETSC_SUCCESS);
9294 }
9295 
9296 /*@
9297   DMPlexCheckGeometry - Check the geometry of mesh cells
9298 
9299   Input Parameter:
9300 . dm - The `DMPLEX` object
9301 
9302   Level: developer
9303 
9304   Notes:
9305   This is a useful diagnostic when creating meshes programmatically.
9306 
9307   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9308 
9309 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9310 @*/
9311 PetscErrorCode DMPlexCheckGeometry(DM dm)
9312 {
9313   Vec       coordinates;
9314   PetscReal detJ, J[9], refVol = 1.0;
9315   PetscReal vol;
9316   PetscInt  dim, depth, dE, d, cStart, cEnd, c;
9317 
9318   PetscFunctionBegin;
9319   PetscCall(DMGetDimension(dm, &dim));
9320   PetscCall(DMGetCoordinateDim(dm, &dE));
9321   if (dim != dE) PetscFunctionReturn(PETSC_SUCCESS);
9322   PetscCall(DMPlexGetDepth(dm, &depth));
9323   for (d = 0; d < dim; ++d) refVol *= 2.0;
9324   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
9325   /* Make sure local coordinates are created, because that step is collective */
9326   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
9327   if (!coordinates) PetscFunctionReturn(PETSC_SUCCESS);
9328   for (c = cStart; c < cEnd; ++c) {
9329     DMPolytopeType ct;
9330     PetscInt       unsplit;
9331     PetscBool      ignoreZeroVol = PETSC_FALSE;
9332 
9333     PetscCall(DMPlexGetCellType(dm, c, &ct));
9334     switch (ct) {
9335     case DM_POLYTOPE_SEG_PRISM_TENSOR:
9336     case DM_POLYTOPE_TRI_PRISM_TENSOR:
9337     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9338       ignoreZeroVol = PETSC_TRUE;
9339       break;
9340     default:
9341       break;
9342     }
9343     switch (ct) {
9344     case DM_POLYTOPE_TRI_PRISM:
9345     case DM_POLYTOPE_TRI_PRISM_TENSOR:
9346     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9347     case DM_POLYTOPE_PYRAMID:
9348       continue;
9349     default:
9350       break;
9351     }
9352     PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9353     if (unsplit) continue;
9354     PetscCall(DMPlexComputeCellGeometryFEM(dm, c, NULL, NULL, J, NULL, &detJ));
9355     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);
9356     PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FEM Volume %g\n", c, (double)(detJ * refVol)));
9357     /* This should work with periodicity since DG coordinates should be used */
9358     if (depth > 1) {
9359       PetscCall(DMPlexComputeCellGeometryFVM(dm, c, &vol, NULL, NULL));
9360       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);
9361       PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FVM Volume %g\n", c, (double)vol));
9362     }
9363   }
9364   PetscFunctionReturn(PETSC_SUCCESS);
9365 }
9366 
9367 /*@
9368   DMPlexCheckPointSF - Check that several necessary conditions are met for the point `PetscSF` of this plex.
9369 
9370   Collective
9371 
9372   Input Parameters:
9373 + dm              - The `DMPLEX` object
9374 . pointSF         - The `PetscSF`, or `NULL` for `PointSF` attached to `DM`
9375 - allowExtraRoots - Flag to allow extra points not present in the `DM`
9376 
9377   Level: developer
9378 
9379   Notes:
9380   This is mainly intended for debugging/testing purposes.
9381 
9382   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9383 
9384   Extra roots can come from periodic cuts, where additional points appear on the boundary
9385 
9386 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMGetPointSF()`, `DMSetFromOptions()`
9387 @*/
9388 PetscErrorCode DMPlexCheckPointSF(DM dm, PetscSF pointSF, PetscBool allowExtraRoots)
9389 {
9390   PetscInt           l, nleaves, nroots, overlap;
9391   const PetscInt    *locals;
9392   const PetscSFNode *remotes;
9393   PetscBool          distributed;
9394   MPI_Comm           comm;
9395   PetscMPIInt        rank;
9396 
9397   PetscFunctionBegin;
9398   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9399   if (pointSF) PetscValidHeaderSpecific(pointSF, PETSCSF_CLASSID, 2);
9400   else pointSF = dm->sf;
9401   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9402   PetscCheck(pointSF, comm, PETSC_ERR_ARG_WRONGSTATE, "DMPlex must have Point SF attached");
9403   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9404   {
9405     PetscMPIInt mpiFlag;
9406 
9407     PetscCallMPI(MPI_Comm_compare(comm, PetscObjectComm((PetscObject)pointSF), &mpiFlag));
9408     PetscCheck(mpiFlag == MPI_CONGRUENT || mpiFlag == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "DM and Point SF have different communicators (flag %d)", mpiFlag);
9409   }
9410   PetscCall(PetscSFGetGraph(pointSF, &nroots, &nleaves, &locals, &remotes));
9411   PetscCall(DMPlexIsDistributed(dm, &distributed));
9412   if (!distributed) {
9413     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);
9414     PetscFunctionReturn(PETSC_SUCCESS);
9415   }
9416   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);
9417   PetscCall(DMPlexGetOverlap(dm, &overlap));
9418 
9419   /* Check SF graph is compatible with DMPlex chart */
9420   {
9421     PetscInt pStart, pEnd, maxLeaf;
9422 
9423     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
9424     PetscCall(PetscSFGetLeafRange(pointSF, NULL, &maxLeaf));
9425     PetscCheck(allowExtraRoots || pEnd - pStart == nroots, PETSC_COMM_SELF, PETSC_ERR_PLIB, "pEnd - pStart = %" PetscInt_FMT " != nroots = %" PetscInt_FMT, pEnd - pStart, nroots);
9426     PetscCheck(maxLeaf < pEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "maxLeaf = %" PetscInt_FMT " >= pEnd = %" PetscInt_FMT, maxLeaf, pEnd);
9427   }
9428 
9429   /* Check Point SF has no local points referenced */
9430   for (l = 0; l < nleaves; l++) {
9431     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);
9432   }
9433 
9434   /* Check there are no cells in interface */
9435   if (!overlap) {
9436     PetscInt cellHeight, cStart, cEnd;
9437 
9438     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9439     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9440     for (l = 0; l < nleaves; ++l) {
9441       const PetscInt point = locals ? locals[l] : l;
9442 
9443       PetscCheck(point < cStart || point >= cEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " which is a cell", point);
9444     }
9445   }
9446 
9447   /* If some point is in interface, then all its cone points must be also in interface (either as leaves or roots) */
9448   {
9449     const PetscInt *rootdegree;
9450 
9451     PetscCall(PetscSFComputeDegreeBegin(pointSF, &rootdegree));
9452     PetscCall(PetscSFComputeDegreeEnd(pointSF, &rootdegree));
9453     for (l = 0; l < nleaves; ++l) {
9454       const PetscInt  point = locals ? locals[l] : l;
9455       const PetscInt *cone;
9456       PetscInt        coneSize, c, idx;
9457 
9458       PetscCall(DMPlexGetConeSize(dm, point, &coneSize));
9459       PetscCall(DMPlexGetCone(dm, point, &cone));
9460       for (c = 0; c < coneSize; ++c) {
9461         if (!rootdegree[cone[c]]) {
9462           if (locals) {
9463             PetscCall(PetscFindInt(cone[c], nleaves, locals, &idx));
9464           } else {
9465             idx = (cone[c] < nleaves) ? cone[c] : -1;
9466           }
9467           PetscCheck(idx >= 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " but not %" PetscInt_FMT " from its cone", point, cone[c]);
9468         }
9469       }
9470     }
9471   }
9472   PetscFunctionReturn(PETSC_SUCCESS);
9473 }
9474 
9475 /*@
9476   DMPlexCheck - Perform various checks of `DMPLEX` sanity
9477 
9478   Input Parameter:
9479 . dm - The `DMPLEX` object
9480 
9481   Level: developer
9482 
9483   Notes:
9484   This is a useful diagnostic when creating meshes programmatically.
9485 
9486   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9487 
9488   Currently does not include `DMPlexCheckCellShape()`.
9489 
9490 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9491 @*/
9492 PetscErrorCode DMPlexCheck(DM dm)
9493 {
9494   PetscInt cellHeight;
9495 
9496   PetscFunctionBegin;
9497   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9498   PetscCall(DMPlexCheckSymmetry(dm));
9499   PetscCall(DMPlexCheckSkeleton(dm, cellHeight));
9500   PetscCall(DMPlexCheckFaces(dm, cellHeight));
9501   PetscCall(DMPlexCheckGeometry(dm));
9502   PetscCall(DMPlexCheckPointSF(dm, NULL, PETSC_FALSE));
9503   PetscCall(DMPlexCheckInterfaceCones(dm));
9504   PetscFunctionReturn(PETSC_SUCCESS);
9505 }
9506 
9507 typedef struct cell_stats {
9508   PetscReal min, max, sum, squaresum;
9509   PetscInt  count;
9510 } cell_stats_t;
9511 
9512 static void MPIAPI cell_stats_reduce(void *a, void *b, int *len, MPI_Datatype *datatype)
9513 {
9514   PetscInt i, N = *len;
9515 
9516   for (i = 0; i < N; i++) {
9517     cell_stats_t *A = (cell_stats_t *)a;
9518     cell_stats_t *B = (cell_stats_t *)b;
9519 
9520     B->min = PetscMin(A->min, B->min);
9521     B->max = PetscMax(A->max, B->max);
9522     B->sum += A->sum;
9523     B->squaresum += A->squaresum;
9524     B->count += A->count;
9525   }
9526 }
9527 
9528 /*@
9529   DMPlexCheckCellShape - Checks the Jacobian of the mapping from reference to real cells and computes some minimal statistics.
9530 
9531   Collective
9532 
9533   Input Parameters:
9534 + dm        - The `DMPLEX` object
9535 . output    - If true, statistics will be displayed on `stdout`
9536 - condLimit - Display all cells above this condition number, or `PETSC_DETERMINE` for no cell output
9537 
9538   Level: developer
9539 
9540   Notes:
9541   This is mainly intended for debugging/testing purposes.
9542 
9543   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9544 
9545 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexComputeOrthogonalQuality()`
9546 @*/
9547 PetscErrorCode DMPlexCheckCellShape(DM dm, PetscBool output, PetscReal condLimit)
9548 {
9549   DM           dmCoarse;
9550   cell_stats_t stats, globalStats;
9551   MPI_Comm     comm = PetscObjectComm((PetscObject)dm);
9552   PetscReal   *J, *invJ, min = 0, max = 0, mean = 0, stdev = 0;
9553   PetscReal    limit = condLimit > 0 ? condLimit : PETSC_MAX_REAL;
9554   PetscInt     cdim, cStart, cEnd, c, eStart, eEnd, count = 0;
9555   PetscMPIInt  rank, size;
9556 
9557   PetscFunctionBegin;
9558   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9559   stats.min = PETSC_MAX_REAL;
9560   stats.max = PETSC_MIN_REAL;
9561   stats.sum = stats.squaresum = 0.;
9562   stats.count                 = 0;
9563 
9564   PetscCallMPI(MPI_Comm_size(comm, &size));
9565   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9566   PetscCall(DMGetCoordinateDim(dm, &cdim));
9567   PetscCall(PetscMalloc2(PetscSqr(cdim), &J, PetscSqr(cdim), &invJ));
9568   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
9569   PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
9570   for (c = cStart; c < cEnd; c++) {
9571     PetscInt  i;
9572     PetscReal frobJ = 0., frobInvJ = 0., cond2, cond, detJ;
9573 
9574     PetscCall(DMPlexComputeCellGeometryAffineFEM(dm, c, NULL, J, invJ, &detJ));
9575     PetscCheck(detJ >= 0.0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Mesh cell %" PetscInt_FMT " is inverted", c);
9576     for (i = 0; i < PetscSqr(cdim); ++i) {
9577       frobJ += J[i] * J[i];
9578       frobInvJ += invJ[i] * invJ[i];
9579     }
9580     cond2 = frobJ * frobInvJ;
9581     cond  = PetscSqrtReal(cond2);
9582 
9583     stats.min = PetscMin(stats.min, cond);
9584     stats.max = PetscMax(stats.max, cond);
9585     stats.sum += cond;
9586     stats.squaresum += cond2;
9587     stats.count++;
9588     if (output && cond > limit) {
9589       PetscSection coordSection;
9590       Vec          coordsLocal;
9591       PetscScalar *coords = NULL;
9592       PetscInt     Nv, d, clSize, cl, *closure = NULL;
9593 
9594       PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
9595       PetscCall(DMGetCoordinateSection(dm, &coordSection));
9596       PetscCall(DMPlexVecGetClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9597       PetscCall(PetscSynchronizedPrintf(comm, "[%d] Cell %" PetscInt_FMT " cond %g\n", rank, c, (double)cond));
9598       for (i = 0; i < Nv / cdim; ++i) {
9599         PetscCall(PetscSynchronizedPrintf(comm, "  Vertex %" PetscInt_FMT ": (", i));
9600         for (d = 0; d < cdim; ++d) {
9601           if (d > 0) PetscCall(PetscSynchronizedPrintf(comm, ", "));
9602           PetscCall(PetscSynchronizedPrintf(comm, "%g", (double)PetscRealPart(coords[i * cdim + d])));
9603         }
9604         PetscCall(PetscSynchronizedPrintf(comm, ")\n"));
9605       }
9606       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9607       for (cl = 0; cl < clSize * 2; cl += 2) {
9608         const PetscInt edge = closure[cl];
9609 
9610         if ((edge >= eStart) && (edge < eEnd)) {
9611           PetscReal len;
9612 
9613           PetscCall(DMPlexComputeCellGeometryFVM(dm, edge, &len, NULL, NULL));
9614           PetscCall(PetscSynchronizedPrintf(comm, "  Edge %" PetscInt_FMT ": length %g\n", edge, (double)len));
9615         }
9616       }
9617       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9618       PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9619     }
9620   }
9621   if (output) PetscCall(PetscSynchronizedFlush(comm, NULL));
9622 
9623   if (size > 1) {
9624     PetscMPIInt  blockLengths[2] = {4, 1};
9625     MPI_Aint     blockOffsets[2] = {offsetof(cell_stats_t, min), offsetof(cell_stats_t, count)};
9626     MPI_Datatype blockTypes[2]   = {MPIU_REAL, MPIU_INT}, statType;
9627     MPI_Op       statReduce;
9628 
9629     PetscCallMPI(MPI_Type_create_struct(2, blockLengths, blockOffsets, blockTypes, &statType));
9630     PetscCallMPI(MPI_Type_commit(&statType));
9631     PetscCallMPI(MPI_Op_create(cell_stats_reduce, PETSC_TRUE, &statReduce));
9632     PetscCallMPI(MPI_Reduce(&stats, &globalStats, 1, statType, statReduce, 0, comm));
9633     PetscCallMPI(MPI_Op_free(&statReduce));
9634     PetscCallMPI(MPI_Type_free(&statType));
9635   } else {
9636     PetscCall(PetscArraycpy(&globalStats, &stats, 1));
9637   }
9638   if (rank == 0) {
9639     count = globalStats.count;
9640     min   = globalStats.min;
9641     max   = globalStats.max;
9642     mean  = globalStats.sum / globalStats.count;
9643     stdev = globalStats.count > 1 ? PetscSqrtReal(PetscMax((globalStats.squaresum - globalStats.count * mean * mean) / (globalStats.count - 1), 0)) : 0.0;
9644   }
9645 
9646   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));
9647   PetscCall(PetscFree2(J, invJ));
9648 
9649   PetscCall(DMGetCoarseDM(dm, &dmCoarse));
9650   if (dmCoarse) {
9651     PetscBool isplex;
9652 
9653     PetscCall(PetscObjectTypeCompare((PetscObject)dmCoarse, DMPLEX, &isplex));
9654     if (isplex) PetscCall(DMPlexCheckCellShape(dmCoarse, output, condLimit));
9655   }
9656   PetscFunctionReturn(PETSC_SUCCESS);
9657 }
9658 
9659 /*@
9660   DMPlexComputeOrthogonalQuality - Compute cell-wise orthogonal quality mesh statistic. Optionally tags all cells with
9661   orthogonal quality below given tolerance.
9662 
9663   Collective
9664 
9665   Input Parameters:
9666 + dm   - The `DMPLEX` object
9667 . fv   - Optional `PetscFV` object for pre-computed cell/face centroid information
9668 - atol - [0, 1] Absolute tolerance for tagging cells.
9669 
9670   Output Parameters:
9671 + OrthQual      - `Vec` containing orthogonal quality per cell
9672 - OrthQualLabel - `DMLabel` tagging cells below atol with `DM_ADAPT_REFINE`
9673 
9674   Options Database Keys:
9675 + -dm_plex_orthogonal_quality_label_view - view OrthQualLabel if label is requested. Currently only `PETSCVIEWERASCII` is supported.
9676 - -dm_plex_orthogonal_quality_vec_view   - view OrthQual vector.
9677 
9678   Level: intermediate
9679 
9680   Notes:
9681   Orthogonal quality is given by the following formula\:
9682 
9683   $ \min \left[ \frac{A_i \cdot f_i}{\|A_i\| \|f_i\|} , \frac{A_i \cdot c_i}{\|A_i\| \|c_i\|} \right]$
9684 
9685   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
9686   is the vector from the current cells centroid to the centroid of its i'th neighbor (which shares a face with the
9687   current cell). This computes the vector similarity between each cell face and its corresponding neighbor centroid by
9688   calculating the cosine of the angle between these vectors.
9689 
9690   Orthogonal quality ranges from 1 (best) to 0 (worst).
9691 
9692   This routine is mainly useful for FVM, however is not restricted to only FVM. The `PetscFV` object is optionally used to check for
9693   pre-computed FVM cell data, but if it is not passed in then this data will be computed.
9694 
9695   Cells are tagged if they have an orthogonal quality less than or equal to the absolute tolerance.
9696 
9697 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCheckCellShape()`, `DMCreateLabel()`, `PetscFV`, `DMLabel`, `Vec`
9698 @*/
9699 PetscErrorCode DMPlexComputeOrthogonalQuality(DM dm, PetscFV fv, PetscReal atol, Vec *OrthQual, DMLabel *OrthQualLabel)
9700 {
9701   PetscInt               nc, cellHeight, cStart, cEnd, cell, cellIter = 0;
9702   PetscInt              *idx;
9703   PetscScalar           *oqVals;
9704   const PetscScalar     *cellGeomArr, *faceGeomArr;
9705   PetscReal             *ci, *fi, *Ai;
9706   MPI_Comm               comm;
9707   Vec                    cellgeom, facegeom;
9708   DM                     dmFace, dmCell;
9709   IS                     glob;
9710   ISLocalToGlobalMapping ltog;
9711   PetscViewer            vwr;
9712 
9713   PetscFunctionBegin;
9714   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9715   if (fv) PetscValidHeaderSpecific(fv, PETSCFV_CLASSID, 2);
9716   PetscAssertPointer(OrthQual, 4);
9717   PetscCheck(atol >= 0.0 && atol <= 1.0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Absolute tolerance %g not in [0,1]", (double)atol);
9718   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9719   PetscCall(DMGetDimension(dm, &nc));
9720   PetscCheck(nc >= 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must have dimension >= 2 (current %" PetscInt_FMT ")", nc);
9721   {
9722     DMPlexInterpolatedFlag interpFlag;
9723 
9724     PetscCall(DMPlexIsInterpolated(dm, &interpFlag));
9725     if (interpFlag != DMPLEX_INTERPOLATED_FULL) {
9726       PetscMPIInt rank;
9727 
9728       PetscCallMPI(MPI_Comm_rank(comm, &rank));
9729       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must be fully interpolated, DM on rank %d is not fully interpolated", rank);
9730     }
9731   }
9732   if (OrthQualLabel) {
9733     PetscAssertPointer(OrthQualLabel, 5);
9734     PetscCall(DMCreateLabel(dm, "Orthogonal_Quality"));
9735     PetscCall(DMGetLabel(dm, "Orthogonal_Quality", OrthQualLabel));
9736   } else {
9737     *OrthQualLabel = NULL;
9738   }
9739   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9740   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9741   PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_TRUE, &glob));
9742   PetscCall(ISLocalToGlobalMappingCreateIS(glob, &ltog));
9743   PetscCall(ISLocalToGlobalMappingSetType(ltog, ISLOCALTOGLOBALMAPPINGHASH));
9744   PetscCall(VecCreate(comm, OrthQual));
9745   PetscCall(VecSetType(*OrthQual, VECSTANDARD));
9746   PetscCall(VecSetSizes(*OrthQual, cEnd - cStart, PETSC_DETERMINE));
9747   PetscCall(VecSetLocalToGlobalMapping(*OrthQual, ltog));
9748   PetscCall(VecSetUp(*OrthQual));
9749   PetscCall(ISDestroy(&glob));
9750   PetscCall(ISLocalToGlobalMappingDestroy(&ltog));
9751   PetscCall(DMPlexGetDataFVM(dm, fv, &cellgeom, &facegeom, NULL));
9752   PetscCall(VecGetArrayRead(cellgeom, &cellGeomArr));
9753   PetscCall(VecGetArrayRead(facegeom, &faceGeomArr));
9754   PetscCall(VecGetDM(cellgeom, &dmCell));
9755   PetscCall(VecGetDM(facegeom, &dmFace));
9756   PetscCall(PetscMalloc5(cEnd - cStart, &idx, cEnd - cStart, &oqVals, nc, &ci, nc, &fi, nc, &Ai));
9757   for (cell = cStart; cell < cEnd; cellIter++, cell++) {
9758     PetscInt         cellneigh, cellneighiter = 0, adjSize = PETSC_DETERMINE;
9759     PetscInt         cellarr[2], *adj = NULL;
9760     PetscScalar     *cArr, *fArr;
9761     PetscReal        minvalc = 1.0, minvalf = 1.0;
9762     PetscFVCellGeom *cg;
9763 
9764     idx[cellIter] = cell - cStart;
9765     cellarr[0]    = cell;
9766     /* Make indexing into cellGeom easier */
9767     PetscCall(DMPlexPointLocalRead(dmCell, cell, cellGeomArr, &cg));
9768     PetscCall(DMPlexGetAdjacency_Internal(dm, cell, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &adjSize, &adj));
9769     /* Technically 1 too big, but easier than fiddling with empty adjacency array */
9770     PetscCall(PetscCalloc2(adjSize, &cArr, adjSize, &fArr));
9771     for (cellneigh = 0; cellneigh < adjSize; cellneighiter++, cellneigh++) {
9772       PetscInt         i;
9773       const PetscInt   neigh  = adj[cellneigh];
9774       PetscReal        normci = 0, normfi = 0, normai = 0;
9775       PetscFVCellGeom *cgneigh;
9776       PetscFVFaceGeom *fg;
9777 
9778       /* Don't count ourselves in the neighbor list */
9779       if (neigh == cell) continue;
9780       PetscCall(DMPlexPointLocalRead(dmCell, neigh, cellGeomArr, &cgneigh));
9781       cellarr[1] = neigh;
9782       {
9783         PetscInt        numcovpts;
9784         const PetscInt *covpts;
9785 
9786         PetscCall(DMPlexGetMeet(dm, 2, cellarr, &numcovpts, &covpts));
9787         PetscCall(DMPlexPointLocalRead(dmFace, covpts[0], faceGeomArr, &fg));
9788         PetscCall(DMPlexRestoreMeet(dm, 2, cellarr, &numcovpts, &covpts));
9789       }
9790 
9791       /* Compute c_i, f_i and their norms */
9792       for (i = 0; i < nc; i++) {
9793         ci[i] = cgneigh->centroid[i] - cg->centroid[i];
9794         fi[i] = fg->centroid[i] - cg->centroid[i];
9795         Ai[i] = fg->normal[i];
9796         normci += PetscPowReal(ci[i], 2);
9797         normfi += PetscPowReal(fi[i], 2);
9798         normai += PetscPowReal(Ai[i], 2);
9799       }
9800       normci = PetscSqrtReal(normci);
9801       normfi = PetscSqrtReal(normfi);
9802       normai = PetscSqrtReal(normai);
9803 
9804       /* Normalize and compute for each face-cell-normal pair */
9805       for (i = 0; i < nc; i++) {
9806         ci[i] = ci[i] / normci;
9807         fi[i] = fi[i] / normfi;
9808         Ai[i] = Ai[i] / normai;
9809         /* PetscAbs because I don't know if normals are guaranteed to point out */
9810         cArr[cellneighiter] += PetscAbs(Ai[i] * ci[i]);
9811         fArr[cellneighiter] += PetscAbs(Ai[i] * fi[i]);
9812       }
9813       if (PetscRealPart(cArr[cellneighiter]) < minvalc) minvalc = PetscRealPart(cArr[cellneighiter]);
9814       if (PetscRealPart(fArr[cellneighiter]) < minvalf) minvalf = PetscRealPart(fArr[cellneighiter]);
9815     }
9816     PetscCall(PetscFree(adj));
9817     PetscCall(PetscFree2(cArr, fArr));
9818     /* Defer to cell if they're equal */
9819     oqVals[cellIter] = PetscMin(minvalf, minvalc);
9820     if (OrthQualLabel) {
9821       if (PetscRealPart(oqVals[cellIter]) <= atol) PetscCall(DMLabelSetValue(*OrthQualLabel, cell, DM_ADAPT_REFINE));
9822     }
9823   }
9824   PetscCall(VecSetValuesLocal(*OrthQual, cEnd - cStart, idx, oqVals, INSERT_VALUES));
9825   PetscCall(VecAssemblyBegin(*OrthQual));
9826   PetscCall(VecAssemblyEnd(*OrthQual));
9827   PetscCall(VecRestoreArrayRead(cellgeom, &cellGeomArr));
9828   PetscCall(VecRestoreArrayRead(facegeom, &faceGeomArr));
9829   PetscCall(PetscOptionsGetViewer(comm, NULL, NULL, "-dm_plex_orthogonal_quality_label_view", &vwr, NULL, NULL));
9830   if (OrthQualLabel) {
9831     if (vwr) PetscCall(DMLabelView(*OrthQualLabel, vwr));
9832   }
9833   PetscCall(PetscFree5(idx, oqVals, ci, fi, Ai));
9834   PetscCall(PetscOptionsRestoreViewer(&vwr));
9835   PetscCall(VecViewFromOptions(*OrthQual, NULL, "-dm_plex_orthogonal_quality_vec_view"));
9836   PetscFunctionReturn(PETSC_SUCCESS);
9837 }
9838 
9839 /* this is here instead of DMGetOutputDM because output DM still has constraints in the local indices that affect
9840  * interpolator construction */
9841 static PetscErrorCode DMGetFullDM(DM dm, DM *odm)
9842 {
9843   PetscSection section, newSection, gsection;
9844   PetscSF      sf;
9845   PetscBool    hasConstraints, ghasConstraints;
9846 
9847   PetscFunctionBegin;
9848   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9849   PetscAssertPointer(odm, 2);
9850   PetscCall(DMGetLocalSection(dm, &section));
9851   PetscCall(PetscSectionHasConstraints(section, &hasConstraints));
9852   PetscCall(MPIU_Allreduce(&hasConstraints, &ghasConstraints, 1, MPIU_BOOL, MPI_LOR, PetscObjectComm((PetscObject)dm)));
9853   if (!ghasConstraints) {
9854     PetscCall(PetscObjectReference((PetscObject)dm));
9855     *odm = dm;
9856     PetscFunctionReturn(PETSC_SUCCESS);
9857   }
9858   PetscCall(DMClone(dm, odm));
9859   PetscCall(DMCopyFields(dm, *odm));
9860   PetscCall(DMGetLocalSection(*odm, &newSection));
9861   PetscCall(DMGetPointSF(*odm, &sf));
9862   PetscCall(PetscSectionCreateGlobalSection(newSection, sf, PETSC_TRUE, PETSC_FALSE, &gsection));
9863   PetscCall(DMSetGlobalSection(*odm, gsection));
9864   PetscCall(PetscSectionDestroy(&gsection));
9865   PetscFunctionReturn(PETSC_SUCCESS);
9866 }
9867 
9868 static PetscErrorCode DMCreateAffineInterpolationCorrection_Plex(DM dmc, DM dmf, Vec *shift)
9869 {
9870   DM        dmco, dmfo;
9871   Mat       interpo;
9872   Vec       rscale;
9873   Vec       cglobalo, clocal;
9874   Vec       fglobal, fglobalo, flocal;
9875   PetscBool regular;
9876 
9877   PetscFunctionBegin;
9878   PetscCall(DMGetFullDM(dmc, &dmco));
9879   PetscCall(DMGetFullDM(dmf, &dmfo));
9880   PetscCall(DMSetCoarseDM(dmfo, dmco));
9881   PetscCall(DMPlexGetRegularRefinement(dmf, &regular));
9882   PetscCall(DMPlexSetRegularRefinement(dmfo, regular));
9883   PetscCall(DMCreateInterpolation(dmco, dmfo, &interpo, &rscale));
9884   PetscCall(DMCreateGlobalVector(dmco, &cglobalo));
9885   PetscCall(DMCreateLocalVector(dmc, &clocal));
9886   PetscCall(VecSet(cglobalo, 0.));
9887   PetscCall(VecSet(clocal, 0.));
9888   PetscCall(DMCreateGlobalVector(dmf, &fglobal));
9889   PetscCall(DMCreateGlobalVector(dmfo, &fglobalo));
9890   PetscCall(DMCreateLocalVector(dmf, &flocal));
9891   PetscCall(VecSet(fglobal, 0.));
9892   PetscCall(VecSet(fglobalo, 0.));
9893   PetscCall(VecSet(flocal, 0.));
9894   PetscCall(DMPlexInsertBoundaryValues(dmc, PETSC_TRUE, clocal, 0., NULL, NULL, NULL));
9895   PetscCall(DMLocalToGlobalBegin(dmco, clocal, INSERT_VALUES, cglobalo));
9896   PetscCall(DMLocalToGlobalEnd(dmco, clocal, INSERT_VALUES, cglobalo));
9897   PetscCall(MatMult(interpo, cglobalo, fglobalo));
9898   PetscCall(DMGlobalToLocalBegin(dmfo, fglobalo, INSERT_VALUES, flocal));
9899   PetscCall(DMGlobalToLocalEnd(dmfo, fglobalo, INSERT_VALUES, flocal));
9900   PetscCall(DMLocalToGlobalBegin(dmf, flocal, INSERT_VALUES, fglobal));
9901   PetscCall(DMLocalToGlobalEnd(dmf, flocal, INSERT_VALUES, fglobal));
9902   *shift = fglobal;
9903   PetscCall(VecDestroy(&flocal));
9904   PetscCall(VecDestroy(&fglobalo));
9905   PetscCall(VecDestroy(&clocal));
9906   PetscCall(VecDestroy(&cglobalo));
9907   PetscCall(VecDestroy(&rscale));
9908   PetscCall(MatDestroy(&interpo));
9909   PetscCall(DMDestroy(&dmfo));
9910   PetscCall(DMDestroy(&dmco));
9911   PetscFunctionReturn(PETSC_SUCCESS);
9912 }
9913 
9914 PETSC_INTERN PetscErrorCode DMInterpolateSolution_Plex(DM coarse, DM fine, Mat interp, Vec coarseSol, Vec fineSol)
9915 {
9916   PetscObject shifto;
9917   Vec         shift;
9918 
9919   PetscFunctionBegin;
9920   if (!interp) {
9921     Vec rscale;
9922 
9923     PetscCall(DMCreateInterpolation(coarse, fine, &interp, &rscale));
9924     PetscCall(VecDestroy(&rscale));
9925   } else {
9926     PetscCall(PetscObjectReference((PetscObject)interp));
9927   }
9928   PetscCall(PetscObjectQuery((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", &shifto));
9929   if (!shifto) {
9930     PetscCall(DMCreateAffineInterpolationCorrection_Plex(coarse, fine, &shift));
9931     PetscCall(PetscObjectCompose((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", (PetscObject)shift));
9932     shifto = (PetscObject)shift;
9933     PetscCall(VecDestroy(&shift));
9934   }
9935   shift = (Vec)shifto;
9936   PetscCall(MatInterpolate(interp, coarseSol, fineSol));
9937   PetscCall(VecAXPY(fineSol, 1.0, shift));
9938   PetscCall(MatDestroy(&interp));
9939   PetscFunctionReturn(PETSC_SUCCESS);
9940 }
9941 
9942 /* Pointwise interpolation
9943      Just code FEM for now
9944      u^f = I u^c
9945      sum_k u^f_k phi^f_k = I sum_j u^c_j phi^c_j
9946      u^f_i = sum_j psi^f_i I phi^c_j u^c_j
9947      I_{ij} = psi^f_i phi^c_j
9948 */
9949 PetscErrorCode DMCreateInterpolation_Plex(DM dmCoarse, DM dmFine, Mat *interpolation, Vec *scaling)
9950 {
9951   PetscSection gsc, gsf;
9952   PetscInt     m, n;
9953   void        *ctx;
9954   DM           cdm;
9955   PetscBool    regular, ismatis, isRefined = dmCoarse->data == dmFine->data ? PETSC_FALSE : PETSC_TRUE;
9956 
9957   PetscFunctionBegin;
9958   PetscCall(DMGetGlobalSection(dmFine, &gsf));
9959   PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
9960   PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
9961   PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
9962 
9963   PetscCall(PetscStrcmp(dmCoarse->mattype, MATIS, &ismatis));
9964   PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), interpolation));
9965   PetscCall(MatSetSizes(*interpolation, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
9966   PetscCall(MatSetType(*interpolation, ismatis ? MATAIJ : dmCoarse->mattype));
9967   PetscCall(DMGetApplicationContext(dmFine, &ctx));
9968 
9969   PetscCall(DMGetCoarseDM(dmFine, &cdm));
9970   PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
9971   if (!isRefined || (regular && cdm == dmCoarse)) PetscCall(DMPlexComputeInterpolatorNested(dmCoarse, dmFine, isRefined, *interpolation, ctx));
9972   else PetscCall(DMPlexComputeInterpolatorGeneral(dmCoarse, dmFine, *interpolation, ctx));
9973   PetscCall(MatViewFromOptions(*interpolation, NULL, "-interp_mat_view"));
9974   if (scaling) {
9975     /* Use naive scaling */
9976     PetscCall(DMCreateInterpolationScale(dmCoarse, dmFine, *interpolation, scaling));
9977   }
9978   PetscFunctionReturn(PETSC_SUCCESS);
9979 }
9980 
9981 PetscErrorCode DMCreateInjection_Plex(DM dmCoarse, DM dmFine, Mat *mat)
9982 {
9983   VecScatter ctx;
9984 
9985   PetscFunctionBegin;
9986   PetscCall(DMPlexComputeInjectorFEM(dmCoarse, dmFine, &ctx, NULL));
9987   PetscCall(MatCreateScatter(PetscObjectComm((PetscObject)ctx), ctx, mat));
9988   PetscCall(VecScatterDestroy(&ctx));
9989   PetscFunctionReturn(PETSC_SUCCESS);
9990 }
9991 
9992 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[])
9993 {
9994   const PetscInt Nc = uOff[1] - uOff[0];
9995   PetscInt       c;
9996   for (c = 0; c < Nc; ++c) g0[c * Nc + c] = 1.0;
9997 }
9998 
9999 PetscErrorCode DMCreateMassMatrixLumped_Plex(DM dm, Vec *mass)
10000 {
10001   DM           dmc;
10002   PetscDS      ds;
10003   Vec          ones, locmass;
10004   IS           cellIS;
10005   PetscFormKey key;
10006   PetscInt     depth;
10007 
10008   PetscFunctionBegin;
10009   PetscCall(DMClone(dm, &dmc));
10010   PetscCall(DMCopyDisc(dm, dmc));
10011   PetscCall(DMGetDS(dmc, &ds));
10012   PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
10013   PetscCall(DMCreateGlobalVector(dmc, mass));
10014   PetscCall(DMGetLocalVector(dmc, &ones));
10015   PetscCall(DMGetLocalVector(dmc, &locmass));
10016   PetscCall(DMPlexGetDepth(dmc, &depth));
10017   PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
10018   PetscCall(VecSet(locmass, 0.0));
10019   PetscCall(VecSet(ones, 1.0));
10020   key.label = NULL;
10021   key.value = 0;
10022   key.field = 0;
10023   key.part  = 0;
10024   PetscCall(DMPlexComputeJacobian_Action_Internal(dmc, key, cellIS, 0.0, 0.0, ones, NULL, ones, locmass, NULL));
10025   PetscCall(ISDestroy(&cellIS));
10026   PetscCall(VecSet(*mass, 0.0));
10027   PetscCall(DMLocalToGlobalBegin(dmc, locmass, ADD_VALUES, *mass));
10028   PetscCall(DMLocalToGlobalEnd(dmc, locmass, ADD_VALUES, *mass));
10029   PetscCall(DMRestoreLocalVector(dmc, &ones));
10030   PetscCall(DMRestoreLocalVector(dmc, &locmass));
10031   PetscCall(DMDestroy(&dmc));
10032   PetscFunctionReturn(PETSC_SUCCESS);
10033 }
10034 
10035 PetscErrorCode DMCreateMassMatrix_Plex(DM dmCoarse, DM dmFine, Mat *mass)
10036 {
10037   PetscSection gsc, gsf;
10038   PetscInt     m, n;
10039   void        *ctx;
10040   DM           cdm;
10041   PetscBool    regular;
10042 
10043   PetscFunctionBegin;
10044   if (dmFine == dmCoarse) {
10045     DM            dmc;
10046     PetscDS       ds;
10047     PetscWeakForm wf;
10048     Vec           u;
10049     IS            cellIS;
10050     PetscFormKey  key;
10051     PetscInt      depth;
10052 
10053     PetscCall(DMClone(dmFine, &dmc));
10054     PetscCall(DMCopyDisc(dmFine, dmc));
10055     PetscCall(DMGetDS(dmc, &ds));
10056     PetscCall(PetscDSGetWeakForm(ds, &wf));
10057     PetscCall(PetscWeakFormClear(wf));
10058     PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
10059     PetscCall(DMCreateMatrix(dmc, mass));
10060     PetscCall(DMGetLocalVector(dmc, &u));
10061     PetscCall(DMPlexGetDepth(dmc, &depth));
10062     PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
10063     PetscCall(MatZeroEntries(*mass));
10064     key.label = NULL;
10065     key.value = 0;
10066     key.field = 0;
10067     key.part  = 0;
10068     PetscCall(DMPlexComputeJacobian_Internal(dmc, key, cellIS, 0.0, 0.0, u, NULL, *mass, *mass, NULL));
10069     PetscCall(ISDestroy(&cellIS));
10070     PetscCall(DMRestoreLocalVector(dmc, &u));
10071     PetscCall(DMDestroy(&dmc));
10072   } else {
10073     PetscCall(DMGetGlobalSection(dmFine, &gsf));
10074     PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
10075     PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
10076     PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
10077 
10078     PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), mass));
10079     PetscCall(MatSetSizes(*mass, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
10080     PetscCall(MatSetType(*mass, dmCoarse->mattype));
10081     PetscCall(DMGetApplicationContext(dmFine, &ctx));
10082 
10083     PetscCall(DMGetCoarseDM(dmFine, &cdm));
10084     PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
10085     if (regular && cdm == dmCoarse) PetscCall(DMPlexComputeMassMatrixNested(dmCoarse, dmFine, *mass, ctx));
10086     else PetscCall(DMPlexComputeMassMatrixGeneral(dmCoarse, dmFine, *mass, ctx));
10087   }
10088   PetscCall(MatViewFromOptions(*mass, NULL, "-mass_mat_view"));
10089   PetscFunctionReturn(PETSC_SUCCESS);
10090 }
10091 
10092 /*@
10093   DMPlexGetRegularRefinement - Get the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
10094 
10095   Input Parameter:
10096 . dm - The `DMPLEX` object
10097 
10098   Output Parameter:
10099 . regular - The flag
10100 
10101   Level: intermediate
10102 
10103 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetRegularRefinement()`
10104 @*/
10105 PetscErrorCode DMPlexGetRegularRefinement(DM dm, PetscBool *regular)
10106 {
10107   PetscFunctionBegin;
10108   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10109   PetscAssertPointer(regular, 2);
10110   *regular = ((DM_Plex *)dm->data)->regularRefinement;
10111   PetscFunctionReturn(PETSC_SUCCESS);
10112 }
10113 
10114 /*@
10115   DMPlexSetRegularRefinement - Set the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
10116 
10117   Input Parameters:
10118 + dm      - The `DMPLEX` object
10119 - regular - The flag
10120 
10121   Level: intermediate
10122 
10123 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetRegularRefinement()`
10124 @*/
10125 PetscErrorCode DMPlexSetRegularRefinement(DM dm, PetscBool regular)
10126 {
10127   PetscFunctionBegin;
10128   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10129   ((DM_Plex *)dm->data)->regularRefinement = regular;
10130   PetscFunctionReturn(PETSC_SUCCESS);
10131 }
10132 
10133 /*@
10134   DMPlexGetAnchors - Get the layout of the anchor (point-to-point) constraints.  Typically, the user will not have to
10135   call DMPlexGetAnchors() directly: if there are anchors, then `DMPlexGetAnchors()` is called during `DMGetDefaultConstraints()`.
10136 
10137   Not Collective
10138 
10139   Input Parameter:
10140 . dm - The `DMPLEX` object
10141 
10142   Output Parameters:
10143 + anchorSection - If not `NULL`, set to the section describing which points anchor the constrained points.
10144 - anchorIS      - If not `NULL`, set to the list of anchors indexed by `anchorSection`
10145 
10146   Level: intermediate
10147 
10148 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`, `IS`, `PetscSection`
10149 @*/
10150 PetscErrorCode DMPlexGetAnchors(DM dm, PetscSection *anchorSection, IS *anchorIS)
10151 {
10152   DM_Plex *plex = (DM_Plex *)dm->data;
10153 
10154   PetscFunctionBegin;
10155   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10156   if (!plex->anchorSection && !plex->anchorIS && plex->createanchors) PetscCall((*plex->createanchors)(dm));
10157   if (anchorSection) *anchorSection = plex->anchorSection;
10158   if (anchorIS) *anchorIS = plex->anchorIS;
10159   PetscFunctionReturn(PETSC_SUCCESS);
10160 }
10161 
10162 /*@
10163   DMPlexSetAnchors - Set the layout of the local anchor (point-to-point) constraints.
10164 
10165   Collective
10166 
10167   Input Parameters:
10168 + dm            - The `DMPLEX` object
10169 . anchorSection - The section that describes the mapping from constrained points to the anchor points listed in anchorIS.
10170                   Must have a local communicator (`PETSC_COMM_SELF` or derivative).
10171 - anchorIS      - The list of all anchor points.  Must have a local communicator (`PETSC_COMM_SELF` or derivative).
10172 
10173   Level: intermediate
10174 
10175   Notes:
10176   Unlike boundary conditions, when a point's degrees of freedom in a section are constrained to
10177   an outside value, the anchor constraints set a point's degrees of freedom to be a linear
10178   combination of other points' degrees of freedom.
10179 
10180   After specifying the layout of constraints with `DMPlexSetAnchors()`, one specifies the constraints by calling
10181   `DMGetDefaultConstraints()` and filling in the entries in the constraint matrix.
10182 
10183   The reference counts of `anchorSection` and `anchorIS` are incremented.
10184 
10185 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`
10186 @*/
10187 PetscErrorCode DMPlexSetAnchors(DM dm, PetscSection anchorSection, IS anchorIS)
10188 {
10189   DM_Plex    *plex = (DM_Plex *)dm->data;
10190   PetscMPIInt result;
10191 
10192   PetscFunctionBegin;
10193   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10194   if (anchorSection) {
10195     PetscValidHeaderSpecific(anchorSection, PETSC_SECTION_CLASSID, 2);
10196     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorSection), &result));
10197     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor section must have local communicator");
10198   }
10199   if (anchorIS) {
10200     PetscValidHeaderSpecific(anchorIS, IS_CLASSID, 3);
10201     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorIS), &result));
10202     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor IS must have local communicator");
10203   }
10204 
10205   PetscCall(PetscObjectReference((PetscObject)anchorSection));
10206   PetscCall(PetscSectionDestroy(&plex->anchorSection));
10207   plex->anchorSection = anchorSection;
10208 
10209   PetscCall(PetscObjectReference((PetscObject)anchorIS));
10210   PetscCall(ISDestroy(&plex->anchorIS));
10211   plex->anchorIS = anchorIS;
10212 
10213   if (PetscUnlikelyDebug(anchorIS && anchorSection)) {
10214     PetscInt        size, a, pStart, pEnd;
10215     const PetscInt *anchors;
10216 
10217     PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
10218     PetscCall(ISGetLocalSize(anchorIS, &size));
10219     PetscCall(ISGetIndices(anchorIS, &anchors));
10220     for (a = 0; a < size; a++) {
10221       PetscInt p;
10222 
10223       p = anchors[a];
10224       if (p >= pStart && p < pEnd) {
10225         PetscInt dof;
10226 
10227         PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
10228         if (dof) {
10229           PetscCall(ISRestoreIndices(anchorIS, &anchors));
10230           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Point %" PetscInt_FMT " cannot be constrained and an anchor", p);
10231         }
10232       }
10233     }
10234     PetscCall(ISRestoreIndices(anchorIS, &anchors));
10235   }
10236   /* reset the generic constraints */
10237   PetscCall(DMSetDefaultConstraints(dm, NULL, NULL, NULL));
10238   PetscFunctionReturn(PETSC_SUCCESS);
10239 }
10240 
10241 static PetscErrorCode DMPlexCreateConstraintSection_Anchors(DM dm, PetscSection section, PetscSection *cSec)
10242 {
10243   PetscSection anchorSection;
10244   PetscInt     pStart, pEnd, sStart, sEnd, p, dof, numFields, f;
10245 
10246   PetscFunctionBegin;
10247   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10248   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
10249   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, cSec));
10250   PetscCall(PetscSectionGetNumFields(section, &numFields));
10251   if (numFields) {
10252     PetscInt f;
10253     PetscCall(PetscSectionSetNumFields(*cSec, numFields));
10254 
10255     for (f = 0; f < numFields; f++) {
10256       PetscInt numComp;
10257 
10258       PetscCall(PetscSectionGetFieldComponents(section, f, &numComp));
10259       PetscCall(PetscSectionSetFieldComponents(*cSec, f, numComp));
10260     }
10261   }
10262   PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
10263   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
10264   pStart = PetscMax(pStart, sStart);
10265   pEnd   = PetscMin(pEnd, sEnd);
10266   pEnd   = PetscMax(pStart, pEnd);
10267   PetscCall(PetscSectionSetChart(*cSec, pStart, pEnd));
10268   for (p = pStart; p < pEnd; p++) {
10269     PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
10270     if (dof) {
10271       PetscCall(PetscSectionGetDof(section, p, &dof));
10272       PetscCall(PetscSectionSetDof(*cSec, p, dof));
10273       for (f = 0; f < numFields; f++) {
10274         PetscCall(PetscSectionGetFieldDof(section, p, f, &dof));
10275         PetscCall(PetscSectionSetFieldDof(*cSec, p, f, dof));
10276       }
10277     }
10278   }
10279   PetscCall(PetscSectionSetUp(*cSec));
10280   PetscCall(PetscObjectSetName((PetscObject)*cSec, "Constraint Section"));
10281   PetscFunctionReturn(PETSC_SUCCESS);
10282 }
10283 
10284 static PetscErrorCode DMPlexCreateConstraintMatrix_Anchors(DM dm, PetscSection section, PetscSection cSec, Mat *cMat)
10285 {
10286   PetscSection    aSec;
10287   PetscInt        pStart, pEnd, p, sStart, sEnd, dof, aDof, aOff, off, nnz, annz, m, n, q, a, offset, *i, *j;
10288   const PetscInt *anchors;
10289   PetscInt        numFields, f;
10290   IS              aIS;
10291   MatType         mtype;
10292   PetscBool       iscuda, iskokkos;
10293 
10294   PetscFunctionBegin;
10295   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10296   PetscCall(PetscSectionGetStorageSize(cSec, &m));
10297   PetscCall(PetscSectionGetStorageSize(section, &n));
10298   PetscCall(MatCreate(PETSC_COMM_SELF, cMat));
10299   PetscCall(MatSetSizes(*cMat, m, n, m, n));
10300   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJCUSPARSE, &iscuda));
10301   if (!iscuda) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJCUSPARSE, &iscuda));
10302   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJKOKKOS, &iskokkos));
10303   if (!iskokkos) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJKOKKOS, &iskokkos));
10304   if (iscuda) mtype = MATSEQAIJCUSPARSE;
10305   else if (iskokkos) mtype = MATSEQAIJKOKKOS;
10306   else mtype = MATSEQAIJ;
10307   PetscCall(MatSetType(*cMat, mtype));
10308   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
10309   PetscCall(ISGetIndices(aIS, &anchors));
10310   /* cSec will be a subset of aSec and section */
10311   PetscCall(PetscSectionGetChart(cSec, &pStart, &pEnd));
10312   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
10313   PetscCall(PetscMalloc1(m + 1, &i));
10314   i[0] = 0;
10315   PetscCall(PetscSectionGetNumFields(section, &numFields));
10316   for (p = pStart; p < pEnd; p++) {
10317     PetscInt rDof, rOff, r;
10318 
10319     PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10320     if (!rDof) continue;
10321     PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10322     if (numFields) {
10323       for (f = 0; f < numFields; f++) {
10324         annz = 0;
10325         for (r = 0; r < rDof; r++) {
10326           a = anchors[rOff + r];
10327           if (a < sStart || a >= sEnd) continue;
10328           PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
10329           annz += aDof;
10330         }
10331         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
10332         PetscCall(PetscSectionGetFieldOffset(cSec, p, f, &off));
10333         for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
10334       }
10335     } else {
10336       annz = 0;
10337       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10338       for (q = 0; q < dof; q++) {
10339         a = anchors[rOff + q];
10340         if (a < sStart || a >= sEnd) continue;
10341         PetscCall(PetscSectionGetDof(section, a, &aDof));
10342         annz += aDof;
10343       }
10344       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10345       PetscCall(PetscSectionGetOffset(cSec, p, &off));
10346       for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
10347     }
10348   }
10349   nnz = i[m];
10350   PetscCall(PetscMalloc1(nnz, &j));
10351   offset = 0;
10352   for (p = pStart; p < pEnd; p++) {
10353     if (numFields) {
10354       for (f = 0; f < numFields; f++) {
10355         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
10356         for (q = 0; q < dof; q++) {
10357           PetscInt rDof, rOff, r;
10358           PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10359           PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10360           for (r = 0; r < rDof; r++) {
10361             PetscInt s;
10362 
10363             a = anchors[rOff + r];
10364             if (a < sStart || a >= sEnd) continue;
10365             PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
10366             PetscCall(PetscSectionGetFieldOffset(section, a, f, &aOff));
10367             for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
10368           }
10369         }
10370       }
10371     } else {
10372       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10373       for (q = 0; q < dof; q++) {
10374         PetscInt rDof, rOff, r;
10375         PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10376         PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10377         for (r = 0; r < rDof; r++) {
10378           PetscInt s;
10379 
10380           a = anchors[rOff + r];
10381           if (a < sStart || a >= sEnd) continue;
10382           PetscCall(PetscSectionGetDof(section, a, &aDof));
10383           PetscCall(PetscSectionGetOffset(section, a, &aOff));
10384           for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
10385         }
10386       }
10387     }
10388   }
10389   PetscCall(MatSeqAIJSetPreallocationCSR(*cMat, i, j, NULL));
10390   PetscCall(PetscFree(i));
10391   PetscCall(PetscFree(j));
10392   PetscCall(ISRestoreIndices(aIS, &anchors));
10393   PetscFunctionReturn(PETSC_SUCCESS);
10394 }
10395 
10396 PetscErrorCode DMCreateDefaultConstraints_Plex(DM dm)
10397 {
10398   DM_Plex     *plex = (DM_Plex *)dm->data;
10399   PetscSection anchorSection, section, cSec;
10400   Mat          cMat;
10401 
10402   PetscFunctionBegin;
10403   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10404   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
10405   if (anchorSection) {
10406     PetscInt Nf;
10407 
10408     PetscCall(DMGetLocalSection(dm, &section));
10409     PetscCall(DMPlexCreateConstraintSection_Anchors(dm, section, &cSec));
10410     PetscCall(DMPlexCreateConstraintMatrix_Anchors(dm, section, cSec, &cMat));
10411     PetscCall(DMGetNumFields(dm, &Nf));
10412     if (Nf && plex->computeanchormatrix) PetscCall((*plex->computeanchormatrix)(dm, section, cSec, cMat));
10413     PetscCall(DMSetDefaultConstraints(dm, cSec, cMat, NULL));
10414     PetscCall(PetscSectionDestroy(&cSec));
10415     PetscCall(MatDestroy(&cMat));
10416   }
10417   PetscFunctionReturn(PETSC_SUCCESS);
10418 }
10419 
10420 PetscErrorCode DMCreateSubDomainDM_Plex(DM dm, DMLabel label, PetscInt value, IS *is, DM *subdm)
10421 {
10422   IS           subis;
10423   PetscSection section, subsection;
10424 
10425   PetscFunctionBegin;
10426   PetscCall(DMGetLocalSection(dm, &section));
10427   PetscCheck(section, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set default section for DM before splitting subdomain");
10428   PetscCheck(subdm, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set output subDM for splitting subdomain");
10429   /* Create subdomain */
10430   PetscCall(DMPlexFilter(dm, label, value, subdm));
10431   /* Create submodel */
10432   PetscCall(DMPlexGetSubpointIS(*subdm, &subis));
10433   PetscCall(PetscSectionCreateSubmeshSection(section, subis, &subsection));
10434   PetscCall(DMSetLocalSection(*subdm, subsection));
10435   PetscCall(PetscSectionDestroy(&subsection));
10436   PetscCall(DMCopyDisc(dm, *subdm));
10437   /* Create map from submodel to global model */
10438   if (is) {
10439     PetscSection    sectionGlobal, subsectionGlobal;
10440     IS              spIS;
10441     const PetscInt *spmap;
10442     PetscInt       *subIndices;
10443     PetscInt        subSize = 0, subOff = 0, pStart, pEnd, p;
10444     PetscInt        Nf, f, bs = -1, bsLocal[2], bsMinMax[2];
10445 
10446     PetscCall(DMPlexGetSubpointIS(*subdm, &spIS));
10447     PetscCall(ISGetIndices(spIS, &spmap));
10448     PetscCall(PetscSectionGetNumFields(section, &Nf));
10449     PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
10450     PetscCall(DMGetGlobalSection(*subdm, &subsectionGlobal));
10451     PetscCall(PetscSectionGetChart(subsection, &pStart, &pEnd));
10452     for (p = pStart; p < pEnd; ++p) {
10453       PetscInt gdof, pSubSize = 0;
10454 
10455       PetscCall(PetscSectionGetDof(sectionGlobal, p, &gdof));
10456       if (gdof > 0) {
10457         for (f = 0; f < Nf; ++f) {
10458           PetscInt fdof, fcdof;
10459 
10460           PetscCall(PetscSectionGetFieldDof(subsection, p, f, &fdof));
10461           PetscCall(PetscSectionGetFieldConstraintDof(subsection, p, f, &fcdof));
10462           pSubSize += fdof - fcdof;
10463         }
10464         subSize += pSubSize;
10465         if (pSubSize) {
10466           if (bs < 0) {
10467             bs = pSubSize;
10468           } else if (bs != pSubSize) {
10469             /* Layout does not admit a pointwise block size */
10470             bs = 1;
10471           }
10472         }
10473       }
10474     }
10475     /* Must have same blocksize on all procs (some might have no points) */
10476     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs;
10477     bsLocal[1] = bs;
10478     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
10479     if (bsMinMax[0] != bsMinMax[1]) {
10480       bs = 1;
10481     } else {
10482       bs = bsMinMax[0];
10483     }
10484     PetscCall(PetscMalloc1(subSize, &subIndices));
10485     for (p = pStart; p < pEnd; ++p) {
10486       PetscInt gdof, goff;
10487 
10488       PetscCall(PetscSectionGetDof(subsectionGlobal, p, &gdof));
10489       if (gdof > 0) {
10490         const PetscInt point = spmap[p];
10491 
10492         PetscCall(PetscSectionGetOffset(sectionGlobal, point, &goff));
10493         for (f = 0; f < Nf; ++f) {
10494           PetscInt fdof, fcdof, fc, f2, poff = 0;
10495 
10496           /* Can get rid of this loop by storing field information in the global section */
10497           for (f2 = 0; f2 < f; ++f2) {
10498             PetscCall(PetscSectionGetFieldDof(section, p, f2, &fdof));
10499             PetscCall(PetscSectionGetFieldConstraintDof(section, p, f2, &fcdof));
10500             poff += fdof - fcdof;
10501           }
10502           PetscCall(PetscSectionGetFieldDof(section, p, f, &fdof));
10503           PetscCall(PetscSectionGetFieldConstraintDof(section, p, f, &fcdof));
10504           for (fc = 0; fc < fdof - fcdof; ++fc, ++subOff) subIndices[subOff] = goff + poff + fc;
10505         }
10506       }
10507     }
10508     PetscCall(ISRestoreIndices(spIS, &spmap));
10509     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), subSize, subIndices, PETSC_OWN_POINTER, is));
10510     if (bs > 1) {
10511       /* We need to check that the block size does not come from non-contiguous fields */
10512       PetscInt i, j, set = 1;
10513       for (i = 0; i < subSize; i += bs) {
10514         for (j = 0; j < bs; ++j) {
10515           if (subIndices[i + j] != subIndices[i] + j) {
10516             set = 0;
10517             break;
10518           }
10519         }
10520       }
10521       if (set) PetscCall(ISSetBlockSize(*is, bs));
10522     }
10523     /* Attach nullspace */
10524     for (f = 0; f < Nf; ++f) {
10525       (*subdm)->nullspaceConstructors[f] = dm->nullspaceConstructors[f];
10526       if ((*subdm)->nullspaceConstructors[f]) break;
10527     }
10528     if (f < Nf) {
10529       MatNullSpace nullSpace;
10530       PetscCall((*(*subdm)->nullspaceConstructors[f])(*subdm, f, f, &nullSpace));
10531 
10532       PetscCall(PetscObjectCompose((PetscObject)*is, "nullspace", (PetscObject)nullSpace));
10533       PetscCall(MatNullSpaceDestroy(&nullSpace));
10534     }
10535   }
10536   PetscFunctionReturn(PETSC_SUCCESS);
10537 }
10538 
10539 /*@
10540   DMPlexMonitorThroughput - Report the cell throughput of FE integration
10541 
10542   Input Parameters:
10543 + dm    - The `DM`
10544 - dummy - unused argument
10545 
10546   Options Database Key:
10547 . -dm_plex_monitor_throughput - Activate the monitor
10548 
10549   Level: developer
10550 
10551 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexCreate()`
10552 @*/
10553 PetscErrorCode DMPlexMonitorThroughput(DM dm, void *dummy)
10554 {
10555   PetscLogHandler default_handler;
10556 
10557   PetscFunctionBegin;
10558   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10559   PetscCall(PetscLogGetDefaultHandler(&default_handler));
10560   if (default_handler) {
10561     PetscLogEvent      event;
10562     PetscEventPerfInfo eventInfo;
10563     PetscReal          cellRate, flopRate;
10564     PetscInt           cStart, cEnd, Nf, N;
10565     const char        *name;
10566 
10567     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
10568     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
10569     PetscCall(DMGetNumFields(dm, &Nf));
10570     PetscCall(PetscLogEventGetId("DMPlexResidualFE", &event));
10571     PetscCall(PetscLogEventGetPerfInfo(PETSC_DEFAULT, event, &eventInfo));
10572     N        = (cEnd - cStart) * Nf * eventInfo.count;
10573     flopRate = eventInfo.flops / eventInfo.time;
10574     cellRate = N / eventInfo.time;
10575     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)));
10576   } else {
10577     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.");
10578   }
10579   PetscFunctionReturn(PETSC_SUCCESS);
10580 }
10581