xref: /petsc/src/dm/impls/plex/plex.c (revision 3d77ad52841f320b3f6ad02ce14f35e73e722480)
1 #include <petsc/private/dmpleximpl.h> /*I      "petscdmplex.h"   I*/
2 #include <petsc/private/dmlabelimpl.h>
3 #include <petsc/private/isimpl.h>
4 #include <petsc/private/vecimpl.h>
5 #include <petsc/private/glvisvecimpl.h>
6 #include <petscsf.h>
7 #include <petscds.h>
8 #include <petscdraw.h>
9 #include <petscdmfield.h>
10 #include <petscdmplextransform.h>
11 #include <petscblaslapack.h>
12 
13 /* Logging support */
14 PetscLogEvent DMPLEX_Interpolate, DMPLEX_Partition, DMPLEX_Distribute, DMPLEX_DistributeCones, DMPLEX_DistributeLabels, DMPLEX_DistributeSF, DMPLEX_DistributeOverlap, DMPLEX_DistributeField, DMPLEX_DistributeData, DMPLEX_Migrate, DMPLEX_InterpolateSF, DMPLEX_GlobalToNaturalBegin, DMPLEX_GlobalToNaturalEnd, DMPLEX_NaturalToGlobalBegin, DMPLEX_NaturalToGlobalEnd, DMPLEX_Stratify, DMPLEX_Symmetrize, DMPLEX_Preallocate, DMPLEX_ResidualFEM, DMPLEX_JacobianFEM, DMPLEX_InterpolatorFEM, DMPLEX_InjectorFEM, DMPLEX_IntegralFEM, DMPLEX_CreateGmsh, DMPLEX_RebalanceSharedPoints, DMPLEX_PartSelf, DMPLEX_PartLabelInvert, DMPLEX_PartLabelCreateSF, DMPLEX_PartStratSF, DMPLEX_CreatePointSF, DMPLEX_LocatePoints, DMPLEX_TopologyView, DMPLEX_LabelsView, DMPLEX_CoordinatesView, DMPLEX_SectionView, DMPLEX_GlobalVectorView, DMPLEX_LocalVectorView, DMPLEX_TopologyLoad, DMPLEX_LabelsLoad, DMPLEX_CoordinatesLoad, DMPLEX_SectionLoad, DMPLEX_GlobalVectorLoad, DMPLEX_LocalVectorLoad;
15 PetscLogEvent DMPLEX_RebalBuildGraph, DMPLEX_RebalRewriteSF, DMPLEX_RebalGatherGraph, DMPLEX_RebalPartition, DMPLEX_RebalScatterPart, DMPLEX_Generate, DMPLEX_Transform, DMPLEX_GetLocalOffsets, DMPLEX_Uninterpolate;
16 
17 PetscBool  Plexcite       = PETSC_FALSE;
18 const char PlexCitation[] = "@article{LangeMitchellKnepleyGorman2015,\n"
19                             "title     = {Efficient mesh management in {Firedrake} using {PETSc-DMPlex}},\n"
20                             "author    = {Michael Lange and Lawrence Mitchell and Matthew G. Knepley and Gerard J. Gorman},\n"
21                             "journal   = {SIAM Journal on Scientific Computing},\n"
22                             "volume    = {38},\n"
23                             "number    = {5},\n"
24                             "pages     = {S143--S155},\n"
25                             "eprint    = {http://arxiv.org/abs/1506.07749},\n"
26                             "doi       = {10.1137/15M1026092},\n"
27                             "year      = {2016},\n"
28                             "petsc_uses={DMPlex},\n}\n";
29 
30 PETSC_EXTERN PetscErrorCode VecView_MPI(Vec, PetscViewer);
31 
32 /*@
33   DMPlexIsSimplex - Is the first cell in this mesh a simplex?
34 
35   Input Parameter:
36 . dm - The `DMPLEX` object
37 
38   Output Parameter:
39 . simplex - Flag checking for a simplex
40 
41   Level: intermediate
42 
43   Note:
44   This just gives the first range of cells found. If the mesh has several cell types, it will only give the first.
45   If the mesh has no cells, this returns `PETSC_FALSE`.
46 
47 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSimplexOrBoxCells()`, `DMPlexGetCellType()`, `DMPlexGetHeightStratum()`, `DMPolytopeTypeGetNumVertices()`
48 @*/
49 PetscErrorCode DMPlexIsSimplex(DM dm, PetscBool *simplex)
50 {
51   DMPolytopeType ct;
52   PetscInt       cStart, cEnd;
53 
54   PetscFunctionBegin;
55   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
56   if (cEnd <= cStart) {
57     *simplex = PETSC_FALSE;
58     PetscFunctionReturn(PETSC_SUCCESS);
59   }
60   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
61   *simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct) + 1 ? PETSC_TRUE : PETSC_FALSE;
62   PetscFunctionReturn(PETSC_SUCCESS);
63 }
64 
65 /*@
66   DMPlexGetSimplexOrBoxCells - Get the range of cells which are neither prisms nor ghost FV cells
67 
68   Input Parameters:
69 + dm     - The `DMPLEX` object
70 - height - The cell height in the Plex, 0 is the default
71 
72   Output Parameters:
73 + cStart - The first "normal" cell
74 - cEnd   - The upper bound on "normal" cells
75 
76   Level: developer
77 
78   Note:
79   This function requires that tensor cells are ordered last.
80 
81 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexConstructGhostCells()`, `DMPlexGetCellTypeStratum()`
82 @*/
83 PetscErrorCode DMPlexGetSimplexOrBoxCells(DM dm, PetscInt height, PetscInt *cStart, PetscInt *cEnd)
84 {
85   DMLabel         ctLabel;
86   IS              valueIS;
87   const PetscInt *ctypes;
88   PetscInt        Nct, cS = PETSC_MAX_INT, cE = 0;
89 
90   PetscFunctionBegin;
91   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
92   PetscCall(DMLabelGetValueIS(ctLabel, &valueIS));
93   PetscCall(ISGetLocalSize(valueIS, &Nct));
94   PetscCall(ISGetIndices(valueIS, &ctypes));
95   if (!Nct) cS = cE = 0;
96   for (PetscInt t = 0; t < Nct; ++t) {
97     const DMPolytopeType ct = (DMPolytopeType)ctypes[t];
98     PetscInt             ctS, ctE, ht;
99 
100     if (ct == DM_POLYTOPE_UNKNOWN) {
101       // If any cells are not typed, just use all cells
102       PetscCall(DMPlexGetHeightStratum(dm, PetscMax(height, 0), cStart, cEnd));
103       break;
104     }
105     if (DMPolytopeTypeIsHybrid(ct) || ct == DM_POLYTOPE_FV_GHOST) continue;
106     PetscCall(DMLabelGetStratumBounds(ctLabel, ct, &ctS, &ctE));
107     if (ctS >= ctE) continue;
108     // Check that a point has the right height
109     PetscCall(DMPlexGetPointHeight(dm, ctS, &ht));
110     if (ht != height) continue;
111     cS = PetscMin(cS, ctS);
112     cE = PetscMax(cE, ctE);
113   }
114   PetscCall(ISDestroy(&valueIS));
115   // Reset label for fast lookup
116   PetscCall(DMLabelMakeAllInvalid_Internal(ctLabel));
117   if (cStart) *cStart = cS;
118   if (cEnd) *cEnd = cE;
119   PetscFunctionReturn(PETSC_SUCCESS);
120 }
121 
122 PetscErrorCode DMPlexGetFieldTypes_Internal(DM dm, PetscSection section, PetscInt field, PetscInt *types, PetscInt **ssStart, PetscInt **ssEnd, PetscViewerVTKFieldType **sft)
123 {
124   PetscInt                 cdim, pStart, pEnd, vStart, vEnd, cStart, cEnd, c, depth, cellHeight, t;
125   PetscInt                *sStart, *sEnd;
126   PetscViewerVTKFieldType *ft;
127   PetscInt                 vcdof[DM_NUM_POLYTOPES + 1], globalvcdof[DM_NUM_POLYTOPES + 1];
128   DMLabel                  depthLabel, ctLabel;
129 
130   PetscFunctionBegin;
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_sfs)
1047       for (PetscInt i = 0; i < mesh->periodic.num_face_sfs; i++) PetscCall(PetscSFView(mesh->periodic.face_sfs[i], viewer));
1048     PetscCall(PetscViewerFlush(viewer));
1049   } else if (format == PETSC_VIEWER_ASCII_LATEX) {
1050     const char  *name, *color;
1051     const char  *defcolors[3]  = {"gray", "orange", "green"};
1052     const char  *deflcolors[4] = {"blue", "cyan", "red", "magenta"};
1053     char         lname[PETSC_MAX_PATH_LEN];
1054     PetscReal    scale      = 2.0;
1055     PetscReal    tikzscale  = 1.0;
1056     PetscBool    useNumbers = PETSC_TRUE, drawNumbers[4], drawColors[4], useLabels, useColors, plotEdges, drawHasse = PETSC_FALSE;
1057     double       tcoords[3];
1058     PetscScalar *coords;
1059     PetscInt     numLabels, l, numColors, numLColors, dim, d, depth, cStart, cEnd, c, vStart, vEnd, v, eStart = 0, eEnd = 0, fStart = 0, fEnd = 0, e, p, n;
1060     PetscMPIInt  rank, size;
1061     char       **names, **colors, **lcolors;
1062     PetscBool    flg, lflg;
1063     PetscBT      wp = NULL;
1064     PetscInt     pEnd, pStart;
1065 
1066     PetscCall(DMGetCoordinateDM(dm, &cdm));
1067     PetscCall(DMGetCoordinateSection(dm, &coordSection));
1068     PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1069     PetscCall(DMGetCellCoordinateDM(dm, &cdmCell));
1070     PetscCall(DMGetCellCoordinateSection(dm, &coordSectionCell));
1071     PetscCall(DMGetCellCoordinatesLocal(dm, &coordinatesCell));
1072     PetscCall(DMGetDimension(dm, &dim));
1073     PetscCall(DMPlexGetDepth(dm, &depth));
1074     PetscCall(DMGetNumLabels(dm, &numLabels));
1075     numLabels  = PetscMax(numLabels, 10);
1076     numColors  = 10;
1077     numLColors = 10;
1078     PetscCall(PetscCalloc3(numLabels, &names, numColors, &colors, numLColors, &lcolors));
1079     PetscCall(PetscOptionsGetReal(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_scale", &scale, NULL));
1080     PetscCall(PetscOptionsGetReal(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_tikzscale", &tikzscale, NULL));
1081     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_numbers", &useNumbers, NULL));
1082     for (d = 0; d < 4; ++d) drawNumbers[d] = useNumbers;
1083     for (d = 0; d < 4; ++d) drawColors[d] = PETSC_TRUE;
1084     n = 4;
1085     PetscCall(PetscOptionsGetBoolArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_numbers_depth", drawNumbers, &n, &flg));
1086     PetscCheck(!flg || n == dim + 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim + 1);
1087     n = 4;
1088     PetscCall(PetscOptionsGetBoolArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_colors_depth", drawColors, &n, &flg));
1089     PetscCheck(!flg || n == dim + 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim + 1);
1090     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_labels", names, &numLabels, &useLabels));
1091     if (!useLabels) numLabels = 0;
1092     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_colors", colors, &numColors, &useColors));
1093     if (!useColors) {
1094       numColors = 3;
1095       for (c = 0; c < numColors; ++c) PetscCall(PetscStrallocpy(defcolors[c], &colors[c]));
1096     }
1097     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_lcolors", lcolors, &numLColors, &useColors));
1098     if (!useColors) {
1099       numLColors = 4;
1100       for (c = 0; c < numLColors; ++c) PetscCall(PetscStrallocpy(deflcolors[c], &lcolors[c]));
1101     }
1102     PetscCall(PetscOptionsGetString(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_label_filter", lname, sizeof(lname), &lflg));
1103     plotEdges = (PetscBool)(depth > 1 && drawNumbers[1] && dim < 3);
1104     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_edges", &plotEdges, &flg));
1105     PetscCheck(!flg || !plotEdges || depth >= dim, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Mesh must be interpolated");
1106     if (depth < dim) plotEdges = PETSC_FALSE;
1107     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_hasse", &drawHasse, NULL));
1108 
1109     /* filter points with labelvalue != labeldefaultvalue */
1110     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
1111     PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1112     PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
1113     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1114     PetscCall(DMPlexGetHeightStratum(dm, 1, &fStart, &fEnd));
1115     if (lflg) {
1116       DMLabel lbl;
1117 
1118       PetscCall(DMGetLabel(dm, lname, &lbl));
1119       if (lbl) {
1120         PetscInt val, defval;
1121 
1122         PetscCall(DMLabelGetDefaultValue(lbl, &defval));
1123         PetscCall(PetscBTCreate(pEnd - pStart, &wp));
1124         for (c = pStart; c < pEnd; c++) {
1125           PetscInt *closure = NULL;
1126           PetscInt  closureSize;
1127 
1128           PetscCall(DMLabelGetValue(lbl, c, &val));
1129           if (val == defval) continue;
1130 
1131           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1132           for (p = 0; p < closureSize * 2; p += 2) PetscCall(PetscBTSet(wp, closure[p] - pStart));
1133           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1134         }
1135       }
1136     }
1137 
1138     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1139     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
1140     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1141     PetscCall(PetscViewerASCIIPrintf(viewer, "\
1142 \\documentclass[tikz]{standalone}\n\n\
1143 \\usepackage{pgflibraryshapes}\n\
1144 \\usetikzlibrary{backgrounds}\n\
1145 \\usetikzlibrary{arrows}\n\
1146 \\begin{document}\n"));
1147     if (size > 1) {
1148       PetscCall(PetscViewerASCIIPrintf(viewer, "%s for process ", name));
1149       for (p = 0; p < size; ++p) {
1150         if (p) PetscCall(PetscViewerASCIIPrintf(viewer, (p == size - 1) ? ", and " : ", "));
1151         PetscCall(PetscViewerASCIIPrintf(viewer, "{\\textcolor{%s}%" PetscInt_FMT "}", colors[p % numColors], p));
1152       }
1153       PetscCall(PetscViewerASCIIPrintf(viewer, ".\n\n\n"));
1154     }
1155     if (drawHasse) {
1156       PetscInt maxStratum = PetscMax(vEnd - vStart, PetscMax(eEnd - eStart, PetscMax(fEnd - fStart, cEnd - cStart)));
1157 
1158       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vStart}{%" PetscInt_FMT "}\n", vStart));
1159       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vEnd}{%" PetscInt_FMT "}\n", vEnd - 1));
1160       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numVertices}{%" PetscInt_FMT "}\n", vEnd - vStart));
1161       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vShift}{%.2f}\n", 3 + (maxStratum - (vEnd - vStart)) / 2.));
1162       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eStart}{%" PetscInt_FMT "}\n", eStart));
1163       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eEnd}{%" PetscInt_FMT "}\n", eEnd - 1));
1164       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eShift}{%.2f}\n", 3 + (maxStratum - (eEnd - eStart)) / 2.));
1165       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numEdges}{%" PetscInt_FMT "}\n", eEnd - eStart));
1166       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\fStart}{%" PetscInt_FMT "}\n", fStart));
1167       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\fEnd}{%" PetscInt_FMT "}\n", fEnd - 1));
1168       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\fShift}{%.2f}\n", 3 + (maxStratum - (fEnd - fStart)) / 2.));
1169       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numFaces}{%" PetscInt_FMT "}\n", fEnd - fStart));
1170       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cStart}{%" PetscInt_FMT "}\n", cStart));
1171       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cEnd}{%" PetscInt_FMT "}\n", cEnd - 1));
1172       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numCells}{%" PetscInt_FMT "}\n", cEnd - cStart));
1173       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cShift}{%.2f}\n", 3 + (maxStratum - (cEnd - cStart)) / 2.));
1174     }
1175     PetscCall(PetscViewerASCIIPrintf(viewer, "\\begin{tikzpicture}[scale = %g,font=\\fontsize{8}{8}\\selectfont]\n", (double)tikzscale));
1176 
1177     /* Plot vertices */
1178     PetscCall(VecGetArray(coordinates, &coords));
1179     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
1180     for (v = vStart; v < vEnd; ++v) {
1181       PetscInt  off, dof, d;
1182       PetscBool isLabeled = PETSC_FALSE;
1183 
1184       if (wp && !PetscBTLookup(wp, v - pStart)) continue;
1185       PetscCall(PetscSectionGetDof(coordSection, v, &dof));
1186       PetscCall(PetscSectionGetOffset(coordSection, v, &off));
1187       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1188       PetscCheck(dof <= 3, PETSC_COMM_SELF, PETSC_ERR_PLIB, "coordSection vertex %" PetscInt_FMT " has dof %" PetscInt_FMT " > 3", v, dof);
1189       for (d = 0; d < dof; ++d) {
1190         tcoords[d] = (double)(scale * PetscRealPart(coords[off + d]));
1191         tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1192       }
1193       /* Rotate coordinates since PGF makes z point out of the page instead of up */
1194       if (dim == 3) {
1195         PetscReal tmp = tcoords[1];
1196         tcoords[1]    = tcoords[2];
1197         tcoords[2]    = -tmp;
1198       }
1199       for (d = 0; d < dof; ++d) {
1200         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1201         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)tcoords[d]));
1202       }
1203       if (drawHasse) color = colors[0 % numColors];
1204       else color = colors[rank % numColors];
1205       for (l = 0; l < numLabels; ++l) {
1206         PetscInt val;
1207         PetscCall(DMGetLabelValue(dm, names[l], v, &val));
1208         if (val >= 0) {
1209           color     = lcolors[l % numLColors];
1210           isLabeled = PETSC_TRUE;
1211           break;
1212         }
1213       }
1214       if (drawNumbers[0]) {
1215         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", v, rank, color, v));
1216       } else if (drawColors[0]) {
1217         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", v, rank, !isLabeled ? 1 : 2, color));
1218       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", v, rank));
1219     }
1220     PetscCall(VecRestoreArray(coordinates, &coords));
1221     PetscCall(PetscViewerFlush(viewer));
1222     /* Plot edges */
1223     if (plotEdges) {
1224       PetscCall(VecGetArray(coordinates, &coords));
1225       PetscCall(PetscViewerASCIIPrintf(viewer, "\\path\n"));
1226       for (e = eStart; e < eEnd; ++e) {
1227         const PetscInt *cone;
1228         PetscInt        coneSize, offA, offB, dof, d;
1229 
1230         if (wp && !PetscBTLookup(wp, e - pStart)) continue;
1231         PetscCall(DMPlexGetConeSize(dm, e, &coneSize));
1232         PetscCheck(coneSize == 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Edge %" PetscInt_FMT " cone should have two vertices, not %" PetscInt_FMT, e, coneSize);
1233         PetscCall(DMPlexGetCone(dm, e, &cone));
1234         PetscCall(PetscSectionGetDof(coordSection, cone[0], &dof));
1235         PetscCall(PetscSectionGetOffset(coordSection, cone[0], &offA));
1236         PetscCall(PetscSectionGetOffset(coordSection, cone[1], &offB));
1237         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "("));
1238         for (d = 0; d < dof; ++d) {
1239           tcoords[d] = (double)(0.5 * scale * PetscRealPart(coords[offA + d] + coords[offB + d]));
1240           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1241         }
1242         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1243         if (dim == 3) {
1244           PetscReal tmp = tcoords[1];
1245           tcoords[1]    = tcoords[2];
1246           tcoords[2]    = -tmp;
1247         }
1248         for (d = 0; d < dof; ++d) {
1249           if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1250           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)tcoords[d]));
1251         }
1252         if (drawHasse) color = colors[1 % numColors];
1253         else color = colors[rank % numColors];
1254         for (l = 0; l < numLabels; ++l) {
1255           PetscInt val;
1256           PetscCall(DMGetLabelValue(dm, names[l], e, &val));
1257           if (val >= 0) {
1258             color = lcolors[l % numLColors];
1259             break;
1260           }
1261         }
1262         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "} --\n", e, rank, color, e));
1263       }
1264       PetscCall(VecRestoreArray(coordinates, &coords));
1265       PetscCall(PetscViewerFlush(viewer));
1266       PetscCall(PetscViewerASCIIPrintf(viewer, "(0,0);\n"));
1267     }
1268     /* Plot cells */
1269     if (dim == 3 || !drawNumbers[1]) {
1270       for (e = eStart; e < eEnd; ++e) {
1271         const PetscInt *cone;
1272 
1273         if (wp && !PetscBTLookup(wp, e - pStart)) continue;
1274         color = colors[rank % numColors];
1275         for (l = 0; l < numLabels; ++l) {
1276           PetscInt val;
1277           PetscCall(DMGetLabelValue(dm, names[l], e, &val));
1278           if (val >= 0) {
1279             color = lcolors[l % numLColors];
1280             break;
1281           }
1282         }
1283         PetscCall(DMPlexGetCone(dm, e, &cone));
1284         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", color, cone[0], rank, cone[1], rank));
1285       }
1286     } else {
1287       DMPolytopeType ct;
1288 
1289       /* Drawing a 2D polygon */
1290       for (c = cStart; c < cEnd; ++c) {
1291         if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1292         PetscCall(DMPlexGetCellType(dm, c, &ct));
1293         if (DMPolytopeTypeIsHybrid(ct)) {
1294           const PetscInt *cone;
1295           PetscInt        coneSize, e;
1296 
1297           PetscCall(DMPlexGetCone(dm, c, &cone));
1298           PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
1299           for (e = 0; e < coneSize; ++e) {
1300             const PetscInt *econe;
1301 
1302             PetscCall(DMPlexGetCone(dm, cone[e], &econe));
1303             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));
1304           }
1305         } else {
1306           PetscInt *closure = NULL;
1307           PetscInt  closureSize, Nv = 0, v;
1308 
1309           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1310           for (p = 0; p < closureSize * 2; p += 2) {
1311             const PetscInt point = closure[p];
1312 
1313             if ((point >= vStart) && (point < vEnd)) closure[Nv++] = point;
1314           }
1315           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] ", colors[rank % numColors]));
1316           for (v = 0; v <= Nv; ++v) {
1317             const PetscInt vertex = closure[v % Nv];
1318 
1319             if (v > 0) {
1320               if (plotEdges) {
1321                 const PetscInt *edge;
1322                 PetscInt        endpoints[2], ne;
1323 
1324                 endpoints[0] = closure[v - 1];
1325                 endpoints[1] = vertex;
1326                 PetscCall(DMPlexGetJoin(dm, 2, endpoints, &ne, &edge));
1327                 PetscCheck(ne == 1, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Could not find edge for vertices %" PetscInt_FMT ", %" PetscInt_FMT, endpoints[0], endpoints[1]);
1328                 PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- (%" PetscInt_FMT "_%d) -- ", edge[0], rank));
1329                 PetscCall(DMPlexRestoreJoin(dm, 2, endpoints, &ne, &edge));
1330               } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- "));
1331             }
1332             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "(%" PetscInt_FMT "_%d)", vertex, rank));
1333           }
1334           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ";\n"));
1335           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1336         }
1337       }
1338     }
1339     for (c = cStart; c < cEnd; ++c) {
1340       double             ccoords[3] = {0.0, 0.0, 0.0};
1341       PetscBool          isLabeled  = PETSC_FALSE;
1342       PetscScalar       *cellCoords = NULL;
1343       const PetscScalar *array;
1344       PetscInt           numCoords, cdim, d;
1345       PetscBool          isDG;
1346 
1347       if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1348       PetscCall(DMGetCoordinateDim(dm, &cdim));
1349       PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1350       PetscCheck(!(numCoords % cdim), PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "coordinate dim %" PetscInt_FMT " does not divide numCoords %" PetscInt_FMT, cdim, numCoords);
1351       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1352       for (p = 0; p < numCoords / cdim; ++p) {
1353         for (d = 0; d < cdim; ++d) {
1354           tcoords[d] = (double)(scale * PetscRealPart(cellCoords[p * cdim + d]));
1355           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1356         }
1357         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1358         if (cdim == 3) {
1359           PetscReal tmp = tcoords[1];
1360           tcoords[1]    = tcoords[2];
1361           tcoords[2]    = -tmp;
1362         }
1363         for (d = 0; d < dim; ++d) ccoords[d] += tcoords[d];
1364       }
1365       for (d = 0; d < cdim; ++d) ccoords[d] /= (numCoords / cdim);
1366       PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1367       for (d = 0; d < cdim; ++d) {
1368         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1369         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)ccoords[d]));
1370       }
1371       if (drawHasse) color = colors[depth % numColors];
1372       else color = colors[rank % numColors];
1373       for (l = 0; l < numLabels; ++l) {
1374         PetscInt val;
1375         PetscCall(DMGetLabelValue(dm, names[l], c, &val));
1376         if (val >= 0) {
1377           color     = lcolors[l % numLColors];
1378           isLabeled = PETSC_TRUE;
1379           break;
1380         }
1381       }
1382       if (drawNumbers[dim]) {
1383         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", c, rank, color, c));
1384       } else if (drawColors[dim]) {
1385         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", c, rank, !isLabeled ? 1 : 2, color));
1386       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", c, rank));
1387     }
1388     if (drawHasse) {
1389       int height = 0;
1390 
1391       color = colors[depth % numColors];
1392       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Cells\n"));
1393       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\c in {\\cStart,...,\\cEnd}\n"));
1394       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1395       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\c_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\cShift+\\c-\\cStart,%d) {\\c};\n", rank, color, height++));
1396       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1397 
1398       if (depth > 2) {
1399         color = colors[1 % numColors];
1400         PetscCall(PetscViewerASCIIPrintf(viewer, "%% Faces\n"));
1401         PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\f in {\\fStart,...,\\fEnd}\n"));
1402         PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1403         PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\f_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\fShift+\\f-\\fStart,%d) {\\f};\n", rank, color, height++));
1404         PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1405       }
1406 
1407       color = colors[1 % numColors];
1408       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Edges\n"));
1409       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\e in {\\eStart,...,\\eEnd}\n"));
1410       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1411       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\e_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\eShift+\\e-\\eStart,%d) {\\e};\n", rank, color, height++));
1412       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1413 
1414       color = colors[0 % numColors];
1415       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Vertices\n"));
1416       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\v in {\\vStart,...,\\vEnd}\n"));
1417       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1418       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\v_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\vShift+\\v-\\vStart,%d) {\\v};\n", rank, color, height++));
1419       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1420 
1421       for (p = pStart; p < pEnd; ++p) {
1422         const PetscInt *cone;
1423         PetscInt        coneSize, cp;
1424 
1425         PetscCall(DMPlexGetCone(dm, p, &cone));
1426         PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
1427         for (cp = 0; cp < coneSize; ++cp) PetscCall(PetscViewerASCIIPrintf(viewer, "\\draw[->, shorten >=1pt] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", cone[cp], rank, p, rank));
1428       }
1429     }
1430     PetscCall(PetscViewerFlush(viewer));
1431     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
1432     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{tikzpicture}\n"));
1433     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{document}\n"));
1434     for (l = 0; l < numLabels; ++l) PetscCall(PetscFree(names[l]));
1435     for (c = 0; c < numColors; ++c) PetscCall(PetscFree(colors[c]));
1436     for (c = 0; c < numLColors; ++c) PetscCall(PetscFree(lcolors[c]));
1437     PetscCall(PetscFree3(names, colors, lcolors));
1438     PetscCall(PetscBTDestroy(&wp));
1439   } else if (format == PETSC_VIEWER_LOAD_BALANCE) {
1440     Vec                    cown, acown;
1441     VecScatter             sct;
1442     ISLocalToGlobalMapping g2l;
1443     IS                     gid, acis;
1444     MPI_Comm               comm, ncomm = MPI_COMM_NULL;
1445     MPI_Group              ggroup, ngroup;
1446     PetscScalar           *array, nid;
1447     const PetscInt        *idxs;
1448     PetscInt              *idxs2, *start, *adjacency, *work;
1449     PetscInt64             lm[3], gm[3];
1450     PetscInt               i, c, cStart, cEnd, cum, numVertices, ect, ectn, cellHeight;
1451     PetscMPIInt            d1, d2, rank;
1452 
1453     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1454     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1455 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1456     PetscCallMPI(MPI_Comm_split_type(comm, MPI_COMM_TYPE_SHARED, rank, MPI_INFO_NULL, &ncomm));
1457 #endif
1458     if (ncomm != MPI_COMM_NULL) {
1459       PetscCallMPI(MPI_Comm_group(comm, &ggroup));
1460       PetscCallMPI(MPI_Comm_group(ncomm, &ngroup));
1461       d1 = 0;
1462       PetscCallMPI(MPI_Group_translate_ranks(ngroup, 1, &d1, ggroup, &d2));
1463       nid = d2;
1464       PetscCallMPI(MPI_Group_free(&ggroup));
1465       PetscCallMPI(MPI_Group_free(&ngroup));
1466       PetscCallMPI(MPI_Comm_free(&ncomm));
1467     } else nid = 0.0;
1468 
1469     /* Get connectivity */
1470     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1471     PetscCall(DMPlexCreatePartitionerGraph(dm, cellHeight, &numVertices, &start, &adjacency, &gid));
1472 
1473     /* filter overlapped local cells */
1474     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
1475     PetscCall(ISGetIndices(gid, &idxs));
1476     PetscCall(ISGetLocalSize(gid, &cum));
1477     PetscCall(PetscMalloc1(cum, &idxs2));
1478     for (c = cStart, cum = 0; c < cEnd; c++) {
1479       if (idxs[c - cStart] < 0) continue;
1480       idxs2[cum++] = idxs[c - cStart];
1481     }
1482     PetscCall(ISRestoreIndices(gid, &idxs));
1483     PetscCheck(numVertices == cum, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Unexpected %" PetscInt_FMT " != %" PetscInt_FMT, numVertices, cum);
1484     PetscCall(ISDestroy(&gid));
1485     PetscCall(ISCreateGeneral(comm, numVertices, idxs2, PETSC_OWN_POINTER, &gid));
1486 
1487     /* support for node-aware cell locality */
1488     PetscCall(ISCreateGeneral(comm, start[numVertices], adjacency, PETSC_USE_POINTER, &acis));
1489     PetscCall(VecCreateSeq(PETSC_COMM_SELF, start[numVertices], &acown));
1490     PetscCall(VecCreateMPI(comm, numVertices, PETSC_DECIDE, &cown));
1491     PetscCall(VecGetArray(cown, &array));
1492     for (c = 0; c < numVertices; c++) array[c] = nid;
1493     PetscCall(VecRestoreArray(cown, &array));
1494     PetscCall(VecScatterCreate(cown, acis, acown, NULL, &sct));
1495     PetscCall(VecScatterBegin(sct, cown, acown, INSERT_VALUES, SCATTER_FORWARD));
1496     PetscCall(VecScatterEnd(sct, cown, acown, INSERT_VALUES, SCATTER_FORWARD));
1497     PetscCall(ISDestroy(&acis));
1498     PetscCall(VecScatterDestroy(&sct));
1499     PetscCall(VecDestroy(&cown));
1500 
1501     /* compute edgeCut */
1502     for (c = 0, cum = 0; c < numVertices; c++) cum = PetscMax(cum, start[c + 1] - start[c]);
1503     PetscCall(PetscMalloc1(cum, &work));
1504     PetscCall(ISLocalToGlobalMappingCreateIS(gid, &g2l));
1505     PetscCall(ISLocalToGlobalMappingSetType(g2l, ISLOCALTOGLOBALMAPPINGHASH));
1506     PetscCall(ISDestroy(&gid));
1507     PetscCall(VecGetArray(acown, &array));
1508     for (c = 0, ect = 0, ectn = 0; c < numVertices; c++) {
1509       PetscInt totl;
1510 
1511       totl = start[c + 1] - start[c];
1512       PetscCall(ISGlobalToLocalMappingApply(g2l, IS_GTOLM_MASK, totl, adjacency + start[c], NULL, work));
1513       for (i = 0; i < totl; i++) {
1514         if (work[i] < 0) {
1515           ect += 1;
1516           ectn += (array[i + start[c]] != nid) ? 0 : 1;
1517         }
1518       }
1519     }
1520     PetscCall(PetscFree(work));
1521     PetscCall(VecRestoreArray(acown, &array));
1522     lm[0] = numVertices > 0 ? numVertices : PETSC_MAX_INT;
1523     lm[1] = -numVertices;
1524     PetscCall(MPIU_Allreduce(lm, gm, 2, MPIU_INT64, MPI_MIN, comm));
1525     PetscCall(PetscViewerASCIIPrintf(viewer, "  Cell balance: %.2f (max %" PetscInt_FMT ", min %" PetscInt_FMT, -((double)gm[1]) / ((double)gm[0]), -(PetscInt)gm[1], (PetscInt)gm[0]));
1526     lm[0] = ect;                     /* edgeCut */
1527     lm[1] = ectn;                    /* node-aware edgeCut */
1528     lm[2] = numVertices > 0 ? 0 : 1; /* empty processes */
1529     PetscCall(MPIU_Allreduce(lm, gm, 3, MPIU_INT64, MPI_SUM, comm));
1530     PetscCall(PetscViewerASCIIPrintf(viewer, ", empty %" PetscInt_FMT ")\n", (PetscInt)gm[2]));
1531 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1532     PetscCall(PetscViewerASCIIPrintf(viewer, "  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n", (PetscInt)(gm[0] / 2), gm[0] ? ((double)gm[1]) / ((double)gm[0]) : 1.));
1533 #else
1534     PetscCall(PetscViewerASCIIPrintf(viewer, "  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n", (PetscInt)(gm[0] / 2), 0.0));
1535 #endif
1536     PetscCall(ISLocalToGlobalMappingDestroy(&g2l));
1537     PetscCall(PetscFree(start));
1538     PetscCall(PetscFree(adjacency));
1539     PetscCall(VecDestroy(&acown));
1540   } else {
1541     const char    *name;
1542     PetscInt      *sizes, *hybsizes, *ghostsizes;
1543     PetscInt       locDepth, depth, cellHeight, dim, d;
1544     PetscInt       pStart, pEnd, p, gcStart, gcEnd, gcNum;
1545     PetscInt       numLabels, l, maxSize = 17;
1546     DMPolytopeType ct0 = DM_POLYTOPE_UNKNOWN;
1547     MPI_Comm       comm;
1548     PetscMPIInt    size, rank;
1549 
1550     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1551     PetscCallMPI(MPI_Comm_size(comm, &size));
1552     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1553     PetscCall(DMGetDimension(dm, &dim));
1554     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1555     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1556     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
1557     else PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
1558     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
1559     PetscCall(DMPlexGetDepth(dm, &locDepth));
1560     PetscCall(MPIU_Allreduce(&locDepth, &depth, 1, MPIU_INT, MPI_MAX, comm));
1561     PetscCall(DMPlexGetCellTypeStratum(dm, DM_POLYTOPE_FV_GHOST, &gcStart, &gcEnd));
1562     gcNum = gcEnd - gcStart;
1563     if (size < maxSize) PetscCall(PetscCalloc3(size, &sizes, size, &hybsizes, size, &ghostsizes));
1564     else PetscCall(PetscCalloc3(3, &sizes, 3, &hybsizes, 3, &ghostsizes));
1565     for (d = 0; d <= depth; d++) {
1566       PetscInt Nc[2] = {0, 0}, ict;
1567 
1568       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
1569       if (pStart < pEnd) PetscCall(DMPlexGetCellType(dm, pStart, &ct0));
1570       ict = ct0;
1571       PetscCallMPI(MPI_Bcast(&ict, 1, MPIU_INT, 0, comm));
1572       ct0 = (DMPolytopeType)ict;
1573       for (p = pStart; p < pEnd; ++p) {
1574         DMPolytopeType ct;
1575 
1576         PetscCall(DMPlexGetCellType(dm, p, &ct));
1577         if (ct == ct0) ++Nc[0];
1578         else ++Nc[1];
1579       }
1580       if (size < maxSize) {
1581         PetscCallMPI(MPI_Gather(&Nc[0], 1, MPIU_INT, sizes, 1, MPIU_INT, 0, comm));
1582         PetscCallMPI(MPI_Gather(&Nc[1], 1, MPIU_INT, hybsizes, 1, MPIU_INT, 0, comm));
1583         if (d == depth) PetscCallMPI(MPI_Gather(&gcNum, 1, MPIU_INT, ghostsizes, 1, MPIU_INT, 0, comm));
1584         PetscCall(PetscViewerASCIIPrintf(viewer, "  Number of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1585         for (p = 0; p < size; ++p) {
1586           if (rank == 0) {
1587             PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT, sizes[p] + hybsizes[p]));
1588             if (hybsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT ")", hybsizes[p]));
1589             if (ghostsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "]", ghostsizes[p]));
1590           }
1591         }
1592       } else {
1593         PetscInt locMinMax[2];
1594 
1595         locMinMax[0] = Nc[0] + Nc[1];
1596         locMinMax[1] = Nc[0] + Nc[1];
1597         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, sizes));
1598         locMinMax[0] = Nc[1];
1599         locMinMax[1] = Nc[1];
1600         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, hybsizes));
1601         if (d == depth) {
1602           locMinMax[0] = gcNum;
1603           locMinMax[1] = gcNum;
1604           PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, ghostsizes));
1605         }
1606         PetscCall(PetscViewerASCIIPrintf(viewer, "  Min/Max of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1607         PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT "/%" PetscInt_FMT, sizes[0], sizes[1]));
1608         if (hybsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT "/%" PetscInt_FMT ")", hybsizes[0], hybsizes[1]));
1609         if (ghostsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "/%" PetscInt_FMT "]", ghostsizes[0], ghostsizes[1]));
1610       }
1611       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
1612     }
1613     PetscCall(PetscFree3(sizes, hybsizes, ghostsizes));
1614     {
1615       const PetscReal *maxCell;
1616       const PetscReal *L;
1617       PetscBool        localized;
1618 
1619       PetscCall(DMGetPeriodicity(dm, &maxCell, NULL, &L));
1620       PetscCall(DMGetCoordinatesLocalized(dm, &localized));
1621       if (L || localized) {
1622         PetscCall(PetscViewerASCIIPrintf(viewer, "Periodic mesh"));
1623         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1624         if (L) {
1625           PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
1626           for (d = 0; d < dim; ++d) {
1627             if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1628             PetscCall(PetscViewerASCIIPrintf(viewer, "%s", L[d] > 0.0 ? "PERIODIC" : "NONE"));
1629           }
1630           PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
1631         }
1632         PetscCall(PetscViewerASCIIPrintf(viewer, " coordinates %s\n", localized ? "localized" : "not localized"));
1633         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1634       }
1635     }
1636     PetscCall(DMGetNumLabels(dm, &numLabels));
1637     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
1638     for (l = 0; l < numLabels; ++l) {
1639       DMLabel         label;
1640       const char     *name;
1641       IS              valueIS;
1642       const PetscInt *values;
1643       PetscInt        numValues, v;
1644 
1645       PetscCall(DMGetLabelName(dm, l, &name));
1646       PetscCall(DMGetLabel(dm, name, &label));
1647       PetscCall(DMLabelGetNumValues(label, &numValues));
1648       PetscCall(PetscViewerASCIIPrintf(viewer, "  %s: %" PetscInt_FMT " strata with value/size (", name, numValues));
1649       PetscCall(DMLabelGetValueIS(label, &valueIS));
1650       PetscCall(ISGetIndices(valueIS, &values));
1651       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1652       for (v = 0; v < numValues; ++v) {
1653         PetscInt size;
1654 
1655         PetscCall(DMLabelGetStratumSize(label, values[v], &size));
1656         if (v > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1657         PetscCall(PetscViewerASCIIPrintf(viewer, "%" PetscInt_FMT " (%" PetscInt_FMT ")", values[v], size));
1658       }
1659       PetscCall(PetscViewerASCIIPrintf(viewer, ")\n"));
1660       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1661       PetscCall(ISRestoreIndices(valueIS, &values));
1662       PetscCall(ISDestroy(&valueIS));
1663     }
1664     {
1665       char    **labelNames;
1666       PetscInt  Nl = numLabels;
1667       PetscBool flg;
1668 
1669       PetscCall(PetscMalloc1(Nl, &labelNames));
1670       PetscCall(PetscOptionsGetStringArray(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_plex_view_labels", labelNames, &Nl, &flg));
1671       for (l = 0; l < Nl; ++l) {
1672         DMLabel label;
1673 
1674         PetscCall(DMHasLabel(dm, labelNames[l], &flg));
1675         if (flg) {
1676           PetscCall(DMGetLabel(dm, labelNames[l], &label));
1677           PetscCall(DMLabelView(label, viewer));
1678         }
1679         PetscCall(PetscFree(labelNames[l]));
1680       }
1681       PetscCall(PetscFree(labelNames));
1682     }
1683     /* If no fields are specified, people do not want to see adjacency */
1684     if (dm->Nf) {
1685       PetscInt f;
1686 
1687       for (f = 0; f < dm->Nf; ++f) {
1688         const char *name;
1689 
1690         PetscCall(PetscObjectGetName(dm->fields[f].disc, &name));
1691         if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Field %s:\n", name));
1692         PetscCall(PetscViewerASCIIPushTab(viewer));
1693         if (dm->fields[f].label) PetscCall(DMLabelView(dm->fields[f].label, viewer));
1694         if (dm->fields[f].adjacency[0]) {
1695           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM++\n"));
1696           else PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM\n"));
1697         } else {
1698           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FEM\n"));
1699           else PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FUNKY\n"));
1700         }
1701         PetscCall(PetscViewerASCIIPopTab(viewer));
1702       }
1703     }
1704     PetscCall(DMGetCoarseDM(dm, &cdm));
1705     if (cdm) {
1706       PetscCall(PetscViewerASCIIPushTab(viewer));
1707       PetscCall(PetscViewerASCIIPrintf(viewer, "Defined by transform from:\n"));
1708       PetscCall(DMPlexView_Ascii(cdm, viewer));
1709       PetscCall(PetscViewerASCIIPopTab(viewer));
1710     }
1711   }
1712   PetscFunctionReturn(PETSC_SUCCESS);
1713 }
1714 
1715 static PetscErrorCode DMPlexDrawCell(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[])
1716 {
1717   DMPolytopeType ct;
1718   PetscMPIInt    rank;
1719   PetscInt       cdim;
1720 
1721   PetscFunctionBegin;
1722   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1723   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1724   PetscCall(DMGetCoordinateDim(dm, &cdim));
1725   switch (ct) {
1726   case DM_POLYTOPE_SEGMENT:
1727   case DM_POLYTOPE_POINT_PRISM_TENSOR:
1728     switch (cdim) {
1729     case 1: {
1730       const PetscReal y  = 0.5;  /* TODO Put it in the middle of the viewport */
1731       const PetscReal dy = 0.05; /* TODO Make it a fraction of the total length */
1732 
1733       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y, PetscRealPart(coords[1]), y, PETSC_DRAW_BLACK));
1734       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y + dy, PetscRealPart(coords[0]), y - dy, PETSC_DRAW_BLACK));
1735       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[1]), y + dy, PetscRealPart(coords[1]), y - dy, PETSC_DRAW_BLACK));
1736     } break;
1737     case 2: {
1738       const PetscReal dx = (PetscRealPart(coords[3]) - PetscRealPart(coords[1]));
1739       const PetscReal dy = (PetscRealPart(coords[2]) - PetscRealPart(coords[0]));
1740       const PetscReal l  = 0.1 / PetscSqrtReal(dx * dx + dy * dy);
1741 
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[0]) + l * dx, PetscRealPart(coords[1]) + l * dy, PetscRealPart(coords[0]) - l * dx, PetscRealPart(coords[1]) - l * dy, PETSC_DRAW_BLACK));
1744       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));
1745     } break;
1746     default:
1747       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of dimension %" PetscInt_FMT, cdim);
1748     }
1749     break;
1750   case DM_POLYTOPE_TRIANGLE:
1751     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));
1752     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1753     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1754     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1755     break;
1756   case DM_POLYTOPE_QUADRILATERAL:
1757     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));
1758     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));
1759     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1760     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1761     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_BLACK));
1762     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1763     break;
1764   case DM_POLYTOPE_SEG_PRISM_TENSOR:
1765     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));
1766     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));
1767     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1768     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_BLACK));
1769     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1770     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1771     break;
1772   case DM_POLYTOPE_FV_GHOST:
1773     break;
1774   default:
1775     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1776   }
1777   PetscFunctionReturn(PETSC_SUCCESS);
1778 }
1779 
1780 static PetscErrorCode DrawPolygon_Private(DM dm, PetscDraw draw, PetscInt cell, PetscInt Nv, const PetscReal refVertices[], const PetscScalar coords[], PetscInt edgeDiv, PetscReal refCoords[], PetscReal edgeCoords[])
1781 {
1782   PetscReal   centroid[2] = {0., 0.};
1783   PetscMPIInt rank;
1784   PetscInt    fillColor;
1785 
1786   PetscFunctionBegin;
1787   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1788   fillColor = PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2;
1789   for (PetscInt v = 0; v < Nv; ++v) {
1790     centroid[0] += PetscRealPart(coords[v * 2 + 0]) / Nv;
1791     centroid[1] += PetscRealPart(coords[v * 2 + 1]) / Nv;
1792   }
1793   for (PetscInt e = 0; e < Nv; ++e) {
1794     refCoords[0] = refVertices[e * 2 + 0];
1795     refCoords[1] = refVertices[e * 2 + 1];
1796     for (PetscInt d = 1; d <= edgeDiv; ++d) {
1797       refCoords[d * 2 + 0] = refCoords[0] + (refVertices[(e + 1) % Nv * 2 + 0] - refCoords[0]) * d / edgeDiv;
1798       refCoords[d * 2 + 1] = refCoords[1] + (refVertices[(e + 1) % Nv * 2 + 1] - refCoords[1]) * d / edgeDiv;
1799     }
1800     PetscCall(DMPlexReferenceToCoordinates(dm, cell, edgeDiv + 1, refCoords, edgeCoords));
1801     for (PetscInt d = 0; d < edgeDiv; ++d) {
1802       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));
1803       PetscCall(PetscDrawLine(draw, edgeCoords[d * 2 + 0], edgeCoords[d * 2 + 1], edgeCoords[(d + 1) * 2 + 0], edgeCoords[(d + 1) * 2 + 1], PETSC_DRAW_BLACK));
1804     }
1805   }
1806   PetscFunctionReturn(PETSC_SUCCESS);
1807 }
1808 
1809 static PetscErrorCode DMPlexDrawCellHighOrder(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[], PetscInt edgeDiv, PetscReal refCoords[], PetscReal edgeCoords[])
1810 {
1811   DMPolytopeType ct;
1812 
1813   PetscFunctionBegin;
1814   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1815   switch (ct) {
1816   case DM_POLYTOPE_TRIANGLE: {
1817     PetscReal refVertices[6] = {-1., -1., 1., -1., -1., 1.};
1818 
1819     PetscCall(DrawPolygon_Private(dm, draw, cell, 3, refVertices, coords, edgeDiv, refCoords, edgeCoords));
1820   } break;
1821   case DM_POLYTOPE_QUADRILATERAL: {
1822     PetscReal refVertices[8] = {-1., -1., 1., -1., 1., 1., -1., 1.};
1823 
1824     PetscCall(DrawPolygon_Private(dm, draw, cell, 4, refVertices, coords, edgeDiv, refCoords, edgeCoords));
1825   } break;
1826   default:
1827     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1828   }
1829   PetscFunctionReturn(PETSC_SUCCESS);
1830 }
1831 
1832 static PetscErrorCode DMPlexView_Draw(DM dm, PetscViewer viewer)
1833 {
1834   PetscDraw    draw;
1835   DM           cdm;
1836   PetscSection coordSection;
1837   Vec          coordinates;
1838   PetscReal    xyl[3], xyr[3];
1839   PetscReal   *refCoords, *edgeCoords;
1840   PetscBool    isnull, drawAffine;
1841   PetscInt     dim, vStart, vEnd, cStart, cEnd, c, cDegree, edgeDiv;
1842 
1843   PetscFunctionBegin;
1844   PetscCall(DMGetCoordinateDim(dm, &dim));
1845   PetscCheck(dim <= 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT, dim);
1846   PetscCall(DMGetCoordinateDegree_Internal(dm, &cDegree));
1847   drawAffine = cDegree > 1 ? PETSC_FALSE : PETSC_TRUE;
1848   edgeDiv    = cDegree + 1;
1849   PetscCall(PetscOptionsGetBool(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_view_draw_affine", &drawAffine, NULL));
1850   if (!drawAffine) PetscCall(PetscMalloc2((edgeDiv + 1) * dim, &refCoords, (edgeDiv + 1) * dim, &edgeCoords));
1851   PetscCall(DMGetCoordinateDM(dm, &cdm));
1852   PetscCall(DMGetLocalSection(cdm, &coordSection));
1853   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1854   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1855   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1856 
1857   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
1858   PetscCall(PetscDrawIsNull(draw, &isnull));
1859   if (isnull) PetscFunctionReturn(PETSC_SUCCESS);
1860   PetscCall(PetscDrawSetTitle(draw, "Mesh"));
1861 
1862   PetscCall(DMGetBoundingBox(dm, xyl, xyr));
1863   PetscCall(PetscDrawSetCoordinates(draw, xyl[0], xyl[1], xyr[0], xyr[1]));
1864   PetscCall(PetscDrawClear(draw));
1865 
1866   for (c = cStart; c < cEnd; ++c) {
1867     PetscScalar       *coords = NULL;
1868     const PetscScalar *coords_arr;
1869     PetscInt           numCoords;
1870     PetscBool          isDG;
1871 
1872     PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
1873     if (drawAffine) PetscCall(DMPlexDrawCell(dm, draw, c, coords));
1874     else PetscCall(DMPlexDrawCellHighOrder(dm, draw, c, coords, edgeDiv, refCoords, edgeCoords));
1875     PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
1876   }
1877   if (!drawAffine) PetscCall(PetscFree2(refCoords, edgeCoords));
1878   PetscCall(PetscDrawFlush(draw));
1879   PetscCall(PetscDrawPause(draw));
1880   PetscCall(PetscDrawSave(draw));
1881   PetscFunctionReturn(PETSC_SUCCESS);
1882 }
1883 
1884 static PetscErrorCode DMPlexCreateHighOrderSurrogate_Internal(DM dm, DM *hdm)
1885 {
1886   DM           odm = dm, rdm = dm, cdm;
1887   PetscFE      fe;
1888   PetscSpace   sp;
1889   PetscClassId id;
1890   PetscInt     degree;
1891   PetscBool    hoView = PETSC_TRUE;
1892 
1893   PetscFunctionBegin;
1894   PetscObjectOptionsBegin((PetscObject)dm);
1895   PetscCall(PetscOptionsBool("-dm_plex_high_order_view", "Subsample to view meshes with high order coordinates", "DMPlexCreateHighOrderSurrogate_Internal", hoView, &hoView, NULL));
1896   PetscOptionsEnd();
1897   PetscCall(PetscObjectReference((PetscObject)dm));
1898   *hdm = dm;
1899   if (!hoView) PetscFunctionReturn(PETSC_SUCCESS);
1900   PetscCall(DMGetCoordinateDM(dm, &cdm));
1901   PetscCall(DMGetField(cdm, 0, NULL, (PetscObject *)&fe));
1902   PetscCall(PetscObjectGetClassId((PetscObject)fe, &id));
1903   if (id != PETSCFE_CLASSID) PetscFunctionReturn(PETSC_SUCCESS);
1904   PetscCall(PetscFEGetBasisSpace(fe, &sp));
1905   PetscCall(PetscSpaceGetDegree(sp, &degree, NULL));
1906   for (PetscInt r = 0, rd = PetscCeilReal(((PetscReal)degree) / 2.); r < (PetscInt)PetscCeilReal(PetscLog2Real(degree)); ++r, rd = PetscCeilReal(((PetscReal)rd) / 2.)) {
1907     DM  cdm, rcdm;
1908     Mat In;
1909     Vec cl, rcl;
1910 
1911     PetscCall(DMRefine(odm, PetscObjectComm((PetscObject)odm), &rdm));
1912     PetscCall(DMPlexCreateCoordinateSpace(rdm, rd, PETSC_FALSE, NULL));
1913     PetscCall(PetscObjectSetName((PetscObject)rdm, "Refined Mesh with Linear Coordinates"));
1914     PetscCall(DMGetCoordinateDM(odm, &cdm));
1915     PetscCall(DMGetCoordinateDM(rdm, &rcdm));
1916     PetscCall(DMGetCoordinatesLocal(odm, &cl));
1917     PetscCall(DMGetCoordinatesLocal(rdm, &rcl));
1918     PetscCall(DMSetCoarseDM(rcdm, cdm));
1919     PetscCall(DMCreateInterpolation(cdm, rcdm, &In, NULL));
1920     PetscCall(MatMult(In, cl, rcl));
1921     PetscCall(MatDestroy(&In));
1922     PetscCall(DMSetCoordinatesLocal(rdm, rcl));
1923     PetscCall(DMDestroy(&odm));
1924     odm = rdm;
1925   }
1926   *hdm = rdm;
1927   PetscFunctionReturn(PETSC_SUCCESS);
1928 }
1929 
1930 #if defined(PETSC_HAVE_EXODUSII)
1931   #include <exodusII.h>
1932   #include <petscviewerexodusii.h>
1933 #endif
1934 
1935 PetscErrorCode DMView_Plex(DM dm, PetscViewer viewer)
1936 {
1937   PetscBool iascii, ishdf5, isvtk, isdraw, flg, isglvis, isexodus, iscgns;
1938   char      name[PETSC_MAX_PATH_LEN];
1939 
1940   PetscFunctionBegin;
1941   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1942   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1943   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &iascii));
1944   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
1945   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1946   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
1947   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
1948   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodus));
1949   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
1950   if (iascii) {
1951     PetscViewerFormat format;
1952     PetscCall(PetscViewerGetFormat(viewer, &format));
1953     if (format == PETSC_VIEWER_ASCII_GLVIS) PetscCall(DMPlexView_GLVis(dm, viewer));
1954     else PetscCall(DMPlexView_Ascii(dm, viewer));
1955   } else if (ishdf5) {
1956 #if defined(PETSC_HAVE_HDF5)
1957     PetscCall(DMPlexView_HDF5_Internal(dm, viewer));
1958 #else
1959     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1960 #endif
1961   } else if (isvtk) {
1962     PetscCall(DMPlexVTKWriteAll((PetscObject)dm, viewer));
1963   } else if (isdraw) {
1964     DM hdm;
1965 
1966     PetscCall(DMPlexCreateHighOrderSurrogate_Internal(dm, &hdm));
1967     PetscCall(DMPlexView_Draw(hdm, viewer));
1968     PetscCall(DMDestroy(&hdm));
1969   } else if (isglvis) {
1970     PetscCall(DMPlexView_GLVis(dm, viewer));
1971 #if defined(PETSC_HAVE_EXODUSII)
1972   } else if (isexodus) {
1973     /*
1974       exodusII requires that all sets be part of exactly one cell set.
1975       If the dm does not have a "Cell Sets" label defined, we create one
1976       with ID 1, containing all cells.
1977       Note that if the Cell Sets label is defined but does not cover all cells,
1978       we may still have a problem. This should probably be checked here or in the viewer;
1979     */
1980     PetscInt numCS;
1981     PetscCall(DMGetLabelSize(dm, "Cell Sets", &numCS));
1982     if (!numCS) {
1983       PetscInt cStart, cEnd, c;
1984       PetscCall(DMCreateLabel(dm, "Cell Sets"));
1985       PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1986       for (c = cStart; c < cEnd; ++c) PetscCall(DMSetLabelValue(dm, "Cell Sets", c, 1));
1987     }
1988     PetscCall(DMView_PlexExodusII(dm, viewer));
1989 #endif
1990 #if defined(PETSC_HAVE_CGNS)
1991   } else if (iscgns) {
1992     PetscCall(DMView_PlexCGNS(dm, viewer));
1993 #endif
1994   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex writing", ((PetscObject)viewer)->type_name);
1995   /* Optionally view the partition */
1996   PetscCall(PetscOptionsHasName(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_partition_view", &flg));
1997   if (flg) {
1998     Vec ranks;
1999     PetscCall(DMPlexCreateRankField(dm, &ranks));
2000     PetscCall(VecView(ranks, viewer));
2001     PetscCall(VecDestroy(&ranks));
2002   }
2003   /* Optionally view a label */
2004   PetscCall(PetscOptionsGetString(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_label_view", name, sizeof(name), &flg));
2005   if (flg) {
2006     DMLabel label;
2007     Vec     val;
2008 
2009     PetscCall(DMGetLabel(dm, name, &label));
2010     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Label %s provided to -dm_label_view does not exist in this DM", name);
2011     PetscCall(DMPlexCreateLabelField(dm, label, &val));
2012     PetscCall(VecView(val, viewer));
2013     PetscCall(VecDestroy(&val));
2014   }
2015   PetscFunctionReturn(PETSC_SUCCESS);
2016 }
2017 
2018 /*@
2019   DMPlexTopologyView - Saves a `DMPLEX` topology into a file
2020 
2021   Collective
2022 
2023   Input Parameters:
2024 + dm     - The `DM` whose topology is to be saved
2025 - viewer - The `PetscViewer` to save it in
2026 
2027   Level: advanced
2028 
2029 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexTopologyLoad()`, `PetscViewer`
2030 @*/
2031 PetscErrorCode DMPlexTopologyView(DM dm, PetscViewer viewer)
2032 {
2033   PetscBool ishdf5;
2034 
2035   PetscFunctionBegin;
2036   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2037   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2038   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2039   PetscCall(PetscLogEventBegin(DMPLEX_TopologyView, viewer, 0, 0, 0));
2040   if (ishdf5) {
2041 #if defined(PETSC_HAVE_HDF5)
2042     PetscViewerFormat format;
2043     PetscCall(PetscViewerGetFormat(viewer, &format));
2044     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2045       IS globalPointNumbering;
2046 
2047       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
2048       PetscCall(DMPlexTopologyView_HDF5_Internal(dm, globalPointNumbering, viewer));
2049       PetscCall(ISDestroy(&globalPointNumbering));
2050     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
2051 #else
2052     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2053 #endif
2054   }
2055   PetscCall(PetscLogEventEnd(DMPLEX_TopologyView, viewer, 0, 0, 0));
2056   PetscFunctionReturn(PETSC_SUCCESS);
2057 }
2058 
2059 /*@
2060   DMPlexCoordinatesView - Saves `DMPLEX` coordinates into a file
2061 
2062   Collective
2063 
2064   Input Parameters:
2065 + dm     - The `DM` whose coordinates are to be saved
2066 - viewer - The `PetscViewer` for saving
2067 
2068   Level: advanced
2069 
2070 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexLabelsView()`, `DMPlexCoordinatesLoad()`, `PetscViewer`
2071 @*/
2072 PetscErrorCode DMPlexCoordinatesView(DM dm, PetscViewer viewer)
2073 {
2074   PetscBool ishdf5;
2075 
2076   PetscFunctionBegin;
2077   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2078   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2079   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2080   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
2081   if (ishdf5) {
2082 #if defined(PETSC_HAVE_HDF5)
2083     PetscViewerFormat format;
2084     PetscCall(PetscViewerGetFormat(viewer, &format));
2085     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2086       PetscCall(DMPlexCoordinatesView_HDF5_Internal(dm, viewer));
2087     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
2088 #else
2089     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2090 #endif
2091   }
2092   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
2093   PetscFunctionReturn(PETSC_SUCCESS);
2094 }
2095 
2096 /*@
2097   DMPlexLabelsView - Saves `DMPLEX` labels into a file
2098 
2099   Collective
2100 
2101   Input Parameters:
2102 + dm     - The `DM` whose labels are to be saved
2103 - viewer - The `PetscViewer` for saving
2104 
2105   Level: advanced
2106 
2107 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsLoad()`, `PetscViewer`
2108 @*/
2109 PetscErrorCode DMPlexLabelsView(DM dm, PetscViewer viewer)
2110 {
2111   PetscBool ishdf5;
2112 
2113   PetscFunctionBegin;
2114   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2115   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2116   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2117   PetscCall(PetscLogEventBegin(DMPLEX_LabelsView, viewer, 0, 0, 0));
2118   if (ishdf5) {
2119 #if defined(PETSC_HAVE_HDF5)
2120     IS                globalPointNumbering;
2121     PetscViewerFormat format;
2122 
2123     PetscCall(PetscViewerGetFormat(viewer, &format));
2124     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2125       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
2126       PetscCall(DMPlexLabelsView_HDF5_Internal(dm, globalPointNumbering, viewer));
2127       PetscCall(ISDestroy(&globalPointNumbering));
2128     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2129 #else
2130     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2131 #endif
2132   }
2133   PetscCall(PetscLogEventEnd(DMPLEX_LabelsView, viewer, 0, 0, 0));
2134   PetscFunctionReturn(PETSC_SUCCESS);
2135 }
2136 
2137 /*@
2138   DMPlexSectionView - Saves a section associated with a `DMPLEX`
2139 
2140   Collective
2141 
2142   Input Parameters:
2143 + dm        - The `DM` that contains the topology on which the section to be saved is defined
2144 . viewer    - The `PetscViewer` for saving
2145 - sectiondm - The `DM` that contains the section to be saved, can be `NULL`
2146 
2147   Level: advanced
2148 
2149   Notes:
2150   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.
2151 
2152   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.
2153 
2154 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`, `PetscSectionView()`, `DMPlexSectionLoad()`, `PetscViewer`
2155 @*/
2156 PetscErrorCode DMPlexSectionView(DM dm, PetscViewer viewer, DM sectiondm)
2157 {
2158   PetscBool ishdf5;
2159 
2160   PetscFunctionBegin;
2161   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2162   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2163   if (!sectiondm) sectiondm = dm;
2164   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2165   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2166   PetscCall(PetscLogEventBegin(DMPLEX_SectionView, viewer, 0, 0, 0));
2167   if (ishdf5) {
2168 #if defined(PETSC_HAVE_HDF5)
2169     PetscCall(DMPlexSectionView_HDF5_Internal(dm, viewer, sectiondm));
2170 #else
2171     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2172 #endif
2173   }
2174   PetscCall(PetscLogEventEnd(DMPLEX_SectionView, viewer, 0, 0, 0));
2175   PetscFunctionReturn(PETSC_SUCCESS);
2176 }
2177 
2178 /*@
2179   DMPlexGlobalVectorView - Saves a global vector
2180 
2181   Collective
2182 
2183   Input Parameters:
2184 + dm        - The `DM` that represents the topology
2185 . viewer    - The `PetscViewer` to save data with
2186 . sectiondm - The `DM` that contains the global section on which vec is defined, can be `NULL`
2187 - vec       - The global vector to be saved
2188 
2189   Level: advanced
2190 
2191   Notes:
2192   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.
2193 
2194   Calling sequence:
2195 .vb
2196        DMCreate(PETSC_COMM_WORLD, &dm);
2197        DMSetType(dm, DMPLEX);
2198        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2199        DMClone(dm, &sectiondm);
2200        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2201        PetscSectionCreate(PETSC_COMM_WORLD, &section);
2202        DMPlexGetChart(sectiondm, &pStart, &pEnd);
2203        PetscSectionSetChart(section, pStart, pEnd);
2204        PetscSectionSetUp(section);
2205        DMSetLocalSection(sectiondm, section);
2206        PetscSectionDestroy(&section);
2207        DMGetGlobalVector(sectiondm, &vec);
2208        PetscObjectSetName((PetscObject)vec, "vec_name");
2209        DMPlexTopologyView(dm, viewer);
2210        DMPlexSectionView(dm, viewer, sectiondm);
2211        DMPlexGlobalVectorView(dm, viewer, sectiondm, vec);
2212        DMRestoreGlobalVector(sectiondm, &vec);
2213        DMDestroy(&sectiondm);
2214        DMDestroy(&dm);
2215 .ve
2216 
2217 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexLocalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2218 @*/
2219 PetscErrorCode DMPlexGlobalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2220 {
2221   PetscBool ishdf5;
2222 
2223   PetscFunctionBegin;
2224   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2225   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2226   if (!sectiondm) sectiondm = dm;
2227   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2228   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2229   /* Check consistency */
2230   {
2231     PetscSection section;
2232     PetscBool    includesConstraints;
2233     PetscInt     m, m1;
2234 
2235     PetscCall(VecGetLocalSize(vec, &m1));
2236     PetscCall(DMGetGlobalSection(sectiondm, &section));
2237     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2238     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2239     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2240     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2241   }
2242   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2243   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorView, viewer, 0, 0, 0));
2244   if (ishdf5) {
2245 #if defined(PETSC_HAVE_HDF5)
2246     PetscCall(DMPlexGlobalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2247 #else
2248     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2249 #endif
2250   }
2251   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorView, viewer, 0, 0, 0));
2252   PetscFunctionReturn(PETSC_SUCCESS);
2253 }
2254 
2255 /*@
2256   DMPlexLocalVectorView - Saves a local vector
2257 
2258   Collective
2259 
2260   Input Parameters:
2261 + dm        - The `DM` that represents the topology
2262 . viewer    - The `PetscViewer` to save data with
2263 . sectiondm - The `DM` that contains the local section on which `vec` is defined, can be `NULL`
2264 - vec       - The local vector to be saved
2265 
2266   Level: advanced
2267 
2268   Note:
2269   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.
2270 
2271   Calling sequence:
2272 .vb
2273        DMCreate(PETSC_COMM_WORLD, &dm);
2274        DMSetType(dm, DMPLEX);
2275        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2276        DMClone(dm, &sectiondm);
2277        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2278        PetscSectionCreate(PETSC_COMM_WORLD, &section);
2279        DMPlexGetChart(sectiondm, &pStart, &pEnd);
2280        PetscSectionSetChart(section, pStart, pEnd);
2281        PetscSectionSetUp(section);
2282        DMSetLocalSection(sectiondm, section);
2283        DMGetLocalVector(sectiondm, &vec);
2284        PetscObjectSetName((PetscObject)vec, "vec_name");
2285        DMPlexTopologyView(dm, viewer);
2286        DMPlexSectionView(dm, viewer, sectiondm);
2287        DMPlexLocalVectorView(dm, viewer, sectiondm, vec);
2288        DMRestoreLocalVector(sectiondm, &vec);
2289        DMDestroy(&sectiondm);
2290        DMDestroy(&dm);
2291 .ve
2292 
2293 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexGlobalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2294 @*/
2295 PetscErrorCode DMPlexLocalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2296 {
2297   PetscBool ishdf5;
2298 
2299   PetscFunctionBegin;
2300   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2301   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2302   if (!sectiondm) sectiondm = dm;
2303   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2304   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2305   /* Check consistency */
2306   {
2307     PetscSection section;
2308     PetscBool    includesConstraints;
2309     PetscInt     m, m1;
2310 
2311     PetscCall(VecGetLocalSize(vec, &m1));
2312     PetscCall(DMGetLocalSection(sectiondm, &section));
2313     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2314     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2315     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2316     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2317   }
2318   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2319   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorView, viewer, 0, 0, 0));
2320   if (ishdf5) {
2321 #if defined(PETSC_HAVE_HDF5)
2322     PetscCall(DMPlexLocalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2323 #else
2324     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2325 #endif
2326   }
2327   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorView, viewer, 0, 0, 0));
2328   PetscFunctionReturn(PETSC_SUCCESS);
2329 }
2330 
2331 PetscErrorCode DMLoad_Plex(DM dm, PetscViewer viewer)
2332 {
2333   PetscBool ishdf5;
2334 
2335   PetscFunctionBegin;
2336   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2337   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2338   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2339   if (ishdf5) {
2340 #if defined(PETSC_HAVE_HDF5)
2341     PetscViewerFormat format;
2342     PetscCall(PetscViewerGetFormat(viewer, &format));
2343     if (format == PETSC_VIEWER_HDF5_XDMF || format == PETSC_VIEWER_HDF5_VIZ) {
2344       PetscCall(DMPlexLoad_HDF5_Xdmf_Internal(dm, viewer));
2345     } else if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2346       PetscCall(DMPlexLoad_HDF5_Internal(dm, viewer));
2347     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2348     PetscFunctionReturn(PETSC_SUCCESS);
2349 #else
2350     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2351 #endif
2352   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex loading", ((PetscObject)viewer)->type_name);
2353 }
2354 
2355 /*@
2356   DMPlexTopologyLoad - Loads a topology into a `DMPLEX`
2357 
2358   Collective
2359 
2360   Input Parameters:
2361 + dm     - The `DM` into which the topology is loaded
2362 - viewer - The `PetscViewer` for the saved topology
2363 
2364   Output Parameter:
2365 . 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
2366 
2367   Level: advanced
2368 
2369 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2370           `PetscViewer`, `PetscSF`
2371 @*/
2372 PetscErrorCode DMPlexTopologyLoad(DM dm, PetscViewer viewer, PetscSF *globalToLocalPointSF)
2373 {
2374   PetscBool ishdf5;
2375 
2376   PetscFunctionBegin;
2377   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2378   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2379   if (globalToLocalPointSF) PetscAssertPointer(globalToLocalPointSF, 3);
2380   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2381   PetscCall(PetscLogEventBegin(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2382   if (ishdf5) {
2383 #if defined(PETSC_HAVE_HDF5)
2384     PetscViewerFormat format;
2385     PetscCall(PetscViewerGetFormat(viewer, &format));
2386     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2387       PetscCall(DMPlexTopologyLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2388     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2389 #else
2390     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2391 #endif
2392   }
2393   PetscCall(PetscLogEventEnd(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2394   PetscFunctionReturn(PETSC_SUCCESS);
2395 }
2396 
2397 /*@
2398   DMPlexCoordinatesLoad - Loads coordinates into a `DMPLEX`
2399 
2400   Collective
2401 
2402   Input Parameters:
2403 + dm                   - The `DM` into which the coordinates are loaded
2404 . viewer               - The `PetscViewer` for the saved coordinates
2405 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad()` when loading dm from viewer
2406 
2407   Level: advanced
2408 
2409 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2410           `PetscSF`, `PetscViewer`
2411 @*/
2412 PetscErrorCode DMPlexCoordinatesLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2413 {
2414   PetscBool ishdf5;
2415 
2416   PetscFunctionBegin;
2417   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2418   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2419   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2420   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2421   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesLoad, viewer, 0, 0, 0));
2422   if (ishdf5) {
2423 #if defined(PETSC_HAVE_HDF5)
2424     PetscViewerFormat format;
2425     PetscCall(PetscViewerGetFormat(viewer, &format));
2426     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2427       PetscCall(DMPlexCoordinatesLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2428     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2429 #else
2430     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2431 #endif
2432   }
2433   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesLoad, viewer, 0, 0, 0));
2434   PetscFunctionReturn(PETSC_SUCCESS);
2435 }
2436 
2437 /*@
2438   DMPlexLabelsLoad - Loads labels into a `DMPLEX`
2439 
2440   Collective
2441 
2442   Input Parameters:
2443 + dm                   - The `DM` into which the labels are loaded
2444 . viewer               - The `PetscViewer` for the saved labels
2445 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad()` when loading `dm` from viewer
2446 
2447   Level: advanced
2448 
2449   Note:
2450   The `PetscSF` argument must not be `NULL` if the `DM` is distributed, otherwise an error occurs.
2451 
2452 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2453           `PetscSF`, `PetscViewer`
2454 @*/
2455 PetscErrorCode DMPlexLabelsLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2456 {
2457   PetscBool ishdf5;
2458 
2459   PetscFunctionBegin;
2460   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2461   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2462   if (globalToLocalPointSF) PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2463   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2464   PetscCall(PetscLogEventBegin(DMPLEX_LabelsLoad, viewer, 0, 0, 0));
2465   if (ishdf5) {
2466 #if defined(PETSC_HAVE_HDF5)
2467     PetscViewerFormat format;
2468 
2469     PetscCall(PetscViewerGetFormat(viewer, &format));
2470     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2471       PetscCall(DMPlexLabelsLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2472     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2473 #else
2474     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2475 #endif
2476   }
2477   PetscCall(PetscLogEventEnd(DMPLEX_LabelsLoad, viewer, 0, 0, 0));
2478   PetscFunctionReturn(PETSC_SUCCESS);
2479 }
2480 
2481 /*@
2482   DMPlexSectionLoad - Loads section into a `DMPLEX`
2483 
2484   Collective
2485 
2486   Input Parameters:
2487 + dm                   - The `DM` that represents the topology
2488 . viewer               - The `PetscViewer` that represents the on-disk section (sectionA)
2489 . sectiondm            - The `DM` into which the on-disk section (sectionA) is migrated, can be `NULL`
2490 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad(`) when loading dm from viewer
2491 
2492   Output Parameters:
2493 + 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)
2494 - 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)
2495 
2496   Level: advanced
2497 
2498   Notes:
2499   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.
2500 
2501   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.
2502 
2503   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.
2504 
2505   Example using 2 processes:
2506 .vb
2507   NX (number of points on dm): 4
2508   sectionA                   : the on-disk section
2509   vecA                       : a vector associated with sectionA
2510   sectionB                   : sectiondm's local section constructed in this function
2511   vecB (local)               : a vector associated with sectiondm's local section
2512   vecB (global)              : a vector associated with sectiondm's global section
2513 
2514                                      rank 0    rank 1
2515   vecA (global)                  : [.0 .4 .1 | .2 .3]        <- to be loaded in DMPlexGlobalVectorLoad() or DMPlexLocalVectorLoad()
2516   sectionA->atlasOff             :       0 2 | 1             <- loaded in PetscSectionLoad()
2517   sectionA->atlasDof             :       1 3 | 1             <- loaded in PetscSectionLoad()
2518   sectionA's global point numbers:       0 2 | 3             <- loaded in DMPlexSectionLoad()
2519   [0, NX)                        :       0 1 | 2 3           <- conceptual partition used in globalToLocalPointSF
2520   sectionB's global point numbers:     0 1 3 | 3 2           <- associated with [0, NX) by globalToLocalPointSF
2521   sectionB->atlasDof             :     1 0 1 | 1 3
2522   sectionB->atlasOff (no perm)   :     0 1 1 | 0 1
2523   vecB (local)                   :   [.0 .4] | [.4 .1 .2 .3] <- to be constructed by calling DMPlexLocalVectorLoad() with localDofSF
2524   vecB (global)                  :    [.0 .4 | .1 .2 .3]     <- to be constructed by calling DMPlexGlobalVectorLoad() with globalDofSF
2525 .ve
2526   where "|" represents a partition of loaded data, and global point 3 is assumed to be owned by rank 0.
2527 
2528 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`, `PetscSectionLoad()`, `DMPlexSectionView()`, `PetscSF`, `PetscViewer`
2529 @*/
2530 PetscErrorCode DMPlexSectionLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF globalToLocalPointSF, PetscSF *globalDofSF, PetscSF *localDofSF)
2531 {
2532   PetscBool ishdf5;
2533 
2534   PetscFunctionBegin;
2535   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2536   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2537   if (!sectiondm) sectiondm = dm;
2538   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2539   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 4);
2540   if (globalDofSF) PetscAssertPointer(globalDofSF, 5);
2541   if (localDofSF) PetscAssertPointer(localDofSF, 6);
2542   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2543   PetscCall(PetscLogEventBegin(DMPLEX_SectionLoad, viewer, 0, 0, 0));
2544   if (ishdf5) {
2545 #if defined(PETSC_HAVE_HDF5)
2546     PetscCall(DMPlexSectionLoad_HDF5_Internal(dm, viewer, sectiondm, globalToLocalPointSF, globalDofSF, localDofSF));
2547 #else
2548     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2549 #endif
2550   }
2551   PetscCall(PetscLogEventEnd(DMPLEX_SectionLoad, viewer, 0, 0, 0));
2552   PetscFunctionReturn(PETSC_SUCCESS);
2553 }
2554 
2555 /*@
2556   DMPlexGlobalVectorLoad - Loads on-disk vector data into a global vector
2557 
2558   Collective
2559 
2560   Input Parameters:
2561 + dm        - The `DM` that represents the topology
2562 . viewer    - The `PetscViewer` that represents the on-disk vector data
2563 . sectiondm - The `DM` that contains the global section on which vec is defined, can be `NULL`
2564 . sf        - The `PetscSF` that migrates the on-disk vector data into vec
2565 - vec       - The global vector to set values of
2566 
2567   Level: advanced
2568 
2569   Notes:
2570   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.
2571 
2572   Calling sequence:
2573 .vb
2574        DMCreate(PETSC_COMM_WORLD, &dm);
2575        DMSetType(dm, DMPLEX);
2576        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2577        DMPlexTopologyLoad(dm, viewer, &sfX);
2578        DMClone(dm, &sectiondm);
2579        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2580        DMPlexSectionLoad(dm, viewer, sectiondm, sfX, &gsf, NULL);
2581        DMGetGlobalVector(sectiondm, &vec);
2582        PetscObjectSetName((PetscObject)vec, "vec_name");
2583        DMPlexGlobalVectorLoad(dm, viewer, sectiondm, gsf, vec);
2584        DMRestoreGlobalVector(sectiondm, &vec);
2585        PetscSFDestroy(&gsf);
2586        PetscSFDestroy(&sfX);
2587        DMDestroy(&sectiondm);
2588        DMDestroy(&dm);
2589 .ve
2590 
2591 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexLocalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`,
2592           `PetscSF`, `PetscViewer`
2593 @*/
2594 PetscErrorCode DMPlexGlobalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2595 {
2596   PetscBool ishdf5;
2597 
2598   PetscFunctionBegin;
2599   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2600   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2601   if (!sectiondm) sectiondm = dm;
2602   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2603   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2604   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2605   /* Check consistency */
2606   {
2607     PetscSection section;
2608     PetscBool    includesConstraints;
2609     PetscInt     m, m1;
2610 
2611     PetscCall(VecGetLocalSize(vec, &m1));
2612     PetscCall(DMGetGlobalSection(sectiondm, &section));
2613     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2614     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2615     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2616     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2617   }
2618   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2619   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorLoad, viewer, 0, 0, 0));
2620   if (ishdf5) {
2621 #if defined(PETSC_HAVE_HDF5)
2622     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2623 #else
2624     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2625 #endif
2626   }
2627   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorLoad, viewer, 0, 0, 0));
2628   PetscFunctionReturn(PETSC_SUCCESS);
2629 }
2630 
2631 /*@
2632   DMPlexLocalVectorLoad - Loads on-disk vector data into a local vector
2633 
2634   Collective
2635 
2636   Input Parameters:
2637 + dm        - The `DM` that represents the topology
2638 . viewer    - The `PetscViewer` that represents the on-disk vector data
2639 . sectiondm - The `DM` that contains the local section on which vec is defined, can be `NULL`
2640 . sf        - The `PetscSF` that migrates the on-disk vector data into vec
2641 - vec       - The local vector to set values of
2642 
2643   Level: advanced
2644 
2645   Notes:
2646   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.
2647 
2648   Calling sequence:
2649 .vb
2650        DMCreate(PETSC_COMM_WORLD, &dm);
2651        DMSetType(dm, DMPLEX);
2652        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2653        DMPlexTopologyLoad(dm, viewer, &sfX);
2654        DMClone(dm, &sectiondm);
2655        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2656        DMPlexSectionLoad(dm, viewer, sectiondm, sfX, NULL, &lsf);
2657        DMGetLocalVector(sectiondm, &vec);
2658        PetscObjectSetName((PetscObject)vec, "vec_name");
2659        DMPlexLocalVectorLoad(dm, viewer, sectiondm, lsf, vec);
2660        DMRestoreLocalVector(sectiondm, &vec);
2661        PetscSFDestroy(&lsf);
2662        PetscSFDestroy(&sfX);
2663        DMDestroy(&sectiondm);
2664        DMDestroy(&dm);
2665 .ve
2666 
2667 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`,
2668           `PetscSF`, `PetscViewer`
2669 @*/
2670 PetscErrorCode DMPlexLocalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2671 {
2672   PetscBool ishdf5;
2673 
2674   PetscFunctionBegin;
2675   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2676   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2677   if (!sectiondm) sectiondm = dm;
2678   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2679   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2680   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2681   /* Check consistency */
2682   {
2683     PetscSection section;
2684     PetscBool    includesConstraints;
2685     PetscInt     m, m1;
2686 
2687     PetscCall(VecGetLocalSize(vec, &m1));
2688     PetscCall(DMGetLocalSection(sectiondm, &section));
2689     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2690     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2691     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2692     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2693   }
2694   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2695   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorLoad, viewer, 0, 0, 0));
2696   if (ishdf5) {
2697 #if defined(PETSC_HAVE_HDF5)
2698     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2699 #else
2700     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2701 #endif
2702   }
2703   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorLoad, viewer, 0, 0, 0));
2704   PetscFunctionReturn(PETSC_SUCCESS);
2705 }
2706 
2707 PetscErrorCode DMDestroy_Plex(DM dm)
2708 {
2709   DM_Plex *mesh = (DM_Plex *)dm->data;
2710 
2711   PetscFunctionBegin;
2712   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMSetUpGLVisViewer_C", NULL));
2713   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertBoundaryValues_C", NULL));
2714   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMCreateNeumannOverlap_C", NULL));
2715   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMInterpolateSolution_C", NULL));
2716   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertTimeDerivativeBoundaryValues_C", NULL));
2717   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", NULL));
2718   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeGetDefault_C", NULL));
2719   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeSetDefault_C", NULL));
2720   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "MatComputeNeumannOverlap_C", NULL));
2721   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderGetDefault_C", NULL));
2722   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderSetDefault_C", NULL));
2723   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionGetDefault_C", NULL));
2724   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionSetDefault_C", NULL));
2725   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionGetType_C", NULL));
2726   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMReorderSectionSetType_C", NULL));
2727   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", NULL));
2728   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexSetOverlap_C", NULL));
2729   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetUseCeed_C", NULL));
2730   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexSetUseCeed_C", NULL));
2731   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMGetIsoperiodicPointSF_C", NULL));
2732   if (--mesh->refct > 0) PetscFunctionReturn(PETSC_SUCCESS);
2733   PetscCall(PetscSectionDestroy(&mesh->coneSection));
2734   PetscCall(PetscFree(mesh->cones));
2735   PetscCall(PetscFree(mesh->coneOrientations));
2736   PetscCall(PetscSectionDestroy(&mesh->supportSection));
2737   PetscCall(PetscSectionDestroy(&mesh->subdomainSection));
2738   PetscCall(PetscFree(mesh->supports));
2739   PetscCall(PetscFree(mesh->cellTypes));
2740   PetscCall(DMPlexTransformDestroy(&mesh->tr));
2741   PetscCall(PetscFree(mesh->tetgenOpts));
2742   PetscCall(PetscFree(mesh->triangleOpts));
2743   PetscCall(PetscFree(mesh->transformType));
2744   PetscCall(PetscFree(mesh->distributionName));
2745   PetscCall(PetscPartitionerDestroy(&mesh->partitioner));
2746   PetscCall(DMLabelDestroy(&mesh->subpointMap));
2747   PetscCall(ISDestroy(&mesh->subpointIS));
2748   PetscCall(ISDestroy(&mesh->globalVertexNumbers));
2749   PetscCall(ISDestroy(&mesh->globalCellNumbers));
2750   if (mesh->periodic.face_sfs) {
2751     for (PetscInt i = 0; i < mesh->periodic.num_face_sfs; i++) PetscCall(PetscSFDestroy(&mesh->periodic.face_sfs[i]));
2752     PetscCall(PetscFree(mesh->periodic.face_sfs));
2753   }
2754   PetscCall(PetscSFDestroy(&mesh->periodic.composed_sf));
2755   if (mesh->periodic.periodic_points) {
2756     for (PetscInt i = 0; i < mesh->periodic.num_face_sfs; i++) PetscCall(ISDestroy(&mesh->periodic.periodic_points[i]));
2757     PetscCall(PetscFree(mesh->periodic.periodic_points));
2758   }
2759   if (mesh->periodic.transform) PetscCall(PetscFree(mesh->periodic.transform));
2760   PetscCall(PetscSectionDestroy(&mesh->anchorSection));
2761   PetscCall(ISDestroy(&mesh->anchorIS));
2762   PetscCall(PetscSectionDestroy(&mesh->parentSection));
2763   PetscCall(PetscFree(mesh->parents));
2764   PetscCall(PetscFree(mesh->childIDs));
2765   PetscCall(PetscSectionDestroy(&mesh->childSection));
2766   PetscCall(PetscFree(mesh->children));
2767   PetscCall(DMDestroy(&mesh->referenceTree));
2768   PetscCall(PetscGridHashDestroy(&mesh->lbox));
2769   PetscCall(PetscFree(mesh->neighbors));
2770   if (mesh->metricCtx) PetscCall(PetscFree(mesh->metricCtx));
2771   /* This was originally freed in DMDestroy(), but that prevents reference counting of backend objects */
2772   PetscCall(PetscFree(mesh));
2773   PetscFunctionReturn(PETSC_SUCCESS);
2774 }
2775 
2776 PetscErrorCode DMCreateMatrix_Plex(DM dm, Mat *J)
2777 {
2778   PetscSection           sectionGlobal, sectionLocal;
2779   PetscInt               bs = -1, mbs;
2780   PetscInt               localSize, localStart = 0;
2781   PetscBool              isShell, isBlock, isSeqBlock, isMPIBlock, isSymBlock, isSymSeqBlock, isSymMPIBlock, isMatIS;
2782   MatType                mtype;
2783   ISLocalToGlobalMapping ltog;
2784 
2785   PetscFunctionBegin;
2786   PetscCall(MatInitializePackage());
2787   mtype = dm->mattype;
2788   PetscCall(DMGetLocalSection(dm, &sectionLocal));
2789   PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
2790   /* PetscCall(PetscSectionGetStorageSize(sectionGlobal, &localSize)); */
2791   PetscCall(PetscSectionGetConstrainedStorageSize(sectionGlobal, &localSize));
2792   PetscCallMPI(MPI_Exscan(&localSize, &localStart, 1, MPIU_INT, MPI_SUM, PetscObjectComm((PetscObject)dm)));
2793   PetscCall(MatCreate(PetscObjectComm((PetscObject)dm), J));
2794   PetscCall(MatSetSizes(*J, localSize, localSize, PETSC_DETERMINE, PETSC_DETERMINE));
2795   PetscCall(MatSetType(*J, mtype));
2796   PetscCall(MatSetFromOptions(*J));
2797   PetscCall(MatGetBlockSize(*J, &mbs));
2798   if (mbs > 1) bs = mbs;
2799   PetscCall(PetscStrcmp(mtype, MATSHELL, &isShell));
2800   PetscCall(PetscStrcmp(mtype, MATBAIJ, &isBlock));
2801   PetscCall(PetscStrcmp(mtype, MATSEQBAIJ, &isSeqBlock));
2802   PetscCall(PetscStrcmp(mtype, MATMPIBAIJ, &isMPIBlock));
2803   PetscCall(PetscStrcmp(mtype, MATSBAIJ, &isSymBlock));
2804   PetscCall(PetscStrcmp(mtype, MATSEQSBAIJ, &isSymSeqBlock));
2805   PetscCall(PetscStrcmp(mtype, MATMPISBAIJ, &isSymMPIBlock));
2806   PetscCall(PetscStrcmp(mtype, MATIS, &isMatIS));
2807   if (!isShell) {
2808     PetscBool fillMatrix = (PetscBool)(!dm->prealloc_only && !isMatIS);
2809     PetscInt *dnz, *onz, *dnzu, *onzu, bsLocal[2], bsMinMax[2], *pblocks;
2810     PetscInt  pStart, pEnd, p, dof, cdof, num_fields;
2811 
2812     PetscCall(DMGetLocalToGlobalMapping(dm, &ltog));
2813 
2814     PetscCall(PetscCalloc1(localSize, &pblocks));
2815     PetscCall(PetscSectionGetChart(sectionGlobal, &pStart, &pEnd));
2816     PetscCall(PetscSectionGetNumFields(sectionGlobal, &num_fields));
2817     for (p = pStart; p < pEnd; ++p) {
2818       switch (dm->blocking_type) {
2819       case DM_BLOCKING_TOPOLOGICAL_POINT: { // One block per topological point
2820         PetscInt bdof, offset;
2821 
2822         PetscCall(PetscSectionGetDof(sectionGlobal, p, &dof));
2823         PetscCall(PetscSectionGetOffset(sectionGlobal, p, &offset));
2824         PetscCall(PetscSectionGetConstraintDof(sectionGlobal, p, &cdof));
2825         if (dof > 0) {
2826           for (PetscInt i = 0; i < dof - cdof; ++i) pblocks[offset - localStart + i] = dof - cdof;
2827           // Signal block concatenation
2828           if (dof - cdof && sectionLocal->blockStarts && !PetscBTLookup(sectionLocal->blockStarts, p)) pblocks[offset - localStart] = -(dof - cdof);
2829         }
2830         dof  = dof < 0 ? -(dof + 1) : dof;
2831         bdof = cdof && (dof - cdof) ? 1 : dof;
2832         if (dof) {
2833           if (bs < 0) {
2834             bs = bdof;
2835           } else if (bs != bdof) {
2836             bs = 1;
2837           }
2838         }
2839       } break;
2840       case DM_BLOCKING_FIELD_NODE: {
2841         for (PetscInt field = 0; field < num_fields; field++) {
2842           PetscInt num_comp, bdof, offset;
2843           PetscCall(PetscSectionGetFieldComponents(sectionGlobal, field, &num_comp));
2844           PetscCall(PetscSectionGetFieldDof(sectionGlobal, p, field, &dof));
2845           if (dof < 0) continue;
2846           PetscCall(PetscSectionGetFieldOffset(sectionGlobal, p, field, &offset));
2847           PetscCall(PetscSectionGetFieldConstraintDof(sectionGlobal, p, field, &cdof));
2848           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);
2849           PetscInt num_nodes = dof / num_comp;
2850           for (PetscInt i = 0; i < dof - cdof; i++) pblocks[offset - localStart + i] = (dof - cdof) / num_nodes;
2851           // Handle possibly constant block size (unlikely)
2852           bdof = cdof && (dof - cdof) ? 1 : dof;
2853           if (dof) {
2854             if (bs < 0) {
2855               bs = bdof;
2856             } else if (bs != bdof) {
2857               bs = 1;
2858             }
2859           }
2860         }
2861       } break;
2862       }
2863     }
2864     /* Must have same blocksize on all procs (some might have no points) */
2865     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs;
2866     bsLocal[1] = bs;
2867     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
2868     if (bsMinMax[0] != bsMinMax[1]) bs = 1;
2869     else bs = bsMinMax[0];
2870     bs = PetscMax(1, bs);
2871     PetscCall(MatSetLocalToGlobalMapping(*J, ltog, ltog));
2872     if (dm->prealloc_skip) { // User will likely use MatSetPreallocationCOO(), but still set structural parameters
2873       PetscCall(MatSetBlockSize(*J, bs));
2874       PetscCall(MatSetUp(*J));
2875     } else {
2876       PetscCall(PetscCalloc4(localSize / bs, &dnz, localSize / bs, &onz, localSize / bs, &dnzu, localSize / bs, &onzu));
2877       PetscCall(DMPlexPreallocateOperator(dm, bs, dnz, onz, dnzu, onzu, *J, fillMatrix));
2878       PetscCall(PetscFree4(dnz, onz, dnzu, onzu));
2879     }
2880     if (pblocks) { // Consolidate blocks
2881       PetscInt nblocks = 0;
2882       pblocks[0]       = PetscAbs(pblocks[0]);
2883       for (PetscInt i = 0; i < localSize; i += PetscMax(1, pblocks[i])) {
2884         if (pblocks[i] == 0) continue;
2885         // Negative block size indicates the blocks should be concatenated
2886         if (pblocks[i] < 0) {
2887           pblocks[i] = -pblocks[i];
2888           pblocks[nblocks - 1] += pblocks[i];
2889         } else {
2890           pblocks[nblocks++] = pblocks[i]; // nblocks always <= i
2891         }
2892         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]);
2893       }
2894       PetscCall(MatSetVariableBlockSizes(*J, nblocks, pblocks));
2895     }
2896     PetscCall(PetscFree(pblocks));
2897   }
2898   PetscCall(MatSetDM(*J, dm));
2899   PetscFunctionReturn(PETSC_SUCCESS);
2900 }
2901 
2902 /*@
2903   DMPlexGetSubdomainSection - Returns the section associated with the subdomain
2904 
2905   Not Collective
2906 
2907   Input Parameter:
2908 . dm - The `DMPLEX`
2909 
2910   Output Parameter:
2911 . subsection - The subdomain section
2912 
2913   Level: developer
2914 
2915 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `PetscSection`
2916 @*/
2917 PetscErrorCode DMPlexGetSubdomainSection(DM dm, PetscSection *subsection)
2918 {
2919   DM_Plex *mesh = (DM_Plex *)dm->data;
2920 
2921   PetscFunctionBegin;
2922   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2923   if (!mesh->subdomainSection) {
2924     PetscSection section;
2925     PetscSF      sf;
2926 
2927     PetscCall(PetscSFCreate(PETSC_COMM_SELF, &sf));
2928     PetscCall(DMGetLocalSection(dm, &section));
2929     PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_TRUE, PETSC_FALSE, PETSC_TRUE, &mesh->subdomainSection));
2930     PetscCall(PetscSFDestroy(&sf));
2931   }
2932   *subsection = mesh->subdomainSection;
2933   PetscFunctionReturn(PETSC_SUCCESS);
2934 }
2935 
2936 /*@
2937   DMPlexGetChart - Return the interval for all mesh points [`pStart`, `pEnd`)
2938 
2939   Not Collective
2940 
2941   Input Parameter:
2942 . dm - The `DMPLEX`
2943 
2944   Output Parameters:
2945 + pStart - The first mesh point
2946 - pEnd   - The upper bound for mesh points
2947 
2948   Level: beginner
2949 
2950 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetChart()`
2951 @*/
2952 PetscErrorCode DMPlexGetChart(DM dm, PetscInt *pStart, PetscInt *pEnd)
2953 {
2954   DM_Plex *mesh = (DM_Plex *)dm->data;
2955 
2956   PetscFunctionBegin;
2957   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2958   if (mesh->tr) PetscCall(DMPlexTransformGetChart(mesh->tr, pStart, pEnd));
2959   else PetscCall(PetscSectionGetChart(mesh->coneSection, pStart, pEnd));
2960   PetscFunctionReturn(PETSC_SUCCESS);
2961 }
2962 
2963 /*@
2964   DMPlexSetChart - Set the interval for all mesh points [`pStart`, `pEnd`)
2965 
2966   Not Collective
2967 
2968   Input Parameters:
2969 + dm     - The `DMPLEX`
2970 . pStart - The first mesh point
2971 - pEnd   - The upper bound for mesh points
2972 
2973   Level: beginner
2974 
2975 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetChart()`
2976 @*/
2977 PetscErrorCode DMPlexSetChart(DM dm, PetscInt pStart, PetscInt pEnd)
2978 {
2979   DM_Plex *mesh = (DM_Plex *)dm->data;
2980 
2981   PetscFunctionBegin;
2982   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2983   PetscCall(PetscSectionSetChart(mesh->coneSection, pStart, pEnd));
2984   PetscCall(PetscSectionSetChart(mesh->supportSection, pStart, pEnd));
2985   PetscCall(PetscFree(mesh->cellTypes));
2986   PetscFunctionReturn(PETSC_SUCCESS);
2987 }
2988 
2989 /*@
2990   DMPlexGetConeSize - Return the number of in-edges for this point in the DAG
2991 
2992   Not Collective
2993 
2994   Input Parameters:
2995 + dm - The `DMPLEX`
2996 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
2997 
2998   Output Parameter:
2999 . size - The cone size for point `p`
3000 
3001   Level: beginner
3002 
3003 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
3004 @*/
3005 PetscErrorCode DMPlexGetConeSize(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   PetscAssertPointer(size, 3);
3012   if (mesh->tr) PetscCall(DMPlexTransformGetConeSize(mesh->tr, p, size));
3013   else PetscCall(PetscSectionGetDof(mesh->coneSection, p, size));
3014   PetscFunctionReturn(PETSC_SUCCESS);
3015 }
3016 
3017 /*@
3018   DMPlexSetConeSize - Set the number of in-edges for this point in the DAG
3019 
3020   Not Collective
3021 
3022   Input Parameters:
3023 + dm   - The `DMPLEX`
3024 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3025 - size - The cone size for point `p`
3026 
3027   Level: beginner
3028 
3029   Note:
3030   This should be called after `DMPlexSetChart()`.
3031 
3032 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetConeSize()`, `DMPlexSetChart()`
3033 @*/
3034 PetscErrorCode DMPlexSetConeSize(DM dm, PetscInt p, PetscInt size)
3035 {
3036   DM_Plex *mesh = (DM_Plex *)dm->data;
3037 
3038   PetscFunctionBegin;
3039   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3040   PetscCheck(!mesh->tr, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Cannot call DMPlexSetConeSize() on a mesh with a transform defined.");
3041   PetscCall(PetscSectionSetDof(mesh->coneSection, p, size));
3042   PetscFunctionReturn(PETSC_SUCCESS);
3043 }
3044 
3045 /*@C
3046   DMPlexGetCone - Return the points on the in-edges for this point in the DAG
3047 
3048   Not Collective
3049 
3050   Input Parameters:
3051 + dm - The `DMPLEX`
3052 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3053 
3054   Output Parameter:
3055 . cone - An array of points which are on the in-edges for point `p`
3056 
3057   Level: beginner
3058 
3059   Fortran Notes:
3060   You must also call `DMPlexRestoreCone()` after you finish using the returned array.
3061   `DMPlexRestoreCone()` is not needed/available in C.
3062 
3063 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSize()`, `DMPlexSetCone()`, `DMPlexGetConeTuple()`, `DMPlexSetChart()`, `DMPlexRestoreCone()`
3064 @*/
3065 PetscErrorCode DMPlexGetCone(DM dm, PetscInt p, const PetscInt *cone[])
3066 {
3067   DM_Plex *mesh = (DM_Plex *)dm->data;
3068   PetscInt off;
3069 
3070   PetscFunctionBegin;
3071   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3072   PetscAssertPointer(cone, 3);
3073   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3074   *cone = PetscSafePointerPlusOffset(mesh->cones, off);
3075   PetscFunctionReturn(PETSC_SUCCESS);
3076 }
3077 
3078 /*@
3079   DMPlexGetConeTuple - Return the points on the in-edges of several points in the DAG
3080 
3081   Not Collective
3082 
3083   Input Parameters:
3084 + dm - The `DMPLEX`
3085 - p  - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3086 
3087   Output Parameters:
3088 + pConesSection - `PetscSection` describing the layout of `pCones`
3089 - pCones        - An array of points which are on the in-edges for the point set `p`
3090 
3091   Level: intermediate
3092 
3093 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeRecursive()`, `DMPlexSetChart()`, `PetscSection`, `IS`
3094 @*/
3095 PetscErrorCode DMPlexGetConeTuple(DM dm, IS p, PetscSection *pConesSection, IS *pCones)
3096 {
3097   PetscSection cs, newcs;
3098   PetscInt    *cones;
3099   PetscInt    *newarr = NULL;
3100   PetscInt     n;
3101 
3102   PetscFunctionBegin;
3103   PetscCall(DMPlexGetCones(dm, &cones));
3104   PetscCall(DMPlexGetConeSection(dm, &cs));
3105   PetscCall(PetscSectionExtractDofsFromArray(cs, MPIU_INT, cones, p, &newcs, pCones ? ((void **)&newarr) : NULL));
3106   if (pConesSection) *pConesSection = newcs;
3107   if (pCones) {
3108     PetscCall(PetscSectionGetStorageSize(newcs, &n));
3109     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)p), n, newarr, PETSC_OWN_POINTER, pCones));
3110   }
3111   PetscFunctionReturn(PETSC_SUCCESS);
3112 }
3113 
3114 /*@
3115   DMPlexGetConeRecursiveVertices - Expand each given point into its cone points and do that recursively until we end up just with vertices.
3116 
3117   Not Collective
3118 
3119   Input Parameters:
3120 + dm     - The `DMPLEX`
3121 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3122 
3123   Output Parameter:
3124 . expandedPoints - An array of vertices recursively expanded from input points
3125 
3126   Level: advanced
3127 
3128   Notes:
3129   Like `DMPlexGetConeRecursive()` but returns only the 0-depth `IS` (i.e. vertices only) and no sections.
3130 
3131   There is no corresponding Restore function, just call `ISDestroy()` on the returned `IS` to deallocate.
3132 
3133 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexRestoreConeRecursive()`,
3134           `DMPlexGetDepth()`, `IS`
3135 @*/
3136 PetscErrorCode DMPlexGetConeRecursiveVertices(DM dm, IS points, IS *expandedPoints)
3137 {
3138   IS      *expandedPointsAll;
3139   PetscInt depth;
3140 
3141   PetscFunctionBegin;
3142   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3143   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
3144   PetscAssertPointer(expandedPoints, 3);
3145   PetscCall(DMPlexGetConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
3146   *expandedPoints = expandedPointsAll[0];
3147   PetscCall(PetscObjectReference((PetscObject)expandedPointsAll[0]));
3148   PetscCall(DMPlexRestoreConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
3149   PetscFunctionReturn(PETSC_SUCCESS);
3150 }
3151 
3152 /*@
3153   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).
3154 
3155   Not Collective
3156 
3157   Input Parameters:
3158 + dm     - The `DMPLEX`
3159 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3160 
3161   Output Parameters:
3162 + depth          - (optional) Size of the output arrays, equal to `DMPLEX` depth, returned by `DMPlexGetDepth()`
3163 . expandedPoints - (optional) An array of index sets with recursively expanded cones
3164 - sections       - (optional) An array of sections which describe mappings from points to their cone points
3165 
3166   Level: advanced
3167 
3168   Notes:
3169   Like `DMPlexGetConeTuple()` but recursive.
3170 
3171   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.
3172   For example, for d=0 it contains only vertices, for d=1 it can contain vertices and edges, etc.
3173 
3174   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\:
3175   (1) DAG points in `expandedPoints`[d+1] with `depth` d+1 to their cone points in `expandedPoints`[d];
3176   (2) DAG points in `expandedPoints`[d+1] with `depth` in [0,d] to the same points in `expandedPoints`[d].
3177 
3178 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexRestoreConeRecursive()`, `DMPlexGetConeRecursiveVertices()`,
3179           `DMPlexGetDepth()`, `PetscSection`, `IS`
3180 @*/
3181 PetscErrorCode DMPlexGetConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
3182 {
3183   const PetscInt *arr0 = NULL, *cone = NULL;
3184   PetscInt       *arr = NULL, *newarr = NULL;
3185   PetscInt        d, depth_, i, n, newn, cn, co, start, end;
3186   IS             *expandedPoints_;
3187   PetscSection   *sections_;
3188 
3189   PetscFunctionBegin;
3190   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3191   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
3192   if (depth) PetscAssertPointer(depth, 3);
3193   if (expandedPoints) PetscAssertPointer(expandedPoints, 4);
3194   if (sections) PetscAssertPointer(sections, 5);
3195   PetscCall(ISGetLocalSize(points, &n));
3196   PetscCall(ISGetIndices(points, &arr0));
3197   PetscCall(DMPlexGetDepth(dm, &depth_));
3198   PetscCall(PetscCalloc1(depth_, &expandedPoints_));
3199   PetscCall(PetscCalloc1(depth_, &sections_));
3200   arr = (PetscInt *)arr0; /* this is ok because first generation of arr is not modified */
3201   for (d = depth_ - 1; d >= 0; d--) {
3202     PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &sections_[d]));
3203     PetscCall(PetscSectionSetChart(sections_[d], 0, n));
3204     for (i = 0; i < n; i++) {
3205       PetscCall(DMPlexGetDepthStratum(dm, d + 1, &start, &end));
3206       if (arr[i] >= start && arr[i] < end) {
3207         PetscCall(DMPlexGetConeSize(dm, arr[i], &cn));
3208         PetscCall(PetscSectionSetDof(sections_[d], i, cn));
3209       } else {
3210         PetscCall(PetscSectionSetDof(sections_[d], i, 1));
3211       }
3212     }
3213     PetscCall(PetscSectionSetUp(sections_[d]));
3214     PetscCall(PetscSectionGetStorageSize(sections_[d], &newn));
3215     PetscCall(PetscMalloc1(newn, &newarr));
3216     for (i = 0; i < n; i++) {
3217       PetscCall(PetscSectionGetDof(sections_[d], i, &cn));
3218       PetscCall(PetscSectionGetOffset(sections_[d], i, &co));
3219       if (cn > 1) {
3220         PetscCall(DMPlexGetCone(dm, arr[i], &cone));
3221         PetscCall(PetscMemcpy(&newarr[co], cone, cn * sizeof(PetscInt)));
3222       } else {
3223         newarr[co] = arr[i];
3224       }
3225     }
3226     PetscCall(ISCreateGeneral(PETSC_COMM_SELF, newn, newarr, PETSC_OWN_POINTER, &expandedPoints_[d]));
3227     arr = newarr;
3228     n   = newn;
3229   }
3230   PetscCall(ISRestoreIndices(points, &arr0));
3231   *depth = depth_;
3232   if (expandedPoints) *expandedPoints = expandedPoints_;
3233   else {
3234     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&expandedPoints_[d]));
3235     PetscCall(PetscFree(expandedPoints_));
3236   }
3237   if (sections) *sections = sections_;
3238   else {
3239     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&sections_[d]));
3240     PetscCall(PetscFree(sections_));
3241   }
3242   PetscFunctionReturn(PETSC_SUCCESS);
3243 }
3244 
3245 /*@
3246   DMPlexRestoreConeRecursive - Deallocates arrays created by `DMPlexGetConeRecursive()`
3247 
3248   Not Collective
3249 
3250   Input Parameters:
3251 + dm     - The `DMPLEX`
3252 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3253 
3254   Output Parameters:
3255 + depth          - (optional) Size of the output arrays, equal to `DMPLEX` depth, returned by `DMPlexGetDepth()`
3256 . expandedPoints - (optional) An array of recursively expanded cones
3257 - sections       - (optional) An array of sections which describe mappings from points to their cone points
3258 
3259   Level: advanced
3260 
3261   Note:
3262   See `DMPlexGetConeRecursive()`
3263 
3264 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexGetConeRecursiveVertices()`,
3265           `DMPlexGetDepth()`, `IS`, `PetscSection`
3266 @*/
3267 PetscErrorCode DMPlexRestoreConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
3268 {
3269   PetscInt d, depth_;
3270 
3271   PetscFunctionBegin;
3272   PetscCall(DMPlexGetDepth(dm, &depth_));
3273   PetscCheck(!depth || *depth == depth_, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "depth changed since last call to DMPlexGetConeRecursive");
3274   if (depth) *depth = 0;
3275   if (expandedPoints) {
3276     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&((*expandedPoints)[d])));
3277     PetscCall(PetscFree(*expandedPoints));
3278   }
3279   if (sections) {
3280     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&((*sections)[d])));
3281     PetscCall(PetscFree(*sections));
3282   }
3283   PetscFunctionReturn(PETSC_SUCCESS);
3284 }
3285 
3286 /*@
3287   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
3288 
3289   Not Collective
3290 
3291   Input Parameters:
3292 + dm   - The `DMPLEX`
3293 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3294 - cone - An array of points which are on the in-edges for point `p`
3295 
3296   Level: beginner
3297 
3298   Note:
3299   This should be called after all calls to `DMPlexSetConeSize()` and `DMSetUp()`.
3300 
3301 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`, `DMPlexSetSupport()`, `DMPlexSetSupportSize()`
3302 @*/
3303 PetscErrorCode DMPlexSetCone(DM dm, PetscInt p, const PetscInt cone[])
3304 {
3305   DM_Plex *mesh = (DM_Plex *)dm->data;
3306   PetscInt dof, off, c;
3307 
3308   PetscFunctionBegin;
3309   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3310   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3311   if (dof) PetscAssertPointer(cone, 3);
3312   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3313   if (PetscDefined(USE_DEBUG)) {
3314     PetscInt pStart, pEnd;
3315     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3316     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);
3317     for (c = 0; c < dof; ++c) {
3318       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);
3319       mesh->cones[off + c] = cone[c];
3320     }
3321   } else {
3322     for (c = 0; c < dof; ++c) mesh->cones[off + c] = cone[c];
3323   }
3324   PetscFunctionReturn(PETSC_SUCCESS);
3325 }
3326 
3327 /*@C
3328   DMPlexGetConeOrientation - Return the orientations on the in-edges for this point in the DAG
3329 
3330   Not Collective
3331 
3332   Input Parameters:
3333 + dm - The `DMPLEX`
3334 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3335 
3336   Output Parameter:
3337 . coneOrientation - An array of orientations which are on the in-edges for point `p`. An orientation is an
3338                     integer giving the prescription for cone traversal.
3339 
3340   Level: beginner
3341 
3342   Note:
3343   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3344   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3345   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3346   with the identity.
3347 
3348   Fortran Notes:
3349   You must also call `DMPlexRestoreConeOrientation()` after you finish using the returned array.
3350   `DMPlexRestoreConeOrientation()` is not needed/available in C.
3351 
3352 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPolytopeTypeComposeOrientation()`, `DMPolytopeTypeComposeOrientationInv()`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetCone()`, `DMPlexSetChart()`
3353 @*/
3354 PetscErrorCode DMPlexGetConeOrientation(DM dm, PetscInt p, const PetscInt *coneOrientation[])
3355 {
3356   DM_Plex *mesh = (DM_Plex *)dm->data;
3357   PetscInt off;
3358 
3359   PetscFunctionBegin;
3360   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3361   if (PetscDefined(USE_DEBUG)) {
3362     PetscInt dof;
3363     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3364     if (dof) PetscAssertPointer(coneOrientation, 3);
3365   }
3366   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3367 
3368   *coneOrientation = &mesh->coneOrientations[off];
3369   PetscFunctionReturn(PETSC_SUCCESS);
3370 }
3371 
3372 /*@
3373   DMPlexSetConeOrientation - Set the orientations on the in-edges for this point in the DAG
3374 
3375   Not Collective
3376 
3377   Input Parameters:
3378 + dm              - The `DMPLEX`
3379 . p               - The point, which must lie in the chart set with `DMPlexSetChart()`
3380 - coneOrientation - An array of orientations
3381 
3382   Level: beginner
3383 
3384   Notes:
3385   This should be called after all calls to `DMPlexSetConeSize()` and `DMSetUp()`.
3386 
3387   The meaning of coneOrientation is detailed in `DMPlexGetConeOrientation()`.
3388 
3389 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetConeOrientation()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3390 @*/
3391 PetscErrorCode DMPlexSetConeOrientation(DM dm, PetscInt p, const PetscInt coneOrientation[])
3392 {
3393   DM_Plex *mesh = (DM_Plex *)dm->data;
3394   PetscInt pStart, pEnd;
3395   PetscInt dof, off, c;
3396 
3397   PetscFunctionBegin;
3398   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3399   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3400   if (dof) PetscAssertPointer(coneOrientation, 3);
3401   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3402   if (PetscDefined(USE_DEBUG)) {
3403     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3404     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);
3405     for (c = 0; c < dof; ++c) {
3406       PetscInt cdof, o = coneOrientation[c];
3407 
3408       PetscCall(PetscSectionGetDof(mesh->coneSection, mesh->cones[off + c], &cdof));
3409       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);
3410       mesh->coneOrientations[off + c] = o;
3411     }
3412   } else {
3413     for (c = 0; c < dof; ++c) mesh->coneOrientations[off + c] = coneOrientation[c];
3414   }
3415   PetscFunctionReturn(PETSC_SUCCESS);
3416 }
3417 
3418 /*@
3419   DMPlexInsertCone - Insert a point into the in-edges for the point p in the DAG
3420 
3421   Not Collective
3422 
3423   Input Parameters:
3424 + dm        - The `DMPLEX`
3425 . p         - The point, which must lie in the chart set with `DMPlexSetChart()`
3426 . conePos   - The local index in the cone where the point should be put
3427 - conePoint - The mesh point to insert
3428 
3429   Level: beginner
3430 
3431 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3432 @*/
3433 PetscErrorCode DMPlexInsertCone(DM dm, PetscInt p, PetscInt conePos, PetscInt conePoint)
3434 {
3435   DM_Plex *mesh = (DM_Plex *)dm->data;
3436   PetscInt pStart, pEnd;
3437   PetscInt dof, off;
3438 
3439   PetscFunctionBegin;
3440   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3441   if (PetscDefined(USE_DEBUG)) {
3442     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3443     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);
3444     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);
3445     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3446     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);
3447   }
3448   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3449   mesh->cones[off + conePos] = conePoint;
3450   PetscFunctionReturn(PETSC_SUCCESS);
3451 }
3452 
3453 /*@
3454   DMPlexInsertConeOrientation - Insert a point orientation for the in-edge for the point p in the DAG
3455 
3456   Not Collective
3457 
3458   Input Parameters:
3459 + dm              - The `DMPLEX`
3460 . p               - The point, which must lie in the chart set with `DMPlexSetChart()`
3461 . conePos         - The local index in the cone where the point should be put
3462 - coneOrientation - The point orientation to insert
3463 
3464   Level: beginner
3465 
3466   Note:
3467   The meaning of coneOrientation values is detailed in `DMPlexGetConeOrientation()`.
3468 
3469 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3470 @*/
3471 PetscErrorCode DMPlexInsertConeOrientation(DM dm, PetscInt p, PetscInt conePos, PetscInt coneOrientation)
3472 {
3473   DM_Plex *mesh = (DM_Plex *)dm->data;
3474   PetscInt pStart, pEnd;
3475   PetscInt dof, off;
3476 
3477   PetscFunctionBegin;
3478   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3479   if (PetscDefined(USE_DEBUG)) {
3480     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3481     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);
3482     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3483     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);
3484   }
3485   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3486   mesh->coneOrientations[off + conePos] = coneOrientation;
3487   PetscFunctionReturn(PETSC_SUCCESS);
3488 }
3489 
3490 /*@C
3491   DMPlexGetOrientedCone - Return the points and orientations on the in-edges for this point in the DAG
3492 
3493   Not collective
3494 
3495   Input Parameters:
3496 + dm - The DMPlex
3497 - p  - The point, which must lie in the chart set with DMPlexSetChart()
3498 
3499   Output Parameters:
3500 + cone - An array of points which are on the in-edges for point `p`
3501 - ornt - An array of orientations which are on the in-edges for point `p`. An orientation is an
3502         integer giving the prescription for cone traversal.
3503 
3504   Level: beginner
3505 
3506   Notes:
3507   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3508   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3509   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3510   with the identity.
3511 
3512   Fortran Notes:
3513   You must also call `DMPlexRestoreCone()` after you finish using the returned array.
3514   `DMPlexRestoreCone()` is not needed/available in C.
3515 
3516 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreOrientedCone()`, `DMPlexGetConeSize()`, `DMPlexGetCone()`, `DMPlexGetChart()`
3517 @*/
3518 PetscErrorCode DMPlexGetOrientedCone(DM dm, PetscInt p, const PetscInt *cone[], const PetscInt *ornt[])
3519 {
3520   DM_Plex *mesh = (DM_Plex *)dm->data;
3521 
3522   PetscFunctionBegin;
3523   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3524   if (mesh->tr) {
3525     PetscCall(DMPlexTransformGetCone(mesh->tr, p, cone, ornt));
3526   } else {
3527     PetscInt off;
3528     if (PetscDefined(USE_DEBUG)) {
3529       PetscInt dof;
3530       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3531       if (dof) {
3532         if (cone) PetscAssertPointer(cone, 3);
3533         if (ornt) PetscAssertPointer(ornt, 4);
3534       }
3535     }
3536     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3537     if (cone) *cone = PetscSafePointerPlusOffset(mesh->cones, off);
3538     if (ornt) *ornt = PetscSafePointerPlusOffset(mesh->coneOrientations, off);
3539   }
3540   PetscFunctionReturn(PETSC_SUCCESS);
3541 }
3542 
3543 /*@C
3544   DMPlexRestoreOrientedCone - Restore the points and orientations on the in-edges for this point in the DAG
3545 
3546   Not Collective
3547 
3548   Input Parameters:
3549 + dm   - The DMPlex
3550 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3551 . cone - An array of points which are on the in-edges for point p
3552 - ornt - An array of orientations which are on the in-edges for point `p`. An orientation is an
3553         integer giving the prescription for cone traversal.
3554 
3555   Level: beginner
3556 
3557   Notes:
3558   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3559   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3560   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3561   with the identity.
3562 
3563   Fortran Notes:
3564   You must also call `DMPlexRestoreCone()` after you finish using the returned array.
3565   `DMPlexRestoreCone()` is not needed/available in C.
3566 
3567 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetOrientedCone()`, `DMPlexGetConeSize()`, `DMPlexGetCone()`, `DMPlexGetChart()`
3568 @*/
3569 PetscErrorCode DMPlexRestoreOrientedCone(DM dm, PetscInt p, const PetscInt *cone[], const PetscInt *ornt[])
3570 {
3571   DM_Plex *mesh = (DM_Plex *)dm->data;
3572 
3573   PetscFunctionBegin;
3574   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3575   if (mesh->tr) PetscCall(DMPlexTransformRestoreCone(mesh->tr, p, cone, ornt));
3576   PetscFunctionReturn(PETSC_SUCCESS);
3577 }
3578 
3579 /*@
3580   DMPlexGetSupportSize - Return the number of out-edges for this point in the DAG
3581 
3582   Not Collective
3583 
3584   Input Parameters:
3585 + dm - The `DMPLEX`
3586 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3587 
3588   Output Parameter:
3589 . size - The support size for point `p`
3590 
3591   Level: beginner
3592 
3593 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`, `DMPlexGetConeSize()`
3594 @*/
3595 PetscErrorCode DMPlexGetSupportSize(DM dm, PetscInt p, PetscInt *size)
3596 {
3597   DM_Plex *mesh = (DM_Plex *)dm->data;
3598 
3599   PetscFunctionBegin;
3600   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3601   PetscAssertPointer(size, 3);
3602   PetscCall(PetscSectionGetDof(mesh->supportSection, p, size));
3603   PetscFunctionReturn(PETSC_SUCCESS);
3604 }
3605 
3606 /*@
3607   DMPlexSetSupportSize - Set the number of out-edges for this point in the DAG
3608 
3609   Not Collective
3610 
3611   Input Parameters:
3612 + dm   - The `DMPLEX`
3613 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3614 - size - The support size for point `p`
3615 
3616   Level: beginner
3617 
3618   Note:
3619   This should be called after `DMPlexSetChart()`.
3620 
3621 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetSupportSize()`, `DMPlexSetChart()`
3622 @*/
3623 PetscErrorCode DMPlexSetSupportSize(DM dm, PetscInt p, PetscInt size)
3624 {
3625   DM_Plex *mesh = (DM_Plex *)dm->data;
3626 
3627   PetscFunctionBegin;
3628   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3629   PetscCall(PetscSectionSetDof(mesh->supportSection, p, size));
3630   PetscFunctionReturn(PETSC_SUCCESS);
3631 }
3632 
3633 /*@C
3634   DMPlexGetSupport - Return the points on the out-edges for this point in the DAG
3635 
3636   Not Collective
3637 
3638   Input Parameters:
3639 + dm - The `DMPLEX`
3640 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3641 
3642   Output Parameter:
3643 . support - An array of points which are on the out-edges for point `p`
3644 
3645   Level: beginner
3646 
3647   Fortran Notes:
3648   You must also call `DMPlexRestoreSupport()` after you finish using the returned array.
3649   `DMPlexRestoreSupport()` is not needed/available in C.
3650 
3651 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSupportSize()`, `DMPlexSetSupport()`, `DMPlexGetCone()`, `DMPlexSetChart()`
3652 @*/
3653 PetscErrorCode DMPlexGetSupport(DM dm, PetscInt p, const PetscInt *support[])
3654 {
3655   DM_Plex *mesh = (DM_Plex *)dm->data;
3656   PetscInt off;
3657 
3658   PetscFunctionBegin;
3659   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3660   PetscAssertPointer(support, 3);
3661   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3662   *support = PetscSafePointerPlusOffset(mesh->supports, off);
3663   PetscFunctionReturn(PETSC_SUCCESS);
3664 }
3665 
3666 /*@
3667   DMPlexSetSupport - Set the points on the out-edges for this point in the DAG, that is the list of points that this point covers
3668 
3669   Not Collective
3670 
3671   Input Parameters:
3672 + dm      - The `DMPLEX`
3673 . p       - The point, which must lie in the chart set with `DMPlexSetChart()`
3674 - support - An array of points which are on the out-edges for point `p`
3675 
3676   Level: beginner
3677 
3678   Note:
3679   This should be called after all calls to `DMPlexSetSupportSize()` and `DMSetUp()`.
3680 
3681 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetCone()`, `DMPlexSetConeSize()`, `DMPlexCreate()`, `DMPlexGetSupport()`, `DMPlexSetChart()`, `DMPlexSetSupportSize()`, `DMSetUp()`
3682 @*/
3683 PetscErrorCode DMPlexSetSupport(DM dm, PetscInt p, const PetscInt support[])
3684 {
3685   DM_Plex *mesh = (DM_Plex *)dm->data;
3686   PetscInt pStart, pEnd;
3687   PetscInt dof, off, c;
3688 
3689   PetscFunctionBegin;
3690   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3691   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3692   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3693   if (dof) PetscAssertPointer(support, 3);
3694   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3695   PetscCheck(!(p < pStart) && !(p >= pEnd), PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Mesh point %" PetscInt_FMT " is not in the valid range [%" PetscInt_FMT ", %" PetscInt_FMT ")", p, pStart, pEnd);
3696   for (c = 0; c < dof; ++c) {
3697     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);
3698     mesh->supports[off + c] = support[c];
3699   }
3700   PetscFunctionReturn(PETSC_SUCCESS);
3701 }
3702 
3703 /*@
3704   DMPlexInsertSupport - Insert a point into the out-edges for the point p in the DAG
3705 
3706   Not Collective
3707 
3708   Input Parameters:
3709 + dm           - The `DMPLEX`
3710 . p            - The point, which must lie in the chart set with `DMPlexSetChart()`
3711 . supportPos   - The local index in the cone where the point should be put
3712 - supportPoint - The mesh point to insert
3713 
3714   Level: beginner
3715 
3716 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3717 @*/
3718 PetscErrorCode DMPlexInsertSupport(DM dm, PetscInt p, PetscInt supportPos, PetscInt supportPoint)
3719 {
3720   DM_Plex *mesh = (DM_Plex *)dm->data;
3721   PetscInt pStart, pEnd;
3722   PetscInt dof, off;
3723 
3724   PetscFunctionBegin;
3725   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3726   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3727   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3728   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3729   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);
3730   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);
3731   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);
3732   mesh->supports[off + supportPos] = supportPoint;
3733   PetscFunctionReturn(PETSC_SUCCESS);
3734 }
3735 
3736 /* Converts an orientation o in the current numbering to the previous scheme used in Plex */
3737 PetscInt DMPolytopeConvertNewOrientation_Internal(DMPolytopeType ct, PetscInt o)
3738 {
3739   switch (ct) {
3740   case DM_POLYTOPE_SEGMENT:
3741     if (o == -1) return -2;
3742     break;
3743   case DM_POLYTOPE_TRIANGLE:
3744     if (o == -3) return -1;
3745     if (o == -2) return -3;
3746     if (o == -1) return -2;
3747     break;
3748   case DM_POLYTOPE_QUADRILATERAL:
3749     if (o == -4) return -2;
3750     if (o == -3) return -1;
3751     if (o == -2) return -4;
3752     if (o == -1) return -3;
3753     break;
3754   default:
3755     return o;
3756   }
3757   return o;
3758 }
3759 
3760 /* Converts an orientation o in the previous scheme used in Plex to the current numbering */
3761 PetscInt DMPolytopeConvertOldOrientation_Internal(DMPolytopeType ct, PetscInt o)
3762 {
3763   switch (ct) {
3764   case DM_POLYTOPE_SEGMENT:
3765     if ((o == -2) || (o == 1)) return -1;
3766     if (o == -1) return 0;
3767     break;
3768   case DM_POLYTOPE_TRIANGLE:
3769     if (o == -3) return -2;
3770     if (o == -2) return -1;
3771     if (o == -1) return -3;
3772     break;
3773   case DM_POLYTOPE_QUADRILATERAL:
3774     if (o == -4) return -2;
3775     if (o == -3) return -1;
3776     if (o == -2) return -4;
3777     if (o == -1) return -3;
3778     break;
3779   default:
3780     return o;
3781   }
3782   return o;
3783 }
3784 
3785 /* Takes in a mesh whose orientations are in the previous scheme and converts them all to the current numbering */
3786 PetscErrorCode DMPlexConvertOldOrientations_Internal(DM dm)
3787 {
3788   PetscInt pStart, pEnd, p;
3789 
3790   PetscFunctionBegin;
3791   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3792   for (p = pStart; p < pEnd; ++p) {
3793     const PetscInt *cone, *ornt;
3794     PetscInt        coneSize, c;
3795 
3796     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
3797     PetscCall(DMPlexGetCone(dm, p, &cone));
3798     PetscCall(DMPlexGetConeOrientation(dm, p, &ornt));
3799     for (c = 0; c < coneSize; ++c) {
3800       DMPolytopeType ct;
3801       const PetscInt o = ornt[c];
3802 
3803       PetscCall(DMPlexGetCellType(dm, cone[c], &ct));
3804       switch (ct) {
3805       case DM_POLYTOPE_SEGMENT:
3806         if ((o == -2) || (o == 1)) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3807         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, 0));
3808         break;
3809       case DM_POLYTOPE_TRIANGLE:
3810         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3811         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3812         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3813         break;
3814       case DM_POLYTOPE_QUADRILATERAL:
3815         if (o == -4) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3816         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3817         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -4));
3818         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3819         break;
3820       default:
3821         break;
3822       }
3823     }
3824   }
3825   PetscFunctionReturn(PETSC_SUCCESS);
3826 }
3827 
3828 static inline PetscErrorCode DMPlexGetTransitiveClosure_Hot_Private(DM dm, PetscInt p, PetscBool useCone, PetscInt *size, const PetscInt *arr[], const PetscInt *ornt[])
3829 {
3830   DM_Plex *mesh = (DM_Plex *)dm->data;
3831 
3832   PetscFunctionBeginHot;
3833   if (PetscDefined(USE_DEBUG) || mesh->tr) {
3834     if (useCone) {
3835       PetscCall(DMPlexGetConeSize(dm, p, size));
3836       PetscCall(DMPlexGetOrientedCone(dm, p, arr, ornt));
3837     } else {
3838       PetscCall(DMPlexGetSupportSize(dm, p, size));
3839       PetscCall(DMPlexGetSupport(dm, p, arr));
3840     }
3841   } else {
3842     if (useCone) {
3843       const PetscSection s   = mesh->coneSection;
3844       const PetscInt     ps  = p - s->pStart;
3845       const PetscInt     off = s->atlasOff[ps];
3846 
3847       *size = s->atlasDof[ps];
3848       *arr  = mesh->cones + off;
3849       *ornt = mesh->coneOrientations + off;
3850     } else {
3851       const PetscSection s   = mesh->supportSection;
3852       const PetscInt     ps  = p - s->pStart;
3853       const PetscInt     off = s->atlasOff[ps];
3854 
3855       *size = s->atlasDof[ps];
3856       *arr  = mesh->supports + off;
3857     }
3858   }
3859   PetscFunctionReturn(PETSC_SUCCESS);
3860 }
3861 
3862 static inline PetscErrorCode DMPlexRestoreTransitiveClosure_Hot_Private(DM dm, PetscInt p, PetscBool useCone, PetscInt *size, const PetscInt *arr[], const PetscInt *ornt[])
3863 {
3864   DM_Plex *mesh = (DM_Plex *)dm->data;
3865 
3866   PetscFunctionBeginHot;
3867   if (PetscDefined(USE_DEBUG) || mesh->tr) {
3868     if (useCone) PetscCall(DMPlexRestoreOrientedCone(dm, p, arr, ornt));
3869   }
3870   PetscFunctionReturn(PETSC_SUCCESS);
3871 }
3872 
3873 static PetscErrorCode DMPlexGetTransitiveClosure_Depth1_Private(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3874 {
3875   DMPolytopeType  ct = DM_POLYTOPE_UNKNOWN;
3876   PetscInt       *closure;
3877   const PetscInt *tmp = NULL, *tmpO = NULL;
3878   PetscInt        off = 0, tmpSize, t;
3879 
3880   PetscFunctionBeginHot;
3881   if (ornt) {
3882     PetscCall(DMPlexGetCellType(dm, p, &ct));
3883     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;
3884   }
3885   if (*points) {
3886     closure = *points;
3887   } else {
3888     PetscInt maxConeSize, maxSupportSize;
3889     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3890     PetscCall(DMGetWorkArray(dm, 2 * (PetscMax(maxConeSize, maxSupportSize) + 1), MPIU_INT, &closure));
3891   }
3892   PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, p, useCone, &tmpSize, &tmp, &tmpO));
3893   if (ct == DM_POLYTOPE_UNKNOWN) {
3894     closure[off++] = p;
3895     closure[off++] = 0;
3896     for (t = 0; t < tmpSize; ++t) {
3897       closure[off++] = tmp[t];
3898       closure[off++] = tmpO ? tmpO[t] : 0;
3899     }
3900   } else {
3901     const PetscInt *arr = DMPolytopeTypeGetArrangement(ct, ornt);
3902 
3903     /* We assume that cells with a valid type have faces with a valid type */
3904     closure[off++] = p;
3905     closure[off++] = ornt;
3906     for (t = 0; t < tmpSize; ++t) {
3907       DMPolytopeType ft;
3908 
3909       PetscCall(DMPlexGetCellType(dm, tmp[t], &ft));
3910       closure[off++] = tmp[arr[t]];
3911       closure[off++] = tmpO ? DMPolytopeTypeComposeOrientation(ft, ornt, tmpO[t]) : 0;
3912     }
3913   }
3914   PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, p, useCone, &tmpSize, &tmp, &tmpO));
3915   if (numPoints) *numPoints = tmpSize + 1;
3916   if (points) *points = closure;
3917   PetscFunctionReturn(PETSC_SUCCESS);
3918 }
3919 
3920 /* We need a special tensor version because we want to allow duplicate points in the endcaps for hybrid cells */
3921 static PetscErrorCode DMPlexTransitiveClosure_Tensor_Internal(DM dm, PetscInt point, DMPolytopeType ct, PetscInt o, PetscBool useCone, PetscInt *numPoints, PetscInt **points)
3922 {
3923   const PetscInt *arr = DMPolytopeTypeGetArrangement(ct, o);
3924   const PetscInt *cone, *ornt;
3925   PetscInt       *pts, *closure = NULL;
3926   DMPolytopeType  ft;
3927   PetscInt        maxConeSize, maxSupportSize, coneSeries, supportSeries, maxSize;
3928   PetscInt        dim, coneSize, c, d, clSize, cl;
3929 
3930   PetscFunctionBeginHot;
3931   PetscCall(DMGetDimension(dm, &dim));
3932   PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, point, PETSC_TRUE, &coneSize, &cone, &ornt));
3933   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3934   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, dim + 1) - 1) / (maxConeSize - 1)) : dim + 1;
3935   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, dim + 1) - 1) / (maxSupportSize - 1)) : dim + 1;
3936   maxSize       = PetscMax(coneSeries, supportSeries);
3937   if (*points) {
3938     pts = *points;
3939   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &pts));
3940   c        = 0;
3941   pts[c++] = point;
3942   pts[c++] = o;
3943   PetscCall(DMPlexGetCellType(dm, cone[arr[0 * 2 + 0]], &ft));
3944   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[0 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[0 * 2 + 1], ornt[0]), useCone, &clSize, &closure));
3945   for (cl = 0; cl < clSize * 2; cl += 2) {
3946     pts[c++] = closure[cl];
3947     pts[c++] = closure[cl + 1];
3948   }
3949   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[1 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[1 * 2 + 1], ornt[1]), useCone, &clSize, &closure));
3950   for (cl = 0; cl < clSize * 2; cl += 2) {
3951     pts[c++] = closure[cl];
3952     pts[c++] = closure[cl + 1];
3953   }
3954   PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[0], useCone, &clSize, &closure));
3955   for (d = 2; d < coneSize; ++d) {
3956     PetscCall(DMPlexGetCellType(dm, cone[arr[d * 2 + 0]], &ft));
3957     pts[c++] = cone[arr[d * 2 + 0]];
3958     pts[c++] = DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]);
3959   }
3960   PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, point, PETSC_TRUE, &coneSize, &cone, &ornt));
3961   if (dim >= 3) {
3962     for (d = 2; d < coneSize; ++d) {
3963       const PetscInt  fpoint = cone[arr[d * 2 + 0]];
3964       const PetscInt *fcone, *fornt;
3965       PetscInt        fconeSize, fc, i;
3966 
3967       PetscCall(DMPlexGetCellType(dm, fpoint, &ft));
3968       const PetscInt *farr = DMPolytopeTypeGetArrangement(ft, DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]));
3969       PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, fpoint, PETSC_TRUE, &fconeSize, &fcone, &fornt));
3970       for (fc = 0; fc < fconeSize; ++fc) {
3971         const PetscInt cp = fcone[farr[fc * 2 + 0]];
3972         const PetscInt co = farr[fc * 2 + 1];
3973 
3974         for (i = 0; i < c; i += 2)
3975           if (pts[i] == cp) break;
3976         if (i == c) {
3977           PetscCall(DMPlexGetCellType(dm, cp, &ft));
3978           pts[c++] = cp;
3979           pts[c++] = DMPolytopeTypeComposeOrientation(ft, co, fornt[farr[fc * 2 + 0]]);
3980         }
3981       }
3982       PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, fpoint, PETSC_TRUE, &fconeSize, &fcone, &fornt));
3983     }
3984   }
3985   *numPoints = c / 2;
3986   *points    = pts;
3987   PetscFunctionReturn(PETSC_SUCCESS);
3988 }
3989 
3990 PetscErrorCode DMPlexGetTransitiveClosure_Internal(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3991 {
3992   DMPolytopeType ct;
3993   PetscInt      *closure, *fifo;
3994   PetscInt       closureSize = 0, fifoStart = 0, fifoSize = 0;
3995   PetscInt       maxConeSize, maxSupportSize, coneSeries, supportSeries;
3996   PetscInt       depth, maxSize;
3997 
3998   PetscFunctionBeginHot;
3999   PetscCall(DMPlexGetDepth(dm, &depth));
4000   if (depth == 1) {
4001     PetscCall(DMPlexGetTransitiveClosure_Depth1_Private(dm, p, ornt, useCone, numPoints, points));
4002     PetscFunctionReturn(PETSC_SUCCESS);
4003   }
4004   PetscCall(DMPlexGetCellType(dm, p, &ct));
4005   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;
4006   if (DMPolytopeTypeIsHybrid(ct) && ct != DM_POLYTOPE_POINT_PRISM_TENSOR) {
4007     PetscCall(DMPlexTransitiveClosure_Tensor_Internal(dm, p, ct, ornt, useCone, numPoints, points));
4008     PetscFunctionReturn(PETSC_SUCCESS);
4009   }
4010   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
4011   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, depth + 1) - 1) / (maxConeSize - 1)) : depth + 1;
4012   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, depth + 1) - 1) / (maxSupportSize - 1)) : depth + 1;
4013   maxSize       = PetscMax(coneSeries, supportSeries);
4014   PetscCall(DMGetWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
4015   if (*points) {
4016     closure = *points;
4017   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &closure));
4018   closure[closureSize++] = p;
4019   closure[closureSize++] = ornt;
4020   fifo[fifoSize++]       = p;
4021   fifo[fifoSize++]       = ornt;
4022   fifo[fifoSize++]       = ct;
4023   /* Should kick out early when depth is reached, rather than checking all vertices for empty cones */
4024   while (fifoSize - fifoStart) {
4025     const PetscInt       q    = fifo[fifoStart++];
4026     const PetscInt       o    = fifo[fifoStart++];
4027     const DMPolytopeType qt   = (DMPolytopeType)fifo[fifoStart++];
4028     const PetscInt      *qarr = DMPolytopeTypeGetArrangement(qt, o);
4029     const PetscInt      *tmp, *tmpO = NULL;
4030     PetscInt             tmpSize, t;
4031 
4032     if (PetscDefined(USE_DEBUG)) {
4033       PetscInt nO = DMPolytopeTypeGetNumArrangements(qt) / 2;
4034       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);
4035     }
4036     PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, q, useCone, &tmpSize, &tmp, &tmpO));
4037     for (t = 0; t < tmpSize; ++t) {
4038       const PetscInt ip = useCone && qarr ? qarr[t * 2] : t;
4039       const PetscInt io = useCone && qarr ? qarr[t * 2 + 1] : 0;
4040       const PetscInt cp = tmp[ip];
4041       PetscCall(DMPlexGetCellType(dm, cp, &ct));
4042       const PetscInt co = tmpO ? DMPolytopeTypeComposeOrientation(ct, io, tmpO[ip]) : 0;
4043       PetscInt       c;
4044 
4045       /* Check for duplicate */
4046       for (c = 0; c < closureSize; c += 2) {
4047         if (closure[c] == cp) break;
4048       }
4049       if (c == closureSize) {
4050         closure[closureSize++] = cp;
4051         closure[closureSize++] = co;
4052         fifo[fifoSize++]       = cp;
4053         fifo[fifoSize++]       = co;
4054         fifo[fifoSize++]       = ct;
4055       }
4056     }
4057     PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, q, useCone, &tmpSize, &tmp, &tmpO));
4058   }
4059   PetscCall(DMRestoreWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
4060   if (numPoints) *numPoints = closureSize / 2;
4061   if (points) *points = closure;
4062   PetscFunctionReturn(PETSC_SUCCESS);
4063 }
4064 
4065 /*@C
4066   DMPlexGetTransitiveClosure - Return the points on the transitive closure of the in-edges or out-edges for this point in the DAG
4067 
4068   Not Collective
4069 
4070   Input Parameters:
4071 + dm      - The `DMPLEX`
4072 . p       - The mesh point
4073 - useCone - `PETSC_TRUE` for the closure, otherwise return the star
4074 
4075   Input/Output Parameter:
4076 . points - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...];
4077            if `NULL` on input, internal storage will be returned, otherwise the provided array is used
4078 
4079   Output Parameter:
4080 . numPoints - The number of points in the closure, so points[] is of size 2*`numPoints`
4081 
4082   Level: beginner
4083 
4084   Note:
4085   If using internal storage (points is `NULL` on input), each call overwrites the last output.
4086 
4087   Fortran Notes:
4088   The `numPoints` argument is not present in the Fortran binding since it is internal to the array.
4089 
4090 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
4091 @*/
4092 PetscErrorCode DMPlexGetTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
4093 {
4094   PetscFunctionBeginHot;
4095   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4096   if (numPoints) PetscAssertPointer(numPoints, 4);
4097   if (points) PetscAssertPointer(points, 5);
4098   if (PetscDefined(USE_DEBUG)) {
4099     PetscInt pStart, pEnd;
4100     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4101     PetscCheck(p >= pStart && p < pEnd, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Point %" PetscInt_FMT " is not in [%" PetscInt_FMT ", %" PetscInt_FMT ")", p, pStart, pEnd);
4102   }
4103   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, p, 0, useCone, numPoints, points));
4104   PetscFunctionReturn(PETSC_SUCCESS);
4105 }
4106 
4107 /*@C
4108   DMPlexRestoreTransitiveClosure - Restore the array of points on the transitive closure of the in-edges or out-edges for this point in the DAG
4109 
4110   Not Collective
4111 
4112   Input Parameters:
4113 + dm        - The `DMPLEX`
4114 . p         - The mesh point
4115 . useCone   - `PETSC_TRUE` for the closure, otherwise return the star
4116 . numPoints - The number of points in the closure, so points[] is of size 2*`numPoints`
4117 - points    - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...]
4118 
4119   Level: beginner
4120 
4121   Note:
4122   If not using internal storage (points is not `NULL` on input), this call is unnecessary
4123 
4124 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
4125 @*/
4126 PetscErrorCode DMPlexRestoreTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
4127 {
4128   PetscFunctionBeginHot;
4129   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4130   if (numPoints) *numPoints = 0;
4131   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, points));
4132   PetscFunctionReturn(PETSC_SUCCESS);
4133 }
4134 
4135 /*@
4136   DMPlexGetMaxSizes - Return the maximum number of in-edges (cone) and out-edges (support) for any point in the DAG
4137 
4138   Not Collective
4139 
4140   Input Parameter:
4141 . dm - The `DMPLEX`
4142 
4143   Output Parameters:
4144 + maxConeSize    - The maximum number of in-edges
4145 - maxSupportSize - The maximum number of out-edges
4146 
4147   Level: beginner
4148 
4149 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
4150 @*/
4151 PetscErrorCode DMPlexGetMaxSizes(DM dm, PetscInt *maxConeSize, PetscInt *maxSupportSize)
4152 {
4153   DM_Plex *mesh = (DM_Plex *)dm->data;
4154 
4155   PetscFunctionBegin;
4156   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4157   if (maxConeSize) PetscCall(PetscSectionGetMaxDof(mesh->coneSection, maxConeSize));
4158   if (maxSupportSize) PetscCall(PetscSectionGetMaxDof(mesh->supportSection, maxSupportSize));
4159   PetscFunctionReturn(PETSC_SUCCESS);
4160 }
4161 
4162 PetscErrorCode DMSetUp_Plex(DM dm)
4163 {
4164   DM_Plex *mesh = (DM_Plex *)dm->data;
4165   PetscInt size, maxSupportSize;
4166 
4167   PetscFunctionBegin;
4168   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4169   PetscCall(PetscSectionSetUp(mesh->coneSection));
4170   PetscCall(PetscSectionGetStorageSize(mesh->coneSection, &size));
4171   PetscCall(PetscMalloc1(size, &mesh->cones));
4172   PetscCall(PetscCalloc1(size, &mesh->coneOrientations));
4173   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
4174   if (maxSupportSize) {
4175     PetscCall(PetscSectionSetUp(mesh->supportSection));
4176     PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &size));
4177     PetscCall(PetscMalloc1(size, &mesh->supports));
4178   }
4179   PetscFunctionReturn(PETSC_SUCCESS);
4180 }
4181 
4182 PetscErrorCode DMCreateSubDM_Plex(DM dm, PetscInt numFields, const PetscInt fields[], IS *is, DM *subdm)
4183 {
4184   PetscFunctionBegin;
4185   if (subdm) PetscCall(DMClone(dm, subdm));
4186   PetscCall(DMCreateSectionSubDM(dm, numFields, fields, NULL, NULL, is, subdm));
4187   if (subdm) (*subdm)->useNatural = dm->useNatural;
4188   if (dm->useNatural && dm->sfMigration) {
4189     PetscSF sfNatural;
4190 
4191     (*subdm)->sfMigration = dm->sfMigration;
4192     PetscCall(PetscObjectReference((PetscObject)dm->sfMigration));
4193     PetscCall(DMPlexCreateGlobalToNaturalSF(*subdm, NULL, (*subdm)->sfMigration, &sfNatural));
4194     (*subdm)->sfNatural = sfNatural;
4195   }
4196   PetscFunctionReturn(PETSC_SUCCESS);
4197 }
4198 
4199 PetscErrorCode DMCreateSuperDM_Plex(DM dms[], PetscInt len, IS **is, DM *superdm)
4200 {
4201   PetscInt i = 0;
4202 
4203   PetscFunctionBegin;
4204   PetscCall(DMClone(dms[0], superdm));
4205   PetscCall(DMCreateSectionSuperDM(dms, len, is, superdm));
4206   (*superdm)->useNatural = PETSC_FALSE;
4207   for (i = 0; i < len; i++) {
4208     if (dms[i]->useNatural && dms[i]->sfMigration) {
4209       PetscSF sfNatural;
4210 
4211       (*superdm)->sfMigration = dms[i]->sfMigration;
4212       PetscCall(PetscObjectReference((PetscObject)dms[i]->sfMigration));
4213       (*superdm)->useNatural = PETSC_TRUE;
4214       PetscCall(DMPlexCreateGlobalToNaturalSF(*superdm, NULL, (*superdm)->sfMigration, &sfNatural));
4215       (*superdm)->sfNatural = sfNatural;
4216       break;
4217     }
4218   }
4219   PetscFunctionReturn(PETSC_SUCCESS);
4220 }
4221 
4222 /*@
4223   DMPlexSymmetrize - Create support (out-edge) information from cone (in-edge) information
4224 
4225   Not Collective
4226 
4227   Input Parameter:
4228 . dm - The `DMPLEX`
4229 
4230   Level: beginner
4231 
4232   Note:
4233   This should be called after all calls to `DMPlexSetCone()`
4234 
4235 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMPlexSetCone()`
4236 @*/
4237 PetscErrorCode DMPlexSymmetrize(DM dm)
4238 {
4239   DM_Plex  *mesh = (DM_Plex *)dm->data;
4240   PetscInt *offsets;
4241   PetscInt  supportSize;
4242   PetscInt  pStart, pEnd, p;
4243 
4244   PetscFunctionBegin;
4245   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4246   PetscCheck(!mesh->supports, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONGSTATE, "Supports were already setup in this DMPlex");
4247   PetscCall(PetscLogEventBegin(DMPLEX_Symmetrize, dm, 0, 0, 0));
4248   /* Calculate support sizes */
4249   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4250   for (p = pStart; p < pEnd; ++p) {
4251     PetscInt dof, off, c;
4252 
4253     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
4254     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
4255     for (c = off; c < off + dof; ++c) PetscCall(PetscSectionAddDof(mesh->supportSection, mesh->cones[c], 1));
4256   }
4257   PetscCall(PetscSectionSetUp(mesh->supportSection));
4258   /* Calculate supports */
4259   PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &supportSize));
4260   PetscCall(PetscMalloc1(supportSize, &mesh->supports));
4261   PetscCall(PetscCalloc1(pEnd - pStart, &offsets));
4262   for (p = pStart; p < pEnd; ++p) {
4263     PetscInt dof, off, c;
4264 
4265     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
4266     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
4267     for (c = off; c < off + dof; ++c) {
4268       const PetscInt q = mesh->cones[c];
4269       PetscInt       offS;
4270 
4271       PetscCall(PetscSectionGetOffset(mesh->supportSection, q, &offS));
4272 
4273       mesh->supports[offS + offsets[q]] = p;
4274       ++offsets[q];
4275     }
4276   }
4277   PetscCall(PetscFree(offsets));
4278   PetscCall(PetscLogEventEnd(DMPLEX_Symmetrize, dm, 0, 0, 0));
4279   PetscFunctionReturn(PETSC_SUCCESS);
4280 }
4281 
4282 static PetscErrorCode DMPlexCreateDepthStratum(DM dm, DMLabel label, PetscInt depth, PetscInt pStart, PetscInt pEnd)
4283 {
4284   IS stratumIS;
4285 
4286   PetscFunctionBegin;
4287   if (pStart >= pEnd) PetscFunctionReturn(PETSC_SUCCESS);
4288   if (PetscDefined(USE_DEBUG)) {
4289     PetscInt  qStart, qEnd, numLevels, level;
4290     PetscBool overlap = PETSC_FALSE;
4291     PetscCall(DMLabelGetNumValues(label, &numLevels));
4292     for (level = 0; level < numLevels; level++) {
4293       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4294       if ((pStart >= qStart && pStart < qEnd) || (pEnd > qStart && pEnd <= qEnd)) {
4295         overlap = PETSC_TRUE;
4296         break;
4297       }
4298     }
4299     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);
4300   }
4301   PetscCall(ISCreateStride(PETSC_COMM_SELF, pEnd - pStart, pStart, 1, &stratumIS));
4302   PetscCall(DMLabelSetStratumIS(label, depth, stratumIS));
4303   PetscCall(ISDestroy(&stratumIS));
4304   PetscFunctionReturn(PETSC_SUCCESS);
4305 }
4306 
4307 static PetscErrorCode DMPlexStratify_CellType_Private(DM dm, DMLabel label)
4308 {
4309   PetscInt *pMin, *pMax;
4310   PetscInt  pStart, pEnd;
4311   PetscInt  dmin = PETSC_MAX_INT, dmax = PETSC_MIN_INT;
4312 
4313   PetscFunctionBegin;
4314   {
4315     DMLabel label2;
4316 
4317     PetscCall(DMPlexGetCellTypeLabel(dm, &label2));
4318     PetscCall(PetscObjectViewFromOptions((PetscObject)label2, NULL, "-ct_view"));
4319   }
4320   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4321   for (PetscInt p = pStart; p < pEnd; ++p) {
4322     DMPolytopeType ct;
4323 
4324     PetscCall(DMPlexGetCellType(dm, p, &ct));
4325     dmin = PetscMin(DMPolytopeTypeGetDim(ct), dmin);
4326     dmax = PetscMax(DMPolytopeTypeGetDim(ct), dmax);
4327   }
4328   PetscCall(PetscMalloc2(dmax + 1, &pMin, dmax + 1, &pMax));
4329   for (PetscInt d = dmin; d <= dmax; ++d) {
4330     pMin[d] = PETSC_MAX_INT;
4331     pMax[d] = PETSC_MIN_INT;
4332   }
4333   for (PetscInt p = pStart; p < pEnd; ++p) {
4334     DMPolytopeType ct;
4335     PetscInt       d;
4336 
4337     PetscCall(DMPlexGetCellType(dm, p, &ct));
4338     d       = DMPolytopeTypeGetDim(ct);
4339     pMin[d] = PetscMin(p, pMin[d]);
4340     pMax[d] = PetscMax(p, pMax[d]);
4341   }
4342   for (PetscInt d = dmin; d <= dmax; ++d) {
4343     if (pMin[d] > pMax[d]) continue;
4344     PetscCall(DMPlexCreateDepthStratum(dm, label, d, pMin[d], pMax[d] + 1));
4345   }
4346   PetscCall(PetscFree2(pMin, pMax));
4347   PetscFunctionReturn(PETSC_SUCCESS);
4348 }
4349 
4350 static PetscErrorCode DMPlexStratify_Topological_Private(DM dm, DMLabel label)
4351 {
4352   PetscInt pStart, pEnd;
4353   PetscInt numRoots = 0, numLeaves = 0;
4354 
4355   PetscFunctionBegin;
4356   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4357   {
4358     /* Initialize roots and count leaves */
4359     PetscInt sMin = PETSC_MAX_INT;
4360     PetscInt sMax = PETSC_MIN_INT;
4361     PetscInt coneSize, supportSize;
4362 
4363     for (PetscInt p = pStart; p < pEnd; ++p) {
4364       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4365       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
4366       if (!coneSize && supportSize) {
4367         sMin = PetscMin(p, sMin);
4368         sMax = PetscMax(p, sMax);
4369         ++numRoots;
4370       } else if (!supportSize && coneSize) {
4371         ++numLeaves;
4372       } else if (!supportSize && !coneSize) {
4373         /* Isolated points */
4374         sMin = PetscMin(p, sMin);
4375         sMax = PetscMax(p, sMax);
4376       }
4377     }
4378     PetscCall(DMPlexCreateDepthStratum(dm, label, 0, sMin, sMax + 1));
4379   }
4380 
4381   if (numRoots + numLeaves == (pEnd - pStart)) {
4382     PetscInt sMin = PETSC_MAX_INT;
4383     PetscInt sMax = PETSC_MIN_INT;
4384     PetscInt coneSize, supportSize;
4385 
4386     for (PetscInt p = pStart; p < pEnd; ++p) {
4387       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4388       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
4389       if (!supportSize && coneSize) {
4390         sMin = PetscMin(p, sMin);
4391         sMax = PetscMax(p, sMax);
4392       }
4393     }
4394     PetscCall(DMPlexCreateDepthStratum(dm, label, 1, sMin, sMax + 1));
4395   } else {
4396     PetscInt level = 0;
4397     PetscInt qStart, qEnd;
4398 
4399     PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4400     while (qEnd > qStart) {
4401       PetscInt sMin = PETSC_MAX_INT;
4402       PetscInt sMax = PETSC_MIN_INT;
4403 
4404       for (PetscInt q = qStart; q < qEnd; ++q) {
4405         const PetscInt *support;
4406         PetscInt        supportSize;
4407 
4408         PetscCall(DMPlexGetSupportSize(dm, q, &supportSize));
4409         PetscCall(DMPlexGetSupport(dm, q, &support));
4410         for (PetscInt s = 0; s < supportSize; ++s) {
4411           sMin = PetscMin(support[s], sMin);
4412           sMax = PetscMax(support[s], sMax);
4413         }
4414       }
4415       PetscCall(DMLabelGetNumValues(label, &level));
4416       PetscCall(DMPlexCreateDepthStratum(dm, label, level, sMin, sMax + 1));
4417       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4418     }
4419   }
4420   PetscFunctionReturn(PETSC_SUCCESS);
4421 }
4422 
4423 /*@
4424   DMPlexStratify - Computes the strata for all points in the `DMPLEX`
4425 
4426   Collective
4427 
4428   Input Parameter:
4429 . dm - The `DMPLEX`
4430 
4431   Level: beginner
4432 
4433   Notes:
4434   The strata group all points of the same grade, and this function calculates the strata. This
4435   grade can be seen as the height (or depth) of the point in the DAG.
4436 
4437   The DAG for most topologies is a graded poset (https://en.wikipedia.org/wiki/Graded_poset), and
4438   can be illustrated by a Hasse Diagram (https://en.wikipedia.org/wiki/Hasse_diagram).
4439   Concretely, `DMPlexStratify()` creates a new label named "depth" containing the depth in the DAG of each point. For cell-vertex
4440   meshes, vertices are depth 0 and cells are depth 1. For fully interpolated meshes, depth 0 for vertices, 1 for edges, and so on
4441   until cells have depth equal to the dimension of the mesh. The depth label can be accessed through `DMPlexGetDepthLabel()` or `DMPlexGetDepthStratum()`, or
4442   manually via `DMGetLabel()`.  The height is defined implicitly by height = maxDimension - depth, and can be accessed
4443   via `DMPlexGetHeightStratum()`.  For example, cells have height 0 and faces have height 1.
4444 
4445   The depth of a point is calculated by executing a breadth-first search (BFS) on the DAG. This could produce surprising results
4446   if run on a partially interpolated mesh, meaning one that had some edges and faces, but not others. For example, suppose that
4447   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
4448   to interpolate only that one (e0), so that
4449 .vb
4450   cone(c0) = {e0, v2}
4451   cone(e0) = {v0, v1}
4452 .ve
4453   If `DMPlexStratify()` is run on this mesh, it will give depths
4454 .vb
4455    depth 0 = {v0, v1, v2}
4456    depth 1 = {e0, c0}
4457 .ve
4458   where the triangle has been given depth 1, instead of 2, because it is reachable from vertex v2.
4459 
4460   `DMPlexStratify()` should be called after all calls to `DMPlexSymmetrize()`
4461 
4462 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexComputeCellTypes()`
4463 @*/
4464 PetscErrorCode DMPlexStratify(DM dm)
4465 {
4466   DM_Plex  *mesh = (DM_Plex *)dm->data;
4467   DMLabel   label;
4468   PetscBool flg = PETSC_FALSE;
4469 
4470   PetscFunctionBegin;
4471   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4472   PetscCall(PetscLogEventBegin(DMPLEX_Stratify, dm, 0, 0, 0));
4473 
4474   // Create depth label
4475   PetscCall(DMRemoveLabel(dm, "depth", NULL));
4476   PetscCall(DMCreateLabel(dm, "depth"));
4477   PetscCall(DMPlexGetDepthLabel(dm, &label));
4478 
4479   PetscCall(PetscOptionsGetBool(NULL, dm->hdr.prefix, "-dm_plex_stratify_celltype", &flg, NULL));
4480   if (flg) PetscCall(DMPlexStratify_CellType_Private(dm, label));
4481   else PetscCall(DMPlexStratify_Topological_Private(dm, label));
4482 
4483   { /* just in case there is an empty process */
4484     PetscInt numValues, maxValues = 0, v;
4485 
4486     PetscCall(DMLabelGetNumValues(label, &numValues));
4487     PetscCall(MPIU_Allreduce(&numValues, &maxValues, 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
4488     for (v = numValues; v < maxValues; v++) PetscCall(DMLabelAddStratum(label, v));
4489   }
4490   PetscCall(PetscObjectStateGet((PetscObject)label, &mesh->depthState));
4491   PetscCall(PetscLogEventEnd(DMPLEX_Stratify, dm, 0, 0, 0));
4492   PetscFunctionReturn(PETSC_SUCCESS);
4493 }
4494 
4495 PetscErrorCode DMPlexComputeCellType_Internal(DM dm, PetscInt p, PetscInt pdepth, DMPolytopeType *pt)
4496 {
4497   DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4498   PetscInt       dim, depth, pheight, coneSize;
4499 
4500   PetscFunctionBeginHot;
4501   PetscCall(DMGetDimension(dm, &dim));
4502   PetscCall(DMPlexGetDepth(dm, &depth));
4503   PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4504   pheight = depth - pdepth;
4505   if (depth <= 1) {
4506     switch (pdepth) {
4507     case 0:
4508       ct = DM_POLYTOPE_POINT;
4509       break;
4510     case 1:
4511       switch (coneSize) {
4512       case 2:
4513         ct = DM_POLYTOPE_SEGMENT;
4514         break;
4515       case 3:
4516         ct = DM_POLYTOPE_TRIANGLE;
4517         break;
4518       case 4:
4519         switch (dim) {
4520         case 2:
4521           ct = DM_POLYTOPE_QUADRILATERAL;
4522           break;
4523         case 3:
4524           ct = DM_POLYTOPE_TETRAHEDRON;
4525           break;
4526         default:
4527           break;
4528         }
4529         break;
4530       case 5:
4531         ct = DM_POLYTOPE_PYRAMID;
4532         break;
4533       case 6:
4534         ct = DM_POLYTOPE_TRI_PRISM_TENSOR;
4535         break;
4536       case 8:
4537         ct = DM_POLYTOPE_HEXAHEDRON;
4538         break;
4539       default:
4540         break;
4541       }
4542     }
4543   } else {
4544     if (pdepth == 0) {
4545       ct = DM_POLYTOPE_POINT;
4546     } else if (pheight == 0) {
4547       switch (dim) {
4548       case 1:
4549         switch (coneSize) {
4550         case 2:
4551           ct = DM_POLYTOPE_SEGMENT;
4552           break;
4553         default:
4554           break;
4555         }
4556         break;
4557       case 2:
4558         switch (coneSize) {
4559         case 3:
4560           ct = DM_POLYTOPE_TRIANGLE;
4561           break;
4562         case 4:
4563           ct = DM_POLYTOPE_QUADRILATERAL;
4564           break;
4565         default:
4566           break;
4567         }
4568         break;
4569       case 3:
4570         switch (coneSize) {
4571         case 4:
4572           ct = DM_POLYTOPE_TETRAHEDRON;
4573           break;
4574         case 5: {
4575           const PetscInt *cone;
4576           PetscInt        faceConeSize;
4577 
4578           PetscCall(DMPlexGetCone(dm, p, &cone));
4579           PetscCall(DMPlexGetConeSize(dm, cone[0], &faceConeSize));
4580           switch (faceConeSize) {
4581           case 3:
4582             ct = DM_POLYTOPE_TRI_PRISM_TENSOR;
4583             break;
4584           case 4:
4585             ct = DM_POLYTOPE_PYRAMID;
4586             break;
4587           }
4588         } break;
4589         case 6:
4590           ct = DM_POLYTOPE_HEXAHEDRON;
4591           break;
4592         default:
4593           break;
4594         }
4595         break;
4596       default:
4597         break;
4598       }
4599     } else if (pheight > 0) {
4600       switch (coneSize) {
4601       case 2:
4602         ct = DM_POLYTOPE_SEGMENT;
4603         break;
4604       case 3:
4605         ct = DM_POLYTOPE_TRIANGLE;
4606         break;
4607       case 4:
4608         ct = DM_POLYTOPE_QUADRILATERAL;
4609         break;
4610       default:
4611         break;
4612       }
4613     }
4614   }
4615   *pt = ct;
4616   PetscFunctionReturn(PETSC_SUCCESS);
4617 }
4618 
4619 /*@
4620   DMPlexComputeCellTypes - Infer the polytope type of every cell using its dimension and cone size.
4621 
4622   Collective
4623 
4624   Input Parameter:
4625 . dm - The `DMPLEX`
4626 
4627   Level: developer
4628 
4629   Note:
4630   This function is normally called automatically when a cell type is requested. It creates an
4631   internal `DMLabel` named "celltype" which can be directly accessed using `DMGetLabel()`. A user may disable
4632   automatic creation by creating the label manually, using `DMCreateLabel`(dm, "celltype").
4633 
4634   `DMPlexComputeCellTypes()` should be called after all calls to `DMPlexSymmetrize()` and `DMPlexStratify()`
4635 
4636 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexStratify()`, `DMGetLabel()`, `DMCreateLabel()`
4637 @*/
4638 PetscErrorCode DMPlexComputeCellTypes(DM dm)
4639 {
4640   DM_Plex *mesh;
4641   DMLabel  ctLabel;
4642   PetscInt pStart, pEnd, p;
4643 
4644   PetscFunctionBegin;
4645   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4646   mesh = (DM_Plex *)dm->data;
4647   PetscCall(DMCreateLabel(dm, "celltype"));
4648   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
4649   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4650   PetscCall(PetscFree(mesh->cellTypes));
4651   PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
4652   for (p = pStart; p < pEnd; ++p) {
4653     DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4654     PetscInt       pdepth;
4655 
4656     PetscCall(DMPlexGetPointDepth(dm, p, &pdepth));
4657     PetscCall(DMPlexComputeCellType_Internal(dm, p, pdepth, &ct));
4658     PetscCheck(ct != DM_POLYTOPE_UNKNOWN && ct != DM_POLYTOPE_UNKNOWN_CELL && ct != DM_POLYTOPE_UNKNOWN_FACE, PETSC_COMM_SELF, PETSC_ERR_SUP, "Point %" PetscInt_FMT " has invalid celltype (%s)", p, DMPolytopeTypes[ct]);
4659     PetscCall(DMLabelSetValue(ctLabel, p, ct));
4660     mesh->cellTypes[p - pStart].value_as_uint8 = ct;
4661   }
4662   PetscCall(PetscObjectStateGet((PetscObject)ctLabel, &mesh->celltypeState));
4663   PetscCall(PetscObjectViewFromOptions((PetscObject)ctLabel, NULL, "-dm_plex_celltypes_view"));
4664   PetscFunctionReturn(PETSC_SUCCESS);
4665 }
4666 
4667 /*@C
4668   DMPlexGetJoin - Get an array for the join of the set of points
4669 
4670   Not Collective
4671 
4672   Input Parameters:
4673 + dm        - The `DMPLEX` object
4674 . numPoints - The number of input points for the join
4675 - points    - The input points
4676 
4677   Output Parameters:
4678 + numCoveredPoints - The number of points in the join
4679 - coveredPoints    - The points in the join
4680 
4681   Level: intermediate
4682 
4683   Note:
4684   Currently, this is restricted to a single level join
4685 
4686   Fortran Notes:
4687   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4688 
4689 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4690 @*/
4691 PetscErrorCode DMPlexGetJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4692 {
4693   DM_Plex  *mesh = (DM_Plex *)dm->data;
4694   PetscInt *join[2];
4695   PetscInt  joinSize, i = 0;
4696   PetscInt  dof, off, p, c, m;
4697   PetscInt  maxSupportSize;
4698 
4699   PetscFunctionBegin;
4700   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4701   PetscAssertPointer(points, 3);
4702   PetscAssertPointer(numCoveredPoints, 4);
4703   PetscAssertPointer(coveredPoints, 5);
4704   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
4705   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[0]));
4706   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[1]));
4707   /* Copy in support of first point */
4708   PetscCall(PetscSectionGetDof(mesh->supportSection, points[0], &dof));
4709   PetscCall(PetscSectionGetOffset(mesh->supportSection, points[0], &off));
4710   for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = mesh->supports[off + joinSize];
4711   /* Check each successive support */
4712   for (p = 1; p < numPoints; ++p) {
4713     PetscInt newJoinSize = 0;
4714 
4715     PetscCall(PetscSectionGetDof(mesh->supportSection, points[p], &dof));
4716     PetscCall(PetscSectionGetOffset(mesh->supportSection, points[p], &off));
4717     for (c = 0; c < dof; ++c) {
4718       const PetscInt point = mesh->supports[off + c];
4719 
4720       for (m = 0; m < joinSize; ++m) {
4721         if (point == join[i][m]) {
4722           join[1 - i][newJoinSize++] = point;
4723           break;
4724         }
4725       }
4726     }
4727     joinSize = newJoinSize;
4728     i        = 1 - i;
4729   }
4730   *numCoveredPoints = joinSize;
4731   *coveredPoints    = join[i];
4732   PetscCall(DMRestoreWorkArray(dm, maxSupportSize, MPIU_INT, &join[1 - i]));
4733   PetscFunctionReturn(PETSC_SUCCESS);
4734 }
4735 
4736 /*@C
4737   DMPlexRestoreJoin - Restore an array for the join of the set of points
4738 
4739   Not Collective
4740 
4741   Input Parameters:
4742 + dm        - The `DMPLEX` object
4743 . numPoints - The number of input points for the join
4744 - points    - The input points
4745 
4746   Output Parameters:
4747 + numCoveredPoints - The number of points in the join
4748 - coveredPoints    - The points in the join
4749 
4750   Level: intermediate
4751 
4752   Fortran Notes:
4753   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4754 
4755 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetJoin()`, `DMPlexGetFullJoin()`, `DMPlexGetMeet()`
4756 @*/
4757 PetscErrorCode DMPlexRestoreJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4758 {
4759   PetscFunctionBegin;
4760   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4761   if (points) PetscAssertPointer(points, 3);
4762   if (numCoveredPoints) PetscAssertPointer(numCoveredPoints, 4);
4763   PetscAssertPointer(coveredPoints, 5);
4764   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
4765   if (numCoveredPoints) *numCoveredPoints = 0;
4766   PetscFunctionReturn(PETSC_SUCCESS);
4767 }
4768 
4769 /*@C
4770   DMPlexGetFullJoin - Get an array for the join of the set of points
4771 
4772   Not Collective
4773 
4774   Input Parameters:
4775 + dm        - The `DMPLEX` object
4776 . numPoints - The number of input points for the join
4777 - points    - The input points
4778 
4779   Output Parameters:
4780 + numCoveredPoints - The number of points in the join
4781 - coveredPoints    - The points in the join
4782 
4783   Level: intermediate
4784 
4785   Fortran Notes:
4786   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4787 
4788 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetJoin()`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4789 @*/
4790 PetscErrorCode DMPlexGetFullJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4791 {
4792   PetscInt *offsets, **closures;
4793   PetscInt *join[2];
4794   PetscInt  depth = 0, maxSize, joinSize = 0, i = 0;
4795   PetscInt  p, d, c, m, ms;
4796 
4797   PetscFunctionBegin;
4798   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4799   PetscAssertPointer(points, 3);
4800   PetscAssertPointer(numCoveredPoints, 4);
4801   PetscAssertPointer(coveredPoints, 5);
4802 
4803   PetscCall(DMPlexGetDepth(dm, &depth));
4804   PetscCall(PetscCalloc1(numPoints, &closures));
4805   PetscCall(DMGetWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
4806   PetscCall(DMPlexGetMaxSizes(dm, NULL, &ms));
4807   maxSize = (ms > 1) ? ((PetscPowInt(ms, depth + 1) - 1) / (ms - 1)) : depth + 1;
4808   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[0]));
4809   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[1]));
4810 
4811   for (p = 0; p < numPoints; ++p) {
4812     PetscInt closureSize;
4813 
4814     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_FALSE, &closureSize, &closures[p]));
4815 
4816     offsets[p * (depth + 2) + 0] = 0;
4817     for (d = 0; d < depth + 1; ++d) {
4818       PetscInt pStart, pEnd, i;
4819 
4820       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
4821       for (i = offsets[p * (depth + 2) + d]; i < closureSize; ++i) {
4822         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
4823           offsets[p * (depth + 2) + d + 1] = i;
4824           break;
4825         }
4826       }
4827       if (i == closureSize) offsets[p * (depth + 2) + d + 1] = i;
4828     }
4829     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);
4830   }
4831   for (d = 0; d < depth + 1; ++d) {
4832     PetscInt dof;
4833 
4834     /* Copy in support of first point */
4835     dof = offsets[d + 1] - offsets[d];
4836     for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = closures[0][(offsets[d] + joinSize) * 2];
4837     /* Check each successive cone */
4838     for (p = 1; p < numPoints && joinSize; ++p) {
4839       PetscInt newJoinSize = 0;
4840 
4841       dof = offsets[p * (depth + 2) + d + 1] - offsets[p * (depth + 2) + d];
4842       for (c = 0; c < dof; ++c) {
4843         const PetscInt point = closures[p][(offsets[p * (depth + 2) + d] + c) * 2];
4844 
4845         for (m = 0; m < joinSize; ++m) {
4846           if (point == join[i][m]) {
4847             join[1 - i][newJoinSize++] = point;
4848             break;
4849           }
4850         }
4851       }
4852       joinSize = newJoinSize;
4853       i        = 1 - i;
4854     }
4855     if (joinSize) break;
4856   }
4857   *numCoveredPoints = joinSize;
4858   *coveredPoints    = join[i];
4859   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_FALSE, NULL, &closures[p]));
4860   PetscCall(PetscFree(closures));
4861   PetscCall(DMRestoreWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
4862   PetscCall(DMRestoreWorkArray(dm, ms, MPIU_INT, &join[1 - i]));
4863   PetscFunctionReturn(PETSC_SUCCESS);
4864 }
4865 
4866 /*@C
4867   DMPlexGetMeet - Get an array for the meet of the set of points
4868 
4869   Not Collective
4870 
4871   Input Parameters:
4872 + dm        - The `DMPLEX` object
4873 . numPoints - The number of input points for the meet
4874 - points    - The input points
4875 
4876   Output Parameters:
4877 + numCoveringPoints - The number of points in the meet
4878 - coveringPoints    - The points in the meet
4879 
4880   Level: intermediate
4881 
4882   Note:
4883   Currently, this is restricted to a single level meet
4884 
4885   Fortran Notes:
4886   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4887 
4888 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
4889 @*/
4890 PetscErrorCode DMPlexGetMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveringPoints, const PetscInt **coveringPoints)
4891 {
4892   DM_Plex  *mesh = (DM_Plex *)dm->data;
4893   PetscInt *meet[2];
4894   PetscInt  meetSize, i = 0;
4895   PetscInt  dof, off, p, c, m;
4896   PetscInt  maxConeSize;
4897 
4898   PetscFunctionBegin;
4899   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4900   PetscAssertPointer(points, 3);
4901   PetscAssertPointer(numCoveringPoints, 4);
4902   PetscAssertPointer(coveringPoints, 5);
4903   PetscCall(PetscSectionGetMaxDof(mesh->coneSection, &maxConeSize));
4904   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[0]));
4905   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[1]));
4906   /* Copy in cone of first point */
4907   PetscCall(PetscSectionGetDof(mesh->coneSection, points[0], &dof));
4908   PetscCall(PetscSectionGetOffset(mesh->coneSection, points[0], &off));
4909   for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = mesh->cones[off + meetSize];
4910   /* Check each successive cone */
4911   for (p = 1; p < numPoints; ++p) {
4912     PetscInt newMeetSize = 0;
4913 
4914     PetscCall(PetscSectionGetDof(mesh->coneSection, points[p], &dof));
4915     PetscCall(PetscSectionGetOffset(mesh->coneSection, points[p], &off));
4916     for (c = 0; c < dof; ++c) {
4917       const PetscInt point = mesh->cones[off + c];
4918 
4919       for (m = 0; m < meetSize; ++m) {
4920         if (point == meet[i][m]) {
4921           meet[1 - i][newMeetSize++] = point;
4922           break;
4923         }
4924       }
4925     }
4926     meetSize = newMeetSize;
4927     i        = 1 - i;
4928   }
4929   *numCoveringPoints = meetSize;
4930   *coveringPoints    = meet[i];
4931   PetscCall(DMRestoreWorkArray(dm, maxConeSize, MPIU_INT, &meet[1 - i]));
4932   PetscFunctionReturn(PETSC_SUCCESS);
4933 }
4934 
4935 /*@C
4936   DMPlexRestoreMeet - Restore an array for the meet of the set of points
4937 
4938   Not Collective
4939 
4940   Input Parameters:
4941 + dm        - The `DMPLEX` object
4942 . numPoints - The number of input points for the meet
4943 - points    - The input points
4944 
4945   Output Parameters:
4946 + numCoveredPoints - The number of points in the meet
4947 - coveredPoints    - The points in the meet
4948 
4949   Level: intermediate
4950 
4951   Fortran Notes:
4952   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4953 
4954 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetMeet()`, `DMPlexGetFullMeet()`, `DMPlexGetJoin()`
4955 @*/
4956 PetscErrorCode DMPlexRestoreMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4957 {
4958   PetscFunctionBegin;
4959   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4960   if (points) PetscAssertPointer(points, 3);
4961   if (numCoveredPoints) PetscAssertPointer(numCoveredPoints, 4);
4962   PetscAssertPointer(coveredPoints, 5);
4963   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
4964   if (numCoveredPoints) *numCoveredPoints = 0;
4965   PetscFunctionReturn(PETSC_SUCCESS);
4966 }
4967 
4968 /*@C
4969   DMPlexGetFullMeet - Get an array for the meet of the set of points
4970 
4971   Not Collective
4972 
4973   Input Parameters:
4974 + dm        - The `DMPLEX` object
4975 . numPoints - The number of input points for the meet
4976 - points    - The input points
4977 
4978   Output Parameters:
4979 + numCoveredPoints - The number of points in the meet
4980 - coveredPoints    - The points in the meet
4981 
4982   Level: intermediate
4983 
4984   Fortran Notes:
4985   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4986 
4987 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetMeet()`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
4988 @*/
4989 PetscErrorCode DMPlexGetFullMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4990 {
4991   PetscInt *offsets, **closures;
4992   PetscInt *meet[2];
4993   PetscInt  height = 0, maxSize, meetSize = 0, i = 0;
4994   PetscInt  p, h, c, m, mc;
4995 
4996   PetscFunctionBegin;
4997   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4998   PetscAssertPointer(points, 3);
4999   PetscAssertPointer(numCoveredPoints, 4);
5000   PetscAssertPointer(coveredPoints, 5);
5001 
5002   PetscCall(DMPlexGetDepth(dm, &height));
5003   PetscCall(PetscMalloc1(numPoints, &closures));
5004   PetscCall(DMGetWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
5005   PetscCall(DMPlexGetMaxSizes(dm, &mc, NULL));
5006   maxSize = (mc > 1) ? ((PetscPowInt(mc, height + 1) - 1) / (mc - 1)) : height + 1;
5007   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[0]));
5008   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[1]));
5009 
5010   for (p = 0; p < numPoints; ++p) {
5011     PetscInt closureSize;
5012 
5013     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_TRUE, &closureSize, &closures[p]));
5014 
5015     offsets[p * (height + 2) + 0] = 0;
5016     for (h = 0; h < height + 1; ++h) {
5017       PetscInt pStart, pEnd, i;
5018 
5019       PetscCall(DMPlexGetHeightStratum(dm, h, &pStart, &pEnd));
5020       for (i = offsets[p * (height + 2) + h]; i < closureSize; ++i) {
5021         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
5022           offsets[p * (height + 2) + h + 1] = i;
5023           break;
5024         }
5025       }
5026       if (i == closureSize) offsets[p * (height + 2) + h + 1] = i;
5027     }
5028     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);
5029   }
5030   for (h = 0; h < height + 1; ++h) {
5031     PetscInt dof;
5032 
5033     /* Copy in cone of first point */
5034     dof = offsets[h + 1] - offsets[h];
5035     for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = closures[0][(offsets[h] + meetSize) * 2];
5036     /* Check each successive cone */
5037     for (p = 1; p < numPoints && meetSize; ++p) {
5038       PetscInt newMeetSize = 0;
5039 
5040       dof = offsets[p * (height + 2) + h + 1] - offsets[p * (height + 2) + h];
5041       for (c = 0; c < dof; ++c) {
5042         const PetscInt point = closures[p][(offsets[p * (height + 2) + h] + c) * 2];
5043 
5044         for (m = 0; m < meetSize; ++m) {
5045           if (point == meet[i][m]) {
5046             meet[1 - i][newMeetSize++] = point;
5047             break;
5048           }
5049         }
5050       }
5051       meetSize = newMeetSize;
5052       i        = 1 - i;
5053     }
5054     if (meetSize) break;
5055   }
5056   *numCoveredPoints = meetSize;
5057   *coveredPoints    = meet[i];
5058   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_TRUE, NULL, &closures[p]));
5059   PetscCall(PetscFree(closures));
5060   PetscCall(DMRestoreWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
5061   PetscCall(DMRestoreWorkArray(dm, mc, MPIU_INT, &meet[1 - i]));
5062   PetscFunctionReturn(PETSC_SUCCESS);
5063 }
5064 
5065 /*@
5066   DMPlexEqual - Determine if two `DM` have the same topology
5067 
5068   Not Collective
5069 
5070   Input Parameters:
5071 + dmA - A `DMPLEX` object
5072 - dmB - A `DMPLEX` object
5073 
5074   Output Parameter:
5075 . equal - `PETSC_TRUE` if the topologies are identical
5076 
5077   Level: intermediate
5078 
5079   Note:
5080   We are not solving graph isomorphism, so we do not permute.
5081 
5082 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCone()`
5083 @*/
5084 PetscErrorCode DMPlexEqual(DM dmA, DM dmB, PetscBool *equal)
5085 {
5086   PetscInt depth, depthB, pStart, pEnd, pStartB, pEndB, p;
5087 
5088   PetscFunctionBegin;
5089   PetscValidHeaderSpecific(dmA, DM_CLASSID, 1);
5090   PetscValidHeaderSpecific(dmB, DM_CLASSID, 2);
5091   PetscAssertPointer(equal, 3);
5092 
5093   *equal = PETSC_FALSE;
5094   PetscCall(DMPlexGetDepth(dmA, &depth));
5095   PetscCall(DMPlexGetDepth(dmB, &depthB));
5096   if (depth != depthB) PetscFunctionReturn(PETSC_SUCCESS);
5097   PetscCall(DMPlexGetChart(dmA, &pStart, &pEnd));
5098   PetscCall(DMPlexGetChart(dmB, &pStartB, &pEndB));
5099   if ((pStart != pStartB) || (pEnd != pEndB)) PetscFunctionReturn(PETSC_SUCCESS);
5100   for (p = pStart; p < pEnd; ++p) {
5101     const PetscInt *cone, *coneB, *ornt, *orntB, *support, *supportB;
5102     PetscInt        coneSize, coneSizeB, c, supportSize, supportSizeB, s;
5103 
5104     PetscCall(DMPlexGetConeSize(dmA, p, &coneSize));
5105     PetscCall(DMPlexGetCone(dmA, p, &cone));
5106     PetscCall(DMPlexGetConeOrientation(dmA, p, &ornt));
5107     PetscCall(DMPlexGetConeSize(dmB, p, &coneSizeB));
5108     PetscCall(DMPlexGetCone(dmB, p, &coneB));
5109     PetscCall(DMPlexGetConeOrientation(dmB, p, &orntB));
5110     if (coneSize != coneSizeB) PetscFunctionReturn(PETSC_SUCCESS);
5111     for (c = 0; c < coneSize; ++c) {
5112       if (cone[c] != coneB[c]) PetscFunctionReturn(PETSC_SUCCESS);
5113       if (ornt[c] != orntB[c]) PetscFunctionReturn(PETSC_SUCCESS);
5114     }
5115     PetscCall(DMPlexGetSupportSize(dmA, p, &supportSize));
5116     PetscCall(DMPlexGetSupport(dmA, p, &support));
5117     PetscCall(DMPlexGetSupportSize(dmB, p, &supportSizeB));
5118     PetscCall(DMPlexGetSupport(dmB, p, &supportB));
5119     if (supportSize != supportSizeB) PetscFunctionReturn(PETSC_SUCCESS);
5120     for (s = 0; s < supportSize; ++s) {
5121       if (support[s] != supportB[s]) PetscFunctionReturn(PETSC_SUCCESS);
5122     }
5123   }
5124   *equal = PETSC_TRUE;
5125   PetscFunctionReturn(PETSC_SUCCESS);
5126 }
5127 
5128 /*@
5129   DMPlexGetNumFaceVertices - Returns the number of vertices on a face
5130 
5131   Not Collective
5132 
5133   Input Parameters:
5134 + dm         - The `DMPLEX`
5135 . cellDim    - The cell dimension
5136 - numCorners - The number of vertices on a cell
5137 
5138   Output Parameter:
5139 . numFaceVertices - The number of vertices on a face
5140 
5141   Level: developer
5142 
5143   Note:
5144   Of course this can only work for a restricted set of symmetric shapes
5145 
5146 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCone()`
5147 @*/
5148 PetscErrorCode DMPlexGetNumFaceVertices(DM dm, PetscInt cellDim, PetscInt numCorners, PetscInt *numFaceVertices)
5149 {
5150   MPI_Comm comm;
5151 
5152   PetscFunctionBegin;
5153   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
5154   PetscAssertPointer(numFaceVertices, 4);
5155   switch (cellDim) {
5156   case 0:
5157     *numFaceVertices = 0;
5158     break;
5159   case 1:
5160     *numFaceVertices = 1;
5161     break;
5162   case 2:
5163     switch (numCorners) {
5164     case 3:                 /* triangle */
5165       *numFaceVertices = 2; /* Edge has 2 vertices */
5166       break;
5167     case 4:                 /* quadrilateral */
5168       *numFaceVertices = 2; /* Edge has 2 vertices */
5169       break;
5170     case 6:                 /* quadratic triangle, tri and quad cohesive Lagrange cells */
5171       *numFaceVertices = 3; /* Edge has 3 vertices */
5172       break;
5173     case 9:                 /* quadratic quadrilateral, quadratic quad cohesive Lagrange cells */
5174       *numFaceVertices = 3; /* Edge has 3 vertices */
5175       break;
5176     default:
5177       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
5178     }
5179     break;
5180   case 3:
5181     switch (numCorners) {
5182     case 4:                 /* tetradehdron */
5183       *numFaceVertices = 3; /* Face has 3 vertices */
5184       break;
5185     case 6:                 /* tet cohesive cells */
5186       *numFaceVertices = 4; /* Face has 4 vertices */
5187       break;
5188     case 8:                 /* hexahedron */
5189       *numFaceVertices = 4; /* Face has 4 vertices */
5190       break;
5191     case 9:                 /* tet cohesive Lagrange cells */
5192       *numFaceVertices = 6; /* Face has 6 vertices */
5193       break;
5194     case 10:                /* quadratic tetrahedron */
5195       *numFaceVertices = 6; /* Face has 6 vertices */
5196       break;
5197     case 12:                /* hex cohesive Lagrange cells */
5198       *numFaceVertices = 6; /* Face has 6 vertices */
5199       break;
5200     case 18:                /* quadratic tet cohesive Lagrange cells */
5201       *numFaceVertices = 6; /* Face has 6 vertices */
5202       break;
5203     case 27:                /* quadratic hexahedron, quadratic hex cohesive Lagrange cells */
5204       *numFaceVertices = 9; /* Face has 9 vertices */
5205       break;
5206     default:
5207       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
5208     }
5209     break;
5210   default:
5211     SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid cell dimension %" PetscInt_FMT, cellDim);
5212   }
5213   PetscFunctionReturn(PETSC_SUCCESS);
5214 }
5215 
5216 /*@
5217   DMPlexGetDepthLabel - Get the `DMLabel` recording the depth of each point
5218 
5219   Not Collective
5220 
5221   Input Parameter:
5222 . dm - The `DMPLEX` object
5223 
5224   Output Parameter:
5225 . depthLabel - The `DMLabel` recording point depth
5226 
5227   Level: developer
5228 
5229 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepth()`, `DMPlexGetHeightStratum()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`,
5230 @*/
5231 PetscErrorCode DMPlexGetDepthLabel(DM dm, DMLabel *depthLabel)
5232 {
5233   PetscFunctionBegin;
5234   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5235   PetscAssertPointer(depthLabel, 2);
5236   *depthLabel = dm->depthLabel;
5237   PetscFunctionReturn(PETSC_SUCCESS);
5238 }
5239 
5240 /*@
5241   DMPlexGetDepth - Get the depth of the DAG representing this mesh
5242 
5243   Not Collective
5244 
5245   Input Parameter:
5246 . dm - The `DMPLEX` object
5247 
5248   Output Parameter:
5249 . depth - The number of strata (breadth first levels) in the DAG
5250 
5251   Level: developer
5252 
5253   Notes:
5254   This returns maximum of point depths over all points, i.e. maximum value of the label returned by `DMPlexGetDepthLabel()`.
5255 
5256   The point depth is described more in detail in `DMPlexGetDepthStratum()`.
5257 
5258   An empty mesh gives -1.
5259 
5260 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepthLabel()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`
5261 @*/
5262 PetscErrorCode DMPlexGetDepth(DM dm, PetscInt *depth)
5263 {
5264   DM_Plex *mesh = (DM_Plex *)dm->data;
5265   DMLabel  label;
5266   PetscInt d = -1;
5267 
5268   PetscFunctionBegin;
5269   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5270   PetscAssertPointer(depth, 2);
5271   if (mesh->tr) {
5272     PetscCall(DMPlexTransformGetDepth(mesh->tr, depth));
5273   } else {
5274     PetscCall(DMPlexGetDepthLabel(dm, &label));
5275     // Allow missing depths
5276     if (label) PetscCall(DMLabelGetValueBounds(label, NULL, &d));
5277     *depth = d;
5278   }
5279   PetscFunctionReturn(PETSC_SUCCESS);
5280 }
5281 
5282 /*@
5283   DMPlexGetDepthStratum - Get the bounds [`start`, `end`) for all points at a certain depth.
5284 
5285   Not Collective
5286 
5287   Input Parameters:
5288 + dm    - The `DMPLEX` object
5289 - depth - The requested depth
5290 
5291   Output Parameters:
5292 + start - The first point at this `depth`
5293 - end   - One beyond the last point at this `depth`
5294 
5295   Level: developer
5296 
5297   Notes:
5298   Depth indexing is related to topological dimension.  Depth stratum 0 contains the lowest topological dimension points,
5299   often "vertices".  If the mesh is "interpolated" (see `DMPlexInterpolate()`), then depth stratum 1 contains the next
5300   higher dimension, e.g., "edges".
5301 
5302 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetHeightStratum()`, `DMPlexGetCellTypeStratum()`, `DMPlexGetDepth()`, `DMPlexGetDepthLabel()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`, `DMPlexInterpolate()`
5303 @*/
5304 PetscErrorCode DMPlexGetDepthStratum(DM dm, PetscInt depth, PetscInt *start, PetscInt *end)
5305 {
5306   DM_Plex *mesh = (DM_Plex *)dm->data;
5307   DMLabel  label;
5308   PetscInt pStart, pEnd;
5309 
5310   PetscFunctionBegin;
5311   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5312   if (start) {
5313     PetscAssertPointer(start, 3);
5314     *start = 0;
5315   }
5316   if (end) {
5317     PetscAssertPointer(end, 4);
5318     *end = 0;
5319   }
5320   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
5321   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
5322   if (depth < 0) {
5323     if (start) *start = pStart;
5324     if (end) *end = pEnd;
5325     PetscFunctionReturn(PETSC_SUCCESS);
5326   }
5327   if (mesh->tr) {
5328     PetscCall(DMPlexTransformGetDepthStratum(mesh->tr, depth, start, end));
5329   } else {
5330     PetscCall(DMPlexGetDepthLabel(dm, &label));
5331     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named depth was found");
5332     PetscCall(DMLabelGetStratumBounds(label, depth, start, end));
5333   }
5334   PetscFunctionReturn(PETSC_SUCCESS);
5335 }
5336 
5337 /*@
5338   DMPlexGetHeightStratum - Get the bounds [`start`, `end`) for all points at a certain height.
5339 
5340   Not Collective
5341 
5342   Input Parameters:
5343 + dm     - The `DMPLEX` object
5344 - height - The requested height
5345 
5346   Output Parameters:
5347 + start - The first point at this `height`
5348 - end   - One beyond the last point at this `height`
5349 
5350   Level: developer
5351 
5352   Notes:
5353   Height indexing is related to topological codimension.  Height stratum 0 contains the highest topological dimension
5354   points, often called "cells" or "elements".  If the mesh is "interpolated" (see `DMPlexInterpolate()`), then height
5355   stratum 1 contains the boundary of these "cells", often called "faces" or "facets".
5356 
5357 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepthStratum()`, `DMPlexGetCellTypeStratum()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
5358 @*/
5359 PetscErrorCode DMPlexGetHeightStratum(DM dm, PetscInt height, PetscInt *start, PetscInt *end)
5360 {
5361   DMLabel  label;
5362   PetscInt depth, pStart, pEnd;
5363 
5364   PetscFunctionBegin;
5365   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5366   if (start) {
5367     PetscAssertPointer(start, 3);
5368     *start = 0;
5369   }
5370   if (end) {
5371     PetscAssertPointer(end, 4);
5372     *end = 0;
5373   }
5374   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
5375   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
5376   if (height < 0) {
5377     if (start) *start = pStart;
5378     if (end) *end = pEnd;
5379     PetscFunctionReturn(PETSC_SUCCESS);
5380   }
5381   PetscCall(DMPlexGetDepthLabel(dm, &label));
5382   if (label) PetscCall(DMLabelGetNumValues(label, &depth));
5383   else PetscCall(DMGetDimension(dm, &depth));
5384   PetscCheck(depth >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Depth not yet computed");
5385   PetscCall(DMPlexGetDepthStratum(dm, depth - 1 - height, start, end));
5386   PetscFunctionReturn(PETSC_SUCCESS);
5387 }
5388 
5389 /*@
5390   DMPlexGetPointDepth - Get the `depth` of a given point
5391 
5392   Not Collective
5393 
5394   Input Parameters:
5395 + dm    - The `DMPLEX` object
5396 - point - The point
5397 
5398   Output Parameter:
5399 . depth - The depth of the `point`
5400 
5401   Level: intermediate
5402 
5403 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
5404 @*/
5405 PetscErrorCode DMPlexGetPointDepth(DM dm, PetscInt point, PetscInt *depth)
5406 {
5407   PetscFunctionBegin;
5408   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5409   PetscAssertPointer(depth, 3);
5410   PetscCall(DMLabelGetValue(dm->depthLabel, point, depth));
5411   PetscFunctionReturn(PETSC_SUCCESS);
5412 }
5413 
5414 /*@
5415   DMPlexGetPointHeight - Get the `height` of a given point
5416 
5417   Not Collective
5418 
5419   Input Parameters:
5420 + dm    - The `DMPLEX` object
5421 - point - The point
5422 
5423   Output Parameter:
5424 . height - The height of the `point`
5425 
5426   Level: intermediate
5427 
5428 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointDepth()`
5429 @*/
5430 PetscErrorCode DMPlexGetPointHeight(DM dm, PetscInt point, PetscInt *height)
5431 {
5432   PetscInt n, pDepth;
5433 
5434   PetscFunctionBegin;
5435   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5436   PetscAssertPointer(height, 3);
5437   PetscCall(DMLabelGetNumValues(dm->depthLabel, &n));
5438   PetscCall(DMLabelGetValue(dm->depthLabel, point, &pDepth));
5439   *height = n - 1 - pDepth; /* DAG depth is n-1 */
5440   PetscFunctionReturn(PETSC_SUCCESS);
5441 }
5442 
5443 /*@
5444   DMPlexGetCellTypeLabel - Get the `DMLabel` recording the polytope type of each cell
5445 
5446   Not Collective
5447 
5448   Input Parameter:
5449 . dm - The `DMPLEX` object
5450 
5451   Output Parameter:
5452 . celltypeLabel - The `DMLabel` recording cell polytope type
5453 
5454   Level: developer
5455 
5456   Note:
5457   This function will trigger automatica computation of cell types. This can be disabled by calling
5458   `DMCreateLabel`(dm, "celltype") beforehand.
5459 
5460 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMCreateLabel()`
5461 @*/
5462 PetscErrorCode DMPlexGetCellTypeLabel(DM dm, DMLabel *celltypeLabel)
5463 {
5464   PetscFunctionBegin;
5465   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5466   PetscAssertPointer(celltypeLabel, 2);
5467   if (!dm->celltypeLabel) PetscCall(DMPlexComputeCellTypes(dm));
5468   *celltypeLabel = dm->celltypeLabel;
5469   PetscFunctionReturn(PETSC_SUCCESS);
5470 }
5471 
5472 /*@
5473   DMPlexGetCellType - Get the polytope type of a given cell
5474 
5475   Not Collective
5476 
5477   Input Parameters:
5478 + dm   - The `DMPLEX` object
5479 - cell - The cell
5480 
5481   Output Parameter:
5482 . celltype - The polytope type of the cell
5483 
5484   Level: intermediate
5485 
5486 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPolytopeType`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`
5487 @*/
5488 PetscErrorCode DMPlexGetCellType(DM dm, PetscInt cell, DMPolytopeType *celltype)
5489 {
5490   DM_Plex *mesh = (DM_Plex *)dm->data;
5491   DMLabel  label;
5492   PetscInt ct;
5493 
5494   PetscFunctionBegin;
5495   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5496   PetscAssertPointer(celltype, 3);
5497   if (mesh->tr) {
5498     PetscCall(DMPlexTransformGetCellType(mesh->tr, cell, celltype));
5499   } else {
5500     PetscInt pStart, pEnd;
5501 
5502     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, NULL));
5503     if (!mesh->cellTypes) { /* XXX remove? optimize? */
5504       PetscCall(PetscSectionGetChart(mesh->coneSection, NULL, &pEnd));
5505       PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
5506       PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5507       for (PetscInt p = pStart; p < pEnd; p++) {
5508         PetscCall(DMLabelGetValue(label, p, &ct));
5509         mesh->cellTypes[p - pStart].value_as_uint8 = (DMPolytopeType)ct;
5510       }
5511     }
5512     *celltype = (DMPolytopeType)mesh->cellTypes[cell - pStart].value_as_uint8;
5513     if (PetscDefined(USE_DEBUG)) {
5514       PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5515       PetscCall(DMLabelGetValue(label, cell, &ct));
5516       PetscCheck(ct >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Cell %" PetscInt_FMT " has not been assigned a cell type", cell);
5517       PetscCheck(ct == (PetscInt)*celltype, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid cellType for %" PetscInt_FMT ": %d != %" PetscInt_FMT, cell, (int)*celltype, ct);
5518     }
5519   }
5520   PetscFunctionReturn(PETSC_SUCCESS);
5521 }
5522 
5523 /*@
5524   DMPlexSetCellType - Set the polytope type of a given cell
5525 
5526   Not Collective
5527 
5528   Input Parameters:
5529 + dm       - The `DMPLEX` object
5530 . cell     - The cell
5531 - celltype - The polytope type of the cell
5532 
5533   Level: advanced
5534 
5535   Note:
5536   By default, cell types will be automatically computed using `DMPlexComputeCellTypes()` before this function
5537   is executed. This function will override the computed type. However, if automatic classification will not succeed
5538   and a user wants to manually specify all types, the classification must be disabled by calling
5539   DMCreateLabel(dm, "celltype") before getting or setting any cell types.
5540 
5541 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexComputeCellTypes()`, `DMCreateLabel()`
5542 @*/
5543 PetscErrorCode DMPlexSetCellType(DM dm, PetscInt cell, DMPolytopeType celltype)
5544 {
5545   DM_Plex *mesh = (DM_Plex *)dm->data;
5546   DMLabel  label;
5547   PetscInt pStart, pEnd;
5548 
5549   PetscFunctionBegin;
5550   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5551   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
5552   PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5553   PetscCall(DMLabelSetValue(label, cell, celltype));
5554   if (!mesh->cellTypes) PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
5555   mesh->cellTypes[cell - pStart].value_as_uint8 = celltype;
5556   PetscFunctionReturn(PETSC_SUCCESS);
5557 }
5558 
5559 PetscErrorCode DMCreateCoordinateDM_Plex(DM dm, DM *cdm)
5560 {
5561   PetscSection section;
5562   PetscInt     maxHeight;
5563   const char  *prefix;
5564 
5565   PetscFunctionBegin;
5566   PetscCall(DMClone(dm, cdm));
5567   PetscCall(PetscObjectGetOptionsPrefix((PetscObject)dm, &prefix));
5568   PetscCall(PetscObjectSetOptionsPrefix((PetscObject)*cdm, prefix));
5569   PetscCall(PetscObjectAppendOptionsPrefix((PetscObject)*cdm, "cdm_"));
5570   PetscCall(DMPlexGetMaxProjectionHeight(dm, &maxHeight));
5571   PetscCall(DMPlexSetMaxProjectionHeight(*cdm, maxHeight));
5572   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
5573   PetscCall(DMSetLocalSection(*cdm, section));
5574   PetscCall(PetscSectionDestroy(&section));
5575 
5576   PetscCall(DMSetNumFields(*cdm, 1));
5577   PetscCall(DMCreateDS(*cdm));
5578   (*cdm)->cloneOpts = PETSC_TRUE;
5579   if (dm->setfromoptionscalled) PetscCall(DMSetFromOptions(*cdm));
5580   PetscFunctionReturn(PETSC_SUCCESS);
5581 }
5582 
5583 PetscErrorCode DMCreateCoordinateField_Plex(DM dm, DMField *field)
5584 {
5585   Vec coordsLocal, cellCoordsLocal;
5586   DM  coordsDM, cellCoordsDM;
5587 
5588   PetscFunctionBegin;
5589   *field = NULL;
5590   PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
5591   PetscCall(DMGetCoordinateDM(dm, &coordsDM));
5592   PetscCall(DMGetCellCoordinatesLocal(dm, &cellCoordsLocal));
5593   PetscCall(DMGetCellCoordinateDM(dm, &cellCoordsDM));
5594   if (coordsLocal && coordsDM) {
5595     if (cellCoordsLocal && cellCoordsDM) PetscCall(DMFieldCreateDSWithDG(coordsDM, cellCoordsDM, 0, coordsLocal, cellCoordsLocal, field));
5596     else PetscCall(DMFieldCreateDS(coordsDM, 0, coordsLocal, field));
5597   }
5598   PetscFunctionReturn(PETSC_SUCCESS);
5599 }
5600 
5601 /*@
5602   DMPlexGetConeSection - Return a section which describes the layout of cone data
5603 
5604   Not Collective
5605 
5606   Input Parameter:
5607 . dm - The `DMPLEX` object
5608 
5609   Output Parameter:
5610 . section - The `PetscSection` object
5611 
5612   Level: developer
5613 
5614 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSupportSection()`, `DMPlexGetCones()`, `DMPlexGetConeOrientations()`, `PetscSection`
5615 @*/
5616 PetscErrorCode DMPlexGetConeSection(DM dm, PetscSection *section)
5617 {
5618   DM_Plex *mesh = (DM_Plex *)dm->data;
5619 
5620   PetscFunctionBegin;
5621   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5622   if (section) *section = mesh->coneSection;
5623   PetscFunctionReturn(PETSC_SUCCESS);
5624 }
5625 
5626 /*@
5627   DMPlexGetSupportSection - Return a section which describes the layout of support data
5628 
5629   Not Collective
5630 
5631   Input Parameter:
5632 . dm - The `DMPLEX` object
5633 
5634   Output Parameter:
5635 . section - The `PetscSection` object
5636 
5637   Level: developer
5638 
5639 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `PetscSection`
5640 @*/
5641 PetscErrorCode DMPlexGetSupportSection(DM dm, PetscSection *section)
5642 {
5643   DM_Plex *mesh = (DM_Plex *)dm->data;
5644 
5645   PetscFunctionBegin;
5646   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5647   if (section) *section = mesh->supportSection;
5648   PetscFunctionReturn(PETSC_SUCCESS);
5649 }
5650 
5651 /*@C
5652   DMPlexGetCones - Return cone data
5653 
5654   Not Collective
5655 
5656   Input Parameter:
5657 . dm - The `DMPLEX` object
5658 
5659   Output Parameter:
5660 . cones - The cone for each point
5661 
5662   Level: developer
5663 
5664 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`
5665 @*/
5666 PetscErrorCode DMPlexGetCones(DM dm, PetscInt *cones[])
5667 {
5668   DM_Plex *mesh = (DM_Plex *)dm->data;
5669 
5670   PetscFunctionBegin;
5671   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5672   if (cones) *cones = mesh->cones;
5673   PetscFunctionReturn(PETSC_SUCCESS);
5674 }
5675 
5676 /*@C
5677   DMPlexGetConeOrientations - Return cone orientation data
5678 
5679   Not Collective
5680 
5681   Input Parameter:
5682 . dm - The `DMPLEX` object
5683 
5684   Output Parameter:
5685 . coneOrientations - The array of cone orientations for all points
5686 
5687   Level: developer
5688 
5689   Notes:
5690   The `PetscSection` returned by `DMPlexGetConeSection()` partitions coneOrientations into cone orientations of particular points as returned by `DMPlexGetConeOrientation()`.
5691 
5692   The meaning of coneOrientations values is detailed in `DMPlexGetConeOrientation()`.
5693 
5694 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `DMPlexGetConeOrientation()`, `PetscSection`
5695 @*/
5696 PetscErrorCode DMPlexGetConeOrientations(DM dm, PetscInt *coneOrientations[])
5697 {
5698   DM_Plex *mesh = (DM_Plex *)dm->data;
5699 
5700   PetscFunctionBegin;
5701   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5702   if (coneOrientations) *coneOrientations = mesh->coneOrientations;
5703   PetscFunctionReturn(PETSC_SUCCESS);
5704 }
5705 
5706 /******************************** FEM Support **********************************/
5707 
5708 PetscErrorCode DMPlexGetAllCells_Internal(DM plex, IS *cellIS)
5709 {
5710   PetscInt depth;
5711 
5712   PetscFunctionBegin;
5713   PetscCall(DMPlexGetDepth(plex, &depth));
5714   PetscCall(DMGetStratumIS(plex, "dim", depth, cellIS));
5715   if (!*cellIS) PetscCall(DMGetStratumIS(plex, "depth", depth, cellIS));
5716   PetscFunctionReturn(PETSC_SUCCESS);
5717 }
5718 
5719 PetscErrorCode DMPlexGetAllFaces_Internal(DM plex, IS *faceIS)
5720 {
5721   PetscInt depth;
5722 
5723   PetscFunctionBegin;
5724   PetscCall(DMPlexGetDepth(plex, &depth));
5725   PetscCall(DMGetStratumIS(plex, "dim", depth - 1, faceIS));
5726   if (!*faceIS) PetscCall(DMGetStratumIS(plex, "depth", depth - 1, faceIS));
5727   PetscFunctionReturn(PETSC_SUCCESS);
5728 }
5729 
5730 /*
5731  Returns number of components and tensor degree for the field.  For interpolated meshes, line should be a point
5732  representing a line in the section.
5733 */
5734 static PetscErrorCode PetscSectionFieldGetTensorDegree_Private(DM dm, PetscSection section, PetscInt field, PetscInt line, PetscInt *Nc, PetscInt *k, PetscBool *continuous, PetscBool *tensor)
5735 {
5736   PetscObject  obj;
5737   PetscClassId id;
5738   PetscFE      fe = NULL;
5739 
5740   PetscFunctionBeginHot;
5741   PetscCall(PetscSectionGetFieldComponents(section, field, Nc));
5742   PetscCall(DMGetField(dm, field, NULL, &obj));
5743   PetscCall(PetscObjectGetClassId(obj, &id));
5744   if (id == PETSCFE_CLASSID) fe = (PetscFE)obj;
5745 
5746   if (!fe) {
5747     /* Assume the full interpolated mesh is in the chart; lines in particular */
5748     /* An order k SEM disc has k-1 dofs on an edge */
5749     PetscCall(PetscSectionGetFieldDof(section, line, field, k));
5750     *k = *k / *Nc + 1;
5751   } else {
5752     PetscInt       dual_space_size, dim;
5753     PetscDualSpace dsp;
5754 
5755     PetscCall(DMGetDimension(dm, &dim));
5756     PetscCall(PetscFEGetDualSpace(fe, &dsp));
5757     PetscCall(PetscDualSpaceGetDimension(dsp, &dual_space_size));
5758     *k = (PetscInt)PetscCeilReal(PetscPowReal(dual_space_size / *Nc, 1.0 / dim)) - 1;
5759     PetscCall(PetscDualSpaceLagrangeGetContinuity(dsp, continuous));
5760     PetscCall(PetscDualSpaceLagrangeGetTensor(dsp, tensor));
5761   }
5762   PetscFunctionReturn(PETSC_SUCCESS);
5763 }
5764 
5765 static PetscErrorCode GetFieldSize_Private(PetscInt dim, PetscInt k, PetscBool tensor, PetscInt *dof)
5766 {
5767   PetscFunctionBeginHot;
5768   if (tensor) {
5769     *dof = PetscPowInt(k + 1, dim);
5770   } else {
5771     switch (dim) {
5772     case 1:
5773       *dof = k + 1;
5774       break;
5775     case 2:
5776       *dof = ((k + 1) * (k + 2)) / 2;
5777       break;
5778     case 3:
5779       *dof = ((k + 1) * (k + 2) * (k + 3)) / 6;
5780       break;
5781     default:
5782       *dof = 0;
5783     }
5784   }
5785   PetscFunctionReturn(PETSC_SUCCESS);
5786 }
5787 
5788 /*@
5789 
5790   DMPlexSetClosurePermutationTensor - Create a permutation from the default (BFS) point ordering in the closure, to a
5791   lexicographic ordering over the tensor product cell (i.e., line, quad, hex, etc.), and set this permutation in the
5792   section provided (or the section of the `DM`).
5793 
5794   Input Parameters:
5795 + dm      - The `DM`
5796 . point   - Either a cell (highest dim point) or an edge (dim 1 point), or `PETSC_DETERMINE`
5797 - section - The `PetscSection` to reorder, or `NULL` for the default section
5798 
5799   Example:
5800   A typical interpolated single-quad mesh might order points as
5801 .vb
5802   [c0, v1, v2, v3, v4, e5, e6, e7, e8]
5803 
5804   v4 -- e6 -- v3
5805   |           |
5806   e7    c0    e8
5807   |           |
5808   v1 -- e5 -- v2
5809 .ve
5810 
5811   (There is no significance to the ordering described here.)  The default section for a Q3 quad might typically assign
5812   dofs in the order of points, e.g.,
5813 .vb
5814     c0 -> [0,1,2,3]
5815     v1 -> [4]
5816     ...
5817     e5 -> [8, 9]
5818 .ve
5819 
5820   which corresponds to the dofs
5821 .vb
5822     6   10  11  7
5823     13  2   3   15
5824     12  0   1   14
5825     4   8   9   5
5826 .ve
5827 
5828   The closure in BFS ordering works through height strata (cells, edges, vertices) to produce the ordering
5829 .vb
5830   0 1 2 3 8 9 14 15 11 10 13 12 4 5 7 6
5831 .ve
5832 
5833   After calling DMPlexSetClosurePermutationTensor(), the closure will be ordered lexicographically,
5834 .vb
5835    4 8 9 5 12 0 1 14 13 2 3 15 6 10 11 7
5836 .ve
5837 
5838   Level: developer
5839 
5840   Notes:
5841   The point is used to determine the number of dofs/field on an edge. For SEM, this is related to the polynomial
5842   degree of the basis.
5843 
5844   This is required to run with libCEED.
5845 
5846 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMGetLocalSection()`, `PetscSectionSetClosurePermutation()`, `DMSetGlobalSection()`
5847 @*/
5848 PetscErrorCode DMPlexSetClosurePermutationTensor(DM dm, PetscInt point, PetscSection section)
5849 {
5850   DMLabel   label;
5851   PetscInt  dim, depth = -1, eStart = -1, Nf;
5852   PetscBool continuous = PETSC_TRUE, tensor = PETSC_TRUE;
5853 
5854   PetscFunctionBegin;
5855   PetscCall(DMGetDimension(dm, &dim));
5856   if (dim < 1) PetscFunctionReturn(PETSC_SUCCESS);
5857   if (point < 0) {
5858     PetscInt sStart, sEnd;
5859 
5860     PetscCall(DMPlexGetDepthStratum(dm, 1, &sStart, &sEnd));
5861     point = sEnd - sStart ? sStart : point;
5862   }
5863   PetscCall(DMPlexGetDepthLabel(dm, &label));
5864   if (point >= 0) PetscCall(DMLabelGetValue(label, point, &depth));
5865   if (!section) PetscCall(DMGetLocalSection(dm, &section));
5866   if (depth == 1) {
5867     eStart = point;
5868   } else if (depth == dim) {
5869     const PetscInt *cone;
5870 
5871     PetscCall(DMPlexGetCone(dm, point, &cone));
5872     if (dim == 2) eStart = cone[0];
5873     else if (dim == 3) {
5874       const PetscInt *cone2;
5875       PetscCall(DMPlexGetCone(dm, cone[0], &cone2));
5876       eStart = cone2[0];
5877     } 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);
5878   } 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);
5879 
5880   PetscCall(PetscSectionGetNumFields(section, &Nf));
5881   for (PetscInt d = 1; d <= dim; d++) {
5882     PetscInt  k, f, Nc, c, i, j, size = 0, offset = 0, foffset = 0;
5883     PetscInt *perm;
5884 
5885     for (f = 0; f < Nf; ++f) {
5886       PetscInt dof;
5887 
5888       PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
5889       PetscCheck(dim == 1 || tensor || !continuous, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Continuous field %" PetscInt_FMT " must have a tensor product discretization", f);
5890       if (!continuous && d < dim) continue;
5891       PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
5892       size += dof * Nc;
5893     }
5894     PetscCall(PetscMalloc1(size, &perm));
5895     for (f = 0; f < Nf; ++f) {
5896       switch (d) {
5897       case 1:
5898         PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
5899         if (!continuous && d < dim) continue;
5900         /*
5901          Original ordering is [ edge of length k-1; vtx0; vtx1 ]
5902          We want              [ vtx0; edge of length k-1; vtx1 ]
5903          */
5904         if (continuous) {
5905           for (c = 0; c < Nc; c++, offset++) perm[offset] = (k - 1) * Nc + c + foffset;
5906           for (i = 0; i < k - 1; i++)
5907             for (c = 0; c < Nc; c++, offset++) perm[offset] = i * Nc + c + foffset;
5908           for (c = 0; c < Nc; c++, offset++) perm[offset] = k * Nc + c + foffset;
5909           foffset = offset;
5910         } else {
5911           PetscInt dof;
5912 
5913           PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
5914           for (i = 0; i < dof * Nc; ++i, ++offset) perm[offset] = i + foffset;
5915           foffset = offset;
5916         }
5917         break;
5918       case 2:
5919         /* The original quad closure is oriented clockwise, {f, e_b, e_r, e_t, e_l, v_lb, v_rb, v_tr, v_tl} */
5920         PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
5921         if (!continuous && d < dim) continue;
5922         /* The SEM order is
5923 
5924          v_lb, {e_b}, v_rb,
5925          e^{(k-1)-i}_l, {f^{i*(k-1)}}, e^i_r,
5926          v_lt, reverse {e_t}, v_rt
5927          */
5928         if (continuous) {
5929           const PetscInt of   = 0;
5930           const PetscInt oeb  = of + PetscSqr(k - 1);
5931           const PetscInt oer  = oeb + (k - 1);
5932           const PetscInt oet  = oer + (k - 1);
5933           const PetscInt oel  = oet + (k - 1);
5934           const PetscInt ovlb = oel + (k - 1);
5935           const PetscInt ovrb = ovlb + 1;
5936           const PetscInt ovrt = ovrb + 1;
5937           const PetscInt ovlt = ovrt + 1;
5938           PetscInt       o;
5939 
5940           /* bottom */
5941           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlb * Nc + c + foffset;
5942           for (o = oeb; o < oer; ++o)
5943             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5944           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrb * Nc + c + foffset;
5945           /* middle */
5946           for (i = 0; i < k - 1; ++i) {
5947             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oel + (k - 2) - i) * Nc + c + foffset;
5948             for (o = of + (k - 1) * i; o < of + (k - 1) * (i + 1); ++o)
5949               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5950             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oer + i) * Nc + c + foffset;
5951           }
5952           /* top */
5953           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlt * Nc + c + foffset;
5954           for (o = oel - 1; o >= oet; --o)
5955             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5956           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrt * Nc + c + foffset;
5957           foffset = offset;
5958         } else {
5959           PetscInt dof;
5960 
5961           PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
5962           for (i = 0; i < dof * Nc; ++i, ++offset) perm[offset] = i + foffset;
5963           foffset = offset;
5964         }
5965         break;
5966       case 3:
5967         /* The original hex closure is
5968 
5969          {c,
5970          f_b, f_t, f_f, f_b, f_r, f_l,
5971          e_bl, e_bb, e_br, e_bf,  e_tf, e_tr, e_tb, e_tl,  e_rf, e_lf, e_lb, e_rb,
5972          v_blf, v_blb, v_brb, v_brf, v_tlf, v_trf, v_trb, v_tlb}
5973          */
5974         PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
5975         if (!continuous && d < dim) continue;
5976         /* The SEM order is
5977          Bottom Slice
5978          v_blf, {e^{(k-1)-n}_bf}, v_brf,
5979          e^{i}_bl, f^{n*(k-1)+(k-1)-i}_b, e^{(k-1)-i}_br,
5980          v_blb, {e_bb}, v_brb,
5981 
5982          Middle Slice (j)
5983          {e^{(k-1)-j}_lf}, {f^{j*(k-1)+n}_f}, e^j_rf,
5984          f^{i*(k-1)+j}_l, {c^{(j*(k-1) + i)*(k-1)+n}_t}, f^{j*(k-1)+i}_r,
5985          e^j_lb, {f^{j*(k-1)+(k-1)-n}_b}, e^{(k-1)-j}_rb,
5986 
5987          Top Slice
5988          v_tlf, {e_tf}, v_trf,
5989          e^{(k-1)-i}_tl, {f^{i*(k-1)}_t}, e^{i}_tr,
5990          v_tlb, {e^{(k-1)-n}_tb}, v_trb,
5991          */
5992         if (continuous) {
5993           const PetscInt oc    = 0;
5994           const PetscInt ofb   = oc + PetscSqr(k - 1) * (k - 1);
5995           const PetscInt oft   = ofb + PetscSqr(k - 1);
5996           const PetscInt off   = oft + PetscSqr(k - 1);
5997           const PetscInt ofk   = off + PetscSqr(k - 1);
5998           const PetscInt ofr   = ofk + PetscSqr(k - 1);
5999           const PetscInt ofl   = ofr + PetscSqr(k - 1);
6000           const PetscInt oebl  = ofl + PetscSqr(k - 1);
6001           const PetscInt oebb  = oebl + (k - 1);
6002           const PetscInt oebr  = oebb + (k - 1);
6003           const PetscInt oebf  = oebr + (k - 1);
6004           const PetscInt oetf  = oebf + (k - 1);
6005           const PetscInt oetr  = oetf + (k - 1);
6006           const PetscInt oetb  = oetr + (k - 1);
6007           const PetscInt oetl  = oetb + (k - 1);
6008           const PetscInt oerf  = oetl + (k - 1);
6009           const PetscInt oelf  = oerf + (k - 1);
6010           const PetscInt oelb  = oelf + (k - 1);
6011           const PetscInt oerb  = oelb + (k - 1);
6012           const PetscInt ovblf = oerb + (k - 1);
6013           const PetscInt ovblb = ovblf + 1;
6014           const PetscInt ovbrb = ovblb + 1;
6015           const PetscInt ovbrf = ovbrb + 1;
6016           const PetscInt ovtlf = ovbrf + 1;
6017           const PetscInt ovtrf = ovtlf + 1;
6018           const PetscInt ovtrb = ovtrf + 1;
6019           const PetscInt ovtlb = ovtrb + 1;
6020           PetscInt       o, n;
6021 
6022           /* Bottom Slice */
6023           /*   bottom */
6024           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblf * Nc + c + foffset;
6025           for (o = oetf - 1; o >= oebf; --o)
6026             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6027           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrf * Nc + c + foffset;
6028           /*   middle */
6029           for (i = 0; i < k - 1; ++i) {
6030             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebl + i) * Nc + c + foffset;
6031             for (n = 0; n < k - 1; ++n) {
6032               o = ofb + n * (k - 1) + i;
6033               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6034             }
6035             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebr + (k - 2) - i) * Nc + c + foffset;
6036           }
6037           /*   top */
6038           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblb * Nc + c + foffset;
6039           for (o = oebb; o < oebr; ++o)
6040             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6041           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrb * Nc + c + foffset;
6042 
6043           /* Middle Slice */
6044           for (j = 0; j < k - 1; ++j) {
6045             /*   bottom */
6046             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelf + (k - 2) - j) * Nc + c + foffset;
6047             for (o = off + j * (k - 1); o < off + (j + 1) * (k - 1); ++o)
6048               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6049             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerf + j) * Nc + c + foffset;
6050             /*   middle */
6051             for (i = 0; i < k - 1; ++i) {
6052               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofl + i * (k - 1) + j) * Nc + c + foffset;
6053               for (n = 0; n < k - 1; ++n)
6054                 for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oc + (j * (k - 1) + i) * (k - 1) + n) * Nc + c + foffset;
6055               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofr + j * (k - 1) + i) * Nc + c + foffset;
6056             }
6057             /*   top */
6058             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelb + j) * Nc + c + foffset;
6059             for (o = ofk + j * (k - 1) + (k - 2); o >= ofk + j * (k - 1); --o)
6060               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6061             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerb + (k - 2) - j) * Nc + c + foffset;
6062           }
6063 
6064           /* Top Slice */
6065           /*   bottom */
6066           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlf * Nc + c + foffset;
6067           for (o = oetf; o < oetr; ++o)
6068             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6069           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrf * Nc + c + foffset;
6070           /*   middle */
6071           for (i = 0; i < k - 1; ++i) {
6072             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetl + (k - 2) - i) * Nc + c + foffset;
6073             for (n = 0; n < k - 1; ++n)
6074               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oft + i * (k - 1) + n) * Nc + c + foffset;
6075             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetr + i) * Nc + c + foffset;
6076           }
6077           /*   top */
6078           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlb * Nc + c + foffset;
6079           for (o = oetl - 1; o >= oetb; --o)
6080             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6081           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrb * Nc + c + foffset;
6082 
6083           foffset = offset;
6084         } else {
6085           PetscInt dof;
6086 
6087           PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
6088           for (i = 0; i < dof * Nc; ++i, ++offset) perm[offset] = i + foffset;
6089           foffset = offset;
6090         }
6091         break;
6092       default:
6093         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "No spectral ordering for dimension %" PetscInt_FMT, d);
6094       }
6095     }
6096     PetscCheck(offset == size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Number of permutation entries %" PetscInt_FMT " != %" PetscInt_FMT, offset, size);
6097     /* Check permutation */
6098     {
6099       PetscInt *check;
6100 
6101       PetscCall(PetscMalloc1(size, &check));
6102       for (i = 0; i < size; ++i) {
6103         check[i] = -1;
6104         PetscCheck(perm[i] >= 0 && perm[i] < size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid permutation index p[%" PetscInt_FMT "] = %" PetscInt_FMT, i, perm[i]);
6105       }
6106       for (i = 0; i < size; ++i) check[perm[i]] = i;
6107       for (i = 0; i < size; ++i) PetscCheck(check[i] >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Missing permutation index %" PetscInt_FMT, i);
6108       PetscCall(PetscFree(check));
6109     }
6110     PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size, PETSC_OWN_POINTER, perm));
6111     if (d == dim) { // Add permutation for localized (in case this is a coordinate DM)
6112       PetscInt *loc_perm;
6113       PetscCall(PetscMalloc1(size * 2, &loc_perm));
6114       for (PetscInt i = 0; i < size; i++) {
6115         loc_perm[i]        = perm[i];
6116         loc_perm[size + i] = size + perm[i];
6117       }
6118       PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size * 2, PETSC_OWN_POINTER, loc_perm));
6119     }
6120   }
6121   PetscFunctionReturn(PETSC_SUCCESS);
6122 }
6123 
6124 PetscErrorCode DMPlexGetPointDualSpaceFEM(DM dm, PetscInt point, PetscInt field, PetscDualSpace *dspace)
6125 {
6126   PetscDS  prob;
6127   PetscInt depth, Nf, h;
6128   DMLabel  label;
6129 
6130   PetscFunctionBeginHot;
6131   PetscCall(DMGetDS(dm, &prob));
6132   Nf      = prob->Nf;
6133   label   = dm->depthLabel;
6134   *dspace = NULL;
6135   if (field < Nf) {
6136     PetscObject disc = prob->disc[field];
6137 
6138     if (disc->classid == PETSCFE_CLASSID) {
6139       PetscDualSpace dsp;
6140 
6141       PetscCall(PetscFEGetDualSpace((PetscFE)disc, &dsp));
6142       PetscCall(DMLabelGetNumValues(label, &depth));
6143       PetscCall(DMLabelGetValue(label, point, &h));
6144       h = depth - 1 - h;
6145       if (h) {
6146         PetscCall(PetscDualSpaceGetHeightSubspace(dsp, h, dspace));
6147       } else {
6148         *dspace = dsp;
6149       }
6150     }
6151   }
6152   PetscFunctionReturn(PETSC_SUCCESS);
6153 }
6154 
6155 static inline PetscErrorCode DMPlexVecGetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6156 {
6157   PetscScalar       *array;
6158   const PetscScalar *vArray;
6159   const PetscInt    *cone, *coneO;
6160   PetscInt           pStart, pEnd, p, numPoints, size = 0, offset = 0;
6161 
6162   PetscFunctionBeginHot;
6163   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6164   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
6165   PetscCall(DMPlexGetCone(dm, point, &cone));
6166   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
6167   if (!values || !*values) {
6168     if ((point >= pStart) && (point < pEnd)) {
6169       PetscInt dof;
6170 
6171       PetscCall(PetscSectionGetDof(section, point, &dof));
6172       size += dof;
6173     }
6174     for (p = 0; p < numPoints; ++p) {
6175       const PetscInt cp = cone[p];
6176       PetscInt       dof;
6177 
6178       if ((cp < pStart) || (cp >= pEnd)) continue;
6179       PetscCall(PetscSectionGetDof(section, cp, &dof));
6180       size += dof;
6181     }
6182     if (!values) {
6183       if (csize) *csize = size;
6184       PetscFunctionReturn(PETSC_SUCCESS);
6185     }
6186     PetscCall(DMGetWorkArray(dm, size, MPIU_SCALAR, &array));
6187   } else {
6188     array = *values;
6189   }
6190   size = 0;
6191   PetscCall(VecGetArrayRead(v, &vArray));
6192   if ((point >= pStart) && (point < pEnd)) {
6193     PetscInt           dof, off, d;
6194     const PetscScalar *varr;
6195 
6196     PetscCall(PetscSectionGetDof(section, point, &dof));
6197     PetscCall(PetscSectionGetOffset(section, point, &off));
6198     varr = PetscSafePointerPlusOffset(vArray, off);
6199     for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
6200     size += dof;
6201   }
6202   for (p = 0; p < numPoints; ++p) {
6203     const PetscInt     cp = cone[p];
6204     PetscInt           o  = coneO[p];
6205     PetscInt           dof, off, d;
6206     const PetscScalar *varr;
6207 
6208     if ((cp < pStart) || (cp >= pEnd)) continue;
6209     PetscCall(PetscSectionGetDof(section, cp, &dof));
6210     PetscCall(PetscSectionGetOffset(section, cp, &off));
6211     varr = PetscSafePointerPlusOffset(vArray, off);
6212     if (o >= 0) {
6213       for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
6214     } else {
6215       for (d = dof - 1; d >= 0; --d, ++offset) array[offset] = varr[d];
6216     }
6217     size += dof;
6218   }
6219   PetscCall(VecRestoreArrayRead(v, &vArray));
6220   if (!*values) {
6221     if (csize) *csize = size;
6222     *values = array;
6223   } else {
6224     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
6225     *csize = size;
6226   }
6227   PetscFunctionReturn(PETSC_SUCCESS);
6228 }
6229 
6230 /* Compress out points not in the section */
6231 static inline PetscErrorCode CompressPoints_Private(PetscSection section, PetscInt *numPoints, PetscInt points[])
6232 {
6233   const PetscInt np = *numPoints;
6234   PetscInt       pStart, pEnd, p, q;
6235 
6236   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6237   for (p = 0, q = 0; p < np; ++p) {
6238     const PetscInt r = points[p * 2];
6239     if ((r >= pStart) && (r < pEnd)) {
6240       points[q * 2]     = r;
6241       points[q * 2 + 1] = points[p * 2 + 1];
6242       ++q;
6243     }
6244   }
6245   *numPoints = q;
6246   return PETSC_SUCCESS;
6247 }
6248 
6249 /* Compressed closure does not apply closure permutation */
6250 PetscErrorCode DMPlexGetCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt ornt, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
6251 {
6252   const PetscInt *cla = NULL;
6253   PetscInt        np, *pts = NULL;
6254 
6255   PetscFunctionBeginHot;
6256   PetscCall(PetscSectionGetClosureIndex(section, (PetscObject)dm, clSec, clPoints));
6257   if (!ornt && *clPoints) {
6258     PetscInt dof, off;
6259 
6260     PetscCall(PetscSectionGetDof(*clSec, point, &dof));
6261     PetscCall(PetscSectionGetOffset(*clSec, point, &off));
6262     PetscCall(ISGetIndices(*clPoints, &cla));
6263     np  = dof / 2;
6264     pts = PetscSafePointerPlusOffset((PetscInt *)cla, off);
6265   } else {
6266     PetscCall(DMPlexGetTransitiveClosure_Internal(dm, point, ornt, PETSC_TRUE, &np, &pts));
6267     PetscCall(CompressPoints_Private(section, &np, pts));
6268   }
6269   *numPoints = np;
6270   *points    = pts;
6271   *clp       = cla;
6272   PetscFunctionReturn(PETSC_SUCCESS);
6273 }
6274 
6275 PetscErrorCode DMPlexRestoreCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
6276 {
6277   PetscFunctionBeginHot;
6278   if (!*clPoints) {
6279     PetscCall(DMPlexRestoreTransitiveClosure(dm, point, PETSC_TRUE, numPoints, points));
6280   } else {
6281     PetscCall(ISRestoreIndices(*clPoints, clp));
6282   }
6283   *numPoints = 0;
6284   *points    = NULL;
6285   *clSec     = NULL;
6286   *clPoints  = NULL;
6287   *clp       = NULL;
6288   PetscFunctionReturn(PETSC_SUCCESS);
6289 }
6290 
6291 static inline PetscErrorCode DMPlexVecGetClosure_Static(DM dm, PetscSection section, PetscInt numPoints, const PetscInt points[], const PetscInt clperm[], const PetscScalar vArray[], PetscInt *size, PetscScalar array[])
6292 {
6293   PetscInt            offset = 0, p;
6294   const PetscInt    **perms  = NULL;
6295   const PetscScalar **flips  = NULL;
6296 
6297   PetscFunctionBeginHot;
6298   *size = 0;
6299   PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
6300   for (p = 0; p < numPoints; p++) {
6301     const PetscInt     point = points[2 * p];
6302     const PetscInt    *perm  = perms ? perms[p] : NULL;
6303     const PetscScalar *flip  = flips ? flips[p] : NULL;
6304     PetscInt           dof, off, d;
6305     const PetscScalar *varr;
6306 
6307     PetscCall(PetscSectionGetDof(section, point, &dof));
6308     PetscCall(PetscSectionGetOffset(section, point, &off));
6309     varr = PetscSafePointerPlusOffset(vArray, off);
6310     if (clperm) {
6311       if (perm) {
6312         for (d = 0; d < dof; d++) array[clperm[offset + perm[d]]] = varr[d];
6313       } else {
6314         for (d = 0; d < dof; d++) array[clperm[offset + d]] = varr[d];
6315       }
6316       if (flip) {
6317         for (d = 0; d < dof; d++) array[clperm[offset + d]] *= flip[d];
6318       }
6319     } else {
6320       if (perm) {
6321         for (d = 0; d < dof; d++) array[offset + perm[d]] = varr[d];
6322       } else {
6323         for (d = 0; d < dof; d++) array[offset + d] = varr[d];
6324       }
6325       if (flip) {
6326         for (d = 0; d < dof; d++) array[offset + d] *= flip[d];
6327       }
6328     }
6329     offset += dof;
6330   }
6331   PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
6332   *size = offset;
6333   PetscFunctionReturn(PETSC_SUCCESS);
6334 }
6335 
6336 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[])
6337 {
6338   PetscInt offset = 0, f;
6339 
6340   PetscFunctionBeginHot;
6341   *size = 0;
6342   for (f = 0; f < numFields; ++f) {
6343     PetscInt            p;
6344     const PetscInt    **perms = NULL;
6345     const PetscScalar **flips = NULL;
6346 
6347     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6348     for (p = 0; p < numPoints; p++) {
6349       const PetscInt     point = points[2 * p];
6350       PetscInt           fdof, foff, b;
6351       const PetscScalar *varr;
6352       const PetscInt    *perm = perms ? perms[p] : NULL;
6353       const PetscScalar *flip = flips ? flips[p] : NULL;
6354 
6355       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6356       PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6357       varr = &vArray[foff];
6358       if (clperm) {
6359         if (perm) {
6360           for (b = 0; b < fdof; b++) array[clperm[offset + perm[b]]] = varr[b];
6361         } else {
6362           for (b = 0; b < fdof; b++) array[clperm[offset + b]] = varr[b];
6363         }
6364         if (flip) {
6365           for (b = 0; b < fdof; b++) array[clperm[offset + b]] *= flip[b];
6366         }
6367       } else {
6368         if (perm) {
6369           for (b = 0; b < fdof; b++) array[offset + perm[b]] = varr[b];
6370         } else {
6371           for (b = 0; b < fdof; b++) array[offset + b] = varr[b];
6372         }
6373         if (flip) {
6374           for (b = 0; b < fdof; b++) array[offset + b] *= flip[b];
6375         }
6376       }
6377       offset += fdof;
6378     }
6379     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6380   }
6381   *size = offset;
6382   PetscFunctionReturn(PETSC_SUCCESS);
6383 }
6384 
6385 PetscErrorCode DMPlexVecGetOrientedClosure_Internal(DM dm, PetscSection section, PetscBool useClPerm, Vec v, PetscInt point, PetscInt ornt, PetscInt *csize, PetscScalar *values[])
6386 {
6387   PetscSection    clSection;
6388   IS              clPoints;
6389   PetscInt       *points = NULL;
6390   const PetscInt *clp, *perm = NULL;
6391   PetscInt        depth, numFields, numPoints, asize;
6392 
6393   PetscFunctionBeginHot;
6394   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6395   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6396   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6397   PetscValidHeaderSpecific(v, VEC_CLASSID, 4);
6398   PetscCall(DMPlexGetDepth(dm, &depth));
6399   PetscCall(PetscSectionGetNumFields(section, &numFields));
6400   if (depth == 1 && numFields < 2) {
6401     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6402     PetscFunctionReturn(PETSC_SUCCESS);
6403   }
6404   /* Get points */
6405   PetscCall(DMPlexGetCompressedClosure(dm, section, point, ornt, &numPoints, &points, &clSection, &clPoints, &clp));
6406   /* Get sizes */
6407   asize = 0;
6408   for (PetscInt p = 0; p < numPoints * 2; p += 2) {
6409     PetscInt dof;
6410     PetscCall(PetscSectionGetDof(section, points[p], &dof));
6411     asize += dof;
6412   }
6413   if (values) {
6414     const PetscScalar *vArray;
6415     PetscInt           size;
6416 
6417     if (*values) {
6418       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);
6419     } else PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, values));
6420     if (useClPerm) PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, asize, &perm));
6421     PetscCall(VecGetArrayRead(v, &vArray));
6422     /* Get values */
6423     if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, numPoints, points, numFields, perm, vArray, &size, *values));
6424     else PetscCall(DMPlexVecGetClosure_Static(dm, section, numPoints, points, perm, vArray, &size, *values));
6425     PetscCheck(asize == size, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Section size %" PetscInt_FMT " does not match Vec closure size %" PetscInt_FMT, asize, size);
6426     /* Cleanup array */
6427     PetscCall(VecRestoreArrayRead(v, &vArray));
6428   }
6429   if (csize) *csize = asize;
6430   /* Cleanup points */
6431   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6432   PetscFunctionReturn(PETSC_SUCCESS);
6433 }
6434 
6435 /*@C
6436   DMPlexVecGetClosure - Get an array of the values on the closure of 'point'
6437 
6438   Not collective
6439 
6440   Input Parameters:
6441 + dm      - The `DM`
6442 . section - The section describing the layout in `v`, or `NULL` to use the default section
6443 . v       - The local vector
6444 - point   - The point in the `DM`
6445 
6446   Input/Output Parameters:
6447 + csize  - The size of the input values array, or `NULL`; on output the number of values in the closure
6448 - values - An array to use for the values, or `NULL` to have it allocated automatically;
6449            if the user provided `NULL`, it is a borrowed array and should not be freed
6450 
6451   Level: intermediate
6452 
6453   Notes:
6454   `DMPlexVecGetClosure()`/`DMPlexVecRestoreClosure()` only allocates the values array if it set to `NULL` in the
6455   calling function. This is because `DMPlexVecGetClosure()` is typically called in the inner loop of a `Vec` or `Mat`
6456   assembly function, and a user may already have allocated storage for this operation.
6457 
6458   A typical use could be
6459 .vb
6460    values = NULL;
6461    PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
6462    for (cl = 0; cl < clSize; ++cl) {
6463      <Compute on closure>
6464    }
6465    PetscCall(DMPlexVecRestoreClosure(dm, NULL, v, p, &clSize, &values));
6466 .ve
6467   or
6468 .vb
6469    PetscMalloc1(clMaxSize, &values);
6470    for (p = pStart; p < pEnd; ++p) {
6471      clSize = clMaxSize;
6472      PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
6473      for (cl = 0; cl < clSize; ++cl) {
6474        <Compute on closure>
6475      }
6476    }
6477    PetscFree(values);
6478 .ve
6479 
6480   Fortran Notes:
6481   The `csize` argument is not present in the Fortran binding since it is internal to the array.
6482 
6483 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecRestoreClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6484 @*/
6485 PetscErrorCode DMPlexVecGetClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6486 {
6487   PetscFunctionBeginHot;
6488   PetscCall(DMPlexVecGetOrientedClosure_Internal(dm, section, PETSC_TRUE, v, point, 0, csize, values));
6489   PetscFunctionReturn(PETSC_SUCCESS);
6490 }
6491 
6492 PetscErrorCode DMPlexVecGetClosureAtDepth_Internal(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt depth, PetscInt *csize, PetscScalar *values[])
6493 {
6494   DMLabel            depthLabel;
6495   PetscSection       clSection;
6496   IS                 clPoints;
6497   PetscScalar       *array;
6498   const PetscScalar *vArray;
6499   PetscInt          *points = NULL;
6500   const PetscInt    *clp, *perm = NULL;
6501   PetscInt           mdepth, numFields, numPoints, Np = 0, p, clsize, size;
6502 
6503   PetscFunctionBeginHot;
6504   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6505   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6506   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6507   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6508   PetscCall(DMPlexGetDepth(dm, &mdepth));
6509   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
6510   PetscCall(PetscSectionGetNumFields(section, &numFields));
6511   if (mdepth == 1 && numFields < 2) {
6512     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6513     PetscFunctionReturn(PETSC_SUCCESS);
6514   }
6515   /* Get points */
6516   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
6517   for (clsize = 0, p = 0; p < Np; p++) {
6518     PetscInt dof;
6519     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
6520     clsize += dof;
6521   }
6522   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &perm));
6523   /* Filter points */
6524   for (p = 0; p < numPoints * 2; p += 2) {
6525     PetscInt dep;
6526 
6527     PetscCall(DMLabelGetValue(depthLabel, points[p], &dep));
6528     if (dep != depth) continue;
6529     points[Np * 2 + 0] = points[p];
6530     points[Np * 2 + 1] = points[p + 1];
6531     ++Np;
6532   }
6533   /* Get array */
6534   if (!values || !*values) {
6535     PetscInt asize = 0, dof;
6536 
6537     for (p = 0; p < Np * 2; p += 2) {
6538       PetscCall(PetscSectionGetDof(section, points[p], &dof));
6539       asize += dof;
6540     }
6541     if (!values) {
6542       PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6543       if (csize) *csize = asize;
6544       PetscFunctionReturn(PETSC_SUCCESS);
6545     }
6546     PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, &array));
6547   } else {
6548     array = *values;
6549   }
6550   PetscCall(VecGetArrayRead(v, &vArray));
6551   /* Get values */
6552   if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, Np, points, numFields, perm, vArray, &size, array));
6553   else PetscCall(DMPlexVecGetClosure_Static(dm, section, Np, points, perm, vArray, &size, array));
6554   /* Cleanup points */
6555   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6556   /* Cleanup array */
6557   PetscCall(VecRestoreArrayRead(v, &vArray));
6558   if (!*values) {
6559     if (csize) *csize = size;
6560     *values = array;
6561   } else {
6562     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
6563     *csize = size;
6564   }
6565   PetscFunctionReturn(PETSC_SUCCESS);
6566 }
6567 
6568 /*@C
6569   DMPlexVecRestoreClosure - Restore the array of the values on the closure of 'point'
6570 
6571   Not collective
6572 
6573   Input Parameters:
6574 + dm      - The `DM`
6575 . section - The section describing the layout in `v`, or `NULL` to use the default section
6576 . v       - The local vector
6577 . point   - The point in the `DM`
6578 . csize   - The number of values in the closure, or `NULL`
6579 - values  - The array of values, which is a borrowed array and should not be freed
6580 
6581   Level: intermediate
6582 
6583   Note:
6584   The array values are discarded and not copied back into `v`. In order to copy values back to `v`, use `DMPlexVecSetClosure()`
6585 
6586   Fortran Notes:
6587   The `csize` argument is not present in the Fortran binding since it is internal to the array.
6588 
6589 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6590 @*/
6591 PetscErrorCode DMPlexVecRestoreClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6592 {
6593   PetscInt size = 0;
6594 
6595   PetscFunctionBegin;
6596   /* Should work without recalculating size */
6597   PetscCall(DMRestoreWorkArray(dm, size, MPIU_SCALAR, (void *)values));
6598   *values = NULL;
6599   PetscFunctionReturn(PETSC_SUCCESS);
6600 }
6601 
6602 static inline void add(PetscScalar *x, PetscScalar y)
6603 {
6604   *x += y;
6605 }
6606 static inline void insert(PetscScalar *x, PetscScalar y)
6607 {
6608   *x = y;
6609 }
6610 
6611 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[])
6612 {
6613   PetscInt        cdof;  /* The number of constraints on this point */
6614   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6615   PetscScalar    *a;
6616   PetscInt        off, cind = 0, k;
6617 
6618   PetscFunctionBegin;
6619   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6620   PetscCall(PetscSectionGetOffset(section, point, &off));
6621   a = &array[off];
6622   if (!cdof || setBC) {
6623     if (clperm) {
6624       if (perm) {
6625         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6626       } else {
6627         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6628       }
6629     } else {
6630       if (perm) {
6631         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6632       } else {
6633         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6634       }
6635     }
6636   } else {
6637     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6638     if (clperm) {
6639       if (perm) {
6640         for (k = 0; k < dof; ++k) {
6641           if ((cind < cdof) && (k == cdofs[cind])) {
6642             ++cind;
6643             continue;
6644           }
6645           fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6646         }
6647       } else {
6648         for (k = 0; k < dof; ++k) {
6649           if ((cind < cdof) && (k == cdofs[cind])) {
6650             ++cind;
6651             continue;
6652           }
6653           fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6654         }
6655       }
6656     } else {
6657       if (perm) {
6658         for (k = 0; k < dof; ++k) {
6659           if ((cind < cdof) && (k == cdofs[cind])) {
6660             ++cind;
6661             continue;
6662           }
6663           fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6664         }
6665       } else {
6666         for (k = 0; k < dof; ++k) {
6667           if ((cind < cdof) && (k == cdofs[cind])) {
6668             ++cind;
6669             continue;
6670           }
6671           fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6672         }
6673       }
6674     }
6675   }
6676   PetscFunctionReturn(PETSC_SUCCESS);
6677 }
6678 
6679 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[])
6680 {
6681   PetscInt        cdof;  /* The number of constraints on this point */
6682   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6683   PetscScalar    *a;
6684   PetscInt        off, cind = 0, k;
6685 
6686   PetscFunctionBegin;
6687   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6688   PetscCall(PetscSectionGetOffset(section, point, &off));
6689   a = &array[off];
6690   if (cdof) {
6691     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6692     if (clperm) {
6693       if (perm) {
6694         for (k = 0; k < dof; ++k) {
6695           if ((cind < cdof) && (k == cdofs[cind])) {
6696             fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6697             cind++;
6698           }
6699         }
6700       } else {
6701         for (k = 0; k < dof; ++k) {
6702           if ((cind < cdof) && (k == cdofs[cind])) {
6703             fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6704             cind++;
6705           }
6706         }
6707       }
6708     } else {
6709       if (perm) {
6710         for (k = 0; k < dof; ++k) {
6711           if ((cind < cdof) && (k == cdofs[cind])) {
6712             fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6713             cind++;
6714           }
6715         }
6716       } else {
6717         for (k = 0; k < dof; ++k) {
6718           if ((cind < cdof) && (k == cdofs[cind])) {
6719             fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6720             cind++;
6721           }
6722         }
6723       }
6724     }
6725   }
6726   PetscFunctionReturn(PETSC_SUCCESS);
6727 }
6728 
6729 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[])
6730 {
6731   PetscScalar    *a;
6732   PetscInt        fdof, foff, fcdof, foffset = *offset;
6733   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6734   PetscInt        cind = 0, b;
6735 
6736   PetscFunctionBegin;
6737   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6738   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6739   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6740   a = &array[foff];
6741   if (!fcdof || setBC) {
6742     if (clperm) {
6743       if (perm) {
6744         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6745       } else {
6746         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6747       }
6748     } else {
6749       if (perm) {
6750         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6751       } else {
6752         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6753       }
6754     }
6755   } else {
6756     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6757     if (clperm) {
6758       if (perm) {
6759         for (b = 0; b < fdof; b++) {
6760           if ((cind < fcdof) && (b == fcdofs[cind])) {
6761             ++cind;
6762             continue;
6763           }
6764           fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6765         }
6766       } else {
6767         for (b = 0; b < fdof; b++) {
6768           if ((cind < fcdof) && (b == fcdofs[cind])) {
6769             ++cind;
6770             continue;
6771           }
6772           fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6773         }
6774       }
6775     } else {
6776       if (perm) {
6777         for (b = 0; b < fdof; b++) {
6778           if ((cind < fcdof) && (b == fcdofs[cind])) {
6779             ++cind;
6780             continue;
6781           }
6782           fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6783         }
6784       } else {
6785         for (b = 0; b < fdof; b++) {
6786           if ((cind < fcdof) && (b == fcdofs[cind])) {
6787             ++cind;
6788             continue;
6789           }
6790           fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6791         }
6792       }
6793     }
6794   }
6795   *offset += fdof;
6796   PetscFunctionReturn(PETSC_SUCCESS);
6797 }
6798 
6799 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[])
6800 {
6801   PetscScalar    *a;
6802   PetscInt        fdof, foff, fcdof, foffset = *offset;
6803   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6804   PetscInt        Nc, cind = 0, ncind = 0, b;
6805   PetscBool       ncSet, fcSet;
6806 
6807   PetscFunctionBegin;
6808   PetscCall(PetscSectionGetFieldComponents(section, f, &Nc));
6809   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6810   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6811   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6812   a = &array[foff];
6813   if (fcdof) {
6814     /* We just override fcdof and fcdofs with Ncc and comps */
6815     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6816     if (clperm) {
6817       if (perm) {
6818         if (comps) {
6819           for (b = 0; b < fdof; b++) {
6820             ncSet = fcSet = PETSC_FALSE;
6821             if (b % Nc == comps[ncind]) {
6822               ncind = (ncind + 1) % Ncc;
6823               ncSet = PETSC_TRUE;
6824             }
6825             if ((cind < fcdof) && (b == fcdofs[cind])) {
6826               ++cind;
6827               fcSet = PETSC_TRUE;
6828             }
6829             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6830           }
6831         } else {
6832           for (b = 0; b < fdof; b++) {
6833             if ((cind < fcdof) && (b == fcdofs[cind])) {
6834               fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6835               ++cind;
6836             }
6837           }
6838         }
6839       } else {
6840         if (comps) {
6841           for (b = 0; b < fdof; b++) {
6842             ncSet = fcSet = PETSC_FALSE;
6843             if (b % Nc == comps[ncind]) {
6844               ncind = (ncind + 1) % Ncc;
6845               ncSet = PETSC_TRUE;
6846             }
6847             if ((cind < fcdof) && (b == fcdofs[cind])) {
6848               ++cind;
6849               fcSet = PETSC_TRUE;
6850             }
6851             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6852           }
6853         } else {
6854           for (b = 0; b < fdof; b++) {
6855             if ((cind < fcdof) && (b == fcdofs[cind])) {
6856               fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6857               ++cind;
6858             }
6859           }
6860         }
6861       }
6862     } else {
6863       if (perm) {
6864         if (comps) {
6865           for (b = 0; b < fdof; b++) {
6866             ncSet = fcSet = PETSC_FALSE;
6867             if (b % Nc == comps[ncind]) {
6868               ncind = (ncind + 1) % Ncc;
6869               ncSet = PETSC_TRUE;
6870             }
6871             if ((cind < fcdof) && (b == fcdofs[cind])) {
6872               ++cind;
6873               fcSet = PETSC_TRUE;
6874             }
6875             if (ncSet && fcSet) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6876           }
6877         } else {
6878           for (b = 0; b < fdof; b++) {
6879             if ((cind < fcdof) && (b == fcdofs[cind])) {
6880               fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6881               ++cind;
6882             }
6883           }
6884         }
6885       } else {
6886         if (comps) {
6887           for (b = 0; b < fdof; b++) {
6888             ncSet = fcSet = PETSC_FALSE;
6889             if (b % Nc == comps[ncind]) {
6890               ncind = (ncind + 1) % Ncc;
6891               ncSet = PETSC_TRUE;
6892             }
6893             if ((cind < fcdof) && (b == fcdofs[cind])) {
6894               ++cind;
6895               fcSet = PETSC_TRUE;
6896             }
6897             if (ncSet && fcSet) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6898           }
6899         } else {
6900           for (b = 0; b < fdof; b++) {
6901             if ((cind < fcdof) && (b == fcdofs[cind])) {
6902               fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6903               ++cind;
6904             }
6905           }
6906         }
6907       }
6908     }
6909   }
6910   *offset += fdof;
6911   PetscFunctionReturn(PETSC_SUCCESS);
6912 }
6913 
6914 static inline PetscErrorCode DMPlexVecSetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6915 {
6916   PetscScalar    *array;
6917   const PetscInt *cone, *coneO;
6918   PetscInt        pStart, pEnd, p, numPoints, off, dof;
6919 
6920   PetscFunctionBeginHot;
6921   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6922   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
6923   PetscCall(DMPlexGetCone(dm, point, &cone));
6924   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
6925   PetscCall(VecGetArray(v, &array));
6926   for (p = 0, off = 0; p <= numPoints; ++p, off += dof) {
6927     const PetscInt cp = !p ? point : cone[p - 1];
6928     const PetscInt o  = !p ? 0 : coneO[p - 1];
6929 
6930     if ((cp < pStart) || (cp >= pEnd)) {
6931       dof = 0;
6932       continue;
6933     }
6934     PetscCall(PetscSectionGetDof(section, cp, &dof));
6935     /* ADD_VALUES */
6936     {
6937       const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6938       PetscScalar    *a;
6939       PetscInt        cdof, coff, cind = 0, k;
6940 
6941       PetscCall(PetscSectionGetConstraintDof(section, cp, &cdof));
6942       PetscCall(PetscSectionGetOffset(section, cp, &coff));
6943       a = &array[coff];
6944       if (!cdof) {
6945         if (o >= 0) {
6946           for (k = 0; k < dof; ++k) a[k] += values[off + k];
6947         } else {
6948           for (k = 0; k < dof; ++k) a[k] += values[off + dof - k - 1];
6949         }
6950       } else {
6951         PetscCall(PetscSectionGetConstraintIndices(section, cp, &cdofs));
6952         if (o >= 0) {
6953           for (k = 0; k < dof; ++k) {
6954             if ((cind < cdof) && (k == cdofs[cind])) {
6955               ++cind;
6956               continue;
6957             }
6958             a[k] += values[off + k];
6959           }
6960         } else {
6961           for (k = 0; k < dof; ++k) {
6962             if ((cind < cdof) && (k == cdofs[cind])) {
6963               ++cind;
6964               continue;
6965             }
6966             a[k] += values[off + dof - k - 1];
6967           }
6968         }
6969       }
6970     }
6971   }
6972   PetscCall(VecRestoreArray(v, &array));
6973   PetscFunctionReturn(PETSC_SUCCESS);
6974 }
6975 
6976 /*@C
6977   DMPlexVecSetClosure - Set an array of the values on the closure of `point`
6978 
6979   Not collective
6980 
6981   Input Parameters:
6982 + dm      - The `DM`
6983 . section - The section describing the layout in `v`, or `NULL` to use the default section
6984 . v       - The local vector
6985 . point   - The point in the `DM`
6986 . values  - The array of values
6987 - mode    - The insert mode. One of `INSERT_ALL_VALUES`, `ADD_ALL_VALUES`, `INSERT_VALUES`, `ADD_VALUES`, `INSERT_BC_VALUES`, and `ADD_BC_VALUES`,
6988          where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions.
6989 
6990   Level: intermediate
6991 
6992 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`
6993 @*/
6994 PetscErrorCode DMPlexVecSetClosure(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6995 {
6996   PetscSection    clSection;
6997   IS              clPoints;
6998   PetscScalar    *array;
6999   PetscInt       *points = NULL;
7000   const PetscInt *clp, *clperm = NULL;
7001   PetscInt        depth, numFields, numPoints, p, clsize;
7002 
7003   PetscFunctionBeginHot;
7004   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7005   if (!section) PetscCall(DMGetLocalSection(dm, &section));
7006   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7007   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
7008   PetscCall(DMPlexGetDepth(dm, &depth));
7009   PetscCall(PetscSectionGetNumFields(section, &numFields));
7010   if (depth == 1 && numFields < 2 && mode == ADD_VALUES) {
7011     PetscCall(DMPlexVecSetClosure_Depth1_Static(dm, section, v, point, values, mode));
7012     PetscFunctionReturn(PETSC_SUCCESS);
7013   }
7014   /* Get points */
7015   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
7016   for (clsize = 0, p = 0; p < numPoints; p++) {
7017     PetscInt dof;
7018     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
7019     clsize += dof;
7020   }
7021   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
7022   /* Get array */
7023   PetscCall(VecGetArray(v, &array));
7024   /* Get values */
7025   if (numFields > 0) {
7026     PetscInt offset = 0, f;
7027     for (f = 0; f < numFields; ++f) {
7028       const PetscInt    **perms = NULL;
7029       const PetscScalar **flips = NULL;
7030 
7031       PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7032       switch (mode) {
7033       case INSERT_VALUES:
7034         for (p = 0; p < numPoints; p++) {
7035           const PetscInt     point = points[2 * p];
7036           const PetscInt    *perm  = perms ? perms[p] : NULL;
7037           const PetscScalar *flip  = flips ? flips[p] : NULL;
7038           PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, clperm, values, &offset, array));
7039         }
7040         break;
7041       case INSERT_ALL_VALUES:
7042         for (p = 0; p < numPoints; p++) {
7043           const PetscInt     point = points[2 * p];
7044           const PetscInt    *perm  = perms ? perms[p] : NULL;
7045           const PetscScalar *flip  = flips ? flips[p] : NULL;
7046           PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, clperm, values, &offset, array));
7047         }
7048         break;
7049       case INSERT_BC_VALUES:
7050         for (p = 0; p < numPoints; p++) {
7051           const PetscInt     point = points[2 * p];
7052           const PetscInt    *perm  = perms ? perms[p] : NULL;
7053           const PetscScalar *flip  = flips ? flips[p] : NULL;
7054           PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, insert, clperm, values, &offset, array));
7055         }
7056         break;
7057       case ADD_VALUES:
7058         for (p = 0; p < numPoints; p++) {
7059           const PetscInt     point = points[2 * p];
7060           const PetscInt    *perm  = perms ? perms[p] : NULL;
7061           const PetscScalar *flip  = flips ? flips[p] : NULL;
7062           PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, clperm, values, &offset, array));
7063         }
7064         break;
7065       case ADD_ALL_VALUES:
7066         for (p = 0; p < numPoints; p++) {
7067           const PetscInt     point = points[2 * p];
7068           const PetscInt    *perm  = perms ? perms[p] : NULL;
7069           const PetscScalar *flip  = flips ? flips[p] : NULL;
7070           PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, clperm, values, &offset, array));
7071         }
7072         break;
7073       case ADD_BC_VALUES:
7074         for (p = 0; p < numPoints; p++) {
7075           const PetscInt     point = points[2 * p];
7076           const PetscInt    *perm  = perms ? perms[p] : NULL;
7077           const PetscScalar *flip  = flips ? flips[p] : NULL;
7078           PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, add, clperm, values, &offset, array));
7079         }
7080         break;
7081       default:
7082         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
7083       }
7084       PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7085     }
7086   } else {
7087     PetscInt            dof, off;
7088     const PetscInt    **perms = NULL;
7089     const PetscScalar **flips = NULL;
7090 
7091     PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
7092     switch (mode) {
7093     case INSERT_VALUES:
7094       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7095         const PetscInt     point = points[2 * p];
7096         const PetscInt    *perm  = perms ? perms[p] : NULL;
7097         const PetscScalar *flip  = flips ? flips[p] : NULL;
7098         PetscCall(PetscSectionGetDof(section, point, &dof));
7099         PetscCall(updatePoint_private(section, point, dof, insert, PETSC_FALSE, perm, flip, clperm, values, off, array));
7100       }
7101       break;
7102     case INSERT_ALL_VALUES:
7103       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7104         const PetscInt     point = points[2 * p];
7105         const PetscInt    *perm  = perms ? perms[p] : NULL;
7106         const PetscScalar *flip  = flips ? flips[p] : NULL;
7107         PetscCall(PetscSectionGetDof(section, point, &dof));
7108         PetscCall(updatePoint_private(section, point, dof, insert, PETSC_TRUE, perm, flip, clperm, values, off, array));
7109       }
7110       break;
7111     case INSERT_BC_VALUES:
7112       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7113         const PetscInt     point = points[2 * p];
7114         const PetscInt    *perm  = perms ? perms[p] : NULL;
7115         const PetscScalar *flip  = flips ? flips[p] : NULL;
7116         PetscCall(PetscSectionGetDof(section, point, &dof));
7117         PetscCall(updatePointBC_private(section, point, dof, insert, perm, flip, clperm, values, off, array));
7118       }
7119       break;
7120     case ADD_VALUES:
7121       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7122         const PetscInt     point = points[2 * p];
7123         const PetscInt    *perm  = perms ? perms[p] : NULL;
7124         const PetscScalar *flip  = flips ? flips[p] : NULL;
7125         PetscCall(PetscSectionGetDof(section, point, &dof));
7126         PetscCall(updatePoint_private(section, point, dof, add, PETSC_FALSE, perm, flip, clperm, values, off, array));
7127       }
7128       break;
7129     case ADD_ALL_VALUES:
7130       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7131         const PetscInt     point = points[2 * p];
7132         const PetscInt    *perm  = perms ? perms[p] : NULL;
7133         const PetscScalar *flip  = flips ? flips[p] : NULL;
7134         PetscCall(PetscSectionGetDof(section, point, &dof));
7135         PetscCall(updatePoint_private(section, point, dof, add, PETSC_TRUE, perm, flip, clperm, values, off, array));
7136       }
7137       break;
7138     case ADD_BC_VALUES:
7139       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7140         const PetscInt     point = points[2 * p];
7141         const PetscInt    *perm  = perms ? perms[p] : NULL;
7142         const PetscScalar *flip  = flips ? flips[p] : NULL;
7143         PetscCall(PetscSectionGetDof(section, point, &dof));
7144         PetscCall(updatePointBC_private(section, point, dof, add, perm, flip, clperm, values, off, array));
7145       }
7146       break;
7147     default:
7148       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
7149     }
7150     PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
7151   }
7152   /* Cleanup points */
7153   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
7154   /* Cleanup array */
7155   PetscCall(VecRestoreArray(v, &array));
7156   PetscFunctionReturn(PETSC_SUCCESS);
7157 }
7158 
7159 /* Check whether the given point is in the label. If not, update the offset to skip this point */
7160 static inline PetscErrorCode CheckPoint_Private(DMLabel label, PetscInt labelId, PetscSection section, PetscInt point, PetscInt f, PetscInt *offset, PetscBool *contains)
7161 {
7162   PetscFunctionBegin;
7163   *contains = PETSC_TRUE;
7164   if (label) {
7165     PetscInt fdof;
7166 
7167     PetscCall(DMLabelStratumHasPoint(label, labelId, point, contains));
7168     if (!*contains) {
7169       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7170       *offset += fdof;
7171       PetscFunctionReturn(PETSC_SUCCESS);
7172     }
7173   }
7174   PetscFunctionReturn(PETSC_SUCCESS);
7175 }
7176 
7177 /* Unlike DMPlexVecSetClosure(), this uses plex-native closure permutation, not a user-specified permutation such as DMPlexSetClosurePermutationTensor(). */
7178 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)
7179 {
7180   PetscSection    clSection;
7181   IS              clPoints;
7182   PetscScalar    *array;
7183   PetscInt       *points = NULL;
7184   const PetscInt *clp;
7185   PetscInt        numFields, numPoints, p;
7186   PetscInt        offset = 0, f;
7187 
7188   PetscFunctionBeginHot;
7189   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7190   if (!section) PetscCall(DMGetLocalSection(dm, &section));
7191   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7192   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
7193   PetscCall(PetscSectionGetNumFields(section, &numFields));
7194   /* Get points */
7195   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
7196   /* Get array */
7197   PetscCall(VecGetArray(v, &array));
7198   /* Get values */
7199   for (f = 0; f < numFields; ++f) {
7200     const PetscInt    **perms = NULL;
7201     const PetscScalar **flips = NULL;
7202     PetscBool           contains;
7203 
7204     if (!fieldActive[f]) {
7205       for (p = 0; p < numPoints * 2; p += 2) {
7206         PetscInt fdof;
7207         PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
7208         offset += fdof;
7209       }
7210       continue;
7211     }
7212     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7213     switch (mode) {
7214     case INSERT_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, insert, PETSC_FALSE, NULL, values, &offset, array));
7222       }
7223       break;
7224     case INSERT_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, insert, PETSC_TRUE, NULL, values, &offset, array));
7232       }
7233       break;
7234     case INSERT_BC_VALUES:
7235       for (p = 0; p < numPoints; p++) {
7236         const PetscInt     point = points[2 * p];
7237         const PetscInt    *perm  = perms ? perms[p] : NULL;
7238         const PetscScalar *flip  = flips ? flips[p] : NULL;
7239         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7240         if (!contains) continue;
7241         PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, Ncc, comps, insert, NULL, values, &offset, array));
7242       }
7243       break;
7244     case ADD_VALUES:
7245       for (p = 0; p < numPoints; p++) {
7246         const PetscInt     point = points[2 * p];
7247         const PetscInt    *perm  = perms ? perms[p] : NULL;
7248         const PetscScalar *flip  = flips ? flips[p] : NULL;
7249         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7250         if (!contains) continue;
7251         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, NULL, values, &offset, array));
7252       }
7253       break;
7254     case ADD_ALL_VALUES:
7255       for (p = 0; p < numPoints; p++) {
7256         const PetscInt     point = points[2 * p];
7257         const PetscInt    *perm  = perms ? perms[p] : NULL;
7258         const PetscScalar *flip  = flips ? flips[p] : NULL;
7259         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7260         if (!contains) continue;
7261         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, NULL, values, &offset, array));
7262       }
7263       break;
7264     default:
7265       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
7266     }
7267     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7268   }
7269   /* Cleanup points */
7270   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
7271   /* Cleanup array */
7272   PetscCall(VecRestoreArray(v, &array));
7273   PetscFunctionReturn(PETSC_SUCCESS);
7274 }
7275 
7276 static PetscErrorCode DMPlexPrintMatSetValues(PetscViewer viewer, Mat A, PetscInt point, PetscInt numRIndices, const PetscInt rindices[], PetscInt numCIndices, const PetscInt cindices[], const PetscScalar values[])
7277 {
7278   PetscMPIInt rank;
7279   PetscInt    i, j;
7280 
7281   PetscFunctionBegin;
7282   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7283   PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat for point %" PetscInt_FMT "\n", rank, point));
7284   for (i = 0; i < numRIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat row indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, rindices[i]));
7285   for (i = 0; i < numCIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat col indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, cindices[i]));
7286   numCIndices = numCIndices ? numCIndices : numRIndices;
7287   if (!values) PetscFunctionReturn(PETSC_SUCCESS);
7288   for (i = 0; i < numRIndices; i++) {
7289     PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]", rank));
7290     for (j = 0; j < numCIndices; j++) {
7291 #if defined(PETSC_USE_COMPLEX)
7292       PetscCall(PetscViewerASCIIPrintf(viewer, " (%g,%g)", (double)PetscRealPart(values[i * numCIndices + j]), (double)PetscImaginaryPart(values[i * numCIndices + j])));
7293 #else
7294       PetscCall(PetscViewerASCIIPrintf(viewer, " %g", (double)values[i * numCIndices + j]));
7295 #endif
7296     }
7297     PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
7298   }
7299   PetscFunctionReturn(PETSC_SUCCESS);
7300 }
7301 
7302 /*
7303   DMPlexGetIndicesPoint_Internal - Add the indices for dofs on a point to an index array
7304 
7305   Input Parameters:
7306 + section - The section for this data layout
7307 . islocal - Is the section (and thus indices being requested) local or global?
7308 . point   - The point contributing dofs with these indices
7309 . off     - The global offset of this point
7310 . loff    - The local offset of each field
7311 . setBC   - The flag determining whether to include indices of boundary values
7312 . perm    - A permutation of the dofs on this point, or NULL
7313 - indperm - A permutation of the entire indices array, or NULL
7314 
7315   Output Parameter:
7316 . indices - Indices for dofs on this point
7317 
7318   Level: developer
7319 
7320   Note: The indices could be local or global, depending on the value of 'off'.
7321 */
7322 PetscErrorCode DMPlexGetIndicesPoint_Internal(PetscSection section, PetscBool islocal, PetscInt point, PetscInt off, PetscInt *loff, PetscBool setBC, const PetscInt perm[], const PetscInt indperm[], PetscInt indices[])
7323 {
7324   PetscInt        dof;   /* The number of unknowns on this point */
7325   PetscInt        cdof;  /* The number of constraints on this point */
7326   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
7327   PetscInt        cind = 0, k;
7328 
7329   PetscFunctionBegin;
7330   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
7331   PetscCall(PetscSectionGetDof(section, point, &dof));
7332   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
7333   if (!cdof || setBC) {
7334     for (k = 0; k < dof; ++k) {
7335       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
7336       const PetscInt ind    = indperm ? indperm[preind] : preind;
7337 
7338       indices[ind] = off + k;
7339     }
7340   } else {
7341     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
7342     for (k = 0; k < dof; ++k) {
7343       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
7344       const PetscInt ind    = indperm ? indperm[preind] : preind;
7345 
7346       if ((cind < cdof) && (k == cdofs[cind])) {
7347         /* Insert check for returning constrained indices */
7348         indices[ind] = -(off + k + 1);
7349         ++cind;
7350       } else {
7351         indices[ind] = off + k - (islocal ? 0 : cind);
7352       }
7353     }
7354   }
7355   *loff += dof;
7356   PetscFunctionReturn(PETSC_SUCCESS);
7357 }
7358 
7359 /*
7360  DMPlexGetIndicesPointFields_Internal - gets section indices for a point in its canonical ordering.
7361 
7362  Input Parameters:
7363 + section - a section (global or local)
7364 - islocal - `PETSC_TRUE` if requesting local indices (i.e., section is local); `PETSC_FALSE` for global
7365 . point - point within section
7366 . off - The offset of this point in the (local or global) indexed space - should match islocal and (usually) the section
7367 . foffs - array of length numFields containing the offset in canonical point ordering (the location in indices) of each field
7368 . setBC - identify constrained (boundary condition) points via involution.
7369 . perms - perms[f][permsoff][:] is a permutation of dofs within each field
7370 . permsoff - offset
7371 - indperm - index permutation
7372 
7373  Output Parameter:
7374 . foffs - each entry is incremented by the number of (unconstrained if setBC=FALSE) dofs in that field
7375 . indices - array to hold indices (as defined by section) of each dof associated with point
7376 
7377  Notes:
7378  If section is local and setBC=true, there is no distinction between constrained and unconstrained dofs.
7379  If section is local and setBC=false, the indices for constrained points are the involution -(i+1) of their position
7380  in the local vector.
7381 
7382  If section is global and setBC=false, the indices for constrained points are negative (and their value is not
7383  significant).  It is invalid to call with a global section and setBC=true.
7384 
7385  Developer Note:
7386  The section is only used for field layout, so islocal is technically a statement about the offset (off).  At some point
7387  in the future, global sections may have fields set, in which case we could pass the global section and obtain the
7388  offset could be obtained from the section instead of passing it explicitly as we do now.
7389 
7390  Example:
7391  Suppose a point contains one field with three components, and for which the unconstrained indices are {10, 11, 12}.
7392  When the middle component is constrained, we get the array {10, -12, 12} for (islocal=TRUE, setBC=FALSE).
7393  Note that -12 is the involution of 11, so the user can involute negative indices to recover local indices.
7394  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.
7395 
7396  Level: developer
7397 */
7398 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[])
7399 {
7400   PetscInt numFields, foff, f;
7401 
7402   PetscFunctionBegin;
7403   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
7404   PetscCall(PetscSectionGetNumFields(section, &numFields));
7405   for (f = 0, foff = 0; f < numFields; ++f) {
7406     PetscInt        fdof, cfdof;
7407     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7408     PetscInt        cind = 0, b;
7409     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7410 
7411     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7412     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7413     if (!cfdof || setBC) {
7414       for (b = 0; b < fdof; ++b) {
7415         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7416         const PetscInt ind    = indperm ? indperm[preind] : preind;
7417 
7418         indices[ind] = off + foff + b;
7419       }
7420     } else {
7421       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7422       for (b = 0; b < fdof; ++b) {
7423         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7424         const PetscInt ind    = indperm ? indperm[preind] : preind;
7425 
7426         if ((cind < cfdof) && (b == fcdofs[cind])) {
7427           indices[ind] = -(off + foff + b + 1);
7428           ++cind;
7429         } else {
7430           indices[ind] = off + foff + b - (islocal ? 0 : cind);
7431         }
7432       }
7433     }
7434     foff += (setBC || islocal ? fdof : (fdof - cfdof));
7435     foffs[f] += fdof;
7436   }
7437   PetscFunctionReturn(PETSC_SUCCESS);
7438 }
7439 
7440 /*
7441   This version believes the globalSection offsets for each field, rather than just the point offset
7442 
7443  . foffs - The offset into 'indices' for each field, since it is segregated by field
7444 
7445  Notes:
7446  The semantics of this function relate to that of setBC=FALSE in DMPlexGetIndicesPointFields_Internal.
7447  Since this function uses global indices, setBC=TRUE would be invalid, so no such argument exists.
7448 */
7449 static PetscErrorCode DMPlexGetIndicesPointFieldsSplit_Internal(PetscSection section, PetscSection globalSection, PetscInt point, PetscInt foffs[], const PetscInt ***perms, PetscInt permsoff, const PetscInt indperm[], PetscInt indices[])
7450 {
7451   PetscInt numFields, foff, f;
7452 
7453   PetscFunctionBegin;
7454   PetscCall(PetscSectionGetNumFields(section, &numFields));
7455   for (f = 0; f < numFields; ++f) {
7456     PetscInt        fdof, cfdof;
7457     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7458     PetscInt        cind = 0, b;
7459     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7460 
7461     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7462     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7463     PetscCall(PetscSectionGetFieldOffset(globalSection, point, f, &foff));
7464     if (!cfdof) {
7465       for (b = 0; b < fdof; ++b) {
7466         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7467         const PetscInt ind    = indperm ? indperm[preind] : preind;
7468 
7469         indices[ind] = foff + b;
7470       }
7471     } else {
7472       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7473       for (b = 0; b < fdof; ++b) {
7474         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7475         const PetscInt ind    = indperm ? indperm[preind] : preind;
7476 
7477         if ((cind < cfdof) && (b == fcdofs[cind])) {
7478           indices[ind] = -(foff + b + 1);
7479           ++cind;
7480         } else {
7481           indices[ind] = foff + b - cind;
7482         }
7483       }
7484     }
7485     foffs[f] += fdof;
7486   }
7487   PetscFunctionReturn(PETSC_SUCCESS);
7488 }
7489 
7490 static PetscErrorCode DMPlexAnchorsGetSubMatIndices(PetscInt nPoints, const PetscInt pnts[], PetscSection section, PetscSection cSec, PetscInt tmpIndices[], PetscInt fieldOffsets[], PetscInt indices[], const PetscInt ***perms)
7491 {
7492   PetscInt numFields, sStart, sEnd, cStart, cEnd;
7493 
7494   PetscFunctionBegin;
7495   PetscCall(PetscSectionGetNumFields(section, &numFields));
7496   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
7497   PetscCall(PetscSectionGetChart(cSec, &cStart, &cEnd));
7498   for (PetscInt p = 0; p < nPoints; p++) {
7499     PetscInt     b       = pnts[2 * p];
7500     PetscInt     bSecDof = 0, bOff;
7501     PetscInt     cSecDof = 0;
7502     PetscSection indices_section;
7503 
7504     if (b >= sStart && b < sEnd) PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7505     if (!bSecDof) continue;
7506     if (b >= cStart && b < cEnd) PetscCall(PetscSectionGetDof(cSec, b, &cSecDof));
7507     indices_section = cSecDof > 0 ? cSec : section;
7508     if (numFields) {
7509       PetscInt fStart[32], fEnd[32];
7510 
7511       fStart[0] = 0;
7512       fEnd[0]   = 0;
7513       for (PetscInt f = 0; f < numFields; f++) {
7514         PetscInt fDof = 0;
7515 
7516         PetscCall(PetscSectionGetFieldDof(indices_section, b, f, &fDof));
7517         fStart[f + 1] = fStart[f] + fDof;
7518         fEnd[f + 1]   = fStart[f + 1];
7519       }
7520       PetscCall(PetscSectionGetOffset(indices_section, b, &bOff));
7521       // only apply permutations on one side
7522       PetscCall(DMPlexGetIndicesPointFields_Internal(indices_section, PETSC_TRUE, b, bOff, fEnd, PETSC_TRUE, perms, perms ? p : -1, NULL, tmpIndices));
7523       for (PetscInt f = 0; f < numFields; f++) {
7524         for (PetscInt i = fStart[f]; i < fEnd[f]; i++) { indices[fieldOffsets[f]++] = (cSecDof > 0) ? tmpIndices[i] : -(tmpIndices[i] + 1); }
7525       }
7526     } else {
7527       PetscInt bEnd = 0;
7528 
7529       PetscCall(PetscSectionGetOffset(indices_section, b, &bOff));
7530       PetscCall(DMPlexGetIndicesPoint_Internal(indices_section, PETSC_TRUE, b, bOff, &bEnd, PETSC_TRUE, (perms && perms[0]) ? perms[0][p] : NULL, NULL, tmpIndices));
7531 
7532       for (PetscInt i = 0; i < bEnd; i++) indices[fieldOffsets[0]++] = (cSecDof > 0) ? tmpIndices[i] : -(tmpIndices[i] + 1);
7533     }
7534   }
7535   PetscFunctionReturn(PETSC_SUCCESS);
7536 }
7537 
7538 PETSC_INTERN PetscErrorCode DMPlexAnchorsGetSubMatModification(DM dm, PetscSection section, PetscInt numPoints, PetscInt numIndices, const PetscInt points[], const PetscInt ***perms, PetscInt *outNumPoints, PetscInt *outNumIndices, PetscInt *outPoints[], PetscInt offsets[], PetscScalar *outMat[])
7539 {
7540   Mat             cMat;
7541   PetscSection    aSec, cSec;
7542   IS              aIS;
7543   PetscInt        aStart = -1, aEnd = -1;
7544   PetscInt        sStart = -1, sEnd = -1;
7545   PetscInt        cStart = -1, cEnd = -1;
7546   const PetscInt *anchors;
7547   PetscInt        numFields, p;
7548   PetscInt        newNumPoints = 0, newNumIndices = 0;
7549   PetscInt       *newPoints, *indices, *newIndices, *tmpIndices, *tmpNewIndices;
7550   PetscInt        oldOffsets[32];
7551   PetscInt        newOffsets[32];
7552   PetscInt        oldOffsetsCopy[32];
7553   PetscInt        newOffsetsCopy[32];
7554   PetscScalar    *modMat         = NULL;
7555   PetscBool       anyConstrained = PETSC_FALSE;
7556 
7557   PetscFunctionBegin;
7558   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7559   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7560   PetscCall(PetscSectionGetNumFields(section, &numFields));
7561 
7562   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
7563   /* if there are point-to-point constraints */
7564   if (aSec) {
7565     PetscCall(PetscArrayzero(newOffsets, 32));
7566     PetscCall(PetscArrayzero(oldOffsets, 32));
7567     PetscCall(ISGetIndices(aIS, &anchors));
7568     PetscCall(PetscSectionGetChart(aSec, &aStart, &aEnd));
7569     PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
7570     /* figure out how many points are going to be in the new element matrix
7571      * (we allow double counting, because it's all just going to be summed
7572      * into the global matrix anyway) */
7573     for (p = 0; p < 2 * numPoints; p += 2) {
7574       PetscInt b    = points[p];
7575       PetscInt bDof = 0, bSecDof = 0;
7576 
7577       if (b >= sStart && b < sEnd) PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7578       if (!bSecDof) continue;
7579 
7580       for (PetscInt f = 0; f < numFields; f++) {
7581         PetscInt fDof = 0;
7582 
7583         PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7584         oldOffsets[f + 1] += fDof;
7585       }
7586       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7587       if (bDof) {
7588         /* this point is constrained */
7589         /* it is going to be replaced by its anchors */
7590         PetscInt bOff, q;
7591 
7592         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7593         for (q = 0; q < bDof; q++) {
7594           PetscInt a    = anchors[bOff + q];
7595           PetscInt aDof = 0;
7596 
7597           if (a >= sStart && a < sEnd) PetscCall(PetscSectionGetDof(section, a, &aDof));
7598           if (aDof) {
7599             anyConstrained = PETSC_TRUE;
7600             newNumPoints += 1;
7601           }
7602           newNumIndices += aDof;
7603           for (PetscInt f = 0; f < numFields; ++f) {
7604             PetscInt fDof = 0;
7605 
7606             if (a >= sStart && a < sEnd) PetscCall(PetscSectionGetFieldDof(section, a, f, &fDof));
7607             newOffsets[f + 1] += fDof;
7608           }
7609         }
7610       } else {
7611         /* this point is not constrained */
7612         newNumPoints++;
7613         newNumIndices += bSecDof;
7614         for (PetscInt f = 0; f < numFields; ++f) {
7615           PetscInt fDof;
7616 
7617           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7618           newOffsets[f + 1] += fDof;
7619         }
7620       }
7621     }
7622   }
7623   if (!anyConstrained) {
7624     if (outNumPoints) *outNumPoints = 0;
7625     if (outNumIndices) *outNumIndices = 0;
7626     if (outPoints) *outPoints = NULL;
7627     if (outMat) *outMat = NULL;
7628     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7629     PetscFunctionReturn(PETSC_SUCCESS);
7630   }
7631 
7632   if (outNumPoints) *outNumPoints = newNumPoints;
7633   if (outNumIndices) *outNumIndices = newNumIndices;
7634 
7635   for (PetscInt f = 0; f < numFields; ++f) newOffsets[f + 1] += newOffsets[f];
7636   for (PetscInt f = 0; f < numFields; ++f) oldOffsets[f + 1] += oldOffsets[f];
7637 
7638   if (!outPoints && !outMat) {
7639     if (offsets) {
7640       for (PetscInt f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7641     }
7642     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7643     PetscFunctionReturn(PETSC_SUCCESS);
7644   }
7645 
7646   PetscCheck(!numFields || newOffsets[numFields] == newNumIndices, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, newOffsets[numFields], newNumIndices);
7647   PetscCheck(!numFields || oldOffsets[numFields] == numIndices, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, oldOffsets[numFields], numIndices);
7648 
7649   PetscCall(DMGetDefaultConstraints(dm, &cSec, &cMat, NULL));
7650   PetscCall(PetscSectionGetChart(cSec, &cStart, &cEnd));
7651 
7652   /* output arrays */
7653   PetscCall(DMGetWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7654   PetscCall(PetscArrayzero(newPoints, 2 * newNumPoints));
7655 
7656   // get the new Points
7657   for (PetscInt p = 0, newP = 0; p < numPoints; p++) {
7658     PetscInt b    = points[2 * p];
7659     PetscInt bDof = 0, bSecDof = 0, bOff;
7660 
7661     if (b >= sStart && b < sEnd) PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7662     if (!bSecDof) continue;
7663     if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7664     if (bDof) {
7665       PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7666       for (PetscInt q = 0; q < bDof; q++) {
7667         PetscInt a = anchors[bOff + q], aDof = 0;
7668 
7669         if (a >= sStart && a < sEnd) PetscCall(PetscSectionGetDof(section, a, &aDof));
7670         if (aDof) {
7671           newPoints[2 * newP]     = a;
7672           newPoints[2 * newP + 1] = 0; // orientations are accounted for in constructing the matrix, newly added points are in default orientation
7673           newP++;
7674         }
7675       }
7676     } else {
7677       newPoints[2 * newP]     = b;
7678       newPoints[2 * newP + 1] = points[2 * p + 1];
7679       newP++;
7680     }
7681   }
7682 
7683   if (outMat) {
7684     PetscScalar *tmpMat;
7685     PetscCall(PetscArraycpy(oldOffsetsCopy, oldOffsets, 32));
7686     PetscCall(PetscArraycpy(newOffsetsCopy, newOffsets, 32));
7687 
7688     PetscCall(DMGetWorkArray(dm, numIndices, MPIU_INT, &indices));
7689     PetscCall(DMGetWorkArray(dm, numIndices, MPIU_INT, &tmpIndices));
7690     PetscCall(DMGetWorkArray(dm, newNumIndices, MPIU_INT, &newIndices));
7691     PetscCall(DMGetWorkArray(dm, newNumIndices, MPIU_INT, &tmpNewIndices));
7692 
7693     for (PetscInt i = 0; i < numIndices; i++) indices[i] = -1;
7694     for (PetscInt i = 0; i < newNumIndices; i++) newIndices[i] = -1;
7695 
7696     PetscCall(DMPlexAnchorsGetSubMatIndices(numPoints, points, section, cSec, tmpIndices, oldOffsetsCopy, indices, perms));
7697     PetscCall(DMPlexAnchorsGetSubMatIndices(newNumPoints, newPoints, section, section, tmpNewIndices, newOffsetsCopy, newIndices, NULL));
7698 
7699     PetscCall(DMGetWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &modMat));
7700     PetscCall(DMGetWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &tmpMat));
7701     PetscCall(PetscArrayzero(modMat, newNumIndices * numIndices));
7702     // for each field, insert the anchor modification into modMat
7703     for (PetscInt f = 0; f < PetscMax(1, numFields); f++) {
7704       PetscInt fStart    = oldOffsets[f];
7705       PetscInt fNewStart = newOffsets[f];
7706       for (PetscInt p = 0, newP = 0, o = fStart, oNew = fNewStart; p < numPoints; p++) {
7707         PetscInt b    = points[2 * p];
7708         PetscInt bDof = 0, bSecDof = 0, bOff;
7709 
7710         if (b >= sStart && b < sEnd) {
7711           if (numFields) {
7712             PetscCall(PetscSectionGetFieldDof(section, b, f, &bSecDof));
7713           } else {
7714             PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7715           }
7716         }
7717         if (!bSecDof) continue;
7718         if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7719         if (bDof) {
7720           PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7721           for (PetscInt q = 0; q < bDof; q++, newP++) {
7722             PetscInt a = anchors[bOff + q], aDof = 0;
7723 
7724             if (a >= sStart && a < sEnd) {
7725               if (numFields) {
7726                 PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
7727               } else {
7728                 PetscCall(PetscSectionGetDof(section, a, &aDof));
7729               }
7730             }
7731             if (aDof) {
7732               PetscCall(MatGetValues(cMat, bSecDof, &indices[o], aDof, &newIndices[oNew], tmpMat));
7733               for (PetscInt d = 0; d < bSecDof; d++) {
7734                 for (PetscInt e = 0; e < aDof; e++) modMat[(o + d) * newNumIndices + oNew + e] = tmpMat[d * aDof + e];
7735               }
7736             }
7737             oNew += aDof;
7738           }
7739         } else {
7740           // Insert the identity matrix in this block
7741           for (PetscInt d = 0; d < bSecDof; d++) modMat[(o + d) * newNumIndices + oNew + d] = 1;
7742           oNew += bSecDof;
7743           newP++;
7744         }
7745         o += bSecDof;
7746       }
7747     }
7748 
7749     *outMat = modMat;
7750 
7751     PetscCall(DMRestoreWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &tmpMat));
7752     PetscCall(DMRestoreWorkArray(dm, newNumIndices, MPIU_INT, &tmpNewIndices));
7753     PetscCall(DMRestoreWorkArray(dm, newNumIndices, MPIU_INT, &newIndices));
7754     PetscCall(DMRestoreWorkArray(dm, numIndices, MPIU_INT, &tmpIndices));
7755     PetscCall(DMRestoreWorkArray(dm, numIndices, MPIU_INT, &indices));
7756   }
7757   PetscCall(ISRestoreIndices(aIS, &anchors));
7758 
7759   /* output */
7760   if (outPoints) {
7761     *outPoints = newPoints;
7762   } else {
7763     PetscCall(DMRestoreWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7764   }
7765   for (PetscInt f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7766   PetscFunctionReturn(PETSC_SUCCESS);
7767 }
7768 
7769 PETSC_INTERN PetscErrorCode DMPlexAnchorsModifyMat_Internal(DM dm, PetscSection section, PetscInt numPoints, PetscInt numIndices, const PetscInt points[], const PetscInt ***perms, PetscInt numRows, PetscInt numCols, const PetscScalar values[], PetscInt *outNumPoints, PetscInt *outNumIndices, PetscInt *outPoints[], PetscScalar *outValues[], PetscInt offsets[], PetscBool multiplyRight, PetscBool multiplyLeft)
7770 {
7771   PetscScalar *modMat        = NULL;
7772   PetscInt     newNumIndices = -1;
7773 
7774   PetscFunctionBegin;
7775   /* If M is the matrix represented by values, get the matrix C such that we will add M * C (or, if multiplyLeft, C^T * M * C) into the global matrix.
7776      modMat is that matrix C */
7777   PetscCall(DMPlexAnchorsGetSubMatModification(dm, section, numPoints, numIndices, points, perms, outNumPoints, &newNumIndices, outPoints, offsets, outValues ? &modMat : NULL));
7778   if (outNumIndices) *outNumIndices = newNumIndices;
7779   if (modMat) {
7780     const PetscScalar *newValues = values;
7781 
7782     if (multiplyRight) {
7783       PetscScalar *newNewValues = NULL;
7784       PetscBLASInt M            = newNumIndices;
7785       PetscBLASInt N            = numRows;
7786       PetscBLASInt K            = numIndices;
7787       PetscScalar  a = 1.0, b = 0.0;
7788 
7789       PetscCheck(numCols == numIndices, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "values matrix has the wrong number of columns: %" PetscInt_FMT ", expected %" PetscInt_FMT, numCols, numIndices);
7790 
7791       PetscCall(DMGetWorkArray(dm, numRows * newNumIndices, MPIU_SCALAR, &newNewValues));
7792       // row-major to column-major conversion, right multiplication becomes left multiplication
7793       PetscCallBLAS("BLASgemm", BLASgemm_("N", "N", &M, &N, &K, &a, modMat, &M, newValues, &K, &b, newNewValues, &M));
7794 
7795       numCols   = newNumIndices;
7796       newValues = newNewValues;
7797     }
7798 
7799     if (multiplyLeft) {
7800       PetscScalar *newNewValues = NULL;
7801       PetscBLASInt M            = numCols;
7802       PetscBLASInt N            = newNumIndices;
7803       PetscBLASInt K            = numIndices;
7804       PetscScalar  a = 1.0, b = 0.0;
7805 
7806       PetscCheck(numRows == numIndices, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "values matrix has the wrong number of rows: %" PetscInt_FMT ", expected %" PetscInt_FMT, numRows, numIndices);
7807 
7808       PetscCall(DMGetWorkArray(dm, newNumIndices * numCols, MPIU_SCALAR, &newNewValues));
7809       // row-major to column-major conversion, left multiplication becomes right multiplication
7810       PetscCallBLAS("BLASgemm", BLASgemm_("N", "T", &M, &N, &K, &a, newValues, &M, modMat, &N, &b, newNewValues, &M));
7811       if (newValues != values) PetscCall(DMRestoreWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &newValues));
7812       newValues = newNewValues;
7813     }
7814     *outValues = (PetscScalar *)newValues;
7815     PetscCall(DMRestoreWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &modMat));
7816   }
7817   PetscFunctionReturn(PETSC_SUCCESS);
7818 }
7819 
7820 PETSC_INTERN PetscErrorCode DMPlexAnchorsModifyMat(DM dm, PetscSection section, PetscInt numPoints, PetscInt numIndices, const PetscInt points[], const PetscInt ***perms, const PetscScalar values[], PetscInt *outNumPoints, PetscInt *outNumIndices, PetscInt *outPoints[], PetscScalar *outValues[], PetscInt offsets[], PetscBool multiplyLeft)
7821 {
7822   PetscFunctionBegin;
7823   PetscCall(DMPlexAnchorsModifyMat_Internal(dm, section, numPoints, numIndices, points, perms, numIndices, numIndices, values, outNumPoints, outNumIndices, outPoints, outValues, offsets, PETSC_TRUE, multiplyLeft));
7824   PetscFunctionReturn(PETSC_SUCCESS);
7825 }
7826 
7827 static PetscErrorCode DMPlexGetClosureIndicesSize_Internal(DM dm, PetscSection section, PetscInt point, PetscInt *closureSize)
7828 {
7829   /* Closure ordering */
7830   PetscSection    clSection;
7831   IS              clPoints;
7832   const PetscInt *clp;
7833   PetscInt       *points;
7834   PetscInt        Ncl, Ni = 0;
7835 
7836   PetscFunctionBeginHot;
7837   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &Ncl, &points, &clSection, &clPoints, &clp));
7838   for (PetscInt p = 0; p < Ncl * 2; p += 2) {
7839     PetscInt dof;
7840 
7841     PetscCall(PetscSectionGetDof(section, points[p], &dof));
7842     Ni += dof;
7843   }
7844   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7845   *closureSize = Ni;
7846   PetscFunctionReturn(PETSC_SUCCESS);
7847 }
7848 
7849 static PetscErrorCode DMPlexGetClosureIndices_Internal(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numRows, PetscInt *numCols, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[], PetscBool multiplyRight, PetscBool multiplyLeft)
7850 {
7851   /* Closure ordering */
7852   PetscSection    clSection;
7853   IS              clPoints;
7854   const PetscInt *clp;
7855   PetscInt       *points;
7856   const PetscInt *clperm = NULL;
7857   /* Dof permutation and sign flips */
7858   const PetscInt    **perms[32] = {NULL};
7859   const PetscScalar **flips[32] = {NULL};
7860   PetscScalar        *valCopy   = NULL;
7861   /* Hanging node constraints */
7862   PetscInt    *pointsC = NULL;
7863   PetscScalar *valuesC = NULL;
7864   PetscInt     NclC, NiC;
7865 
7866   PetscInt *idx;
7867   PetscInt  Nf, Ncl, Ni = 0, offsets[32], p, f;
7868   PetscBool isLocal = (section == idxSection) ? PETSC_TRUE : PETSC_FALSE;
7869   PetscInt  idxStart, idxEnd;
7870   PetscInt  nRows, nCols;
7871 
7872   PetscFunctionBeginHot;
7873   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7874   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7875   PetscValidHeaderSpecific(idxSection, PETSC_SECTION_CLASSID, 3);
7876   PetscAssertPointer(numRows, 6);
7877   PetscAssertPointer(numCols, 7);
7878   if (indices) PetscAssertPointer(indices, 8);
7879   if (outOffsets) PetscAssertPointer(outOffsets, 9);
7880   if (values) PetscAssertPointer(values, 10);
7881   PetscCall(PetscSectionGetNumFields(section, &Nf));
7882   PetscCheck(Nf <= 31, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", Nf);
7883   PetscCall(PetscArrayzero(offsets, 32));
7884   /* 1) Get points in closure */
7885   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &Ncl, &points, &clSection, &clPoints, &clp));
7886   if (useClPerm) {
7887     PetscInt depth, clsize;
7888     PetscCall(DMPlexGetPointDepth(dm, point, &depth));
7889     for (clsize = 0, p = 0; p < Ncl; p++) {
7890       PetscInt dof;
7891       PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
7892       clsize += dof;
7893     }
7894     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
7895   }
7896   /* 2) Get number of indices on these points and field offsets from section */
7897   for (p = 0; p < Ncl * 2; p += 2) {
7898     PetscInt dof, fdof;
7899 
7900     PetscCall(PetscSectionGetDof(section, points[p], &dof));
7901     for (f = 0; f < Nf; ++f) {
7902       PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
7903       offsets[f + 1] += fdof;
7904     }
7905     Ni += dof;
7906   }
7907   if (*numRows == -1) *numRows = Ni;
7908   if (*numCols == -1) *numCols = Ni;
7909   nRows = *numRows;
7910   nCols = *numCols;
7911   for (f = 1; f < Nf; ++f) offsets[f + 1] += offsets[f];
7912   PetscCheck(!Nf || offsets[Nf] == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, offsets[Nf], Ni);
7913   /* 3) Get symmetries and sign flips. Apply sign flips to values if passed in (only works for square values matrix) */
7914   if (multiplyRight) PetscCheck(nCols == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Expected %" PetscInt_FMT " columns, got %" PetscInt_FMT, Ni, nCols);
7915   if (multiplyLeft) PetscCheck(nRows == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Expected %" PetscInt_FMT " rows, got %" PetscInt_FMT, Ni, nRows);
7916   for (f = 0; f < PetscMax(1, Nf); ++f) {
7917     if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7918     else PetscCall(PetscSectionGetPointSyms(section, Ncl, points, &perms[f], &flips[f]));
7919     /* may need to apply sign changes to the element matrix */
7920     if (values && flips[f]) {
7921       PetscInt foffset = offsets[f];
7922 
7923       for (p = 0; p < Ncl; ++p) {
7924         PetscInt           pnt  = points[2 * p], fdof;
7925         const PetscScalar *flip = flips[f] ? flips[f][p] : NULL;
7926 
7927         if (!Nf) PetscCall(PetscSectionGetDof(section, pnt, &fdof));
7928         else PetscCall(PetscSectionGetFieldDof(section, pnt, f, &fdof));
7929         if (flip) {
7930           PetscInt i, j, k;
7931 
7932           if (!valCopy) {
7933             PetscCall(DMGetWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
7934             for (j = 0; j < Ni * Ni; ++j) valCopy[j] = (*values)[j];
7935             *values = valCopy;
7936           }
7937           for (i = 0; i < fdof; ++i) {
7938             PetscScalar fval = flip[i];
7939 
7940             if (multiplyRight) {
7941               for (k = 0; k < nRows; ++k) { valCopy[Ni * k + (foffset + i)] *= fval; }
7942             }
7943             if (multiplyLeft) {
7944               for (k = 0; k < nCols; ++k) { valCopy[nCols * (foffset + i) + k] *= fval; }
7945             }
7946           }
7947         }
7948         foffset += fdof;
7949       }
7950     }
7951   }
7952   /* 4) Apply hanging node constraints. Get new symmetries and replace all storage with constrained storage */
7953   PetscCall(DMPlexAnchorsModifyMat_Internal(dm, section, Ncl, Ni, points, perms, nRows, nCols, values ? *values : NULL, &NclC, &NiC, &pointsC, values ? &valuesC : NULL, offsets, multiplyRight, multiplyLeft));
7954   if (NclC) {
7955     if (multiplyRight) { *numCols = nCols = NiC; }
7956     if (multiplyLeft) { *numRows = nRows = NiC; }
7957     if (valCopy) PetscCall(DMRestoreWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
7958     for (f = 0; f < PetscMax(1, Nf); ++f) {
7959       if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7960       else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
7961     }
7962     for (f = 0; f < PetscMax(1, Nf); ++f) {
7963       if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, NclC, pointsC, &perms[f], &flips[f]));
7964       else PetscCall(PetscSectionGetPointSyms(section, NclC, pointsC, &perms[f], &flips[f]));
7965     }
7966     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7967     Ncl    = NclC;
7968     Ni     = NiC;
7969     points = pointsC;
7970     if (values) *values = valuesC;
7971   }
7972   /* 5) Calculate indices */
7973   PetscCall(DMGetWorkArray(dm, Ni, MPIU_INT, &idx));
7974   PetscCall(PetscSectionGetChart(idxSection, &idxStart, &idxEnd));
7975   if (Nf) {
7976     PetscInt  idxOff;
7977     PetscBool useFieldOffsets;
7978 
7979     if (outOffsets) {
7980       for (f = 0; f <= Nf; f++) outOffsets[f] = offsets[f];
7981     }
7982     PetscCall(PetscSectionGetUseFieldOffsets(idxSection, &useFieldOffsets));
7983     if (useFieldOffsets) {
7984       for (p = 0; p < Ncl; ++p) {
7985         const PetscInt pnt = points[p * 2];
7986 
7987         PetscCall(DMPlexGetIndicesPointFieldsSplit_Internal(section, idxSection, pnt, offsets, perms, p, clperm, idx));
7988       }
7989     } else {
7990       for (p = 0; p < Ncl; ++p) {
7991         const PetscInt pnt = points[p * 2];
7992 
7993         if (pnt < idxStart || pnt >= idxEnd) continue;
7994         PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
7995         /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
7996          * not (at the time of this writing) have fields set. They probably should, in which case we would pass the
7997          * global section. */
7998         PetscCall(DMPlexGetIndicesPointFields_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, offsets, PETSC_FALSE, perms, p, clperm, idx));
7999       }
8000     }
8001   } else {
8002     PetscInt off = 0, idxOff;
8003 
8004     for (p = 0; p < Ncl; ++p) {
8005       const PetscInt  pnt  = points[p * 2];
8006       const PetscInt *perm = perms[0] ? perms[0][p] : NULL;
8007 
8008       if (pnt < idxStart || pnt >= idxEnd) continue;
8009       PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
8010       /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
8011        * not (at the time of this writing) have fields set. They probably should, in which case we would pass the global section. */
8012       PetscCall(DMPlexGetIndicesPoint_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, &off, PETSC_FALSE, perm, clperm, idx));
8013     }
8014   }
8015   /* 6) Cleanup */
8016   for (f = 0; f < PetscMax(1, Nf); ++f) {
8017     if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
8018     else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
8019   }
8020   if (NclC) {
8021     PetscCall(DMRestoreWorkArray(dm, NclC * 2, MPIU_INT, &pointsC));
8022   } else {
8023     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
8024   }
8025 
8026   if (indices) *indices = idx;
8027   PetscFunctionReturn(PETSC_SUCCESS);
8028 }
8029 
8030 /*@C
8031   DMPlexGetClosureIndices - Gets the global dof indices associated with the closure of the given point within the provided sections.
8032 
8033   Not collective
8034 
8035   Input Parameters:
8036 + dm         - The `DM`
8037 . section    - The `PetscSection` describing the points (a local section)
8038 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
8039 . point      - The point defining the closure
8040 - useClPerm  - Use the closure point permutation if available
8041 
8042   Output Parameters:
8043 + numIndices - The number of dof indices in the closure of point with the input sections
8044 . indices    - The dof indices
8045 . outOffsets - Array to write the field offsets into, or `NULL`
8046 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or `NULL`
8047 
8048   Level: advanced
8049 
8050   Notes:
8051   Must call `DMPlexRestoreClosureIndices()` to free allocated memory
8052 
8053   If `idxSection` is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
8054   of those indices is not significant.  If `idxSection` is local, the constrained dofs will yield the involution -(idx+1)
8055   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
8056   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when `idxSection` == section, otherwise global
8057   indices (with the above semantics) are implied.
8058 
8059 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`,
8060           `PetscSection`, `DMGetGlobalSection()`
8061 @*/
8062 PetscErrorCode DMPlexGetClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
8063 {
8064   PetscInt numRows = -1, numCols = -1;
8065 
8066   PetscFunctionBeginHot;
8067   PetscCall(DMPlexGetClosureIndices_Internal(dm, section, idxSection, point, useClPerm, &numRows, &numCols, indices, outOffsets, values, PETSC_TRUE, PETSC_TRUE));
8068   PetscCheck(numRows == numCols, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Symmetric matrix transformation produces rectangular dimensions (%" PetscInt_FMT ", %" PetscInt_FMT ")", numRows, numCols);
8069   *numIndices = numRows;
8070   PetscFunctionReturn(PETSC_SUCCESS);
8071 }
8072 
8073 /*@C
8074   DMPlexRestoreClosureIndices - Restores the global dof indices associated with the closure of the given point within the provided sections.
8075 
8076   Not collective
8077 
8078   Input Parameters:
8079 + dm         - The `DM`
8080 . section    - The `PetscSection` describing the points (a local section)
8081 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
8082 . point      - The point defining the closure
8083 - useClPerm  - Use the closure point permutation if available
8084 
8085   Output Parameters:
8086 + numIndices - The number of dof indices in the closure of point with the input sections
8087 . indices    - The dof indices
8088 . outOffsets - Array to write the field offsets into, or `NULL`
8089 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or `NULL`
8090 
8091   Level: advanced
8092 
8093   Notes:
8094   If values were modified, the user is responsible for calling `DMRestoreWorkArray`(dm, 0, `MPIU_SCALAR`, &values).
8095 
8096   If idxSection is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
8097   of those indices is not significant.  If idxSection is local, the constrained dofs will yield the involution -(idx+1)
8098   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
8099   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when idxSection == section, otherwise global
8100   indices (with the above semantics) are implied.
8101 
8102 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`, `DMGetGlobalSection()`
8103 @*/
8104 PetscErrorCode DMPlexRestoreClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
8105 {
8106   PetscFunctionBegin;
8107   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8108   PetscAssertPointer(indices, 7);
8109   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, indices));
8110   PetscFunctionReturn(PETSC_SUCCESS);
8111 }
8112 
8113 PetscErrorCode DMPlexMatSetClosure_Internal(DM dm, PetscSection section, PetscSection globalSection, PetscBool useClPerm, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8114 {
8115   DM_Plex           *mesh = (DM_Plex *)dm->data;
8116   PetscInt          *indices;
8117   PetscInt           numIndices;
8118   const PetscScalar *valuesOrig = values;
8119   PetscErrorCode     ierr;
8120 
8121   PetscFunctionBegin;
8122   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8123   if (!section) PetscCall(DMGetLocalSection(dm, &section));
8124   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
8125   if (!globalSection) PetscCall(DMGetGlobalSection(dm, &globalSection));
8126   PetscValidHeaderSpecific(globalSection, PETSC_SECTION_CLASSID, 3);
8127   PetscValidHeaderSpecific(A, MAT_CLASSID, 5);
8128 
8129   PetscCall(DMPlexGetClosureIndices(dm, section, globalSection, point, useClPerm, &numIndices, &indices, NULL, (PetscScalar **)&values));
8130 
8131   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndices, indices, 0, NULL, values));
8132   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8133   ierr = MatSetValues(A, numIndices, indices, numIndices, indices, values, mode);
8134   if (ierr) {
8135     PetscMPIInt rank;
8136 
8137     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8138     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8139     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndices, indices, 0, NULL, values));
8140     PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
8141     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
8142     SETERRQ(PetscObjectComm((PetscObject)dm), ierr, "Not possible to set matrix values");
8143   }
8144   if (mesh->printFEM > 1) {
8145     PetscInt i;
8146     PetscCall(PetscPrintf(PETSC_COMM_SELF, "  Indices:"));
8147     for (i = 0; i < numIndices; ++i) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, indices[i]));
8148     PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8149   }
8150 
8151   PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
8152   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
8153   PetscFunctionReturn(PETSC_SUCCESS);
8154 }
8155 
8156 /*@C
8157   DMPlexMatSetClosure - Set an array of the values on the closure of 'point'
8158 
8159   Not collective
8160 
8161   Input Parameters:
8162 + dm            - The `DM`
8163 . section       - The section describing the layout in `v`, or `NULL` to use the default section
8164 . globalSection - The section describing the layout in `v`, or `NULL` to use the default global section
8165 . A             - The matrix
8166 . point         - The point in the `DM`
8167 . values        - The array of values
8168 - mode          - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
8169 
8170   Level: intermediate
8171 
8172 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosureGeneral()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
8173 @*/
8174 PetscErrorCode DMPlexMatSetClosure(DM dm, PetscSection section, PetscSection globalSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8175 {
8176   PetscFunctionBegin;
8177   PetscCall(DMPlexMatSetClosure_Internal(dm, section, globalSection, PETSC_TRUE, A, point, values, mode));
8178   PetscFunctionReturn(PETSC_SUCCESS);
8179 }
8180 
8181 /*@C
8182   DMPlexMatSetClosureGeneral - Set an array of the values on the closure of 'point' using a different row and column section
8183 
8184   Not collective
8185 
8186   Input Parameters:
8187 + dmRow            - The `DM` for the row fields
8188 . sectionRow       - The section describing the layout, or `NULL` to use the default section in `dmRow`
8189 . useRowPerm       - The flag to use the closure permutation of the `dmRow` if available
8190 . globalSectionRow - The section describing the layout, or `NULL` to use the default global section in `dmRow`
8191 . dmCol            - The `DM` for the column fields
8192 . sectionCol       - The section describing the layout, or `NULL` to use the default section in `dmCol`
8193 . useColPerm       - The flag to use the closure permutation of the `dmCol` if available
8194 . globalSectionCol - The section describing the layout, or `NULL` to use the default global section in `dmCol`
8195 . A                - The matrix
8196 . point            - The point in the `DM`
8197 . values           - The array of values
8198 - mode             - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
8199 
8200   Level: intermediate
8201 
8202 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosure()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
8203 @*/
8204 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)
8205 {
8206   DM_Plex           *mesh = (DM_Plex *)dmRow->data;
8207   PetscInt          *indicesRow, *indicesCol;
8208   PetscInt           numIndicesRow = -1, numIndicesCol = -1;
8209   const PetscScalar *valuesV0 = values, *valuesV1, *valuesV2;
8210 
8211   PetscErrorCode ierr;
8212 
8213   PetscFunctionBegin;
8214   PetscValidHeaderSpecific(dmRow, DM_CLASSID, 1);
8215   if (!sectionRow) PetscCall(DMGetLocalSection(dmRow, &sectionRow));
8216   PetscValidHeaderSpecific(sectionRow, PETSC_SECTION_CLASSID, 2);
8217   if (!globalSectionRow) PetscCall(DMGetGlobalSection(dmRow, &globalSectionRow));
8218   PetscValidHeaderSpecific(globalSectionRow, PETSC_SECTION_CLASSID, 3);
8219   PetscValidHeaderSpecific(dmCol, DM_CLASSID, 5);
8220   if (!sectionCol) PetscCall(DMGetLocalSection(dmCol, &sectionCol));
8221   PetscValidHeaderSpecific(sectionCol, PETSC_SECTION_CLASSID, 6);
8222   if (!globalSectionCol) PetscCall(DMGetGlobalSection(dmCol, &globalSectionCol));
8223   PetscValidHeaderSpecific(globalSectionCol, PETSC_SECTION_CLASSID, 7);
8224   PetscValidHeaderSpecific(A, MAT_CLASSID, 9);
8225 
8226   PetscCall(DMPlexGetClosureIndicesSize_Internal(dmRow, sectionRow, point, &numIndicesRow));
8227   PetscCall(DMPlexGetClosureIndicesSize_Internal(dmCol, sectionCol, point, &numIndicesCol));
8228   valuesV1 = valuesV0;
8229   PetscCall(DMPlexGetClosureIndices_Internal(dmRow, sectionRow, globalSectionRow, point, useRowPerm, &numIndicesRow, &numIndicesCol, &indicesRow, NULL, (PetscScalar **)&valuesV1, PETSC_FALSE, PETSC_TRUE));
8230   valuesV2 = valuesV1;
8231   PetscCall(DMPlexGetClosureIndices_Internal(dmCol, sectionCol, globalSectionCol, point, useColPerm, &numIndicesRow, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&valuesV2, PETSC_TRUE, PETSC_FALSE));
8232 
8233   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, valuesV2));
8234   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8235   ierr = MatSetValues(A, numIndicesRow, indicesRow, numIndicesCol, indicesCol, valuesV2, mode);
8236   if (ierr) {
8237     PetscMPIInt rank;
8238 
8239     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8240     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8241     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
8242     PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesRow, NULL, (PetscScalar **)&valuesV2));
8243     PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&valuesV1));
8244     if (valuesV2 != valuesV1) PetscCall(DMRestoreWorkArray(dmCol, 0, MPIU_SCALAR, &valuesV2));
8245     if (valuesV1 != valuesV0) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &valuesV1));
8246   }
8247 
8248   PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, useColPerm, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&valuesV2));
8249   PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, useRowPerm, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&valuesV1));
8250   if (valuesV2 != valuesV1) PetscCall(DMRestoreWorkArray(dmCol, 0, MPIU_SCALAR, &valuesV2));
8251   if (valuesV1 != valuesV0) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &valuesV1));
8252   PetscFunctionReturn(PETSC_SUCCESS);
8253 }
8254 
8255 PetscErrorCode DMPlexMatSetClosureRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8256 {
8257   DM_Plex        *mesh    = (DM_Plex *)dmf->data;
8258   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
8259   PetscInt       *cpoints = NULL;
8260   PetscInt       *findices, *cindices;
8261   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
8262   PetscInt        foffsets[32], coffsets[32];
8263   DMPolytopeType  ct;
8264   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
8265   PetscErrorCode  ierr;
8266 
8267   PetscFunctionBegin;
8268   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
8269   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
8270   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
8271   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
8272   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
8273   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
8274   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
8275   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
8276   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
8277   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
8278   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
8279   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
8280   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
8281   PetscCall(PetscArrayzero(foffsets, 32));
8282   PetscCall(PetscArrayzero(coffsets, 32));
8283   /* Column indices */
8284   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8285   maxFPoints = numCPoints;
8286   /* Compress out points not in the section */
8287   /*   TODO: Squeeze out points with 0 dof as well */
8288   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
8289   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
8290     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
8291       cpoints[q * 2]     = cpoints[p];
8292       cpoints[q * 2 + 1] = cpoints[p + 1];
8293       ++q;
8294     }
8295   }
8296   numCPoints = q;
8297   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
8298     PetscInt fdof;
8299 
8300     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
8301     if (!dof) continue;
8302     for (f = 0; f < numFields; ++f) {
8303       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
8304       coffsets[f + 1] += fdof;
8305     }
8306     numCIndices += dof;
8307   }
8308   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
8309   /* Row indices */
8310   PetscCall(DMPlexGetCellType(dmc, point, &ct));
8311   {
8312     DMPlexTransform tr;
8313     DMPolytopeType *rct;
8314     PetscInt       *rsize, *rcone, *rornt, Nt;
8315 
8316     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8317     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8318     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8319     numSubcells = rsize[Nt - 1];
8320     PetscCall(DMPlexTransformDestroy(&tr));
8321   }
8322   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8323   for (r = 0, q = 0; r < numSubcells; ++r) {
8324     /* TODO Map from coarse to fine cells */
8325     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8326     /* Compress out points not in the section */
8327     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8328     for (p = 0; p < numFPoints * 2; p += 2) {
8329       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8330         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8331         if (!dof) continue;
8332         for (s = 0; s < q; ++s)
8333           if (fpoints[p] == ftotpoints[s * 2]) break;
8334         if (s < q) continue;
8335         ftotpoints[q * 2]     = fpoints[p];
8336         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8337         ++q;
8338       }
8339     }
8340     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8341   }
8342   numFPoints = q;
8343   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8344     PetscInt fdof;
8345 
8346     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8347     if (!dof) continue;
8348     for (f = 0; f < numFields; ++f) {
8349       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8350       foffsets[f + 1] += fdof;
8351     }
8352     numFIndices += dof;
8353   }
8354   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8355 
8356   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8357   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8358   PetscCall(DMGetWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8359   PetscCall(DMGetWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8360   if (numFields) {
8361     const PetscInt **permsF[32] = {NULL};
8362     const PetscInt **permsC[32] = {NULL};
8363 
8364     for (f = 0; f < numFields; f++) {
8365       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8366       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8367     }
8368     for (p = 0; p < numFPoints; p++) {
8369       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8370       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8371     }
8372     for (p = 0; p < numCPoints; p++) {
8373       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8374       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8375     }
8376     for (f = 0; f < numFields; f++) {
8377       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8378       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8379     }
8380   } else {
8381     const PetscInt **permsF = NULL;
8382     const PetscInt **permsC = NULL;
8383 
8384     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8385     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8386     for (p = 0, off = 0; p < numFPoints; p++) {
8387       const PetscInt *perm = permsF ? permsF[p] : NULL;
8388 
8389       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8390       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8391     }
8392     for (p = 0, off = 0; p < numCPoints; p++) {
8393       const PetscInt *perm = permsC ? permsC[p] : NULL;
8394 
8395       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8396       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8397     }
8398     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8399     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8400   }
8401   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8402   /* TODO: flips */
8403   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8404   ierr = MatSetValues(A, numFIndices, findices, numCIndices, cindices, values, mode);
8405   if (ierr) {
8406     PetscMPIInt rank;
8407 
8408     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8409     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8410     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8411     PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8412     PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8413   }
8414   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8415   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8416   PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8417   PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8418   PetscFunctionReturn(PETSC_SUCCESS);
8419 }
8420 
8421 PetscErrorCode DMPlexMatGetClosureIndicesRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, PetscInt point, PetscInt cindices[], PetscInt findices[])
8422 {
8423   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
8424   PetscInt       *cpoints      = NULL;
8425   PetscInt        foffsets[32] = {0}, coffsets[32] = {0};
8426   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
8427   DMPolytopeType  ct;
8428   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
8429 
8430   PetscFunctionBegin;
8431   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
8432   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
8433   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
8434   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
8435   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
8436   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
8437   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
8438   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
8439   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
8440   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
8441   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
8442   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
8443   /* Column indices */
8444   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8445   maxFPoints = numCPoints;
8446   /* Compress out points not in the section */
8447   /*   TODO: Squeeze out points with 0 dof as well */
8448   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
8449   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
8450     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
8451       cpoints[q * 2]     = cpoints[p];
8452       cpoints[q * 2 + 1] = cpoints[p + 1];
8453       ++q;
8454     }
8455   }
8456   numCPoints = q;
8457   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
8458     PetscInt fdof;
8459 
8460     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
8461     if (!dof) continue;
8462     for (f = 0; f < numFields; ++f) {
8463       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
8464       coffsets[f + 1] += fdof;
8465     }
8466     numCIndices += dof;
8467   }
8468   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
8469   /* Row indices */
8470   PetscCall(DMPlexGetCellType(dmc, point, &ct));
8471   {
8472     DMPlexTransform tr;
8473     DMPolytopeType *rct;
8474     PetscInt       *rsize, *rcone, *rornt, Nt;
8475 
8476     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8477     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8478     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8479     numSubcells = rsize[Nt - 1];
8480     PetscCall(DMPlexTransformDestroy(&tr));
8481   }
8482   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8483   for (r = 0, q = 0; r < numSubcells; ++r) {
8484     /* TODO Map from coarse to fine cells */
8485     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8486     /* Compress out points not in the section */
8487     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8488     for (p = 0; p < numFPoints * 2; p += 2) {
8489       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8490         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8491         if (!dof) continue;
8492         for (s = 0; s < q; ++s)
8493           if (fpoints[p] == ftotpoints[s * 2]) break;
8494         if (s < q) continue;
8495         ftotpoints[q * 2]     = fpoints[p];
8496         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8497         ++q;
8498       }
8499     }
8500     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8501   }
8502   numFPoints = q;
8503   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8504     PetscInt fdof;
8505 
8506     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8507     if (!dof) continue;
8508     for (f = 0; f < numFields; ++f) {
8509       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8510       foffsets[f + 1] += fdof;
8511     }
8512     numFIndices += dof;
8513   }
8514   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8515 
8516   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8517   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8518   if (numFields) {
8519     const PetscInt **permsF[32] = {NULL};
8520     const PetscInt **permsC[32] = {NULL};
8521 
8522     for (f = 0; f < numFields; f++) {
8523       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8524       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8525     }
8526     for (p = 0; p < numFPoints; p++) {
8527       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8528       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8529     }
8530     for (p = 0; p < numCPoints; p++) {
8531       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8532       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8533     }
8534     for (f = 0; f < numFields; f++) {
8535       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8536       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8537     }
8538   } else {
8539     const PetscInt **permsF = NULL;
8540     const PetscInt **permsC = NULL;
8541 
8542     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8543     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8544     for (p = 0, off = 0; p < numFPoints; p++) {
8545       const PetscInt *perm = permsF ? permsF[p] : NULL;
8546 
8547       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8548       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8549     }
8550     for (p = 0, off = 0; p < numCPoints; p++) {
8551       const PetscInt *perm = permsC ? permsC[p] : NULL;
8552 
8553       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8554       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8555     }
8556     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8557     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8558   }
8559   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8560   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8561   PetscFunctionReturn(PETSC_SUCCESS);
8562 }
8563 
8564 /*@
8565   DMPlexGetVTKCellHeight - Returns the height in the DAG used to determine which points are cells (normally 0)
8566 
8567   Input Parameter:
8568 . dm - The `DMPLEX` object
8569 
8570   Output Parameter:
8571 . cellHeight - The height of a cell
8572 
8573   Level: developer
8574 
8575 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetVTKCellHeight()`
8576 @*/
8577 PetscErrorCode DMPlexGetVTKCellHeight(DM dm, PetscInt *cellHeight)
8578 {
8579   DM_Plex *mesh = (DM_Plex *)dm->data;
8580 
8581   PetscFunctionBegin;
8582   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8583   PetscAssertPointer(cellHeight, 2);
8584   *cellHeight = mesh->vtkCellHeight;
8585   PetscFunctionReturn(PETSC_SUCCESS);
8586 }
8587 
8588 /*@
8589   DMPlexSetVTKCellHeight - Sets the height in the DAG used to determine which points are cells (normally 0)
8590 
8591   Input Parameters:
8592 + dm         - The `DMPLEX` object
8593 - cellHeight - The height of a cell
8594 
8595   Level: developer
8596 
8597 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetVTKCellHeight()`
8598 @*/
8599 PetscErrorCode DMPlexSetVTKCellHeight(DM dm, PetscInt cellHeight)
8600 {
8601   DM_Plex *mesh = (DM_Plex *)dm->data;
8602 
8603   PetscFunctionBegin;
8604   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8605   mesh->vtkCellHeight = cellHeight;
8606   PetscFunctionReturn(PETSC_SUCCESS);
8607 }
8608 
8609 /*@
8610   DMPlexGetCellTypeStratum - Get the range of cells of a given celltype
8611 
8612   Input Parameters:
8613 + dm - The `DMPLEX` object
8614 - ct - The `DMPolytopeType` of the cell
8615 
8616   Output Parameters:
8617 + start - The first cell of this type, or `NULL`
8618 - end   - The upper bound on this celltype, or `NULL`
8619 
8620   Level: advanced
8621 
8622 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexConstructGhostCells()`, `DMPlexGetDepthStratum()`, `DMPlexGetHeightStratum()`
8623 @*/
8624 PetscErrorCode DMPlexGetCellTypeStratum(DM dm, DMPolytopeType ct, PetscInt *start, PetscInt *end)
8625 {
8626   DM_Plex *mesh = (DM_Plex *)dm->data;
8627   DMLabel  label;
8628   PetscInt pStart, pEnd;
8629 
8630   PetscFunctionBegin;
8631   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8632   if (start) {
8633     PetscAssertPointer(start, 3);
8634     *start = 0;
8635   }
8636   if (end) {
8637     PetscAssertPointer(end, 4);
8638     *end = 0;
8639   }
8640   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8641   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
8642   if (mesh->tr) {
8643     PetscCall(DMPlexTransformGetCellTypeStratum(mesh->tr, ct, start, end));
8644   } else {
8645     PetscCall(DMPlexGetCellTypeLabel(dm, &label));
8646     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named celltype was found");
8647     PetscCall(DMLabelGetStratumBounds(label, ct, start, end));
8648   }
8649   PetscFunctionReturn(PETSC_SUCCESS);
8650 }
8651 
8652 PetscErrorCode DMPlexCreateNumbering_Plex(DM dm, PetscInt pStart, PetscInt pEnd, PetscInt shift, PetscInt *globalSize, PetscSF sf, IS *numbering)
8653 {
8654   PetscSection section, globalSection;
8655   PetscInt    *numbers, p;
8656 
8657   PetscFunctionBegin;
8658   if (PetscDefined(USE_DEBUG)) PetscCall(DMPlexCheckPointSF(dm, sf, PETSC_TRUE));
8659   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
8660   PetscCall(PetscSectionSetChart(section, pStart, pEnd));
8661   for (p = pStart; p < pEnd; ++p) PetscCall(PetscSectionSetDof(section, p, 1));
8662   PetscCall(PetscSectionSetUp(section));
8663   PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &globalSection));
8664   PetscCall(PetscMalloc1(pEnd - pStart, &numbers));
8665   for (p = pStart; p < pEnd; ++p) {
8666     PetscCall(PetscSectionGetOffset(globalSection, p, &numbers[p - pStart]));
8667     if (numbers[p - pStart] < 0) numbers[p - pStart] -= shift;
8668     else numbers[p - pStart] += shift;
8669   }
8670   PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), pEnd - pStart, numbers, PETSC_OWN_POINTER, numbering));
8671   if (globalSize) {
8672     PetscLayout layout;
8673     PetscCall(PetscSectionGetPointLayout(PetscObjectComm((PetscObject)dm), globalSection, &layout));
8674     PetscCall(PetscLayoutGetSize(layout, globalSize));
8675     PetscCall(PetscLayoutDestroy(&layout));
8676   }
8677   PetscCall(PetscSectionDestroy(&section));
8678   PetscCall(PetscSectionDestroy(&globalSection));
8679   PetscFunctionReturn(PETSC_SUCCESS);
8680 }
8681 
8682 PetscErrorCode DMPlexCreateCellNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalCellNumbers)
8683 {
8684   PetscInt cellHeight, cStart, cEnd;
8685 
8686   PetscFunctionBegin;
8687   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8688   if (includeHybrid) PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8689   else PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd));
8690   PetscCall(DMPlexCreateNumbering_Plex(dm, cStart, cEnd, 0, NULL, dm->sf, globalCellNumbers));
8691   PetscFunctionReturn(PETSC_SUCCESS);
8692 }
8693 
8694 /*@
8695   DMPlexGetCellNumbering - Get a global cell numbering for all cells on this process
8696 
8697   Input Parameter:
8698 . dm - The `DMPLEX` object
8699 
8700   Output Parameter:
8701 . globalCellNumbers - Global cell numbers for all cells on this process
8702 
8703   Level: developer
8704 
8705 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetVertexNumbering()`
8706 @*/
8707 PetscErrorCode DMPlexGetCellNumbering(DM dm, IS *globalCellNumbers)
8708 {
8709   DM_Plex *mesh = (DM_Plex *)dm->data;
8710 
8711   PetscFunctionBegin;
8712   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8713   if (!mesh->globalCellNumbers) PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_FALSE, &mesh->globalCellNumbers));
8714   *globalCellNumbers = mesh->globalCellNumbers;
8715   PetscFunctionReturn(PETSC_SUCCESS);
8716 }
8717 
8718 PetscErrorCode DMPlexCreateVertexNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalVertexNumbers)
8719 {
8720   PetscInt vStart, vEnd;
8721 
8722   PetscFunctionBegin;
8723   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8724   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8725   PetscCall(DMPlexCreateNumbering_Plex(dm, vStart, vEnd, 0, NULL, dm->sf, globalVertexNumbers));
8726   PetscFunctionReturn(PETSC_SUCCESS);
8727 }
8728 
8729 /*@
8730   DMPlexGetVertexNumbering - Get a global vertex numbering for all vertices on this process
8731 
8732   Input Parameter:
8733 . dm - The `DMPLEX` object
8734 
8735   Output Parameter:
8736 . globalVertexNumbers - Global vertex numbers for all vertices on this process
8737 
8738   Level: developer
8739 
8740 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
8741 @*/
8742 PetscErrorCode DMPlexGetVertexNumbering(DM dm, IS *globalVertexNumbers)
8743 {
8744   DM_Plex *mesh = (DM_Plex *)dm->data;
8745 
8746   PetscFunctionBegin;
8747   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8748   if (!mesh->globalVertexNumbers) PetscCall(DMPlexCreateVertexNumbering_Internal(dm, PETSC_FALSE, &mesh->globalVertexNumbers));
8749   *globalVertexNumbers = mesh->globalVertexNumbers;
8750   PetscFunctionReturn(PETSC_SUCCESS);
8751 }
8752 
8753 /*@
8754   DMPlexCreatePointNumbering - Create a global numbering for all points.
8755 
8756   Collective
8757 
8758   Input Parameter:
8759 . dm - The `DMPLEX` object
8760 
8761   Output Parameter:
8762 . globalPointNumbers - Global numbers for all points on this process
8763 
8764   Level: developer
8765 
8766   Notes:
8767   The point numbering `IS` is parallel, with local portion indexed by local points (see `DMGetLocalSection()`). The global
8768   points are taken as stratified, with each MPI rank owning a contiguous subset of each stratum. In the IS, owned points
8769   will have their non-negative value while points owned by different ranks will be involuted -(idx+1). As an example,
8770   consider a parallel mesh in which the first two elements and first two vertices are owned by rank 0.
8771 
8772   The partitioned mesh is
8773   ```
8774   (2)--0--(3)--1--(4)    (1)--0--(2)
8775   ```
8776   and its global numbering is
8777   ```
8778   (3)--0--(4)--1--(5)--2--(6)
8779   ```
8780   Then the global numbering is provided as
8781   ```
8782   [0] Number of indices in set 5
8783   [0] 0 0
8784   [0] 1 1
8785   [0] 2 3
8786   [0] 3 4
8787   [0] 4 -6
8788   [1] Number of indices in set 3
8789   [1] 0 2
8790   [1] 1 5
8791   [1] 2 6
8792   ```
8793 
8794 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
8795 @*/
8796 PetscErrorCode DMPlexCreatePointNumbering(DM dm, IS *globalPointNumbers)
8797 {
8798   IS        nums[4];
8799   PetscInt  depths[4], gdepths[4], starts[4];
8800   PetscInt  depth, d, shift = 0;
8801   PetscBool empty = PETSC_FALSE;
8802 
8803   PetscFunctionBegin;
8804   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8805   PetscCall(DMPlexGetDepth(dm, &depth));
8806   // For unstratified meshes use dim instead of depth
8807   if (depth < 0) PetscCall(DMGetDimension(dm, &depth));
8808   // If any stratum is empty, we must mark all empty
8809   for (d = 0; d <= depth; ++d) {
8810     PetscInt end;
8811 
8812     depths[d] = depth - d;
8813     PetscCall(DMPlexGetDepthStratum(dm, depths[d], &starts[d], &end));
8814     if (!(starts[d] - end)) empty = PETSC_TRUE;
8815   }
8816   if (empty)
8817     for (d = 0; d <= depth; ++d) {
8818       depths[d] = -1;
8819       starts[d] = -1;
8820     }
8821   else PetscCall(PetscSortIntWithArray(depth + 1, starts, depths));
8822   PetscCall(MPIU_Allreduce(depths, gdepths, depth + 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
8823   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]);
8824   // Note here that 'shift' is collective, so that the numbering is stratified by depth
8825   for (d = 0; d <= depth; ++d) {
8826     PetscInt pStart, pEnd, gsize;
8827 
8828     PetscCall(DMPlexGetDepthStratum(dm, gdepths[d], &pStart, &pEnd));
8829     PetscCall(DMPlexCreateNumbering_Plex(dm, pStart, pEnd, shift, &gsize, dm->sf, &nums[d]));
8830     shift += gsize;
8831   }
8832   PetscCall(ISConcatenate(PETSC_COMM_SELF, depth + 1, nums, globalPointNumbers));
8833   for (d = 0; d <= depth; ++d) PetscCall(ISDestroy(&nums[d]));
8834   PetscFunctionReturn(PETSC_SUCCESS);
8835 }
8836 
8837 /*@
8838   DMPlexCreateRankField - Create a cell field whose value is the rank of the owner
8839 
8840   Input Parameter:
8841 . dm - The `DMPLEX` object
8842 
8843   Output Parameter:
8844 . ranks - The rank field
8845 
8846   Options Database Key:
8847 . -dm_partition_view - Adds the rank field into the `DM` output from `-dm_view` using the same viewer
8848 
8849   Level: intermediate
8850 
8851 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`
8852 @*/
8853 PetscErrorCode DMPlexCreateRankField(DM dm, Vec *ranks)
8854 {
8855   DM             rdm;
8856   PetscFE        fe;
8857   PetscScalar   *r;
8858   PetscMPIInt    rank;
8859   DMPolytopeType ct;
8860   PetscInt       dim, cStart, cEnd, c;
8861   PetscBool      simplex;
8862 
8863   PetscFunctionBeginUser;
8864   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8865   PetscAssertPointer(ranks, 2);
8866   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
8867   PetscCall(DMClone(dm, &rdm));
8868   PetscCall(DMGetDimension(rdm, &dim));
8869   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
8870   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
8871   simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct) + 1 ? PETSC_TRUE : PETSC_FALSE;
8872   PetscCall(PetscFECreateDefault(PETSC_COMM_SELF, dim, 1, simplex, "PETSc___rank_", -1, &fe));
8873   PetscCall(PetscObjectSetName((PetscObject)fe, "rank"));
8874   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
8875   PetscCall(PetscFEDestroy(&fe));
8876   PetscCall(DMCreateDS(rdm));
8877   PetscCall(DMCreateGlobalVector(rdm, ranks));
8878   PetscCall(PetscObjectSetName((PetscObject)*ranks, "partition"));
8879   PetscCall(VecGetArray(*ranks, &r));
8880   for (c = cStart; c < cEnd; ++c) {
8881     PetscScalar *lr;
8882 
8883     PetscCall(DMPlexPointGlobalRef(rdm, c, r, &lr));
8884     if (lr) *lr = rank;
8885   }
8886   PetscCall(VecRestoreArray(*ranks, &r));
8887   PetscCall(DMDestroy(&rdm));
8888   PetscFunctionReturn(PETSC_SUCCESS);
8889 }
8890 
8891 /*@
8892   DMPlexCreateLabelField - Create a field whose value is the label value for that point
8893 
8894   Input Parameters:
8895 + dm    - The `DMPLEX`
8896 - label - The `DMLabel`
8897 
8898   Output Parameter:
8899 . val - The label value field
8900 
8901   Options Database Key:
8902 . -dm_label_view - Adds the label value field into the `DM` output from `-dm_view` using the same viewer
8903 
8904   Level: intermediate
8905 
8906 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`
8907 @*/
8908 PetscErrorCode DMPlexCreateLabelField(DM dm, DMLabel label, Vec *val)
8909 {
8910   DM             rdm, plex;
8911   Vec            lval;
8912   PetscSection   section;
8913   PetscFE        fe;
8914   PetscScalar   *v;
8915   PetscInt       dim, pStart, pEnd, p, cStart;
8916   DMPolytopeType ct;
8917   char           name[PETSC_MAX_PATH_LEN];
8918   const char    *lname, *prefix;
8919 
8920   PetscFunctionBeginUser;
8921   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8922   PetscAssertPointer(label, 2);
8923   PetscAssertPointer(val, 3);
8924   PetscCall(DMClone(dm, &rdm));
8925   PetscCall(DMConvert(rdm, DMPLEX, &plex));
8926   PetscCall(DMPlexGetHeightStratum(plex, 0, &cStart, NULL));
8927   PetscCall(DMPlexGetCellType(plex, cStart, &ct));
8928   PetscCall(DMDestroy(&plex));
8929   PetscCall(DMGetDimension(rdm, &dim));
8930   PetscCall(DMGetOptionsPrefix(dm, &prefix));
8931   PetscCall(PetscObjectGetName((PetscObject)label, &lname));
8932   PetscCall(PetscSNPrintf(name, sizeof(name), "%s%s_", prefix ? prefix : "", lname));
8933   PetscCall(PetscFECreateByCell(PETSC_COMM_SELF, dim, 1, ct, name, -1, &fe));
8934   PetscCall(PetscObjectSetName((PetscObject)fe, ""));
8935   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
8936   PetscCall(PetscFEDestroy(&fe));
8937   PetscCall(DMCreateDS(rdm));
8938   PetscCall(DMCreateGlobalVector(rdm, val));
8939   PetscCall(DMCreateLocalVector(rdm, &lval));
8940   PetscCall(PetscObjectSetName((PetscObject)*val, lname));
8941   PetscCall(DMGetLocalSection(rdm, &section));
8942   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
8943   PetscCall(VecGetArray(lval, &v));
8944   for (p = pStart; p < pEnd; ++p) {
8945     PetscInt cval, dof, off;
8946 
8947     PetscCall(PetscSectionGetDof(section, p, &dof));
8948     if (!dof) continue;
8949     PetscCall(DMLabelGetValue(label, p, &cval));
8950     PetscCall(PetscSectionGetOffset(section, p, &off));
8951     for (PetscInt d = 0; d < dof; d++) v[off + d] = cval;
8952   }
8953   PetscCall(VecRestoreArray(lval, &v));
8954   PetscCall(DMLocalToGlobal(rdm, lval, INSERT_VALUES, *val));
8955   PetscCall(VecDestroy(&lval));
8956   PetscCall(DMDestroy(&rdm));
8957   PetscFunctionReturn(PETSC_SUCCESS);
8958 }
8959 
8960 /*@
8961   DMPlexCheckSymmetry - Check that the adjacency information in the mesh is symmetric.
8962 
8963   Input Parameter:
8964 . dm - The `DMPLEX` object
8965 
8966   Level: developer
8967 
8968   Notes:
8969   This is a useful diagnostic when creating meshes programmatically.
8970 
8971   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
8972 
8973 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
8974 @*/
8975 PetscErrorCode DMPlexCheckSymmetry(DM dm)
8976 {
8977   PetscSection    coneSection, supportSection;
8978   const PetscInt *cone, *support;
8979   PetscInt        coneSize, c, supportSize, s;
8980   PetscInt        pStart, pEnd, p, pp, csize, ssize;
8981   PetscBool       storagecheck = PETSC_TRUE;
8982 
8983   PetscFunctionBegin;
8984   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8985   PetscCall(DMViewFromOptions(dm, NULL, "-sym_dm_view"));
8986   PetscCall(DMPlexGetConeSection(dm, &coneSection));
8987   PetscCall(DMPlexGetSupportSection(dm, &supportSection));
8988   /* Check that point p is found in the support of its cone points, and vice versa */
8989   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8990   for (p = pStart; p < pEnd; ++p) {
8991     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
8992     PetscCall(DMPlexGetCone(dm, p, &cone));
8993     for (c = 0; c < coneSize; ++c) {
8994       PetscBool dup = PETSC_FALSE;
8995       PetscInt  d;
8996       for (d = c - 1; d >= 0; --d) {
8997         if (cone[c] == cone[d]) {
8998           dup = PETSC_TRUE;
8999           break;
9000         }
9001       }
9002       PetscCall(DMPlexGetSupportSize(dm, cone[c], &supportSize));
9003       PetscCall(DMPlexGetSupport(dm, cone[c], &support));
9004       for (s = 0; s < supportSize; ++s) {
9005         if (support[s] == p) break;
9006       }
9007       if ((s >= supportSize) || (dup && (support[s + 1] != p))) {
9008         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", p));
9009         for (s = 0; s < coneSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[s]));
9010         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9011         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", cone[c]));
9012         for (s = 0; s < supportSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[s]));
9013         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9014         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]);
9015         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in support of cone point %" PetscInt_FMT, p, cone[c]);
9016       }
9017     }
9018     PetscCall(DMPlexGetTreeParent(dm, p, &pp, NULL));
9019     if (p != pp) {
9020       storagecheck = PETSC_FALSE;
9021       continue;
9022     }
9023     PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
9024     PetscCall(DMPlexGetSupport(dm, p, &support));
9025     for (s = 0; s < supportSize; ++s) {
9026       PetscCall(DMPlexGetConeSize(dm, support[s], &coneSize));
9027       PetscCall(DMPlexGetCone(dm, support[s], &cone));
9028       for (c = 0; c < coneSize; ++c) {
9029         PetscCall(DMPlexGetTreeParent(dm, cone[c], &pp, NULL));
9030         if (cone[c] != pp) {
9031           c = 0;
9032           break;
9033         }
9034         if (cone[c] == p) break;
9035       }
9036       if (c >= coneSize) {
9037         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", p));
9038         for (c = 0; c < supportSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[c]));
9039         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9040         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", support[s]));
9041         for (c = 0; c < coneSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[c]));
9042         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9043         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in cone of support point %" PetscInt_FMT, p, support[s]);
9044       }
9045     }
9046   }
9047   if (storagecheck) {
9048     PetscCall(PetscSectionGetStorageSize(coneSection, &csize));
9049     PetscCall(PetscSectionGetStorageSize(supportSection, &ssize));
9050     PetscCheck(csize == ssize, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Total cone size %" PetscInt_FMT " != Total support size %" PetscInt_FMT, csize, ssize);
9051   }
9052   PetscFunctionReturn(PETSC_SUCCESS);
9053 }
9054 
9055 /*
9056   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.
9057 */
9058 static PetscErrorCode DMPlexCellUnsplitVertices_Private(DM dm, PetscInt c, DMPolytopeType ct, PetscInt *unsplit)
9059 {
9060   DMPolytopeType  cct;
9061   PetscInt        ptpoints[4];
9062   const PetscInt *cone, *ccone, *ptcone;
9063   PetscInt        coneSize, cp, cconeSize, ccp, npt = 0, pt;
9064 
9065   PetscFunctionBegin;
9066   *unsplit = 0;
9067   switch (ct) {
9068   case DM_POLYTOPE_POINT_PRISM_TENSOR:
9069     ptpoints[npt++] = c;
9070     break;
9071   case DM_POLYTOPE_SEG_PRISM_TENSOR:
9072     PetscCall(DMPlexGetCone(dm, c, &cone));
9073     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9074     for (cp = 0; cp < coneSize; ++cp) {
9075       PetscCall(DMPlexGetCellType(dm, cone[cp], &cct));
9076       if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) ptpoints[npt++] = cone[cp];
9077     }
9078     break;
9079   case DM_POLYTOPE_TRI_PRISM_TENSOR:
9080   case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9081     PetscCall(DMPlexGetCone(dm, c, &cone));
9082     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9083     for (cp = 0; cp < coneSize; ++cp) {
9084       PetscCall(DMPlexGetCone(dm, cone[cp], &ccone));
9085       PetscCall(DMPlexGetConeSize(dm, cone[cp], &cconeSize));
9086       for (ccp = 0; ccp < cconeSize; ++ccp) {
9087         PetscCall(DMPlexGetCellType(dm, ccone[ccp], &cct));
9088         if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) {
9089           PetscInt p;
9090           for (p = 0; p < npt; ++p)
9091             if (ptpoints[p] == ccone[ccp]) break;
9092           if (p == npt) ptpoints[npt++] = ccone[ccp];
9093         }
9094       }
9095     }
9096     break;
9097   default:
9098     break;
9099   }
9100   for (pt = 0; pt < npt; ++pt) {
9101     PetscCall(DMPlexGetCone(dm, ptpoints[pt], &ptcone));
9102     if (ptcone[0] == ptcone[1]) ++(*unsplit);
9103   }
9104   PetscFunctionReturn(PETSC_SUCCESS);
9105 }
9106 
9107 /*@
9108   DMPlexCheckSkeleton - Check that each cell has the correct number of vertices
9109 
9110   Input Parameters:
9111 + dm         - The `DMPLEX` object
9112 - cellHeight - Normally 0
9113 
9114   Level: developer
9115 
9116   Notes:
9117   This is a useful diagnostic when creating meshes programmatically.
9118   Currently applicable only to homogeneous simplex or tensor meshes.
9119 
9120   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9121 
9122 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9123 @*/
9124 PetscErrorCode DMPlexCheckSkeleton(DM dm, PetscInt cellHeight)
9125 {
9126   DMPlexInterpolatedFlag interp;
9127   DMPolytopeType         ct;
9128   PetscInt               vStart, vEnd, cStart, cEnd, c;
9129 
9130   PetscFunctionBegin;
9131   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9132   PetscCall(DMPlexIsInterpolated(dm, &interp));
9133   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9134   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
9135   for (c = cStart; c < cEnd; ++c) {
9136     PetscInt *closure = NULL;
9137     PetscInt  coneSize, closureSize, cl, Nv = 0;
9138 
9139     PetscCall(DMPlexGetCellType(dm, c, &ct));
9140     PetscCheck((PetscInt)ct >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell %" PetscInt_FMT " has no cell type", c);
9141     if (ct == DM_POLYTOPE_UNKNOWN) continue;
9142     if (interp == DMPLEX_INTERPOLATED_FULL) {
9143       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9144       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));
9145     }
9146     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9147     for (cl = 0; cl < closureSize * 2; cl += 2) {
9148       const PetscInt p = closure[cl];
9149       if ((p >= vStart) && (p < vEnd)) ++Nv;
9150     }
9151     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9152     /* Special Case: Tensor faces with identified vertices */
9153     if (Nv < DMPolytopeTypeGetNumVertices(ct)) {
9154       PetscInt unsplit;
9155 
9156       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9157       if (Nv + unsplit == DMPolytopeTypeGetNumVertices(ct)) continue;
9158     }
9159     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));
9160   }
9161   PetscFunctionReturn(PETSC_SUCCESS);
9162 }
9163 
9164 /*@
9165   DMPlexCheckFaces - Check that the faces of each cell give a vertex order this is consistent with what we expect from the cell type
9166 
9167   Collective
9168 
9169   Input Parameters:
9170 + dm         - The `DMPLEX` object
9171 - cellHeight - Normally 0
9172 
9173   Level: developer
9174 
9175   Notes:
9176   This is a useful diagnostic when creating meshes programmatically.
9177   This routine is only relevant for meshes that are fully interpolated across all ranks.
9178   It will error out if a partially interpolated mesh is given on some rank.
9179   It will do nothing for locally uninterpolated mesh (as there is nothing to check).
9180 
9181   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9182 
9183 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMPlexGetVTKCellHeight()`, `DMSetFromOptions()`
9184 @*/
9185 PetscErrorCode DMPlexCheckFaces(DM dm, PetscInt cellHeight)
9186 {
9187   PetscInt               dim, depth, vStart, vEnd, cStart, cEnd, c, h;
9188   DMPlexInterpolatedFlag interpEnum;
9189 
9190   PetscFunctionBegin;
9191   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9192   PetscCall(DMPlexIsInterpolatedCollective(dm, &interpEnum));
9193   if (interpEnum == DMPLEX_INTERPOLATED_NONE) PetscFunctionReturn(PETSC_SUCCESS);
9194   if (interpEnum != DMPLEX_INTERPOLATED_FULL) {
9195     PetscCall(PetscPrintf(PetscObjectComm((PetscObject)dm), "DMPlexCheckFaces() warning: Mesh is only partially interpolated, this is currently not supported"));
9196     PetscFunctionReturn(PETSC_SUCCESS);
9197   }
9198 
9199   PetscCall(DMGetDimension(dm, &dim));
9200   PetscCall(DMPlexGetDepth(dm, &depth));
9201   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
9202   for (h = cellHeight; h < PetscMin(depth, dim); ++h) {
9203     PetscCall(DMPlexGetHeightStratum(dm, h, &cStart, &cEnd));
9204     for (c = cStart; c < cEnd; ++c) {
9205       const PetscInt       *cone, *ornt, *faceSizes, *faces;
9206       const DMPolytopeType *faceTypes;
9207       DMPolytopeType        ct;
9208       PetscInt              numFaces, coneSize, f;
9209       PetscInt             *closure = NULL, closureSize, cl, numCorners = 0, fOff = 0, unsplit;
9210 
9211       PetscCall(DMPlexGetCellType(dm, c, &ct));
9212       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9213       if (unsplit) continue;
9214       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9215       PetscCall(DMPlexGetCone(dm, c, &cone));
9216       PetscCall(DMPlexGetConeOrientation(dm, c, &ornt));
9217       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9218       for (cl = 0; cl < closureSize * 2; cl += 2) {
9219         const PetscInt p = closure[cl];
9220         if ((p >= vStart) && (p < vEnd)) closure[numCorners++] = p;
9221       }
9222       PetscCall(DMPlexGetRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
9223       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);
9224       for (f = 0; f < numFaces; ++f) {
9225         DMPolytopeType fct;
9226         PetscInt      *fclosure = NULL, fclosureSize, cl, fnumCorners = 0, v;
9227 
9228         PetscCall(DMPlexGetCellType(dm, cone[f], &fct));
9229         PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[f], ornt[f], PETSC_TRUE, &fclosureSize, &fclosure));
9230         for (cl = 0; cl < fclosureSize * 2; cl += 2) {
9231           const PetscInt p = fclosure[cl];
9232           if ((p >= vStart) && (p < vEnd)) fclosure[fnumCorners++] = p;
9233         }
9234         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]);
9235         for (v = 0; v < fnumCorners; ++v) {
9236           if (fclosure[v] != faces[fOff + v]) {
9237             PetscInt v1;
9238 
9239             PetscCall(PetscPrintf(PETSC_COMM_SELF, "face closure:"));
9240             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, fclosure[v1]));
9241             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\ncell face:"));
9242             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, faces[fOff + v1]));
9243             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9244             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]);
9245           }
9246         }
9247         PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[f], PETSC_TRUE, &fclosureSize, &fclosure));
9248         fOff += faceSizes[f];
9249       }
9250       PetscCall(DMPlexRestoreRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
9251       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9252     }
9253   }
9254   PetscFunctionReturn(PETSC_SUCCESS);
9255 }
9256 
9257 /*@
9258   DMPlexCheckGeometry - Check the geometry of mesh cells
9259 
9260   Input Parameter:
9261 . dm - The `DMPLEX` object
9262 
9263   Level: developer
9264 
9265   Notes:
9266   This is a useful diagnostic when creating meshes programmatically.
9267 
9268   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9269 
9270 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9271 @*/
9272 PetscErrorCode DMPlexCheckGeometry(DM dm)
9273 {
9274   Vec       coordinates;
9275   PetscReal detJ, J[9], refVol = 1.0;
9276   PetscReal vol;
9277   PetscInt  dim, depth, dE, d, cStart, cEnd, c;
9278 
9279   PetscFunctionBegin;
9280   PetscCall(DMGetDimension(dm, &dim));
9281   PetscCall(DMGetCoordinateDim(dm, &dE));
9282   if (dim != dE) PetscFunctionReturn(PETSC_SUCCESS);
9283   PetscCall(DMPlexGetDepth(dm, &depth));
9284   for (d = 0; d < dim; ++d) refVol *= 2.0;
9285   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
9286   /* Make sure local coordinates are created, because that step is collective */
9287   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
9288   if (!coordinates) PetscFunctionReturn(PETSC_SUCCESS);
9289   for (c = cStart; c < cEnd; ++c) {
9290     DMPolytopeType ct;
9291     PetscInt       unsplit;
9292     PetscBool      ignoreZeroVol = PETSC_FALSE;
9293 
9294     PetscCall(DMPlexGetCellType(dm, c, &ct));
9295     switch (ct) {
9296     case DM_POLYTOPE_SEG_PRISM_TENSOR:
9297     case DM_POLYTOPE_TRI_PRISM_TENSOR:
9298     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9299       ignoreZeroVol = PETSC_TRUE;
9300       break;
9301     default:
9302       break;
9303     }
9304     switch (ct) {
9305     case DM_POLYTOPE_TRI_PRISM:
9306     case DM_POLYTOPE_TRI_PRISM_TENSOR:
9307     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9308     case DM_POLYTOPE_PYRAMID:
9309       continue;
9310     default:
9311       break;
9312     }
9313     PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9314     if (unsplit) continue;
9315     PetscCall(DMPlexComputeCellGeometryFEM(dm, c, NULL, NULL, J, NULL, &detJ));
9316     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);
9317     PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FEM Volume %g\n", c, (double)(detJ * refVol)));
9318     /* This should work with periodicity since DG coordinates should be used */
9319     if (depth > 1) {
9320       PetscCall(DMPlexComputeCellGeometryFVM(dm, c, &vol, NULL, NULL));
9321       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);
9322       PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FVM Volume %g\n", c, (double)vol));
9323     }
9324   }
9325   PetscFunctionReturn(PETSC_SUCCESS);
9326 }
9327 
9328 /*@
9329   DMPlexCheckPointSF - Check that several necessary conditions are met for the point `PetscSF` of this plex.
9330 
9331   Collective
9332 
9333   Input Parameters:
9334 + dm              - The `DMPLEX` object
9335 . pointSF         - The `PetscSF`, or `NULL` for `PointSF` attached to `DM`
9336 - allowExtraRoots - Flag to allow extra points not present in the `DM`
9337 
9338   Level: developer
9339 
9340   Notes:
9341   This is mainly intended for debugging/testing purposes.
9342 
9343   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9344 
9345   Extra roots can come from periodic cuts, where additional points appear on the boundary
9346 
9347 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMGetPointSF()`, `DMSetFromOptions()`
9348 @*/
9349 PetscErrorCode DMPlexCheckPointSF(DM dm, PetscSF pointSF, PetscBool allowExtraRoots)
9350 {
9351   PetscInt           l, nleaves, nroots, overlap;
9352   const PetscInt    *locals;
9353   const PetscSFNode *remotes;
9354   PetscBool          distributed;
9355   MPI_Comm           comm;
9356   PetscMPIInt        rank;
9357 
9358   PetscFunctionBegin;
9359   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9360   if (pointSF) PetscValidHeaderSpecific(pointSF, PETSCSF_CLASSID, 2);
9361   else pointSF = dm->sf;
9362   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9363   PetscCheck(pointSF, comm, PETSC_ERR_ARG_WRONGSTATE, "DMPlex must have Point SF attached");
9364   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9365   {
9366     PetscMPIInt mpiFlag;
9367 
9368     PetscCallMPI(MPI_Comm_compare(comm, PetscObjectComm((PetscObject)pointSF), &mpiFlag));
9369     PetscCheck(mpiFlag == MPI_CONGRUENT || mpiFlag == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "DM and Point SF have different communicators (flag %d)", mpiFlag);
9370   }
9371   PetscCall(PetscSFGetGraph(pointSF, &nroots, &nleaves, &locals, &remotes));
9372   PetscCall(DMPlexIsDistributed(dm, &distributed));
9373   if (!distributed) {
9374     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);
9375     PetscFunctionReturn(PETSC_SUCCESS);
9376   }
9377   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);
9378   PetscCall(DMPlexGetOverlap(dm, &overlap));
9379 
9380   /* Check SF graph is compatible with DMPlex chart */
9381   {
9382     PetscInt pStart, pEnd, maxLeaf;
9383 
9384     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
9385     PetscCall(PetscSFGetLeafRange(pointSF, NULL, &maxLeaf));
9386     PetscCheck(allowExtraRoots || pEnd - pStart == nroots, PETSC_COMM_SELF, PETSC_ERR_PLIB, "pEnd - pStart = %" PetscInt_FMT " != nroots = %" PetscInt_FMT, pEnd - pStart, nroots);
9387     PetscCheck(maxLeaf < pEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "maxLeaf = %" PetscInt_FMT " >= pEnd = %" PetscInt_FMT, maxLeaf, pEnd);
9388   }
9389 
9390   /* Check Point SF has no local points referenced */
9391   for (l = 0; l < nleaves; l++) {
9392     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);
9393   }
9394 
9395   /* Check there are no cells in interface */
9396   if (!overlap) {
9397     PetscInt cellHeight, cStart, cEnd;
9398 
9399     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9400     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9401     for (l = 0; l < nleaves; ++l) {
9402       const PetscInt point = locals ? locals[l] : l;
9403 
9404       PetscCheck(point < cStart || point >= cEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " which is a cell", point);
9405     }
9406   }
9407 
9408   /* If some point is in interface, then all its cone points must be also in interface (either as leaves or roots) */
9409   {
9410     const PetscInt *rootdegree;
9411 
9412     PetscCall(PetscSFComputeDegreeBegin(pointSF, &rootdegree));
9413     PetscCall(PetscSFComputeDegreeEnd(pointSF, &rootdegree));
9414     for (l = 0; l < nleaves; ++l) {
9415       const PetscInt  point = locals ? locals[l] : l;
9416       const PetscInt *cone;
9417       PetscInt        coneSize, c, idx;
9418 
9419       PetscCall(DMPlexGetConeSize(dm, point, &coneSize));
9420       PetscCall(DMPlexGetCone(dm, point, &cone));
9421       for (c = 0; c < coneSize; ++c) {
9422         if (!rootdegree[cone[c]]) {
9423           if (locals) {
9424             PetscCall(PetscFindInt(cone[c], nleaves, locals, &idx));
9425           } else {
9426             idx = (cone[c] < nleaves) ? cone[c] : -1;
9427           }
9428           PetscCheck(idx >= 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " but not %" PetscInt_FMT " from its cone", point, cone[c]);
9429         }
9430       }
9431     }
9432   }
9433   PetscFunctionReturn(PETSC_SUCCESS);
9434 }
9435 
9436 /*@
9437   DMPlexCheckOrphanVertices - Check that no vertices are disconnected from the mesh, unless the mesh only consists of disconnected vertices.
9438 
9439   Collective
9440 
9441   Input Parameter:
9442 . dm - The `DMPLEX` object
9443 
9444   Level: developer
9445 
9446   Notes:
9447   This is mainly intended for debugging/testing purposes.
9448 
9449   Other cell types which are disconnected would be caught by the symmetry and face checks.
9450 
9451   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9452 
9453 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCheck()`, `DMSetFromOptions()`
9454 @*/
9455 PetscErrorCode DMPlexCheckOrphanVertices(DM dm)
9456 {
9457   PetscInt pStart, pEnd, vStart, vEnd;
9458 
9459   PetscFunctionBegin;
9460   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
9461   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
9462   if (pStart == vStart && pEnd == vEnd) PetscFunctionReturn(PETSC_SUCCESS);
9463   for (PetscInt v = vStart; v < vEnd; ++v) {
9464     PetscInt suppSize;
9465 
9466     PetscCall(DMPlexGetSupportSize(dm, v, &suppSize));
9467     PetscCheck(suppSize, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Vertex %" PetscInt_FMT " is disconnected from the mesh", v);
9468   }
9469   PetscFunctionReturn(PETSC_SUCCESS);
9470 }
9471 
9472 /*@
9473   DMPlexCheck - Perform various checks of `DMPLEX` sanity
9474 
9475   Input Parameter:
9476 . dm - The `DMPLEX` object
9477 
9478   Level: developer
9479 
9480   Notes:
9481   This is a useful diagnostic when creating meshes programmatically.
9482 
9483   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9484 
9485   Currently does not include `DMPlexCheckCellShape()`.
9486 
9487 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9488 @*/
9489 PetscErrorCode DMPlexCheck(DM dm)
9490 {
9491   PetscInt cellHeight;
9492 
9493   PetscFunctionBegin;
9494   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9495   PetscCall(DMPlexCheckSymmetry(dm));
9496   PetscCall(DMPlexCheckSkeleton(dm, cellHeight));
9497   PetscCall(DMPlexCheckFaces(dm, cellHeight));
9498   PetscCall(DMPlexCheckGeometry(dm));
9499   PetscCall(DMPlexCheckPointSF(dm, NULL, PETSC_FALSE));
9500   PetscCall(DMPlexCheckInterfaceCones(dm));
9501   PetscCall(DMPlexCheckOrphanVertices(dm));
9502   PetscFunctionReturn(PETSC_SUCCESS);
9503 }
9504 
9505 typedef struct cell_stats {
9506   PetscReal min, max, sum, squaresum;
9507   PetscInt  count;
9508 } cell_stats_t;
9509 
9510 static void MPIAPI cell_stats_reduce(void *a, void *b, int *len, MPI_Datatype *datatype)
9511 {
9512   PetscInt i, N = *len;
9513 
9514   for (i = 0; i < N; i++) {
9515     cell_stats_t *A = (cell_stats_t *)a;
9516     cell_stats_t *B = (cell_stats_t *)b;
9517 
9518     B->min = PetscMin(A->min, B->min);
9519     B->max = PetscMax(A->max, B->max);
9520     B->sum += A->sum;
9521     B->squaresum += A->squaresum;
9522     B->count += A->count;
9523   }
9524 }
9525 
9526 /*@
9527   DMPlexCheckCellShape - Checks the Jacobian of the mapping from reference to real cells and computes some minimal statistics.
9528 
9529   Collective
9530 
9531   Input Parameters:
9532 + dm        - The `DMPLEX` object
9533 . output    - If true, statistics will be displayed on `stdout`
9534 - condLimit - Display all cells above this condition number, or `PETSC_DETERMINE` for no cell output
9535 
9536   Level: developer
9537 
9538   Notes:
9539   This is mainly intended for debugging/testing purposes.
9540 
9541   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9542 
9543 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexComputeOrthogonalQuality()`
9544 @*/
9545 PetscErrorCode DMPlexCheckCellShape(DM dm, PetscBool output, PetscReal condLimit)
9546 {
9547   DM           dmCoarse;
9548   cell_stats_t stats, globalStats;
9549   MPI_Comm     comm = PetscObjectComm((PetscObject)dm);
9550   PetscReal   *J, *invJ, min = 0, max = 0, mean = 0, stdev = 0;
9551   PetscReal    limit = condLimit > 0 ? condLimit : PETSC_MAX_REAL;
9552   PetscInt     cdim, cStart, cEnd, c, eStart, eEnd, count = 0;
9553   PetscMPIInt  rank, size;
9554 
9555   PetscFunctionBegin;
9556   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9557   stats.min = PETSC_MAX_REAL;
9558   stats.max = PETSC_MIN_REAL;
9559   stats.sum = stats.squaresum = 0.;
9560   stats.count                 = 0;
9561 
9562   PetscCallMPI(MPI_Comm_size(comm, &size));
9563   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9564   PetscCall(DMGetCoordinateDim(dm, &cdim));
9565   PetscCall(PetscMalloc2(PetscSqr(cdim), &J, PetscSqr(cdim), &invJ));
9566   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
9567   PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
9568   for (c = cStart; c < cEnd; c++) {
9569     PetscInt  i;
9570     PetscReal frobJ = 0., frobInvJ = 0., cond2, cond, detJ;
9571 
9572     PetscCall(DMPlexComputeCellGeometryAffineFEM(dm, c, NULL, J, invJ, &detJ));
9573     PetscCheck(detJ >= 0.0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Mesh cell %" PetscInt_FMT " is inverted", c);
9574     for (i = 0; i < PetscSqr(cdim); ++i) {
9575       frobJ += J[i] * J[i];
9576       frobInvJ += invJ[i] * invJ[i];
9577     }
9578     cond2 = frobJ * frobInvJ;
9579     cond  = PetscSqrtReal(cond2);
9580 
9581     stats.min = PetscMin(stats.min, cond);
9582     stats.max = PetscMax(stats.max, cond);
9583     stats.sum += cond;
9584     stats.squaresum += cond2;
9585     stats.count++;
9586     if (output && cond > limit) {
9587       PetscSection coordSection;
9588       Vec          coordsLocal;
9589       PetscScalar *coords = NULL;
9590       PetscInt     Nv, d, clSize, cl, *closure = NULL;
9591 
9592       PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
9593       PetscCall(DMGetCoordinateSection(dm, &coordSection));
9594       PetscCall(DMPlexVecGetClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9595       PetscCall(PetscSynchronizedPrintf(comm, "[%d] Cell %" PetscInt_FMT " cond %g\n", rank, c, (double)cond));
9596       for (i = 0; i < Nv / cdim; ++i) {
9597         PetscCall(PetscSynchronizedPrintf(comm, "  Vertex %" PetscInt_FMT ": (", i));
9598         for (d = 0; d < cdim; ++d) {
9599           if (d > 0) PetscCall(PetscSynchronizedPrintf(comm, ", "));
9600           PetscCall(PetscSynchronizedPrintf(comm, "%g", (double)PetscRealPart(coords[i * cdim + d])));
9601         }
9602         PetscCall(PetscSynchronizedPrintf(comm, ")\n"));
9603       }
9604       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9605       for (cl = 0; cl < clSize * 2; cl += 2) {
9606         const PetscInt edge = closure[cl];
9607 
9608         if ((edge >= eStart) && (edge < eEnd)) {
9609           PetscReal len;
9610 
9611           PetscCall(DMPlexComputeCellGeometryFVM(dm, edge, &len, NULL, NULL));
9612           PetscCall(PetscSynchronizedPrintf(comm, "  Edge %" PetscInt_FMT ": length %g\n", edge, (double)len));
9613         }
9614       }
9615       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9616       PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9617     }
9618   }
9619   if (output) PetscCall(PetscSynchronizedFlush(comm, NULL));
9620 
9621   if (size > 1) {
9622     PetscMPIInt  blockLengths[2] = {4, 1};
9623     MPI_Aint     blockOffsets[2] = {offsetof(cell_stats_t, min), offsetof(cell_stats_t, count)};
9624     MPI_Datatype blockTypes[2]   = {MPIU_REAL, MPIU_INT}, statType;
9625     MPI_Op       statReduce;
9626 
9627     PetscCallMPI(MPI_Type_create_struct(2, blockLengths, blockOffsets, blockTypes, &statType));
9628     PetscCallMPI(MPI_Type_commit(&statType));
9629     PetscCallMPI(MPI_Op_create(cell_stats_reduce, PETSC_TRUE, &statReduce));
9630     PetscCallMPI(MPI_Reduce(&stats, &globalStats, 1, statType, statReduce, 0, comm));
9631     PetscCallMPI(MPI_Op_free(&statReduce));
9632     PetscCallMPI(MPI_Type_free(&statType));
9633   } else {
9634     PetscCall(PetscArraycpy(&globalStats, &stats, 1));
9635   }
9636   if (rank == 0) {
9637     count = globalStats.count;
9638     min   = globalStats.min;
9639     max   = globalStats.max;
9640     mean  = globalStats.sum / globalStats.count;
9641     stdev = globalStats.count > 1 ? PetscSqrtReal(PetscMax((globalStats.squaresum - globalStats.count * mean * mean) / (globalStats.count - 1), 0)) : 0.0;
9642   }
9643 
9644   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));
9645   PetscCall(PetscFree2(J, invJ));
9646 
9647   PetscCall(DMGetCoarseDM(dm, &dmCoarse));
9648   if (dmCoarse) {
9649     PetscBool isplex;
9650 
9651     PetscCall(PetscObjectTypeCompare((PetscObject)dmCoarse, DMPLEX, &isplex));
9652     if (isplex) PetscCall(DMPlexCheckCellShape(dmCoarse, output, condLimit));
9653   }
9654   PetscFunctionReturn(PETSC_SUCCESS);
9655 }
9656 
9657 /*@
9658   DMPlexComputeOrthogonalQuality - Compute cell-wise orthogonal quality mesh statistic. Optionally tags all cells with
9659   orthogonal quality below given tolerance.
9660 
9661   Collective
9662 
9663   Input Parameters:
9664 + dm   - The `DMPLEX` object
9665 . fv   - Optional `PetscFV` object for pre-computed cell/face centroid information
9666 - atol - [0, 1] Absolute tolerance for tagging cells.
9667 
9668   Output Parameters:
9669 + OrthQual      - `Vec` containing orthogonal quality per cell
9670 - OrthQualLabel - `DMLabel` tagging cells below atol with `DM_ADAPT_REFINE`
9671 
9672   Options Database Keys:
9673 + -dm_plex_orthogonal_quality_label_view - view OrthQualLabel if label is requested. Currently only `PETSCVIEWERASCII` is supported.
9674 - -dm_plex_orthogonal_quality_vec_view   - view OrthQual vector.
9675 
9676   Level: intermediate
9677 
9678   Notes:
9679   Orthogonal quality is given by the following formula\:
9680 
9681   $ \min \left[ \frac{A_i \cdot f_i}{\|A_i\| \|f_i\|} , \frac{A_i \cdot c_i}{\|A_i\| \|c_i\|} \right]$
9682 
9683   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
9684   is the vector from the current cells centroid to the centroid of its i'th neighbor (which shares a face with the
9685   current cell). This computes the vector similarity between each cell face and its corresponding neighbor centroid by
9686   calculating the cosine of the angle between these vectors.
9687 
9688   Orthogonal quality ranges from 1 (best) to 0 (worst).
9689 
9690   This routine is mainly useful for FVM, however is not restricted to only FVM. The `PetscFV` object is optionally used to check for
9691   pre-computed FVM cell data, but if it is not passed in then this data will be computed.
9692 
9693   Cells are tagged if they have an orthogonal quality less than or equal to the absolute tolerance.
9694 
9695 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCheckCellShape()`, `DMCreateLabel()`, `PetscFV`, `DMLabel`, `Vec`
9696 @*/
9697 PetscErrorCode DMPlexComputeOrthogonalQuality(DM dm, PetscFV fv, PetscReal atol, Vec *OrthQual, DMLabel *OrthQualLabel)
9698 {
9699   PetscInt               nc, cellHeight, cStart, cEnd, cell, cellIter = 0;
9700   PetscInt              *idx;
9701   PetscScalar           *oqVals;
9702   const PetscScalar     *cellGeomArr, *faceGeomArr;
9703   PetscReal             *ci, *fi, *Ai;
9704   MPI_Comm               comm;
9705   Vec                    cellgeom, facegeom;
9706   DM                     dmFace, dmCell;
9707   IS                     glob;
9708   ISLocalToGlobalMapping ltog;
9709   PetscViewer            vwr;
9710 
9711   PetscFunctionBegin;
9712   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9713   if (fv) PetscValidHeaderSpecific(fv, PETSCFV_CLASSID, 2);
9714   PetscAssertPointer(OrthQual, 4);
9715   PetscCheck(atol >= 0.0 && atol <= 1.0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Absolute tolerance %g not in [0,1]", (double)atol);
9716   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9717   PetscCall(DMGetDimension(dm, &nc));
9718   PetscCheck(nc >= 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must have dimension >= 2 (current %" PetscInt_FMT ")", nc);
9719   {
9720     DMPlexInterpolatedFlag interpFlag;
9721 
9722     PetscCall(DMPlexIsInterpolated(dm, &interpFlag));
9723     if (interpFlag != DMPLEX_INTERPOLATED_FULL) {
9724       PetscMPIInt rank;
9725 
9726       PetscCallMPI(MPI_Comm_rank(comm, &rank));
9727       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must be fully interpolated, DM on rank %d is not fully interpolated", rank);
9728     }
9729   }
9730   if (OrthQualLabel) {
9731     PetscAssertPointer(OrthQualLabel, 5);
9732     PetscCall(DMCreateLabel(dm, "Orthogonal_Quality"));
9733     PetscCall(DMGetLabel(dm, "Orthogonal_Quality", OrthQualLabel));
9734   } else {
9735     *OrthQualLabel = NULL;
9736   }
9737   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9738   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9739   PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_TRUE, &glob));
9740   PetscCall(ISLocalToGlobalMappingCreateIS(glob, &ltog));
9741   PetscCall(ISLocalToGlobalMappingSetType(ltog, ISLOCALTOGLOBALMAPPINGHASH));
9742   PetscCall(VecCreate(comm, OrthQual));
9743   PetscCall(VecSetType(*OrthQual, VECSTANDARD));
9744   PetscCall(VecSetSizes(*OrthQual, cEnd - cStart, PETSC_DETERMINE));
9745   PetscCall(VecSetLocalToGlobalMapping(*OrthQual, ltog));
9746   PetscCall(VecSetUp(*OrthQual));
9747   PetscCall(ISDestroy(&glob));
9748   PetscCall(ISLocalToGlobalMappingDestroy(&ltog));
9749   PetscCall(DMPlexGetDataFVM(dm, fv, &cellgeom, &facegeom, NULL));
9750   PetscCall(VecGetArrayRead(cellgeom, &cellGeomArr));
9751   PetscCall(VecGetArrayRead(facegeom, &faceGeomArr));
9752   PetscCall(VecGetDM(cellgeom, &dmCell));
9753   PetscCall(VecGetDM(facegeom, &dmFace));
9754   PetscCall(PetscMalloc5(cEnd - cStart, &idx, cEnd - cStart, &oqVals, nc, &ci, nc, &fi, nc, &Ai));
9755   for (cell = cStart; cell < cEnd; cellIter++, cell++) {
9756     PetscInt         cellneigh, cellneighiter = 0, adjSize = PETSC_DETERMINE;
9757     PetscInt         cellarr[2], *adj = NULL;
9758     PetscScalar     *cArr, *fArr;
9759     PetscReal        minvalc = 1.0, minvalf = 1.0;
9760     PetscFVCellGeom *cg;
9761 
9762     idx[cellIter] = cell - cStart;
9763     cellarr[0]    = cell;
9764     /* Make indexing into cellGeom easier */
9765     PetscCall(DMPlexPointLocalRead(dmCell, cell, cellGeomArr, &cg));
9766     PetscCall(DMPlexGetAdjacency_Internal(dm, cell, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &adjSize, &adj));
9767     /* Technically 1 too big, but easier than fiddling with empty adjacency array */
9768     PetscCall(PetscCalloc2(adjSize, &cArr, adjSize, &fArr));
9769     for (cellneigh = 0; cellneigh < adjSize; cellneighiter++, cellneigh++) {
9770       PetscInt         i;
9771       const PetscInt   neigh  = adj[cellneigh];
9772       PetscReal        normci = 0, normfi = 0, normai = 0;
9773       PetscFVCellGeom *cgneigh;
9774       PetscFVFaceGeom *fg;
9775 
9776       /* Don't count ourselves in the neighbor list */
9777       if (neigh == cell) continue;
9778       PetscCall(DMPlexPointLocalRead(dmCell, neigh, cellGeomArr, &cgneigh));
9779       cellarr[1] = neigh;
9780       {
9781         PetscInt        numcovpts;
9782         const PetscInt *covpts;
9783 
9784         PetscCall(DMPlexGetMeet(dm, 2, cellarr, &numcovpts, &covpts));
9785         PetscCall(DMPlexPointLocalRead(dmFace, covpts[0], faceGeomArr, &fg));
9786         PetscCall(DMPlexRestoreMeet(dm, 2, cellarr, &numcovpts, &covpts));
9787       }
9788 
9789       /* Compute c_i, f_i and their norms */
9790       for (i = 0; i < nc; i++) {
9791         ci[i] = cgneigh->centroid[i] - cg->centroid[i];
9792         fi[i] = fg->centroid[i] - cg->centroid[i];
9793         Ai[i] = fg->normal[i];
9794         normci += PetscPowReal(ci[i], 2);
9795         normfi += PetscPowReal(fi[i], 2);
9796         normai += PetscPowReal(Ai[i], 2);
9797       }
9798       normci = PetscSqrtReal(normci);
9799       normfi = PetscSqrtReal(normfi);
9800       normai = PetscSqrtReal(normai);
9801 
9802       /* Normalize and compute for each face-cell-normal pair */
9803       for (i = 0; i < nc; i++) {
9804         ci[i] = ci[i] / normci;
9805         fi[i] = fi[i] / normfi;
9806         Ai[i] = Ai[i] / normai;
9807         /* PetscAbs because I don't know if normals are guaranteed to point out */
9808         cArr[cellneighiter] += PetscAbs(Ai[i] * ci[i]);
9809         fArr[cellneighiter] += PetscAbs(Ai[i] * fi[i]);
9810       }
9811       if (PetscRealPart(cArr[cellneighiter]) < minvalc) minvalc = PetscRealPart(cArr[cellneighiter]);
9812       if (PetscRealPart(fArr[cellneighiter]) < minvalf) minvalf = PetscRealPart(fArr[cellneighiter]);
9813     }
9814     PetscCall(PetscFree(adj));
9815     PetscCall(PetscFree2(cArr, fArr));
9816     /* Defer to cell if they're equal */
9817     oqVals[cellIter] = PetscMin(minvalf, minvalc);
9818     if (OrthQualLabel) {
9819       if (PetscRealPart(oqVals[cellIter]) <= atol) PetscCall(DMLabelSetValue(*OrthQualLabel, cell, DM_ADAPT_REFINE));
9820     }
9821   }
9822   PetscCall(VecSetValuesLocal(*OrthQual, cEnd - cStart, idx, oqVals, INSERT_VALUES));
9823   PetscCall(VecAssemblyBegin(*OrthQual));
9824   PetscCall(VecAssemblyEnd(*OrthQual));
9825   PetscCall(VecRestoreArrayRead(cellgeom, &cellGeomArr));
9826   PetscCall(VecRestoreArrayRead(facegeom, &faceGeomArr));
9827   PetscCall(PetscOptionsGetViewer(comm, NULL, NULL, "-dm_plex_orthogonal_quality_label_view", &vwr, NULL, NULL));
9828   if (OrthQualLabel) {
9829     if (vwr) PetscCall(DMLabelView(*OrthQualLabel, vwr));
9830   }
9831   PetscCall(PetscFree5(idx, oqVals, ci, fi, Ai));
9832   PetscCall(PetscOptionsRestoreViewer(&vwr));
9833   PetscCall(VecViewFromOptions(*OrthQual, NULL, "-dm_plex_orthogonal_quality_vec_view"));
9834   PetscFunctionReturn(PETSC_SUCCESS);
9835 }
9836 
9837 /* this is here instead of DMGetOutputDM because output DM still has constraints in the local indices that affect
9838  * interpolator construction */
9839 static PetscErrorCode DMGetFullDM(DM dm, DM *odm)
9840 {
9841   PetscSection section, newSection, gsection;
9842   PetscSF      sf;
9843   PetscBool    hasConstraints, ghasConstraints;
9844 
9845   PetscFunctionBegin;
9846   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9847   PetscAssertPointer(odm, 2);
9848   PetscCall(DMGetLocalSection(dm, &section));
9849   PetscCall(PetscSectionHasConstraints(section, &hasConstraints));
9850   PetscCall(MPIU_Allreduce(&hasConstraints, &ghasConstraints, 1, MPIU_BOOL, MPI_LOR, PetscObjectComm((PetscObject)dm)));
9851   if (!ghasConstraints) {
9852     PetscCall(PetscObjectReference((PetscObject)dm));
9853     *odm = dm;
9854     PetscFunctionReturn(PETSC_SUCCESS);
9855   }
9856   PetscCall(DMClone(dm, odm));
9857   PetscCall(DMCopyFields(dm, *odm));
9858   PetscCall(DMGetLocalSection(*odm, &newSection));
9859   PetscCall(DMGetPointSF(*odm, &sf));
9860   PetscCall(PetscSectionCreateGlobalSection(newSection, sf, PETSC_TRUE, PETSC_TRUE, PETSC_FALSE, &gsection));
9861   PetscCall(DMSetGlobalSection(*odm, gsection));
9862   PetscCall(PetscSectionDestroy(&gsection));
9863   PetscFunctionReturn(PETSC_SUCCESS);
9864 }
9865 
9866 static PetscErrorCode DMCreateAffineInterpolationCorrection_Plex(DM dmc, DM dmf, Vec *shift)
9867 {
9868   DM        dmco, dmfo;
9869   Mat       interpo;
9870   Vec       rscale;
9871   Vec       cglobalo, clocal;
9872   Vec       fglobal, fglobalo, flocal;
9873   PetscBool regular;
9874 
9875   PetscFunctionBegin;
9876   PetscCall(DMGetFullDM(dmc, &dmco));
9877   PetscCall(DMGetFullDM(dmf, &dmfo));
9878   PetscCall(DMSetCoarseDM(dmfo, dmco));
9879   PetscCall(DMPlexGetRegularRefinement(dmf, &regular));
9880   PetscCall(DMPlexSetRegularRefinement(dmfo, regular));
9881   PetscCall(DMCreateInterpolation(dmco, dmfo, &interpo, &rscale));
9882   PetscCall(DMCreateGlobalVector(dmco, &cglobalo));
9883   PetscCall(DMCreateLocalVector(dmc, &clocal));
9884   PetscCall(VecSet(cglobalo, 0.));
9885   PetscCall(VecSet(clocal, 0.));
9886   PetscCall(DMCreateGlobalVector(dmf, &fglobal));
9887   PetscCall(DMCreateGlobalVector(dmfo, &fglobalo));
9888   PetscCall(DMCreateLocalVector(dmf, &flocal));
9889   PetscCall(VecSet(fglobal, 0.));
9890   PetscCall(VecSet(fglobalo, 0.));
9891   PetscCall(VecSet(flocal, 0.));
9892   PetscCall(DMPlexInsertBoundaryValues(dmc, PETSC_TRUE, clocal, 0., NULL, NULL, NULL));
9893   PetscCall(DMLocalToGlobalBegin(dmco, clocal, INSERT_VALUES, cglobalo));
9894   PetscCall(DMLocalToGlobalEnd(dmco, clocal, INSERT_VALUES, cglobalo));
9895   PetscCall(MatMult(interpo, cglobalo, fglobalo));
9896   PetscCall(DMGlobalToLocalBegin(dmfo, fglobalo, INSERT_VALUES, flocal));
9897   PetscCall(DMGlobalToLocalEnd(dmfo, fglobalo, INSERT_VALUES, flocal));
9898   PetscCall(DMLocalToGlobalBegin(dmf, flocal, INSERT_VALUES, fglobal));
9899   PetscCall(DMLocalToGlobalEnd(dmf, flocal, INSERT_VALUES, fglobal));
9900   *shift = fglobal;
9901   PetscCall(VecDestroy(&flocal));
9902   PetscCall(VecDestroy(&fglobalo));
9903   PetscCall(VecDestroy(&clocal));
9904   PetscCall(VecDestroy(&cglobalo));
9905   PetscCall(VecDestroy(&rscale));
9906   PetscCall(MatDestroy(&interpo));
9907   PetscCall(DMDestroy(&dmfo));
9908   PetscCall(DMDestroy(&dmco));
9909   PetscFunctionReturn(PETSC_SUCCESS);
9910 }
9911 
9912 PETSC_INTERN PetscErrorCode DMInterpolateSolution_Plex(DM coarse, DM fine, Mat interp, Vec coarseSol, Vec fineSol)
9913 {
9914   PetscObject shifto;
9915   Vec         shift;
9916 
9917   PetscFunctionBegin;
9918   if (!interp) {
9919     Vec rscale;
9920 
9921     PetscCall(DMCreateInterpolation(coarse, fine, &interp, &rscale));
9922     PetscCall(VecDestroy(&rscale));
9923   } else {
9924     PetscCall(PetscObjectReference((PetscObject)interp));
9925   }
9926   PetscCall(PetscObjectQuery((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", &shifto));
9927   if (!shifto) {
9928     PetscCall(DMCreateAffineInterpolationCorrection_Plex(coarse, fine, &shift));
9929     PetscCall(PetscObjectCompose((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", (PetscObject)shift));
9930     shifto = (PetscObject)shift;
9931     PetscCall(VecDestroy(&shift));
9932   }
9933   shift = (Vec)shifto;
9934   PetscCall(MatInterpolate(interp, coarseSol, fineSol));
9935   PetscCall(VecAXPY(fineSol, 1.0, shift));
9936   PetscCall(MatDestroy(&interp));
9937   PetscFunctionReturn(PETSC_SUCCESS);
9938 }
9939 
9940 /* Pointwise interpolation
9941      Just code FEM for now
9942      u^f = I u^c
9943      sum_k u^f_k phi^f_k = I sum_j u^c_j phi^c_j
9944      u^f_i = sum_j psi^f_i I phi^c_j u^c_j
9945      I_{ij} = psi^f_i phi^c_j
9946 */
9947 PetscErrorCode DMCreateInterpolation_Plex(DM dmCoarse, DM dmFine, Mat *interpolation, Vec *scaling)
9948 {
9949   PetscSection gsc, gsf;
9950   PetscInt     m, n;
9951   void        *ctx;
9952   DM           cdm;
9953   PetscBool    regular, ismatis, isRefined = dmCoarse->data == dmFine->data ? PETSC_FALSE : PETSC_TRUE;
9954 
9955   PetscFunctionBegin;
9956   PetscCall(DMGetGlobalSection(dmFine, &gsf));
9957   PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
9958   PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
9959   PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
9960 
9961   PetscCall(PetscStrcmp(dmCoarse->mattype, MATIS, &ismatis));
9962   PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), interpolation));
9963   PetscCall(MatSetSizes(*interpolation, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
9964   PetscCall(MatSetType(*interpolation, ismatis ? MATAIJ : dmCoarse->mattype));
9965   PetscCall(DMGetApplicationContext(dmFine, &ctx));
9966 
9967   PetscCall(DMGetCoarseDM(dmFine, &cdm));
9968   PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
9969   if (!isRefined || (regular && cdm == dmCoarse)) PetscCall(DMPlexComputeInterpolatorNested(dmCoarse, dmFine, isRefined, *interpolation, ctx));
9970   else PetscCall(DMPlexComputeInterpolatorGeneral(dmCoarse, dmFine, *interpolation, ctx));
9971   PetscCall(MatViewFromOptions(*interpolation, NULL, "-interp_mat_view"));
9972   if (scaling) {
9973     /* Use naive scaling */
9974     PetscCall(DMCreateInterpolationScale(dmCoarse, dmFine, *interpolation, scaling));
9975   }
9976   PetscFunctionReturn(PETSC_SUCCESS);
9977 }
9978 
9979 PetscErrorCode DMCreateInjection_Plex(DM dmCoarse, DM dmFine, Mat *mat)
9980 {
9981   VecScatter ctx;
9982 
9983   PetscFunctionBegin;
9984   PetscCall(DMPlexComputeInjectorFEM(dmCoarse, dmFine, &ctx, NULL));
9985   PetscCall(MatCreateScatter(PetscObjectComm((PetscObject)ctx), ctx, mat));
9986   PetscCall(VecScatterDestroy(&ctx));
9987   PetscFunctionReturn(PETSC_SUCCESS);
9988 }
9989 
9990 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[])
9991 {
9992   const PetscInt Nc = uOff[1] - uOff[0];
9993   PetscInt       c;
9994   for (c = 0; c < Nc; ++c) g0[c * Nc + c] = 1.0;
9995 }
9996 
9997 PetscErrorCode DMCreateMassMatrixLumped_Plex(DM dm, Vec *mass)
9998 {
9999   DM           dmc;
10000   PetscDS      ds;
10001   Vec          ones, locmass;
10002   IS           cellIS;
10003   PetscFormKey key;
10004   PetscInt     depth;
10005 
10006   PetscFunctionBegin;
10007   PetscCall(DMClone(dm, &dmc));
10008   PetscCall(DMCopyDisc(dm, dmc));
10009   PetscCall(DMGetDS(dmc, &ds));
10010   PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
10011   PetscCall(DMCreateGlobalVector(dmc, mass));
10012   PetscCall(DMGetLocalVector(dmc, &ones));
10013   PetscCall(DMGetLocalVector(dmc, &locmass));
10014   PetscCall(DMPlexGetDepth(dmc, &depth));
10015   PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
10016   PetscCall(VecSet(locmass, 0.0));
10017   PetscCall(VecSet(ones, 1.0));
10018   key.label = NULL;
10019   key.value = 0;
10020   key.field = 0;
10021   key.part  = 0;
10022   PetscCall(DMPlexComputeJacobian_Action_Internal(dmc, key, cellIS, 0.0, 0.0, ones, NULL, ones, locmass, NULL));
10023   PetscCall(ISDestroy(&cellIS));
10024   PetscCall(VecSet(*mass, 0.0));
10025   PetscCall(DMLocalToGlobalBegin(dmc, locmass, ADD_VALUES, *mass));
10026   PetscCall(DMLocalToGlobalEnd(dmc, locmass, ADD_VALUES, *mass));
10027   PetscCall(DMRestoreLocalVector(dmc, &ones));
10028   PetscCall(DMRestoreLocalVector(dmc, &locmass));
10029   PetscCall(DMDestroy(&dmc));
10030   PetscFunctionReturn(PETSC_SUCCESS);
10031 }
10032 
10033 PetscErrorCode DMCreateMassMatrix_Plex(DM dmCoarse, DM dmFine, Mat *mass)
10034 {
10035   PetscSection gsc, gsf;
10036   PetscInt     m, n;
10037   void        *ctx;
10038   DM           cdm;
10039   PetscBool    regular;
10040 
10041   PetscFunctionBegin;
10042   if (dmFine == dmCoarse) {
10043     DM            dmc;
10044     PetscDS       ds;
10045     PetscWeakForm wf;
10046     Vec           u;
10047     IS            cellIS;
10048     PetscFormKey  key;
10049     PetscInt      depth;
10050 
10051     PetscCall(DMClone(dmFine, &dmc));
10052     PetscCall(DMCopyDisc(dmFine, dmc));
10053     PetscCall(DMGetDS(dmc, &ds));
10054     PetscCall(PetscDSGetWeakForm(ds, &wf));
10055     PetscCall(PetscWeakFormClear(wf));
10056     PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
10057     PetscCall(DMCreateMatrix(dmc, mass));
10058     PetscCall(DMGetLocalVector(dmc, &u));
10059     PetscCall(DMPlexGetDepth(dmc, &depth));
10060     PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
10061     PetscCall(MatZeroEntries(*mass));
10062     key.label = NULL;
10063     key.value = 0;
10064     key.field = 0;
10065     key.part  = 0;
10066     PetscCall(DMPlexComputeJacobian_Internal(dmc, key, cellIS, 0.0, 0.0, u, NULL, *mass, *mass, NULL));
10067     PetscCall(ISDestroy(&cellIS));
10068     PetscCall(DMRestoreLocalVector(dmc, &u));
10069     PetscCall(DMDestroy(&dmc));
10070   } else {
10071     PetscCall(DMGetGlobalSection(dmFine, &gsf));
10072     PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
10073     PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
10074     PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
10075 
10076     PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), mass));
10077     PetscCall(MatSetSizes(*mass, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
10078     PetscCall(MatSetType(*mass, dmCoarse->mattype));
10079     PetscCall(DMGetApplicationContext(dmFine, &ctx));
10080 
10081     PetscCall(DMGetCoarseDM(dmFine, &cdm));
10082     PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
10083     if (regular && cdm == dmCoarse) PetscCall(DMPlexComputeMassMatrixNested(dmCoarse, dmFine, *mass, ctx));
10084     else PetscCall(DMPlexComputeMassMatrixGeneral(dmCoarse, dmFine, *mass, ctx));
10085   }
10086   PetscCall(MatViewFromOptions(*mass, NULL, "-mass_mat_view"));
10087   PetscFunctionReturn(PETSC_SUCCESS);
10088 }
10089 
10090 /*@
10091   DMPlexGetRegularRefinement - Get the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
10092 
10093   Input Parameter:
10094 . dm - The `DMPLEX` object
10095 
10096   Output Parameter:
10097 . regular - The flag
10098 
10099   Level: intermediate
10100 
10101 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetRegularRefinement()`
10102 @*/
10103 PetscErrorCode DMPlexGetRegularRefinement(DM dm, PetscBool *regular)
10104 {
10105   PetscFunctionBegin;
10106   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10107   PetscAssertPointer(regular, 2);
10108   *regular = ((DM_Plex *)dm->data)->regularRefinement;
10109   PetscFunctionReturn(PETSC_SUCCESS);
10110 }
10111 
10112 /*@
10113   DMPlexSetRegularRefinement - Set the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
10114 
10115   Input Parameters:
10116 + dm      - The `DMPLEX` object
10117 - regular - The flag
10118 
10119   Level: intermediate
10120 
10121 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetRegularRefinement()`
10122 @*/
10123 PetscErrorCode DMPlexSetRegularRefinement(DM dm, PetscBool regular)
10124 {
10125   PetscFunctionBegin;
10126   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10127   ((DM_Plex *)dm->data)->regularRefinement = regular;
10128   PetscFunctionReturn(PETSC_SUCCESS);
10129 }
10130 
10131 /*@
10132   DMPlexGetAnchors - Get the layout of the anchor (point-to-point) constraints.  Typically, the user will not have to
10133   call DMPlexGetAnchors() directly: if there are anchors, then `DMPlexGetAnchors()` is called during `DMGetDefaultConstraints()`.
10134 
10135   Not Collective
10136 
10137   Input Parameter:
10138 . dm - The `DMPLEX` object
10139 
10140   Output Parameters:
10141 + anchorSection - If not `NULL`, set to the section describing which points anchor the constrained points.
10142 - anchorIS      - If not `NULL`, set to the list of anchors indexed by `anchorSection`
10143 
10144   Level: intermediate
10145 
10146 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`, `IS`, `PetscSection`
10147 @*/
10148 PetscErrorCode DMPlexGetAnchors(DM dm, PetscSection *anchorSection, IS *anchorIS)
10149 {
10150   DM_Plex *plex = (DM_Plex *)dm->data;
10151 
10152   PetscFunctionBegin;
10153   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10154   if (!plex->anchorSection && !plex->anchorIS && plex->createanchors) PetscCall((*plex->createanchors)(dm));
10155   if (anchorSection) *anchorSection = plex->anchorSection;
10156   if (anchorIS) *anchorIS = plex->anchorIS;
10157   PetscFunctionReturn(PETSC_SUCCESS);
10158 }
10159 
10160 /*@
10161   DMPlexSetAnchors - Set the layout of the local anchor (point-to-point) constraints.
10162 
10163   Collective
10164 
10165   Input Parameters:
10166 + dm            - The `DMPLEX` object
10167 . anchorSection - The section that describes the mapping from constrained points to the anchor points listed in anchorIS.
10168                   Must have a local communicator (`PETSC_COMM_SELF` or derivative).
10169 - anchorIS      - The list of all anchor points.  Must have a local communicator (`PETSC_COMM_SELF` or derivative).
10170 
10171   Level: intermediate
10172 
10173   Notes:
10174   Unlike boundary conditions, when a point's degrees of freedom in a section are constrained to
10175   an outside value, the anchor constraints set a point's degrees of freedom to be a linear
10176   combination of other points' degrees of freedom.
10177 
10178   After specifying the layout of constraints with `DMPlexSetAnchors()`, one specifies the constraints by calling
10179   `DMGetDefaultConstraints()` and filling in the entries in the constraint matrix.
10180 
10181   The reference counts of `anchorSection` and `anchorIS` are incremented.
10182 
10183 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`
10184 @*/
10185 PetscErrorCode DMPlexSetAnchors(DM dm, PetscSection anchorSection, IS anchorIS)
10186 {
10187   DM_Plex    *plex = (DM_Plex *)dm->data;
10188   PetscMPIInt result;
10189 
10190   PetscFunctionBegin;
10191   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10192   if (anchorSection) {
10193     PetscValidHeaderSpecific(anchorSection, PETSC_SECTION_CLASSID, 2);
10194     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorSection), &result));
10195     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor section must have local communicator");
10196   }
10197   if (anchorIS) {
10198     PetscValidHeaderSpecific(anchorIS, IS_CLASSID, 3);
10199     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorIS), &result));
10200     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor IS must have local communicator");
10201   }
10202 
10203   PetscCall(PetscObjectReference((PetscObject)anchorSection));
10204   PetscCall(PetscSectionDestroy(&plex->anchorSection));
10205   plex->anchorSection = anchorSection;
10206 
10207   PetscCall(PetscObjectReference((PetscObject)anchorIS));
10208   PetscCall(ISDestroy(&plex->anchorIS));
10209   plex->anchorIS = anchorIS;
10210 
10211   if (PetscUnlikelyDebug(anchorIS && anchorSection)) {
10212     PetscInt        size, a, pStart, pEnd;
10213     const PetscInt *anchors;
10214 
10215     PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
10216     PetscCall(ISGetLocalSize(anchorIS, &size));
10217     PetscCall(ISGetIndices(anchorIS, &anchors));
10218     for (a = 0; a < size; a++) {
10219       PetscInt p;
10220 
10221       p = anchors[a];
10222       if (p >= pStart && p < pEnd) {
10223         PetscInt dof;
10224 
10225         PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
10226         if (dof) {
10227           PetscCall(ISRestoreIndices(anchorIS, &anchors));
10228           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Point %" PetscInt_FMT " cannot be constrained and an anchor", p);
10229         }
10230       }
10231     }
10232     PetscCall(ISRestoreIndices(anchorIS, &anchors));
10233   }
10234   /* reset the generic constraints */
10235   PetscCall(DMSetDefaultConstraints(dm, NULL, NULL, NULL));
10236   PetscFunctionReturn(PETSC_SUCCESS);
10237 }
10238 
10239 static PetscErrorCode DMPlexCreateConstraintSection_Anchors(DM dm, PetscSection section, PetscSection *cSec)
10240 {
10241   PetscSection anchorSection;
10242   PetscInt     pStart, pEnd, sStart, sEnd, p, dof, numFields, f;
10243 
10244   PetscFunctionBegin;
10245   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10246   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
10247   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, cSec));
10248   PetscCall(PetscSectionGetNumFields(section, &numFields));
10249   if (numFields) {
10250     PetscInt f;
10251     PetscCall(PetscSectionSetNumFields(*cSec, numFields));
10252 
10253     for (f = 0; f < numFields; f++) {
10254       PetscInt numComp;
10255 
10256       PetscCall(PetscSectionGetFieldComponents(section, f, &numComp));
10257       PetscCall(PetscSectionSetFieldComponents(*cSec, f, numComp));
10258     }
10259   }
10260   PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
10261   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
10262   pStart = PetscMax(pStart, sStart);
10263   pEnd   = PetscMin(pEnd, sEnd);
10264   pEnd   = PetscMax(pStart, pEnd);
10265   PetscCall(PetscSectionSetChart(*cSec, pStart, pEnd));
10266   for (p = pStart; p < pEnd; p++) {
10267     PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
10268     if (dof) {
10269       PetscCall(PetscSectionGetDof(section, p, &dof));
10270       PetscCall(PetscSectionSetDof(*cSec, p, dof));
10271       for (f = 0; f < numFields; f++) {
10272         PetscCall(PetscSectionGetFieldDof(section, p, f, &dof));
10273         PetscCall(PetscSectionSetFieldDof(*cSec, p, f, dof));
10274       }
10275     }
10276   }
10277   PetscCall(PetscSectionSetUp(*cSec));
10278   PetscCall(PetscObjectSetName((PetscObject)*cSec, "Constraint Section"));
10279   PetscFunctionReturn(PETSC_SUCCESS);
10280 }
10281 
10282 static PetscErrorCode DMPlexCreateConstraintMatrix_Anchors(DM dm, PetscSection section, PetscSection cSec, Mat *cMat)
10283 {
10284   PetscSection    aSec;
10285   PetscInt        pStart, pEnd, p, sStart, sEnd, dof, aDof, aOff, off, nnz, annz, m, n, q, a, offset, *i, *j;
10286   const PetscInt *anchors;
10287   PetscInt        numFields, f;
10288   IS              aIS;
10289   MatType         mtype;
10290   PetscBool       iscuda, iskokkos;
10291 
10292   PetscFunctionBegin;
10293   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10294   PetscCall(PetscSectionGetStorageSize(cSec, &m));
10295   PetscCall(PetscSectionGetStorageSize(section, &n));
10296   PetscCall(MatCreate(PETSC_COMM_SELF, cMat));
10297   PetscCall(MatSetSizes(*cMat, m, n, m, n));
10298   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJCUSPARSE, &iscuda));
10299   if (!iscuda) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJCUSPARSE, &iscuda));
10300   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJKOKKOS, &iskokkos));
10301   if (!iskokkos) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJKOKKOS, &iskokkos));
10302   if (iscuda) mtype = MATSEQAIJCUSPARSE;
10303   else if (iskokkos) mtype = MATSEQAIJKOKKOS;
10304   else mtype = MATSEQAIJ;
10305   PetscCall(MatSetType(*cMat, mtype));
10306   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
10307   PetscCall(ISGetIndices(aIS, &anchors));
10308   /* cSec will be a subset of aSec and section */
10309   PetscCall(PetscSectionGetChart(cSec, &pStart, &pEnd));
10310   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
10311   PetscCall(PetscMalloc1(m + 1, &i));
10312   i[0] = 0;
10313   PetscCall(PetscSectionGetNumFields(section, &numFields));
10314   for (p = pStart; p < pEnd; p++) {
10315     PetscInt rDof, rOff, r;
10316 
10317     PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10318     if (!rDof) continue;
10319     PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10320     if (numFields) {
10321       for (f = 0; f < numFields; f++) {
10322         annz = 0;
10323         for (r = 0; r < rDof; r++) {
10324           a = anchors[rOff + r];
10325           if (a < sStart || a >= sEnd) continue;
10326           PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
10327           annz += aDof;
10328         }
10329         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
10330         PetscCall(PetscSectionGetFieldOffset(cSec, p, f, &off));
10331         for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
10332       }
10333     } else {
10334       annz = 0;
10335       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10336       for (q = 0; q < dof; q++) {
10337         a = anchors[rOff + q];
10338         if (a < sStart || a >= sEnd) continue;
10339         PetscCall(PetscSectionGetDof(section, a, &aDof));
10340         annz += aDof;
10341       }
10342       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10343       PetscCall(PetscSectionGetOffset(cSec, p, &off));
10344       for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
10345     }
10346   }
10347   nnz = i[m];
10348   PetscCall(PetscMalloc1(nnz, &j));
10349   offset = 0;
10350   for (p = pStart; p < pEnd; p++) {
10351     if (numFields) {
10352       for (f = 0; f < numFields; f++) {
10353         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
10354         for (q = 0; q < dof; q++) {
10355           PetscInt rDof, rOff, r;
10356           PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10357           PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10358           for (r = 0; r < rDof; r++) {
10359             PetscInt s;
10360 
10361             a = anchors[rOff + r];
10362             if (a < sStart || a >= sEnd) continue;
10363             PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
10364             PetscCall(PetscSectionGetFieldOffset(section, a, f, &aOff));
10365             for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
10366           }
10367         }
10368       }
10369     } else {
10370       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10371       for (q = 0; q < dof; q++) {
10372         PetscInt rDof, rOff, r;
10373         PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10374         PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10375         for (r = 0; r < rDof; r++) {
10376           PetscInt s;
10377 
10378           a = anchors[rOff + r];
10379           if (a < sStart || a >= sEnd) continue;
10380           PetscCall(PetscSectionGetDof(section, a, &aDof));
10381           PetscCall(PetscSectionGetOffset(section, a, &aOff));
10382           for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
10383         }
10384       }
10385     }
10386   }
10387   PetscCall(MatSeqAIJSetPreallocationCSR(*cMat, i, j, NULL));
10388   PetscCall(PetscFree(i));
10389   PetscCall(PetscFree(j));
10390   PetscCall(ISRestoreIndices(aIS, &anchors));
10391   PetscFunctionReturn(PETSC_SUCCESS);
10392 }
10393 
10394 PetscErrorCode DMCreateDefaultConstraints_Plex(DM dm)
10395 {
10396   DM_Plex     *plex = (DM_Plex *)dm->data;
10397   PetscSection anchorSection, section, cSec;
10398   Mat          cMat;
10399 
10400   PetscFunctionBegin;
10401   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10402   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
10403   if (anchorSection) {
10404     PetscInt Nf;
10405 
10406     PetscCall(DMGetLocalSection(dm, &section));
10407     PetscCall(DMPlexCreateConstraintSection_Anchors(dm, section, &cSec));
10408     PetscCall(DMPlexCreateConstraintMatrix_Anchors(dm, section, cSec, &cMat));
10409     PetscCall(DMGetNumFields(dm, &Nf));
10410     if (Nf && plex->computeanchormatrix) PetscCall((*plex->computeanchormatrix)(dm, section, cSec, cMat));
10411     PetscCall(DMSetDefaultConstraints(dm, cSec, cMat, NULL));
10412     PetscCall(PetscSectionDestroy(&cSec));
10413     PetscCall(MatDestroy(&cMat));
10414   }
10415   PetscFunctionReturn(PETSC_SUCCESS);
10416 }
10417 
10418 PetscErrorCode DMCreateSubDomainDM_Plex(DM dm, DMLabel label, PetscInt value, IS *is, DM *subdm)
10419 {
10420   IS           subis;
10421   PetscSection section, subsection;
10422 
10423   PetscFunctionBegin;
10424   PetscCall(DMGetLocalSection(dm, &section));
10425   PetscCheck(section, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set default section for DM before splitting subdomain");
10426   PetscCheck(subdm, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set output subDM for splitting subdomain");
10427   /* Create subdomain */
10428   PetscCall(DMPlexFilter(dm, label, value, PETSC_FALSE, PETSC_FALSE, NULL, subdm));
10429   /* Create submodel */
10430   PetscCall(DMPlexGetSubpointIS(*subdm, &subis));
10431   PetscCall(PetscSectionCreateSubmeshSection(section, subis, &subsection));
10432   PetscCall(DMSetLocalSection(*subdm, subsection));
10433   PetscCall(PetscSectionDestroy(&subsection));
10434   PetscCall(DMCopyDisc(dm, *subdm));
10435   /* Create map from submodel to global model */
10436   if (is) {
10437     PetscSection    sectionGlobal, subsectionGlobal;
10438     IS              spIS;
10439     const PetscInt *spmap;
10440     PetscInt       *subIndices;
10441     PetscInt        subSize = 0, subOff = 0, pStart, pEnd, p;
10442     PetscInt        Nf, f, bs = -1, bsLocal[2], bsMinMax[2];
10443 
10444     PetscCall(DMPlexGetSubpointIS(*subdm, &spIS));
10445     PetscCall(ISGetIndices(spIS, &spmap));
10446     PetscCall(PetscSectionGetNumFields(section, &Nf));
10447     PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
10448     PetscCall(DMGetGlobalSection(*subdm, &subsectionGlobal));
10449     PetscCall(PetscSectionGetChart(subsection, &pStart, &pEnd));
10450     for (p = pStart; p < pEnd; ++p) {
10451       PetscInt gdof, pSubSize = 0;
10452 
10453       PetscCall(PetscSectionGetDof(sectionGlobal, p, &gdof));
10454       if (gdof > 0) {
10455         for (f = 0; f < Nf; ++f) {
10456           PetscInt fdof, fcdof;
10457 
10458           PetscCall(PetscSectionGetFieldDof(subsection, p, f, &fdof));
10459           PetscCall(PetscSectionGetFieldConstraintDof(subsection, p, f, &fcdof));
10460           pSubSize += fdof - fcdof;
10461         }
10462         subSize += pSubSize;
10463         if (pSubSize) {
10464           if (bs < 0) {
10465             bs = pSubSize;
10466           } else if (bs != pSubSize) {
10467             /* Layout does not admit a pointwise block size */
10468             bs = 1;
10469           }
10470         }
10471       }
10472     }
10473     /* Must have same blocksize on all procs (some might have no points) */
10474     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs;
10475     bsLocal[1] = bs;
10476     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
10477     if (bsMinMax[0] != bsMinMax[1]) {
10478       bs = 1;
10479     } else {
10480       bs = bsMinMax[0];
10481     }
10482     PetscCall(PetscMalloc1(subSize, &subIndices));
10483     for (p = pStart; p < pEnd; ++p) {
10484       PetscInt gdof, goff;
10485 
10486       PetscCall(PetscSectionGetDof(subsectionGlobal, p, &gdof));
10487       if (gdof > 0) {
10488         const PetscInt point = spmap[p];
10489 
10490         PetscCall(PetscSectionGetOffset(sectionGlobal, point, &goff));
10491         for (f = 0; f < Nf; ++f) {
10492           PetscInt fdof, fcdof, fc, f2, poff = 0;
10493 
10494           /* Can get rid of this loop by storing field information in the global section */
10495           for (f2 = 0; f2 < f; ++f2) {
10496             PetscCall(PetscSectionGetFieldDof(section, p, f2, &fdof));
10497             PetscCall(PetscSectionGetFieldConstraintDof(section, p, f2, &fcdof));
10498             poff += fdof - fcdof;
10499           }
10500           PetscCall(PetscSectionGetFieldDof(section, p, f, &fdof));
10501           PetscCall(PetscSectionGetFieldConstraintDof(section, p, f, &fcdof));
10502           for (fc = 0; fc < fdof - fcdof; ++fc, ++subOff) subIndices[subOff] = goff + poff + fc;
10503         }
10504       }
10505     }
10506     PetscCall(ISRestoreIndices(spIS, &spmap));
10507     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), subSize, subIndices, PETSC_OWN_POINTER, is));
10508     if (bs > 1) {
10509       /* We need to check that the block size does not come from non-contiguous fields */
10510       PetscInt i, j, set = 1;
10511       for (i = 0; i < subSize; i += bs) {
10512         for (j = 0; j < bs; ++j) {
10513           if (subIndices[i + j] != subIndices[i] + j) {
10514             set = 0;
10515             break;
10516           }
10517         }
10518       }
10519       if (set) PetscCall(ISSetBlockSize(*is, bs));
10520     }
10521     /* Attach nullspace */
10522     for (f = 0; f < Nf; ++f) {
10523       (*subdm)->nullspaceConstructors[f] = dm->nullspaceConstructors[f];
10524       if ((*subdm)->nullspaceConstructors[f]) break;
10525     }
10526     if (f < Nf) {
10527       MatNullSpace nullSpace;
10528       PetscCall((*(*subdm)->nullspaceConstructors[f])(*subdm, f, f, &nullSpace));
10529 
10530       PetscCall(PetscObjectCompose((PetscObject)*is, "nullspace", (PetscObject)nullSpace));
10531       PetscCall(MatNullSpaceDestroy(&nullSpace));
10532     }
10533   }
10534   PetscFunctionReturn(PETSC_SUCCESS);
10535 }
10536 
10537 /*@
10538   DMPlexMonitorThroughput - Report the cell throughput of FE integration
10539 
10540   Input Parameters:
10541 + dm    - The `DM`
10542 - dummy - unused argument
10543 
10544   Options Database Key:
10545 . -dm_plex_monitor_throughput - Activate the monitor
10546 
10547   Level: developer
10548 
10549 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexCreate()`
10550 @*/
10551 PetscErrorCode DMPlexMonitorThroughput(DM dm, void *dummy)
10552 {
10553   PetscLogHandler default_handler;
10554 
10555   PetscFunctionBegin;
10556   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10557   PetscCall(PetscLogGetDefaultHandler(&default_handler));
10558   if (default_handler) {
10559     PetscLogEvent      event;
10560     PetscEventPerfInfo eventInfo;
10561     PetscReal          cellRate, flopRate;
10562     PetscInt           cStart, cEnd, Nf, N;
10563     const char        *name;
10564 
10565     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
10566     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
10567     PetscCall(DMGetNumFields(dm, &Nf));
10568     PetscCall(PetscLogEventGetId("DMPlexResidualFE", &event));
10569     PetscCall(PetscLogEventGetPerfInfo(PETSC_DEFAULT, event, &eventInfo));
10570     N        = (cEnd - cStart) * Nf * eventInfo.count;
10571     flopRate = eventInfo.flops / eventInfo.time;
10572     cellRate = N / eventInfo.time;
10573     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)));
10574   } else {
10575     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.");
10576   }
10577   PetscFunctionReturn(PETSC_SUCCESS);
10578 }
10579