xref: /petsc/src/dm/impls/plex/plex.c (revision f1769f9d415d9491324134ec5d493a36ebb01003)
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     // There are three states with pblocks, since block starts can have no dofs:
2809     // UNKNOWN) New Block:   An open block has been signalled by pblocks[p] == 1
2810     // TRUE)    Block Start: The first entry in a block has been added
2811     // FALSE)   Block Add:   An additional block entry has been added, since pblocks[p] == 0
2812     PetscBT         blst;
2813     PetscBool3      bstate     = PETSC_BOOL3_UNKNOWN;
2814     PetscBool       fillMatrix = (PetscBool)(!dm->prealloc_only && !isMatIS);
2815     const PetscInt *perm       = NULL;
2816     PetscInt       *dnz, *onz, *dnzu, *onzu, bsLocal[2], bsMinMax[2], *pblocks;
2817     PetscInt        pStart, pEnd, dof, cdof, num_fields;
2818 
2819     PetscCall(DMGetLocalToGlobalMapping(dm, &ltog));
2820     PetscCall(PetscSectionGetBlockStarts(sectionLocal, &blst));
2821     if (sectionLocal->perm) PetscCall(ISGetIndices(sectionLocal->perm, &perm));
2822 
2823     PetscCall(PetscCalloc1(localSize, &pblocks));
2824     PetscCall(PetscSectionGetChart(sectionGlobal, &pStart, &pEnd));
2825     PetscCall(PetscSectionGetNumFields(sectionGlobal, &num_fields));
2826     // We need to process in the permuted order to get block sizes right
2827     for (PetscInt point = pStart; point < pEnd; ++point) {
2828       const PetscInt p = perm ? perm[point] : point;
2829 
2830       switch (dm->blocking_type) {
2831       case DM_BLOCKING_TOPOLOGICAL_POINT: { // One block per topological point
2832         PetscInt bdof, offset;
2833 
2834         PetscCall(PetscSectionGetDof(sectionGlobal, p, &dof));
2835         PetscCall(PetscSectionGetOffset(sectionGlobal, p, &offset));
2836         PetscCall(PetscSectionGetConstraintDof(sectionGlobal, p, &cdof));
2837         if (blst && PetscBTLookup(blst, p)) bstate = PETSC_BOOL3_UNKNOWN;
2838         if (dof > 0) {
2839           // State change
2840           if (bstate == PETSC_BOOL3_UNKNOWN) bstate = PETSC_BOOL3_TRUE;
2841           else if (bstate == PETSC_BOOL3_TRUE && blst && !PetscBTLookup(blst, p)) bstate = PETSC_BOOL3_FALSE;
2842 
2843           for (PetscInt i = 0; i < dof - cdof; ++i) pblocks[offset - localStart + i] = dof - cdof;
2844           // Signal block concatenation
2845           if (bstate == PETSC_BOOL3_FALSE && dof - cdof) pblocks[offset - localStart] = -(dof - cdof);
2846         }
2847         dof  = dof < 0 ? -(dof + 1) : dof;
2848         bdof = cdof && (dof - cdof) ? 1 : dof;
2849         if (dof) {
2850           if (bs < 0) {
2851             bs = bdof;
2852           } else if (bs != bdof) {
2853             bs = 1;
2854           }
2855         }
2856       } break;
2857       case DM_BLOCKING_FIELD_NODE: {
2858         for (PetscInt field = 0; field < num_fields; field++) {
2859           PetscInt num_comp, bdof, offset;
2860           PetscCall(PetscSectionGetFieldComponents(sectionGlobal, field, &num_comp));
2861           PetscCall(PetscSectionGetFieldDof(sectionGlobal, p, field, &dof));
2862           if (dof < 0) continue;
2863           PetscCall(PetscSectionGetFieldOffset(sectionGlobal, p, field, &offset));
2864           PetscCall(PetscSectionGetFieldConstraintDof(sectionGlobal, p, field, &cdof));
2865           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);
2866           PetscInt num_nodes = dof / num_comp;
2867           for (PetscInt i = 0; i < dof - cdof; i++) pblocks[offset - localStart + i] = (dof - cdof) / num_nodes;
2868           // Handle possibly constant block size (unlikely)
2869           bdof = cdof && (dof - cdof) ? 1 : dof;
2870           if (dof) {
2871             if (bs < 0) {
2872               bs = bdof;
2873             } else if (bs != bdof) {
2874               bs = 1;
2875             }
2876           }
2877         }
2878       } break;
2879       }
2880     }
2881     if (sectionLocal->perm) PetscCall(ISRestoreIndices(sectionLocal->perm, &perm));
2882     /* Must have same blocksize on all procs (some might have no points) */
2883     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs;
2884     bsLocal[1] = bs;
2885     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
2886     if (bsMinMax[0] != bsMinMax[1]) bs = 1;
2887     else bs = bsMinMax[0];
2888     bs = PetscMax(1, bs);
2889     PetscCall(MatSetLocalToGlobalMapping(*J, ltog, ltog));
2890     if (dm->prealloc_skip) { // User will likely use MatSetPreallocationCOO(), but still set structural parameters
2891       PetscCall(MatSetBlockSize(*J, bs));
2892       PetscCall(MatSetUp(*J));
2893     } else {
2894       PetscCall(PetscCalloc4(localSize / bs, &dnz, localSize / bs, &onz, localSize / bs, &dnzu, localSize / bs, &onzu));
2895       PetscCall(DMPlexPreallocateOperator(dm, bs, dnz, onz, dnzu, onzu, *J, fillMatrix));
2896       PetscCall(PetscFree4(dnz, onz, dnzu, onzu));
2897     }
2898     if (pblocks) { // Consolidate blocks
2899       PetscInt nblocks = 0;
2900       pblocks[0]       = PetscAbs(pblocks[0]);
2901       for (PetscInt i = 0; i < localSize; i += PetscMax(1, pblocks[i])) {
2902         if (pblocks[i] == 0) continue;
2903         // Negative block size indicates the blocks should be concatenated
2904         if (pblocks[i] < 0) {
2905           pblocks[i] = -pblocks[i];
2906           pblocks[nblocks - 1] += pblocks[i];
2907         } else {
2908           pblocks[nblocks++] = pblocks[i]; // nblocks always <= i
2909         }
2910         for (PetscInt j = 1; j < pblocks[i]; j++)
2911           PetscCheck(pblocks[i + j] == pblocks[i], PETSC_COMM_SELF, PETSC_ERR_PLIB, "Block of size %" PetscInt_FMT " at %" PetscInt_FMT " mismatches entry %" PetscInt_FMT " at %" PetscInt_FMT, pblocks[i], i, pblocks[i + j], i + j);
2912       }
2913       PetscCall(MatSetVariableBlockSizes(*J, nblocks, pblocks));
2914     }
2915     PetscCall(PetscFree(pblocks));
2916   }
2917   PetscCall(MatSetDM(*J, dm));
2918   PetscFunctionReturn(PETSC_SUCCESS);
2919 }
2920 
2921 /*@
2922   DMPlexGetSubdomainSection - Returns the section associated with the subdomain
2923 
2924   Not Collective
2925 
2926   Input Parameter:
2927 . dm - The `DMPLEX`
2928 
2929   Output Parameter:
2930 . subsection - The subdomain section
2931 
2932   Level: developer
2933 
2934 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `PetscSection`
2935 @*/
2936 PetscErrorCode DMPlexGetSubdomainSection(DM dm, PetscSection *subsection)
2937 {
2938   DM_Plex *mesh = (DM_Plex *)dm->data;
2939 
2940   PetscFunctionBegin;
2941   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2942   if (!mesh->subdomainSection) {
2943     PetscSection section;
2944     PetscSF      sf;
2945 
2946     PetscCall(PetscSFCreate(PETSC_COMM_SELF, &sf));
2947     PetscCall(DMGetLocalSection(dm, &section));
2948     PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_TRUE, PETSC_FALSE, PETSC_TRUE, &mesh->subdomainSection));
2949     PetscCall(PetscSFDestroy(&sf));
2950   }
2951   *subsection = mesh->subdomainSection;
2952   PetscFunctionReturn(PETSC_SUCCESS);
2953 }
2954 
2955 /*@
2956   DMPlexGetChart - Return the interval for all mesh points [`pStart`, `pEnd`)
2957 
2958   Not Collective
2959 
2960   Input Parameter:
2961 . dm - The `DMPLEX`
2962 
2963   Output Parameters:
2964 + pStart - The first mesh point
2965 - pEnd   - The upper bound for mesh points
2966 
2967   Level: beginner
2968 
2969 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetChart()`
2970 @*/
2971 PetscErrorCode DMPlexGetChart(DM dm, PetscInt *pStart, PetscInt *pEnd)
2972 {
2973   DM_Plex *mesh = (DM_Plex *)dm->data;
2974 
2975   PetscFunctionBegin;
2976   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2977   if (mesh->tr) PetscCall(DMPlexTransformGetChart(mesh->tr, pStart, pEnd));
2978   else PetscCall(PetscSectionGetChart(mesh->coneSection, pStart, pEnd));
2979   PetscFunctionReturn(PETSC_SUCCESS);
2980 }
2981 
2982 /*@
2983   DMPlexSetChart - Set the interval for all mesh points [`pStart`, `pEnd`)
2984 
2985   Not Collective
2986 
2987   Input Parameters:
2988 + dm     - The `DMPLEX`
2989 . pStart - The first mesh point
2990 - pEnd   - The upper bound for mesh points
2991 
2992   Level: beginner
2993 
2994 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetChart()`
2995 @*/
2996 PetscErrorCode DMPlexSetChart(DM dm, PetscInt pStart, PetscInt pEnd)
2997 {
2998   DM_Plex *mesh = (DM_Plex *)dm->data;
2999 
3000   PetscFunctionBegin;
3001   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3002   PetscCall(PetscSectionSetChart(mesh->coneSection, pStart, pEnd));
3003   PetscCall(PetscSectionSetChart(mesh->supportSection, pStart, pEnd));
3004   PetscCall(PetscFree(mesh->cellTypes));
3005   PetscFunctionReturn(PETSC_SUCCESS);
3006 }
3007 
3008 /*@
3009   DMPlexGetConeSize - Return the number of in-edges for this point in the DAG
3010 
3011   Not Collective
3012 
3013   Input Parameters:
3014 + dm - The `DMPLEX`
3015 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3016 
3017   Output Parameter:
3018 . size - The cone size for point `p`
3019 
3020   Level: beginner
3021 
3022 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
3023 @*/
3024 PetscErrorCode DMPlexGetConeSize(DM dm, PetscInt p, PetscInt *size)
3025 {
3026   DM_Plex *mesh = (DM_Plex *)dm->data;
3027 
3028   PetscFunctionBegin;
3029   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3030   PetscAssertPointer(size, 3);
3031   if (mesh->tr) PetscCall(DMPlexTransformGetConeSize(mesh->tr, p, size));
3032   else PetscCall(PetscSectionGetDof(mesh->coneSection, p, size));
3033   PetscFunctionReturn(PETSC_SUCCESS);
3034 }
3035 
3036 /*@
3037   DMPlexSetConeSize - Set the number of in-edges for this point in the DAG
3038 
3039   Not Collective
3040 
3041   Input Parameters:
3042 + dm   - The `DMPLEX`
3043 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3044 - size - The cone size for point `p`
3045 
3046   Level: beginner
3047 
3048   Note:
3049   This should be called after `DMPlexSetChart()`.
3050 
3051 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetConeSize()`, `DMPlexSetChart()`
3052 @*/
3053 PetscErrorCode DMPlexSetConeSize(DM dm, PetscInt p, PetscInt size)
3054 {
3055   DM_Plex *mesh = (DM_Plex *)dm->data;
3056 
3057   PetscFunctionBegin;
3058   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3059   PetscCheck(!mesh->tr, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Cannot call DMPlexSetConeSize() on a mesh with a transform defined.");
3060   PetscCall(PetscSectionSetDof(mesh->coneSection, p, size));
3061   PetscFunctionReturn(PETSC_SUCCESS);
3062 }
3063 
3064 /*@C
3065   DMPlexGetCone - Return the points on the in-edges for this point in the DAG
3066 
3067   Not Collective
3068 
3069   Input Parameters:
3070 + dm - The `DMPLEX`
3071 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3072 
3073   Output Parameter:
3074 . cone - An array of points which are on the in-edges for point `p`
3075 
3076   Level: beginner
3077 
3078   Fortran Notes:
3079   You must also call `DMPlexRestoreCone()` after you finish using the returned array.
3080   `DMPlexRestoreCone()` is not needed/available in C.
3081 
3082 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSize()`, `DMPlexSetCone()`, `DMPlexGetConeTuple()`, `DMPlexSetChart()`, `DMPlexRestoreCone()`
3083 @*/
3084 PetscErrorCode DMPlexGetCone(DM dm, PetscInt p, const PetscInt *cone[])
3085 {
3086   DM_Plex *mesh = (DM_Plex *)dm->data;
3087   PetscInt off;
3088 
3089   PetscFunctionBegin;
3090   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3091   PetscAssertPointer(cone, 3);
3092   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3093   *cone = PetscSafePointerPlusOffset(mesh->cones, off);
3094   PetscFunctionReturn(PETSC_SUCCESS);
3095 }
3096 
3097 /*@
3098   DMPlexGetConeTuple - Return the points on the in-edges of several points in the DAG
3099 
3100   Not Collective
3101 
3102   Input Parameters:
3103 + dm - The `DMPLEX`
3104 - p  - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3105 
3106   Output Parameters:
3107 + pConesSection - `PetscSection` describing the layout of `pCones`
3108 - pCones        - An array of points which are on the in-edges for the point set `p`
3109 
3110   Level: intermediate
3111 
3112 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeRecursive()`, `DMPlexSetChart()`, `PetscSection`, `IS`
3113 @*/
3114 PetscErrorCode DMPlexGetConeTuple(DM dm, IS p, PetscSection *pConesSection, IS *pCones)
3115 {
3116   PetscSection cs, newcs;
3117   PetscInt    *cones;
3118   PetscInt    *newarr = NULL;
3119   PetscInt     n;
3120 
3121   PetscFunctionBegin;
3122   PetscCall(DMPlexGetCones(dm, &cones));
3123   PetscCall(DMPlexGetConeSection(dm, &cs));
3124   PetscCall(PetscSectionExtractDofsFromArray(cs, MPIU_INT, cones, p, &newcs, pCones ? ((void **)&newarr) : NULL));
3125   if (pConesSection) *pConesSection = newcs;
3126   if (pCones) {
3127     PetscCall(PetscSectionGetStorageSize(newcs, &n));
3128     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)p), n, newarr, PETSC_OWN_POINTER, pCones));
3129   }
3130   PetscFunctionReturn(PETSC_SUCCESS);
3131 }
3132 
3133 /*@
3134   DMPlexGetConeRecursiveVertices - Expand each given point into its cone points and do that recursively until we end up just with vertices.
3135 
3136   Not Collective
3137 
3138   Input Parameters:
3139 + dm     - The `DMPLEX`
3140 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3141 
3142   Output Parameter:
3143 . expandedPoints - An array of vertices recursively expanded from input points
3144 
3145   Level: advanced
3146 
3147   Notes:
3148   Like `DMPlexGetConeRecursive()` but returns only the 0-depth `IS` (i.e. vertices only) and no sections.
3149 
3150   There is no corresponding Restore function, just call `ISDestroy()` on the returned `IS` to deallocate.
3151 
3152 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexRestoreConeRecursive()`,
3153           `DMPlexGetDepth()`, `IS`
3154 @*/
3155 PetscErrorCode DMPlexGetConeRecursiveVertices(DM dm, IS points, IS *expandedPoints)
3156 {
3157   IS      *expandedPointsAll;
3158   PetscInt depth;
3159 
3160   PetscFunctionBegin;
3161   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3162   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
3163   PetscAssertPointer(expandedPoints, 3);
3164   PetscCall(DMPlexGetConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
3165   *expandedPoints = expandedPointsAll[0];
3166   PetscCall(PetscObjectReference((PetscObject)expandedPointsAll[0]));
3167   PetscCall(DMPlexRestoreConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
3168   PetscFunctionReturn(PETSC_SUCCESS);
3169 }
3170 
3171 /*@
3172   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).
3173 
3174   Not Collective
3175 
3176   Input Parameters:
3177 + dm     - The `DMPLEX`
3178 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3179 
3180   Output Parameters:
3181 + depth          - (optional) Size of the output arrays, equal to `DMPLEX` depth, returned by `DMPlexGetDepth()`
3182 . expandedPoints - (optional) An array of index sets with recursively expanded cones
3183 - sections       - (optional) An array of sections which describe mappings from points to their cone points
3184 
3185   Level: advanced
3186 
3187   Notes:
3188   Like `DMPlexGetConeTuple()` but recursive.
3189 
3190   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.
3191   For example, for d=0 it contains only vertices, for d=1 it can contain vertices and edges, etc.
3192 
3193   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\:
3194   (1) DAG points in `expandedPoints`[d+1] with `depth` d+1 to their cone points in `expandedPoints`[d];
3195   (2) DAG points in `expandedPoints`[d+1] with `depth` in [0,d] to the same points in `expandedPoints`[d].
3196 
3197 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexRestoreConeRecursive()`, `DMPlexGetConeRecursiveVertices()`,
3198           `DMPlexGetDepth()`, `PetscSection`, `IS`
3199 @*/
3200 PetscErrorCode DMPlexGetConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
3201 {
3202   const PetscInt *arr0 = NULL, *cone = NULL;
3203   PetscInt       *arr = NULL, *newarr = NULL;
3204   PetscInt        d, depth_, i, n, newn, cn, co, start, end;
3205   IS             *expandedPoints_;
3206   PetscSection   *sections_;
3207 
3208   PetscFunctionBegin;
3209   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3210   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
3211   if (depth) PetscAssertPointer(depth, 3);
3212   if (expandedPoints) PetscAssertPointer(expandedPoints, 4);
3213   if (sections) PetscAssertPointer(sections, 5);
3214   PetscCall(ISGetLocalSize(points, &n));
3215   PetscCall(ISGetIndices(points, &arr0));
3216   PetscCall(DMPlexGetDepth(dm, &depth_));
3217   PetscCall(PetscCalloc1(depth_, &expandedPoints_));
3218   PetscCall(PetscCalloc1(depth_, &sections_));
3219   arr = (PetscInt *)arr0; /* this is ok because first generation of arr is not modified */
3220   for (d = depth_ - 1; d >= 0; d--) {
3221     PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &sections_[d]));
3222     PetscCall(PetscSectionSetChart(sections_[d], 0, n));
3223     for (i = 0; i < n; i++) {
3224       PetscCall(DMPlexGetDepthStratum(dm, d + 1, &start, &end));
3225       if (arr[i] >= start && arr[i] < end) {
3226         PetscCall(DMPlexGetConeSize(dm, arr[i], &cn));
3227         PetscCall(PetscSectionSetDof(sections_[d], i, cn));
3228       } else {
3229         PetscCall(PetscSectionSetDof(sections_[d], i, 1));
3230       }
3231     }
3232     PetscCall(PetscSectionSetUp(sections_[d]));
3233     PetscCall(PetscSectionGetStorageSize(sections_[d], &newn));
3234     PetscCall(PetscMalloc1(newn, &newarr));
3235     for (i = 0; i < n; i++) {
3236       PetscCall(PetscSectionGetDof(sections_[d], i, &cn));
3237       PetscCall(PetscSectionGetOffset(sections_[d], i, &co));
3238       if (cn > 1) {
3239         PetscCall(DMPlexGetCone(dm, arr[i], &cone));
3240         PetscCall(PetscMemcpy(&newarr[co], cone, cn * sizeof(PetscInt)));
3241       } else {
3242         newarr[co] = arr[i];
3243       }
3244     }
3245     PetscCall(ISCreateGeneral(PETSC_COMM_SELF, newn, newarr, PETSC_OWN_POINTER, &expandedPoints_[d]));
3246     arr = newarr;
3247     n   = newn;
3248   }
3249   PetscCall(ISRestoreIndices(points, &arr0));
3250   *depth = depth_;
3251   if (expandedPoints) *expandedPoints = expandedPoints_;
3252   else {
3253     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&expandedPoints_[d]));
3254     PetscCall(PetscFree(expandedPoints_));
3255   }
3256   if (sections) *sections = sections_;
3257   else {
3258     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&sections_[d]));
3259     PetscCall(PetscFree(sections_));
3260   }
3261   PetscFunctionReturn(PETSC_SUCCESS);
3262 }
3263 
3264 /*@
3265   DMPlexRestoreConeRecursive - Deallocates arrays created by `DMPlexGetConeRecursive()`
3266 
3267   Not Collective
3268 
3269   Input Parameters:
3270 + dm     - The `DMPLEX`
3271 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3272 
3273   Output Parameters:
3274 + depth          - (optional) Size of the output arrays, equal to `DMPLEX` depth, returned by `DMPlexGetDepth()`
3275 . expandedPoints - (optional) An array of recursively expanded cones
3276 - sections       - (optional) An array of sections which describe mappings from points to their cone points
3277 
3278   Level: advanced
3279 
3280   Note:
3281   See `DMPlexGetConeRecursive()`
3282 
3283 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexGetConeRecursiveVertices()`,
3284           `DMPlexGetDepth()`, `IS`, `PetscSection`
3285 @*/
3286 PetscErrorCode DMPlexRestoreConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
3287 {
3288   PetscInt d, depth_;
3289 
3290   PetscFunctionBegin;
3291   PetscCall(DMPlexGetDepth(dm, &depth_));
3292   PetscCheck(!depth || *depth == depth_, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "depth changed since last call to DMPlexGetConeRecursive");
3293   if (depth) *depth = 0;
3294   if (expandedPoints) {
3295     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&((*expandedPoints)[d])));
3296     PetscCall(PetscFree(*expandedPoints));
3297   }
3298   if (sections) {
3299     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&((*sections)[d])));
3300     PetscCall(PetscFree(*sections));
3301   }
3302   PetscFunctionReturn(PETSC_SUCCESS);
3303 }
3304 
3305 /*@
3306   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
3307 
3308   Not Collective
3309 
3310   Input Parameters:
3311 + dm   - The `DMPLEX`
3312 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3313 - cone - An array of points which are on the in-edges for point `p`
3314 
3315   Level: beginner
3316 
3317   Note:
3318   This should be called after all calls to `DMPlexSetConeSize()` and `DMSetUp()`.
3319 
3320 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`, `DMPlexSetSupport()`, `DMPlexSetSupportSize()`
3321 @*/
3322 PetscErrorCode DMPlexSetCone(DM dm, PetscInt p, const PetscInt cone[])
3323 {
3324   DM_Plex *mesh = (DM_Plex *)dm->data;
3325   PetscInt dof, off, c;
3326 
3327   PetscFunctionBegin;
3328   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3329   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3330   if (dof) PetscAssertPointer(cone, 3);
3331   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3332   if (PetscDefined(USE_DEBUG)) {
3333     PetscInt pStart, pEnd;
3334     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3335     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);
3336     for (c = 0; c < dof; ++c) {
3337       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);
3338       mesh->cones[off + c] = cone[c];
3339     }
3340   } else {
3341     for (c = 0; c < dof; ++c) mesh->cones[off + c] = cone[c];
3342   }
3343   PetscFunctionReturn(PETSC_SUCCESS);
3344 }
3345 
3346 /*@C
3347   DMPlexGetConeOrientation - Return the orientations on the in-edges for this point in the DAG
3348 
3349   Not Collective
3350 
3351   Input Parameters:
3352 + dm - The `DMPLEX`
3353 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3354 
3355   Output Parameter:
3356 . coneOrientation - An array of orientations which are on the in-edges for point `p`. An orientation is an
3357                     integer giving the prescription for cone traversal.
3358 
3359   Level: beginner
3360 
3361   Note:
3362   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3363   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3364   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3365   with the identity.
3366 
3367   Fortran Notes:
3368   You must also call `DMPlexRestoreConeOrientation()` after you finish using the returned array.
3369   `DMPlexRestoreConeOrientation()` is not needed/available in C.
3370 
3371 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPolytopeTypeComposeOrientation()`, `DMPolytopeTypeComposeOrientationInv()`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetCone()`, `DMPlexSetChart()`
3372 @*/
3373 PetscErrorCode DMPlexGetConeOrientation(DM dm, PetscInt p, const PetscInt *coneOrientation[])
3374 {
3375   DM_Plex *mesh = (DM_Plex *)dm->data;
3376   PetscInt off;
3377 
3378   PetscFunctionBegin;
3379   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3380   if (PetscDefined(USE_DEBUG)) {
3381     PetscInt dof;
3382     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3383     if (dof) PetscAssertPointer(coneOrientation, 3);
3384   }
3385   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3386 
3387   *coneOrientation = &mesh->coneOrientations[off];
3388   PetscFunctionReturn(PETSC_SUCCESS);
3389 }
3390 
3391 /*@
3392   DMPlexSetConeOrientation - Set the orientations on the in-edges for this point in the DAG
3393 
3394   Not Collective
3395 
3396   Input Parameters:
3397 + dm              - The `DMPLEX`
3398 . p               - The point, which must lie in the chart set with `DMPlexSetChart()`
3399 - coneOrientation - An array of orientations
3400 
3401   Level: beginner
3402 
3403   Notes:
3404   This should be called after all calls to `DMPlexSetConeSize()` and `DMSetUp()`.
3405 
3406   The meaning of coneOrientation is detailed in `DMPlexGetConeOrientation()`.
3407 
3408 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetConeOrientation()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3409 @*/
3410 PetscErrorCode DMPlexSetConeOrientation(DM dm, PetscInt p, const PetscInt coneOrientation[])
3411 {
3412   DM_Plex *mesh = (DM_Plex *)dm->data;
3413   PetscInt pStart, pEnd;
3414   PetscInt dof, off, c;
3415 
3416   PetscFunctionBegin;
3417   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3418   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3419   if (dof) PetscAssertPointer(coneOrientation, 3);
3420   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3421   if (PetscDefined(USE_DEBUG)) {
3422     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3423     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);
3424     for (c = 0; c < dof; ++c) {
3425       PetscInt cdof, o = coneOrientation[c];
3426 
3427       PetscCall(PetscSectionGetDof(mesh->coneSection, mesh->cones[off + c], &cdof));
3428       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);
3429       mesh->coneOrientations[off + c] = o;
3430     }
3431   } else {
3432     for (c = 0; c < dof; ++c) mesh->coneOrientations[off + c] = coneOrientation[c];
3433   }
3434   PetscFunctionReturn(PETSC_SUCCESS);
3435 }
3436 
3437 /*@
3438   DMPlexInsertCone - Insert a point into the in-edges for the point p in the DAG
3439 
3440   Not Collective
3441 
3442   Input Parameters:
3443 + dm        - The `DMPLEX`
3444 . p         - The point, which must lie in the chart set with `DMPlexSetChart()`
3445 . conePos   - The local index in the cone where the point should be put
3446 - conePoint - The mesh point to insert
3447 
3448   Level: beginner
3449 
3450 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3451 @*/
3452 PetscErrorCode DMPlexInsertCone(DM dm, PetscInt p, PetscInt conePos, PetscInt conePoint)
3453 {
3454   DM_Plex *mesh = (DM_Plex *)dm->data;
3455   PetscInt pStart, pEnd;
3456   PetscInt dof, off;
3457 
3458   PetscFunctionBegin;
3459   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3460   if (PetscDefined(USE_DEBUG)) {
3461     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3462     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);
3463     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);
3464     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3465     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);
3466   }
3467   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3468   mesh->cones[off + conePos] = conePoint;
3469   PetscFunctionReturn(PETSC_SUCCESS);
3470 }
3471 
3472 /*@
3473   DMPlexInsertConeOrientation - Insert a point orientation for the in-edge for the point p in the DAG
3474 
3475   Not Collective
3476 
3477   Input Parameters:
3478 + dm              - The `DMPLEX`
3479 . p               - The point, which must lie in the chart set with `DMPlexSetChart()`
3480 . conePos         - The local index in the cone where the point should be put
3481 - coneOrientation - The point orientation to insert
3482 
3483   Level: beginner
3484 
3485   Note:
3486   The meaning of coneOrientation values is detailed in `DMPlexGetConeOrientation()`.
3487 
3488 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3489 @*/
3490 PetscErrorCode DMPlexInsertConeOrientation(DM dm, PetscInt p, PetscInt conePos, PetscInt coneOrientation)
3491 {
3492   DM_Plex *mesh = (DM_Plex *)dm->data;
3493   PetscInt pStart, pEnd;
3494   PetscInt dof, off;
3495 
3496   PetscFunctionBegin;
3497   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3498   if (PetscDefined(USE_DEBUG)) {
3499     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3500     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);
3501     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3502     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);
3503   }
3504   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3505   mesh->coneOrientations[off + conePos] = coneOrientation;
3506   PetscFunctionReturn(PETSC_SUCCESS);
3507 }
3508 
3509 /*@C
3510   DMPlexGetOrientedCone - Return the points and orientations on the in-edges for this point in the DAG
3511 
3512   Not collective
3513 
3514   Input Parameters:
3515 + dm - The DMPlex
3516 - p  - The point, which must lie in the chart set with DMPlexSetChart()
3517 
3518   Output Parameters:
3519 + cone - An array of points which are on the in-edges for point `p`
3520 - ornt - An array of orientations which are on the in-edges for point `p`. An orientation is an
3521         integer giving the prescription for cone traversal.
3522 
3523   Level: beginner
3524 
3525   Notes:
3526   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3527   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3528   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3529   with the identity.
3530 
3531   Fortran Notes:
3532   You must also call `DMPlexRestoreCone()` after you finish using the returned array.
3533   `DMPlexRestoreCone()` is not needed/available in C.
3534 
3535 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreOrientedCone()`, `DMPlexGetConeSize()`, `DMPlexGetCone()`, `DMPlexGetChart()`
3536 @*/
3537 PetscErrorCode DMPlexGetOrientedCone(DM dm, PetscInt p, const PetscInt *cone[], const PetscInt *ornt[])
3538 {
3539   DM_Plex *mesh = (DM_Plex *)dm->data;
3540 
3541   PetscFunctionBegin;
3542   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3543   if (mesh->tr) {
3544     PetscCall(DMPlexTransformGetCone(mesh->tr, p, cone, ornt));
3545   } else {
3546     PetscInt off;
3547     if (PetscDefined(USE_DEBUG)) {
3548       PetscInt dof;
3549       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3550       if (dof) {
3551         if (cone) PetscAssertPointer(cone, 3);
3552         if (ornt) PetscAssertPointer(ornt, 4);
3553       }
3554     }
3555     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3556     if (cone) *cone = PetscSafePointerPlusOffset(mesh->cones, off);
3557     if (ornt) *ornt = PetscSafePointerPlusOffset(mesh->coneOrientations, off);
3558   }
3559   PetscFunctionReturn(PETSC_SUCCESS);
3560 }
3561 
3562 /*@C
3563   DMPlexRestoreOrientedCone - Restore the points and orientations on the in-edges for this point in the DAG
3564 
3565   Not Collective
3566 
3567   Input Parameters:
3568 + dm   - The DMPlex
3569 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3570 . cone - An array of points which are on the in-edges for point p
3571 - ornt - An array of orientations which are on the in-edges for point `p`. An orientation is an
3572         integer giving the prescription for cone traversal.
3573 
3574   Level: beginner
3575 
3576   Notes:
3577   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3578   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3579   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3580   with the identity.
3581 
3582   Fortran Notes:
3583   You must also call `DMPlexRestoreCone()` after you finish using the returned array.
3584   `DMPlexRestoreCone()` is not needed/available in C.
3585 
3586 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetOrientedCone()`, `DMPlexGetConeSize()`, `DMPlexGetCone()`, `DMPlexGetChart()`
3587 @*/
3588 PetscErrorCode DMPlexRestoreOrientedCone(DM dm, PetscInt p, const PetscInt *cone[], const PetscInt *ornt[])
3589 {
3590   DM_Plex *mesh = (DM_Plex *)dm->data;
3591 
3592   PetscFunctionBegin;
3593   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3594   if (mesh->tr) PetscCall(DMPlexTransformRestoreCone(mesh->tr, p, cone, ornt));
3595   PetscFunctionReturn(PETSC_SUCCESS);
3596 }
3597 
3598 /*@
3599   DMPlexGetSupportSize - Return the number of out-edges for this point in the DAG
3600 
3601   Not Collective
3602 
3603   Input Parameters:
3604 + dm - The `DMPLEX`
3605 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3606 
3607   Output Parameter:
3608 . size - The support size for point `p`
3609 
3610   Level: beginner
3611 
3612 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`, `DMPlexGetConeSize()`
3613 @*/
3614 PetscErrorCode DMPlexGetSupportSize(DM dm, PetscInt p, PetscInt *size)
3615 {
3616   DM_Plex *mesh = (DM_Plex *)dm->data;
3617 
3618   PetscFunctionBegin;
3619   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3620   PetscAssertPointer(size, 3);
3621   PetscCall(PetscSectionGetDof(mesh->supportSection, p, size));
3622   PetscFunctionReturn(PETSC_SUCCESS);
3623 }
3624 
3625 /*@
3626   DMPlexSetSupportSize - Set the number of out-edges for this point in the DAG
3627 
3628   Not Collective
3629 
3630   Input Parameters:
3631 + dm   - The `DMPLEX`
3632 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3633 - size - The support size for point `p`
3634 
3635   Level: beginner
3636 
3637   Note:
3638   This should be called after `DMPlexSetChart()`.
3639 
3640 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetSupportSize()`, `DMPlexSetChart()`
3641 @*/
3642 PetscErrorCode DMPlexSetSupportSize(DM dm, PetscInt p, PetscInt size)
3643 {
3644   DM_Plex *mesh = (DM_Plex *)dm->data;
3645 
3646   PetscFunctionBegin;
3647   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3648   PetscCall(PetscSectionSetDof(mesh->supportSection, p, size));
3649   PetscFunctionReturn(PETSC_SUCCESS);
3650 }
3651 
3652 /*@C
3653   DMPlexGetSupport - Return the points on the out-edges for this point in the DAG
3654 
3655   Not Collective
3656 
3657   Input Parameters:
3658 + dm - The `DMPLEX`
3659 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3660 
3661   Output Parameter:
3662 . support - An array of points which are on the out-edges for point `p`
3663 
3664   Level: beginner
3665 
3666   Fortran Notes:
3667   You must also call `DMPlexRestoreSupport()` after you finish using the returned array.
3668   `DMPlexRestoreSupport()` is not needed/available in C.
3669 
3670 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSupportSize()`, `DMPlexSetSupport()`, `DMPlexGetCone()`, `DMPlexSetChart()`
3671 @*/
3672 PetscErrorCode DMPlexGetSupport(DM dm, PetscInt p, const PetscInt *support[])
3673 {
3674   DM_Plex *mesh = (DM_Plex *)dm->data;
3675   PetscInt off;
3676 
3677   PetscFunctionBegin;
3678   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3679   PetscAssertPointer(support, 3);
3680   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3681   *support = PetscSafePointerPlusOffset(mesh->supports, off);
3682   PetscFunctionReturn(PETSC_SUCCESS);
3683 }
3684 
3685 /*@
3686   DMPlexSetSupport - Set the points on the out-edges for this point in the DAG, that is the list of points that this point covers
3687 
3688   Not Collective
3689 
3690   Input Parameters:
3691 + dm      - The `DMPLEX`
3692 . p       - The point, which must lie in the chart set with `DMPlexSetChart()`
3693 - support - An array of points which are on the out-edges for point `p`
3694 
3695   Level: beginner
3696 
3697   Note:
3698   This should be called after all calls to `DMPlexSetSupportSize()` and `DMSetUp()`.
3699 
3700 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetCone()`, `DMPlexSetConeSize()`, `DMPlexCreate()`, `DMPlexGetSupport()`, `DMPlexSetChart()`, `DMPlexSetSupportSize()`, `DMSetUp()`
3701 @*/
3702 PetscErrorCode DMPlexSetSupport(DM dm, PetscInt p, const PetscInt support[])
3703 {
3704   DM_Plex *mesh = (DM_Plex *)dm->data;
3705   PetscInt pStart, pEnd;
3706   PetscInt dof, off, c;
3707 
3708   PetscFunctionBegin;
3709   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3710   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3711   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3712   if (dof) PetscAssertPointer(support, 3);
3713   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3714   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);
3715   for (c = 0; c < dof; ++c) {
3716     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);
3717     mesh->supports[off + c] = support[c];
3718   }
3719   PetscFunctionReturn(PETSC_SUCCESS);
3720 }
3721 
3722 /*@
3723   DMPlexInsertSupport - Insert a point into the out-edges for the point p in the DAG
3724 
3725   Not Collective
3726 
3727   Input Parameters:
3728 + dm           - The `DMPLEX`
3729 . p            - The point, which must lie in the chart set with `DMPlexSetChart()`
3730 . supportPos   - The local index in the cone where the point should be put
3731 - supportPoint - The mesh point to insert
3732 
3733   Level: beginner
3734 
3735 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3736 @*/
3737 PetscErrorCode DMPlexInsertSupport(DM dm, PetscInt p, PetscInt supportPos, PetscInt supportPoint)
3738 {
3739   DM_Plex *mesh = (DM_Plex *)dm->data;
3740   PetscInt pStart, pEnd;
3741   PetscInt dof, off;
3742 
3743   PetscFunctionBegin;
3744   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3745   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3746   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3747   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3748   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);
3749   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);
3750   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);
3751   mesh->supports[off + supportPos] = supportPoint;
3752   PetscFunctionReturn(PETSC_SUCCESS);
3753 }
3754 
3755 /* Converts an orientation o in the current numbering to the previous scheme used in Plex */
3756 PetscInt DMPolytopeConvertNewOrientation_Internal(DMPolytopeType ct, PetscInt o)
3757 {
3758   switch (ct) {
3759   case DM_POLYTOPE_SEGMENT:
3760     if (o == -1) return -2;
3761     break;
3762   case DM_POLYTOPE_TRIANGLE:
3763     if (o == -3) return -1;
3764     if (o == -2) return -3;
3765     if (o == -1) return -2;
3766     break;
3767   case DM_POLYTOPE_QUADRILATERAL:
3768     if (o == -4) return -2;
3769     if (o == -3) return -1;
3770     if (o == -2) return -4;
3771     if (o == -1) return -3;
3772     break;
3773   default:
3774     return o;
3775   }
3776   return o;
3777 }
3778 
3779 /* Converts an orientation o in the previous scheme used in Plex to the current numbering */
3780 PetscInt DMPolytopeConvertOldOrientation_Internal(DMPolytopeType ct, PetscInt o)
3781 {
3782   switch (ct) {
3783   case DM_POLYTOPE_SEGMENT:
3784     if ((o == -2) || (o == 1)) return -1;
3785     if (o == -1) return 0;
3786     break;
3787   case DM_POLYTOPE_TRIANGLE:
3788     if (o == -3) return -2;
3789     if (o == -2) return -1;
3790     if (o == -1) return -3;
3791     break;
3792   case DM_POLYTOPE_QUADRILATERAL:
3793     if (o == -4) return -2;
3794     if (o == -3) return -1;
3795     if (o == -2) return -4;
3796     if (o == -1) return -3;
3797     break;
3798   default:
3799     return o;
3800   }
3801   return o;
3802 }
3803 
3804 /* Takes in a mesh whose orientations are in the previous scheme and converts them all to the current numbering */
3805 PetscErrorCode DMPlexConvertOldOrientations_Internal(DM dm)
3806 {
3807   PetscInt pStart, pEnd, p;
3808 
3809   PetscFunctionBegin;
3810   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3811   for (p = pStart; p < pEnd; ++p) {
3812     const PetscInt *cone, *ornt;
3813     PetscInt        coneSize, c;
3814 
3815     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
3816     PetscCall(DMPlexGetCone(dm, p, &cone));
3817     PetscCall(DMPlexGetConeOrientation(dm, p, &ornt));
3818     for (c = 0; c < coneSize; ++c) {
3819       DMPolytopeType ct;
3820       const PetscInt o = ornt[c];
3821 
3822       PetscCall(DMPlexGetCellType(dm, cone[c], &ct));
3823       switch (ct) {
3824       case DM_POLYTOPE_SEGMENT:
3825         if ((o == -2) || (o == 1)) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3826         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, 0));
3827         break;
3828       case DM_POLYTOPE_TRIANGLE:
3829         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3830         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3831         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3832         break;
3833       case DM_POLYTOPE_QUADRILATERAL:
3834         if (o == -4) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3835         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3836         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -4));
3837         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3838         break;
3839       default:
3840         break;
3841       }
3842     }
3843   }
3844   PetscFunctionReturn(PETSC_SUCCESS);
3845 }
3846 
3847 static inline PetscErrorCode DMPlexGetTransitiveClosure_Hot_Private(DM dm, PetscInt p, PetscBool useCone, PetscInt *size, const PetscInt *arr[], const PetscInt *ornt[])
3848 {
3849   DM_Plex *mesh = (DM_Plex *)dm->data;
3850 
3851   PetscFunctionBeginHot;
3852   if (PetscDefined(USE_DEBUG) || mesh->tr) {
3853     if (useCone) {
3854       PetscCall(DMPlexGetConeSize(dm, p, size));
3855       PetscCall(DMPlexGetOrientedCone(dm, p, arr, ornt));
3856     } else {
3857       PetscCall(DMPlexGetSupportSize(dm, p, size));
3858       PetscCall(DMPlexGetSupport(dm, p, arr));
3859     }
3860   } else {
3861     if (useCone) {
3862       const PetscSection s   = mesh->coneSection;
3863       const PetscInt     ps  = p - s->pStart;
3864       const PetscInt     off = s->atlasOff[ps];
3865 
3866       *size = s->atlasDof[ps];
3867       *arr  = mesh->cones + off;
3868       *ornt = mesh->coneOrientations + off;
3869     } else {
3870       const PetscSection s   = mesh->supportSection;
3871       const PetscInt     ps  = p - s->pStart;
3872       const PetscInt     off = s->atlasOff[ps];
3873 
3874       *size = s->atlasDof[ps];
3875       *arr  = mesh->supports + off;
3876     }
3877   }
3878   PetscFunctionReturn(PETSC_SUCCESS);
3879 }
3880 
3881 static inline PetscErrorCode DMPlexRestoreTransitiveClosure_Hot_Private(DM dm, PetscInt p, PetscBool useCone, PetscInt *size, const PetscInt *arr[], const PetscInt *ornt[])
3882 {
3883   DM_Plex *mesh = (DM_Plex *)dm->data;
3884 
3885   PetscFunctionBeginHot;
3886   if (PetscDefined(USE_DEBUG) || mesh->tr) {
3887     if (useCone) PetscCall(DMPlexRestoreOrientedCone(dm, p, arr, ornt));
3888   }
3889   PetscFunctionReturn(PETSC_SUCCESS);
3890 }
3891 
3892 static PetscErrorCode DMPlexGetTransitiveClosure_Depth1_Private(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3893 {
3894   DMPolytopeType  ct = DM_POLYTOPE_UNKNOWN;
3895   PetscInt       *closure;
3896   const PetscInt *tmp = NULL, *tmpO = NULL;
3897   PetscInt        off = 0, tmpSize, t;
3898 
3899   PetscFunctionBeginHot;
3900   if (ornt) {
3901     PetscCall(DMPlexGetCellType(dm, p, &ct));
3902     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;
3903   }
3904   if (*points) {
3905     closure = *points;
3906   } else {
3907     PetscInt maxConeSize, maxSupportSize;
3908     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3909     PetscCall(DMGetWorkArray(dm, 2 * (PetscMax(maxConeSize, maxSupportSize) + 1), MPIU_INT, &closure));
3910   }
3911   PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, p, useCone, &tmpSize, &tmp, &tmpO));
3912   if (ct == DM_POLYTOPE_UNKNOWN) {
3913     closure[off++] = p;
3914     closure[off++] = 0;
3915     for (t = 0; t < tmpSize; ++t) {
3916       closure[off++] = tmp[t];
3917       closure[off++] = tmpO ? tmpO[t] : 0;
3918     }
3919   } else {
3920     const PetscInt *arr = DMPolytopeTypeGetArrangement(ct, ornt);
3921 
3922     /* We assume that cells with a valid type have faces with a valid type */
3923     closure[off++] = p;
3924     closure[off++] = ornt;
3925     for (t = 0; t < tmpSize; ++t) {
3926       DMPolytopeType ft;
3927 
3928       PetscCall(DMPlexGetCellType(dm, tmp[t], &ft));
3929       closure[off++] = tmp[arr[t]];
3930       closure[off++] = tmpO ? DMPolytopeTypeComposeOrientation(ft, ornt, tmpO[t]) : 0;
3931     }
3932   }
3933   PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, p, useCone, &tmpSize, &tmp, &tmpO));
3934   if (numPoints) *numPoints = tmpSize + 1;
3935   if (points) *points = closure;
3936   PetscFunctionReturn(PETSC_SUCCESS);
3937 }
3938 
3939 /* We need a special tensor version because we want to allow duplicate points in the endcaps for hybrid cells */
3940 static PetscErrorCode DMPlexTransitiveClosure_Tensor_Internal(DM dm, PetscInt point, DMPolytopeType ct, PetscInt o, PetscBool useCone, PetscInt *numPoints, PetscInt **points)
3941 {
3942   const PetscInt *arr = DMPolytopeTypeGetArrangement(ct, o);
3943   const PetscInt *cone, *ornt;
3944   PetscInt       *pts, *closure = NULL;
3945   DMPolytopeType  ft;
3946   PetscInt        maxConeSize, maxSupportSize, coneSeries, supportSeries, maxSize;
3947   PetscInt        dim, coneSize, c, d, clSize, cl;
3948 
3949   PetscFunctionBeginHot;
3950   PetscCall(DMGetDimension(dm, &dim));
3951   PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, point, PETSC_TRUE, &coneSize, &cone, &ornt));
3952   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3953   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, dim + 1) - 1) / (maxConeSize - 1)) : dim + 1;
3954   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, dim + 1) - 1) / (maxSupportSize - 1)) : dim + 1;
3955   maxSize       = PetscMax(coneSeries, supportSeries);
3956   if (*points) {
3957     pts = *points;
3958   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &pts));
3959   c        = 0;
3960   pts[c++] = point;
3961   pts[c++] = o;
3962   PetscCall(DMPlexGetCellType(dm, cone[arr[0 * 2 + 0]], &ft));
3963   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[0 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[0 * 2 + 1], ornt[0]), useCone, &clSize, &closure));
3964   for (cl = 0; cl < clSize * 2; cl += 2) {
3965     pts[c++] = closure[cl];
3966     pts[c++] = closure[cl + 1];
3967   }
3968   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[1 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[1 * 2 + 1], ornt[1]), useCone, &clSize, &closure));
3969   for (cl = 0; cl < clSize * 2; cl += 2) {
3970     pts[c++] = closure[cl];
3971     pts[c++] = closure[cl + 1];
3972   }
3973   PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[0], useCone, &clSize, &closure));
3974   for (d = 2; d < coneSize; ++d) {
3975     PetscCall(DMPlexGetCellType(dm, cone[arr[d * 2 + 0]], &ft));
3976     pts[c++] = cone[arr[d * 2 + 0]];
3977     pts[c++] = DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]);
3978   }
3979   PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, point, PETSC_TRUE, &coneSize, &cone, &ornt));
3980   if (dim >= 3) {
3981     for (d = 2; d < coneSize; ++d) {
3982       const PetscInt  fpoint = cone[arr[d * 2 + 0]];
3983       const PetscInt *fcone, *fornt;
3984       PetscInt        fconeSize, fc, i;
3985 
3986       PetscCall(DMPlexGetCellType(dm, fpoint, &ft));
3987       const PetscInt *farr = DMPolytopeTypeGetArrangement(ft, DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]));
3988       PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, fpoint, PETSC_TRUE, &fconeSize, &fcone, &fornt));
3989       for (fc = 0; fc < fconeSize; ++fc) {
3990         const PetscInt cp = fcone[farr[fc * 2 + 0]];
3991         const PetscInt co = farr[fc * 2 + 1];
3992 
3993         for (i = 0; i < c; i += 2)
3994           if (pts[i] == cp) break;
3995         if (i == c) {
3996           PetscCall(DMPlexGetCellType(dm, cp, &ft));
3997           pts[c++] = cp;
3998           pts[c++] = DMPolytopeTypeComposeOrientation(ft, co, fornt[farr[fc * 2 + 0]]);
3999         }
4000       }
4001       PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, fpoint, PETSC_TRUE, &fconeSize, &fcone, &fornt));
4002     }
4003   }
4004   *numPoints = c / 2;
4005   *points    = pts;
4006   PetscFunctionReturn(PETSC_SUCCESS);
4007 }
4008 
4009 PetscErrorCode DMPlexGetTransitiveClosure_Internal(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
4010 {
4011   DMPolytopeType ct;
4012   PetscInt      *closure, *fifo;
4013   PetscInt       closureSize = 0, fifoStart = 0, fifoSize = 0;
4014   PetscInt       maxConeSize, maxSupportSize, coneSeries, supportSeries;
4015   PetscInt       depth, maxSize;
4016 
4017   PetscFunctionBeginHot;
4018   PetscCall(DMPlexGetDepth(dm, &depth));
4019   if (depth == 1) {
4020     PetscCall(DMPlexGetTransitiveClosure_Depth1_Private(dm, p, ornt, useCone, numPoints, points));
4021     PetscFunctionReturn(PETSC_SUCCESS);
4022   }
4023   PetscCall(DMPlexGetCellType(dm, p, &ct));
4024   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;
4025   if (DMPolytopeTypeIsHybrid(ct) && ct != DM_POLYTOPE_POINT_PRISM_TENSOR) {
4026     PetscCall(DMPlexTransitiveClosure_Tensor_Internal(dm, p, ct, ornt, useCone, numPoints, points));
4027     PetscFunctionReturn(PETSC_SUCCESS);
4028   }
4029   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
4030   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, depth + 1) - 1) / (maxConeSize - 1)) : depth + 1;
4031   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, depth + 1) - 1) / (maxSupportSize - 1)) : depth + 1;
4032   maxSize       = PetscMax(coneSeries, supportSeries);
4033   PetscCall(DMGetWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
4034   if (*points) {
4035     closure = *points;
4036   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &closure));
4037   closure[closureSize++] = p;
4038   closure[closureSize++] = ornt;
4039   fifo[fifoSize++]       = p;
4040   fifo[fifoSize++]       = ornt;
4041   fifo[fifoSize++]       = ct;
4042   /* Should kick out early when depth is reached, rather than checking all vertices for empty cones */
4043   while (fifoSize - fifoStart) {
4044     const PetscInt       q    = fifo[fifoStart++];
4045     const PetscInt       o    = fifo[fifoStart++];
4046     const DMPolytopeType qt   = (DMPolytopeType)fifo[fifoStart++];
4047     const PetscInt      *qarr = DMPolytopeTypeGetArrangement(qt, o);
4048     const PetscInt      *tmp, *tmpO = NULL;
4049     PetscInt             tmpSize, t;
4050 
4051     if (PetscDefined(USE_DEBUG)) {
4052       PetscInt nO = DMPolytopeTypeGetNumArrangements(qt) / 2;
4053       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);
4054     }
4055     PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, q, useCone, &tmpSize, &tmp, &tmpO));
4056     for (t = 0; t < tmpSize; ++t) {
4057       const PetscInt ip = useCone && qarr ? qarr[t * 2] : t;
4058       const PetscInt io = useCone && qarr ? qarr[t * 2 + 1] : 0;
4059       const PetscInt cp = tmp[ip];
4060       PetscCall(DMPlexGetCellType(dm, cp, &ct));
4061       const PetscInt co = tmpO ? DMPolytopeTypeComposeOrientation(ct, io, tmpO[ip]) : 0;
4062       PetscInt       c;
4063 
4064       /* Check for duplicate */
4065       for (c = 0; c < closureSize; c += 2) {
4066         if (closure[c] == cp) break;
4067       }
4068       if (c == closureSize) {
4069         closure[closureSize++] = cp;
4070         closure[closureSize++] = co;
4071         fifo[fifoSize++]       = cp;
4072         fifo[fifoSize++]       = co;
4073         fifo[fifoSize++]       = ct;
4074       }
4075     }
4076     PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, q, useCone, &tmpSize, &tmp, &tmpO));
4077   }
4078   PetscCall(DMRestoreWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
4079   if (numPoints) *numPoints = closureSize / 2;
4080   if (points) *points = closure;
4081   PetscFunctionReturn(PETSC_SUCCESS);
4082 }
4083 
4084 /*@C
4085   DMPlexGetTransitiveClosure - Return the points on the transitive closure of the in-edges or out-edges for this point in the DAG
4086 
4087   Not Collective
4088 
4089   Input Parameters:
4090 + dm      - The `DMPLEX`
4091 . p       - The mesh point
4092 - useCone - `PETSC_TRUE` for the closure, otherwise return the star
4093 
4094   Input/Output Parameter:
4095 . points - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...];
4096            if `NULL` on input, internal storage will be returned, otherwise the provided array is used
4097 
4098   Output Parameter:
4099 . numPoints - The number of points in the closure, so points[] is of size 2*`numPoints`
4100 
4101   Level: beginner
4102 
4103   Note:
4104   If using internal storage (points is `NULL` on input), each call overwrites the last output.
4105 
4106   Fortran Notes:
4107   The `numPoints` argument is not present in the Fortran binding since it is internal to the array.
4108 
4109 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
4110 @*/
4111 PetscErrorCode DMPlexGetTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
4112 {
4113   PetscFunctionBeginHot;
4114   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4115   if (numPoints) PetscAssertPointer(numPoints, 4);
4116   if (points) PetscAssertPointer(points, 5);
4117   if (PetscDefined(USE_DEBUG)) {
4118     PetscInt pStart, pEnd;
4119     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4120     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);
4121   }
4122   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, p, 0, useCone, numPoints, points));
4123   PetscFunctionReturn(PETSC_SUCCESS);
4124 }
4125 
4126 /*@C
4127   DMPlexRestoreTransitiveClosure - Restore the array of points on the transitive closure of the in-edges or out-edges for this point in the DAG
4128 
4129   Not Collective
4130 
4131   Input Parameters:
4132 + dm        - The `DMPLEX`
4133 . p         - The mesh point
4134 . useCone   - `PETSC_TRUE` for the closure, otherwise return the star
4135 . numPoints - The number of points in the closure, so points[] is of size 2*`numPoints`
4136 - points    - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...]
4137 
4138   Level: beginner
4139 
4140   Note:
4141   If not using internal storage (points is not `NULL` on input), this call is unnecessary
4142 
4143 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
4144 @*/
4145 PetscErrorCode DMPlexRestoreTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
4146 {
4147   PetscFunctionBeginHot;
4148   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4149   if (numPoints) *numPoints = 0;
4150   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, points));
4151   PetscFunctionReturn(PETSC_SUCCESS);
4152 }
4153 
4154 /*@
4155   DMPlexGetMaxSizes - Return the maximum number of in-edges (cone) and out-edges (support) for any point in the DAG
4156 
4157   Not Collective
4158 
4159   Input Parameter:
4160 . dm - The `DMPLEX`
4161 
4162   Output Parameters:
4163 + maxConeSize    - The maximum number of in-edges
4164 - maxSupportSize - The maximum number of out-edges
4165 
4166   Level: beginner
4167 
4168 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
4169 @*/
4170 PetscErrorCode DMPlexGetMaxSizes(DM dm, PetscInt *maxConeSize, PetscInt *maxSupportSize)
4171 {
4172   DM_Plex *mesh = (DM_Plex *)dm->data;
4173 
4174   PetscFunctionBegin;
4175   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4176   if (maxConeSize) PetscCall(PetscSectionGetMaxDof(mesh->coneSection, maxConeSize));
4177   if (maxSupportSize) PetscCall(PetscSectionGetMaxDof(mesh->supportSection, maxSupportSize));
4178   PetscFunctionReturn(PETSC_SUCCESS);
4179 }
4180 
4181 PetscErrorCode DMSetUp_Plex(DM dm)
4182 {
4183   DM_Plex *mesh = (DM_Plex *)dm->data;
4184   PetscInt size, maxSupportSize;
4185 
4186   PetscFunctionBegin;
4187   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4188   PetscCall(PetscSectionSetUp(mesh->coneSection));
4189   PetscCall(PetscSectionGetStorageSize(mesh->coneSection, &size));
4190   PetscCall(PetscMalloc1(size, &mesh->cones));
4191   PetscCall(PetscCalloc1(size, &mesh->coneOrientations));
4192   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
4193   if (maxSupportSize) {
4194     PetscCall(PetscSectionSetUp(mesh->supportSection));
4195     PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &size));
4196     PetscCall(PetscMalloc1(size, &mesh->supports));
4197   }
4198   PetscFunctionReturn(PETSC_SUCCESS);
4199 }
4200 
4201 PetscErrorCode DMCreateSubDM_Plex(DM dm, PetscInt numFields, const PetscInt fields[], IS *is, DM *subdm)
4202 {
4203   PetscFunctionBegin;
4204   if (subdm) PetscCall(DMClone(dm, subdm));
4205   PetscCall(DMCreateSectionSubDM(dm, numFields, fields, NULL, NULL, is, subdm));
4206   if (subdm) (*subdm)->useNatural = dm->useNatural;
4207   if (dm->useNatural && dm->sfMigration) {
4208     PetscSF sfNatural;
4209 
4210     (*subdm)->sfMigration = dm->sfMigration;
4211     PetscCall(PetscObjectReference((PetscObject)dm->sfMigration));
4212     PetscCall(DMPlexCreateGlobalToNaturalSF(*subdm, NULL, (*subdm)->sfMigration, &sfNatural));
4213     (*subdm)->sfNatural = sfNatural;
4214   }
4215   PetscFunctionReturn(PETSC_SUCCESS);
4216 }
4217 
4218 PetscErrorCode DMCreateSuperDM_Plex(DM dms[], PetscInt len, IS **is, DM *superdm)
4219 {
4220   PetscInt i = 0;
4221 
4222   PetscFunctionBegin;
4223   PetscCall(DMClone(dms[0], superdm));
4224   PetscCall(DMCreateSectionSuperDM(dms, len, is, superdm));
4225   (*superdm)->useNatural = PETSC_FALSE;
4226   for (i = 0; i < len; i++) {
4227     if (dms[i]->useNatural && dms[i]->sfMigration) {
4228       PetscSF sfNatural;
4229 
4230       (*superdm)->sfMigration = dms[i]->sfMigration;
4231       PetscCall(PetscObjectReference((PetscObject)dms[i]->sfMigration));
4232       (*superdm)->useNatural = PETSC_TRUE;
4233       PetscCall(DMPlexCreateGlobalToNaturalSF(*superdm, NULL, (*superdm)->sfMigration, &sfNatural));
4234       (*superdm)->sfNatural = sfNatural;
4235       break;
4236     }
4237   }
4238   PetscFunctionReturn(PETSC_SUCCESS);
4239 }
4240 
4241 /*@
4242   DMPlexSymmetrize - Create support (out-edge) information from cone (in-edge) information
4243 
4244   Not Collective
4245 
4246   Input Parameter:
4247 . dm - The `DMPLEX`
4248 
4249   Level: beginner
4250 
4251   Note:
4252   This should be called after all calls to `DMPlexSetCone()`
4253 
4254 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMPlexSetCone()`
4255 @*/
4256 PetscErrorCode DMPlexSymmetrize(DM dm)
4257 {
4258   DM_Plex  *mesh = (DM_Plex *)dm->data;
4259   PetscInt *offsets;
4260   PetscInt  supportSize;
4261   PetscInt  pStart, pEnd, p;
4262 
4263   PetscFunctionBegin;
4264   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4265   PetscCheck(!mesh->supports, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONGSTATE, "Supports were already setup in this DMPlex");
4266   PetscCall(PetscLogEventBegin(DMPLEX_Symmetrize, dm, 0, 0, 0));
4267   /* Calculate support sizes */
4268   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4269   for (p = pStart; p < pEnd; ++p) {
4270     PetscInt dof, off, c;
4271 
4272     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
4273     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
4274     for (c = off; c < off + dof; ++c) PetscCall(PetscSectionAddDof(mesh->supportSection, mesh->cones[c], 1));
4275   }
4276   PetscCall(PetscSectionSetUp(mesh->supportSection));
4277   /* Calculate supports */
4278   PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &supportSize));
4279   PetscCall(PetscMalloc1(supportSize, &mesh->supports));
4280   PetscCall(PetscCalloc1(pEnd - pStart, &offsets));
4281   for (p = pStart; p < pEnd; ++p) {
4282     PetscInt dof, off, c;
4283 
4284     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
4285     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
4286     for (c = off; c < off + dof; ++c) {
4287       const PetscInt q = mesh->cones[c];
4288       PetscInt       offS;
4289 
4290       PetscCall(PetscSectionGetOffset(mesh->supportSection, q, &offS));
4291 
4292       mesh->supports[offS + offsets[q]] = p;
4293       ++offsets[q];
4294     }
4295   }
4296   PetscCall(PetscFree(offsets));
4297   PetscCall(PetscLogEventEnd(DMPLEX_Symmetrize, dm, 0, 0, 0));
4298   PetscFunctionReturn(PETSC_SUCCESS);
4299 }
4300 
4301 static PetscErrorCode DMPlexCreateDepthStratum(DM dm, DMLabel label, PetscInt depth, PetscInt pStart, PetscInt pEnd)
4302 {
4303   IS stratumIS;
4304 
4305   PetscFunctionBegin;
4306   if (pStart >= pEnd) PetscFunctionReturn(PETSC_SUCCESS);
4307   if (PetscDefined(USE_DEBUG)) {
4308     PetscInt  qStart, qEnd, numLevels, level;
4309     PetscBool overlap = PETSC_FALSE;
4310     PetscCall(DMLabelGetNumValues(label, &numLevels));
4311     for (level = 0; level < numLevels; level++) {
4312       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4313       if ((pStart >= qStart && pStart < qEnd) || (pEnd > qStart && pEnd <= qEnd)) {
4314         overlap = PETSC_TRUE;
4315         break;
4316       }
4317     }
4318     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);
4319   }
4320   PetscCall(ISCreateStride(PETSC_COMM_SELF, pEnd - pStart, pStart, 1, &stratumIS));
4321   PetscCall(DMLabelSetStratumIS(label, depth, stratumIS));
4322   PetscCall(ISDestroy(&stratumIS));
4323   PetscFunctionReturn(PETSC_SUCCESS);
4324 }
4325 
4326 static PetscErrorCode DMPlexStratify_CellType_Private(DM dm, DMLabel label)
4327 {
4328   PetscInt *pMin, *pMax;
4329   PetscInt  pStart, pEnd;
4330   PetscInt  dmin = PETSC_MAX_INT, dmax = PETSC_MIN_INT;
4331 
4332   PetscFunctionBegin;
4333   {
4334     DMLabel label2;
4335 
4336     PetscCall(DMPlexGetCellTypeLabel(dm, &label2));
4337     PetscCall(PetscObjectViewFromOptions((PetscObject)label2, NULL, "-ct_view"));
4338   }
4339   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4340   for (PetscInt p = pStart; p < pEnd; ++p) {
4341     DMPolytopeType ct;
4342 
4343     PetscCall(DMPlexGetCellType(dm, p, &ct));
4344     dmin = PetscMin(DMPolytopeTypeGetDim(ct), dmin);
4345     dmax = PetscMax(DMPolytopeTypeGetDim(ct), dmax);
4346   }
4347   PetscCall(PetscMalloc2(dmax + 1, &pMin, dmax + 1, &pMax));
4348   for (PetscInt d = dmin; d <= dmax; ++d) {
4349     pMin[d] = PETSC_MAX_INT;
4350     pMax[d] = PETSC_MIN_INT;
4351   }
4352   for (PetscInt p = pStart; p < pEnd; ++p) {
4353     DMPolytopeType ct;
4354     PetscInt       d;
4355 
4356     PetscCall(DMPlexGetCellType(dm, p, &ct));
4357     d       = DMPolytopeTypeGetDim(ct);
4358     pMin[d] = PetscMin(p, pMin[d]);
4359     pMax[d] = PetscMax(p, pMax[d]);
4360   }
4361   for (PetscInt d = dmin; d <= dmax; ++d) {
4362     if (pMin[d] > pMax[d]) continue;
4363     PetscCall(DMPlexCreateDepthStratum(dm, label, d, pMin[d], pMax[d] + 1));
4364   }
4365   PetscCall(PetscFree2(pMin, pMax));
4366   PetscFunctionReturn(PETSC_SUCCESS);
4367 }
4368 
4369 static PetscErrorCode DMPlexStratify_Topological_Private(DM dm, DMLabel label)
4370 {
4371   PetscInt pStart, pEnd;
4372   PetscInt numRoots = 0, numLeaves = 0;
4373 
4374   PetscFunctionBegin;
4375   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4376   {
4377     /* Initialize roots and count leaves */
4378     PetscInt sMin = PETSC_MAX_INT;
4379     PetscInt sMax = PETSC_MIN_INT;
4380     PetscInt coneSize, supportSize;
4381 
4382     for (PetscInt p = pStart; p < pEnd; ++p) {
4383       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4384       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
4385       if (!coneSize && supportSize) {
4386         sMin = PetscMin(p, sMin);
4387         sMax = PetscMax(p, sMax);
4388         ++numRoots;
4389       } else if (!supportSize && coneSize) {
4390         ++numLeaves;
4391       } else if (!supportSize && !coneSize) {
4392         /* Isolated points */
4393         sMin = PetscMin(p, sMin);
4394         sMax = PetscMax(p, sMax);
4395       }
4396     }
4397     PetscCall(DMPlexCreateDepthStratum(dm, label, 0, sMin, sMax + 1));
4398   }
4399 
4400   if (numRoots + numLeaves == (pEnd - pStart)) {
4401     PetscInt sMin = PETSC_MAX_INT;
4402     PetscInt sMax = PETSC_MIN_INT;
4403     PetscInt coneSize, supportSize;
4404 
4405     for (PetscInt p = pStart; p < pEnd; ++p) {
4406       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4407       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
4408       if (!supportSize && coneSize) {
4409         sMin = PetscMin(p, sMin);
4410         sMax = PetscMax(p, sMax);
4411       }
4412     }
4413     PetscCall(DMPlexCreateDepthStratum(dm, label, 1, sMin, sMax + 1));
4414   } else {
4415     PetscInt level = 0;
4416     PetscInt qStart, qEnd;
4417 
4418     PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4419     while (qEnd > qStart) {
4420       PetscInt sMin = PETSC_MAX_INT;
4421       PetscInt sMax = PETSC_MIN_INT;
4422 
4423       for (PetscInt q = qStart; q < qEnd; ++q) {
4424         const PetscInt *support;
4425         PetscInt        supportSize;
4426 
4427         PetscCall(DMPlexGetSupportSize(dm, q, &supportSize));
4428         PetscCall(DMPlexGetSupport(dm, q, &support));
4429         for (PetscInt s = 0; s < supportSize; ++s) {
4430           sMin = PetscMin(support[s], sMin);
4431           sMax = PetscMax(support[s], sMax);
4432         }
4433       }
4434       PetscCall(DMLabelGetNumValues(label, &level));
4435       PetscCall(DMPlexCreateDepthStratum(dm, label, level, sMin, sMax + 1));
4436       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4437     }
4438   }
4439   PetscFunctionReturn(PETSC_SUCCESS);
4440 }
4441 
4442 /*@
4443   DMPlexStratify - Computes the strata for all points in the `DMPLEX`
4444 
4445   Collective
4446 
4447   Input Parameter:
4448 . dm - The `DMPLEX`
4449 
4450   Level: beginner
4451 
4452   Notes:
4453   The strata group all points of the same grade, and this function calculates the strata. This
4454   grade can be seen as the height (or depth) of the point in the DAG.
4455 
4456   The DAG for most topologies is a graded poset (https://en.wikipedia.org/wiki/Graded_poset), and
4457   can be illustrated by a Hasse Diagram (https://en.wikipedia.org/wiki/Hasse_diagram).
4458   Concretely, `DMPlexStratify()` creates a new label named "depth" containing the depth in the DAG of each point. For cell-vertex
4459   meshes, vertices are depth 0 and cells are depth 1. For fully interpolated meshes, depth 0 for vertices, 1 for edges, and so on
4460   until cells have depth equal to the dimension of the mesh. The depth label can be accessed through `DMPlexGetDepthLabel()` or `DMPlexGetDepthStratum()`, or
4461   manually via `DMGetLabel()`.  The height is defined implicitly by height = maxDimension - depth, and can be accessed
4462   via `DMPlexGetHeightStratum()`.  For example, cells have height 0 and faces have height 1.
4463 
4464   The depth of a point is calculated by executing a breadth-first search (BFS) on the DAG. This could produce surprising results
4465   if run on a partially interpolated mesh, meaning one that had some edges and faces, but not others. For example, suppose that
4466   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
4467   to interpolate only that one (e0), so that
4468 .vb
4469   cone(c0) = {e0, v2}
4470   cone(e0) = {v0, v1}
4471 .ve
4472   If `DMPlexStratify()` is run on this mesh, it will give depths
4473 .vb
4474    depth 0 = {v0, v1, v2}
4475    depth 1 = {e0, c0}
4476 .ve
4477   where the triangle has been given depth 1, instead of 2, because it is reachable from vertex v2.
4478 
4479   `DMPlexStratify()` should be called after all calls to `DMPlexSymmetrize()`
4480 
4481 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexComputeCellTypes()`
4482 @*/
4483 PetscErrorCode DMPlexStratify(DM dm)
4484 {
4485   DM_Plex  *mesh = (DM_Plex *)dm->data;
4486   DMLabel   label;
4487   PetscBool flg = PETSC_FALSE;
4488 
4489   PetscFunctionBegin;
4490   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4491   PetscCall(PetscLogEventBegin(DMPLEX_Stratify, dm, 0, 0, 0));
4492 
4493   // Create depth label
4494   PetscCall(DMRemoveLabel(dm, "depth", NULL));
4495   PetscCall(DMCreateLabel(dm, "depth"));
4496   PetscCall(DMPlexGetDepthLabel(dm, &label));
4497 
4498   PetscCall(PetscOptionsGetBool(NULL, dm->hdr.prefix, "-dm_plex_stratify_celltype", &flg, NULL));
4499   if (flg) PetscCall(DMPlexStratify_CellType_Private(dm, label));
4500   else PetscCall(DMPlexStratify_Topological_Private(dm, label));
4501 
4502   { /* just in case there is an empty process */
4503     PetscInt numValues, maxValues = 0, v;
4504 
4505     PetscCall(DMLabelGetNumValues(label, &numValues));
4506     PetscCall(MPIU_Allreduce(&numValues, &maxValues, 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
4507     for (v = numValues; v < maxValues; v++) PetscCall(DMLabelAddStratum(label, v));
4508   }
4509   PetscCall(PetscObjectStateGet((PetscObject)label, &mesh->depthState));
4510   PetscCall(PetscLogEventEnd(DMPLEX_Stratify, dm, 0, 0, 0));
4511   PetscFunctionReturn(PETSC_SUCCESS);
4512 }
4513 
4514 PetscErrorCode DMPlexComputeCellType_Internal(DM dm, PetscInt p, PetscInt pdepth, DMPolytopeType *pt)
4515 {
4516   DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4517   PetscInt       dim, depth, pheight, coneSize;
4518 
4519   PetscFunctionBeginHot;
4520   PetscCall(DMGetDimension(dm, &dim));
4521   PetscCall(DMPlexGetDepth(dm, &depth));
4522   PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4523   pheight = depth - pdepth;
4524   if (depth <= 1) {
4525     switch (pdepth) {
4526     case 0:
4527       ct = DM_POLYTOPE_POINT;
4528       break;
4529     case 1:
4530       switch (coneSize) {
4531       case 2:
4532         ct = DM_POLYTOPE_SEGMENT;
4533         break;
4534       case 3:
4535         ct = DM_POLYTOPE_TRIANGLE;
4536         break;
4537       case 4:
4538         switch (dim) {
4539         case 2:
4540           ct = DM_POLYTOPE_QUADRILATERAL;
4541           break;
4542         case 3:
4543           ct = DM_POLYTOPE_TETRAHEDRON;
4544           break;
4545         default:
4546           break;
4547         }
4548         break;
4549       case 5:
4550         ct = DM_POLYTOPE_PYRAMID;
4551         break;
4552       case 6:
4553         ct = DM_POLYTOPE_TRI_PRISM_TENSOR;
4554         break;
4555       case 8:
4556         ct = DM_POLYTOPE_HEXAHEDRON;
4557         break;
4558       default:
4559         break;
4560       }
4561     }
4562   } else {
4563     if (pdepth == 0) {
4564       ct = DM_POLYTOPE_POINT;
4565     } else if (pheight == 0) {
4566       switch (dim) {
4567       case 1:
4568         switch (coneSize) {
4569         case 2:
4570           ct = DM_POLYTOPE_SEGMENT;
4571           break;
4572         default:
4573           break;
4574         }
4575         break;
4576       case 2:
4577         switch (coneSize) {
4578         case 3:
4579           ct = DM_POLYTOPE_TRIANGLE;
4580           break;
4581         case 4:
4582           ct = DM_POLYTOPE_QUADRILATERAL;
4583           break;
4584         default:
4585           break;
4586         }
4587         break;
4588       case 3:
4589         switch (coneSize) {
4590         case 4:
4591           ct = DM_POLYTOPE_TETRAHEDRON;
4592           break;
4593         case 5: {
4594           const PetscInt *cone;
4595           PetscInt        faceConeSize;
4596 
4597           PetscCall(DMPlexGetCone(dm, p, &cone));
4598           PetscCall(DMPlexGetConeSize(dm, cone[0], &faceConeSize));
4599           switch (faceConeSize) {
4600           case 3:
4601             ct = DM_POLYTOPE_TRI_PRISM_TENSOR;
4602             break;
4603           case 4:
4604             ct = DM_POLYTOPE_PYRAMID;
4605             break;
4606           }
4607         } break;
4608         case 6:
4609           ct = DM_POLYTOPE_HEXAHEDRON;
4610           break;
4611         default:
4612           break;
4613         }
4614         break;
4615       default:
4616         break;
4617       }
4618     } else if (pheight > 0) {
4619       switch (coneSize) {
4620       case 2:
4621         ct = DM_POLYTOPE_SEGMENT;
4622         break;
4623       case 3:
4624         ct = DM_POLYTOPE_TRIANGLE;
4625         break;
4626       case 4:
4627         ct = DM_POLYTOPE_QUADRILATERAL;
4628         break;
4629       default:
4630         break;
4631       }
4632     }
4633   }
4634   *pt = ct;
4635   PetscFunctionReturn(PETSC_SUCCESS);
4636 }
4637 
4638 /*@
4639   DMPlexComputeCellTypes - Infer the polytope type of every cell using its dimension and cone size.
4640 
4641   Collective
4642 
4643   Input Parameter:
4644 . dm - The `DMPLEX`
4645 
4646   Level: developer
4647 
4648   Note:
4649   This function is normally called automatically when a cell type is requested. It creates an
4650   internal `DMLabel` named "celltype" which can be directly accessed using `DMGetLabel()`. A user may disable
4651   automatic creation by creating the label manually, using `DMCreateLabel`(dm, "celltype").
4652 
4653   `DMPlexComputeCellTypes()` should be called after all calls to `DMPlexSymmetrize()` and `DMPlexStratify()`
4654 
4655 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexStratify()`, `DMGetLabel()`, `DMCreateLabel()`
4656 @*/
4657 PetscErrorCode DMPlexComputeCellTypes(DM dm)
4658 {
4659   DM_Plex *mesh;
4660   DMLabel  ctLabel;
4661   PetscInt pStart, pEnd, p;
4662 
4663   PetscFunctionBegin;
4664   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4665   mesh = (DM_Plex *)dm->data;
4666   PetscCall(DMCreateLabel(dm, "celltype"));
4667   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
4668   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4669   PetscCall(PetscFree(mesh->cellTypes));
4670   PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
4671   for (p = pStart; p < pEnd; ++p) {
4672     DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4673     PetscInt       pdepth;
4674 
4675     PetscCall(DMPlexGetPointDepth(dm, p, &pdepth));
4676     PetscCall(DMPlexComputeCellType_Internal(dm, p, pdepth, &ct));
4677     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]);
4678     PetscCall(DMLabelSetValue(ctLabel, p, ct));
4679     mesh->cellTypes[p - pStart].value_as_uint8 = ct;
4680   }
4681   PetscCall(PetscObjectStateGet((PetscObject)ctLabel, &mesh->celltypeState));
4682   PetscCall(PetscObjectViewFromOptions((PetscObject)ctLabel, NULL, "-dm_plex_celltypes_view"));
4683   PetscFunctionReturn(PETSC_SUCCESS);
4684 }
4685 
4686 /*@C
4687   DMPlexGetJoin - Get an array for the join of the set of points
4688 
4689   Not Collective
4690 
4691   Input Parameters:
4692 + dm        - The `DMPLEX` object
4693 . numPoints - The number of input points for the join
4694 - points    - The input points
4695 
4696   Output Parameters:
4697 + numCoveredPoints - The number of points in the join
4698 - coveredPoints    - The points in the join
4699 
4700   Level: intermediate
4701 
4702   Note:
4703   Currently, this is restricted to a single level join
4704 
4705   Fortran Notes:
4706   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4707 
4708 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4709 @*/
4710 PetscErrorCode DMPlexGetJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4711 {
4712   DM_Plex  *mesh = (DM_Plex *)dm->data;
4713   PetscInt *join[2];
4714   PetscInt  joinSize, i = 0;
4715   PetscInt  dof, off, p, c, m;
4716   PetscInt  maxSupportSize;
4717 
4718   PetscFunctionBegin;
4719   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4720   PetscAssertPointer(points, 3);
4721   PetscAssertPointer(numCoveredPoints, 4);
4722   PetscAssertPointer(coveredPoints, 5);
4723   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
4724   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[0]));
4725   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[1]));
4726   /* Copy in support of first point */
4727   PetscCall(PetscSectionGetDof(mesh->supportSection, points[0], &dof));
4728   PetscCall(PetscSectionGetOffset(mesh->supportSection, points[0], &off));
4729   for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = mesh->supports[off + joinSize];
4730   /* Check each successive support */
4731   for (p = 1; p < numPoints; ++p) {
4732     PetscInt newJoinSize = 0;
4733 
4734     PetscCall(PetscSectionGetDof(mesh->supportSection, points[p], &dof));
4735     PetscCall(PetscSectionGetOffset(mesh->supportSection, points[p], &off));
4736     for (c = 0; c < dof; ++c) {
4737       const PetscInt point = mesh->supports[off + c];
4738 
4739       for (m = 0; m < joinSize; ++m) {
4740         if (point == join[i][m]) {
4741           join[1 - i][newJoinSize++] = point;
4742           break;
4743         }
4744       }
4745     }
4746     joinSize = newJoinSize;
4747     i        = 1 - i;
4748   }
4749   *numCoveredPoints = joinSize;
4750   *coveredPoints    = join[i];
4751   PetscCall(DMRestoreWorkArray(dm, maxSupportSize, MPIU_INT, &join[1 - i]));
4752   PetscFunctionReturn(PETSC_SUCCESS);
4753 }
4754 
4755 /*@C
4756   DMPlexRestoreJoin - Restore an array for the join of the set of points
4757 
4758   Not Collective
4759 
4760   Input Parameters:
4761 + dm        - The `DMPLEX` object
4762 . numPoints - The number of input points for the join
4763 - points    - The input points
4764 
4765   Output Parameters:
4766 + numCoveredPoints - The number of points in the join
4767 - coveredPoints    - The points in the join
4768 
4769   Level: intermediate
4770 
4771   Fortran Notes:
4772   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4773 
4774 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetJoin()`, `DMPlexGetFullJoin()`, `DMPlexGetMeet()`
4775 @*/
4776 PetscErrorCode DMPlexRestoreJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4777 {
4778   PetscFunctionBegin;
4779   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4780   if (points) PetscAssertPointer(points, 3);
4781   if (numCoveredPoints) PetscAssertPointer(numCoveredPoints, 4);
4782   PetscAssertPointer(coveredPoints, 5);
4783   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
4784   if (numCoveredPoints) *numCoveredPoints = 0;
4785   PetscFunctionReturn(PETSC_SUCCESS);
4786 }
4787 
4788 /*@C
4789   DMPlexGetFullJoin - Get an array for the join of the set of points
4790 
4791   Not Collective
4792 
4793   Input Parameters:
4794 + dm        - The `DMPLEX` object
4795 . numPoints - The number of input points for the join
4796 - points    - The input points
4797 
4798   Output Parameters:
4799 + numCoveredPoints - The number of points in the join
4800 - coveredPoints    - The points in the join
4801 
4802   Level: intermediate
4803 
4804   Fortran Notes:
4805   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4806 
4807 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetJoin()`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4808 @*/
4809 PetscErrorCode DMPlexGetFullJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4810 {
4811   PetscInt *offsets, **closures;
4812   PetscInt *join[2];
4813   PetscInt  depth = 0, maxSize, joinSize = 0, i = 0;
4814   PetscInt  p, d, c, m, ms;
4815 
4816   PetscFunctionBegin;
4817   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4818   PetscAssertPointer(points, 3);
4819   PetscAssertPointer(numCoveredPoints, 4);
4820   PetscAssertPointer(coveredPoints, 5);
4821 
4822   PetscCall(DMPlexGetDepth(dm, &depth));
4823   PetscCall(PetscCalloc1(numPoints, &closures));
4824   PetscCall(DMGetWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
4825   PetscCall(DMPlexGetMaxSizes(dm, NULL, &ms));
4826   maxSize = (ms > 1) ? ((PetscPowInt(ms, depth + 1) - 1) / (ms - 1)) : depth + 1;
4827   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[0]));
4828   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[1]));
4829 
4830   for (p = 0; p < numPoints; ++p) {
4831     PetscInt closureSize;
4832 
4833     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_FALSE, &closureSize, &closures[p]));
4834 
4835     offsets[p * (depth + 2) + 0] = 0;
4836     for (d = 0; d < depth + 1; ++d) {
4837       PetscInt pStart, pEnd, i;
4838 
4839       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
4840       for (i = offsets[p * (depth + 2) + d]; i < closureSize; ++i) {
4841         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
4842           offsets[p * (depth + 2) + d + 1] = i;
4843           break;
4844         }
4845       }
4846       if (i == closureSize) offsets[p * (depth + 2) + d + 1] = i;
4847     }
4848     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);
4849   }
4850   for (d = 0; d < depth + 1; ++d) {
4851     PetscInt dof;
4852 
4853     /* Copy in support of first point */
4854     dof = offsets[d + 1] - offsets[d];
4855     for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = closures[0][(offsets[d] + joinSize) * 2];
4856     /* Check each successive cone */
4857     for (p = 1; p < numPoints && joinSize; ++p) {
4858       PetscInt newJoinSize = 0;
4859 
4860       dof = offsets[p * (depth + 2) + d + 1] - offsets[p * (depth + 2) + d];
4861       for (c = 0; c < dof; ++c) {
4862         const PetscInt point = closures[p][(offsets[p * (depth + 2) + d] + c) * 2];
4863 
4864         for (m = 0; m < joinSize; ++m) {
4865           if (point == join[i][m]) {
4866             join[1 - i][newJoinSize++] = point;
4867             break;
4868           }
4869         }
4870       }
4871       joinSize = newJoinSize;
4872       i        = 1 - i;
4873     }
4874     if (joinSize) break;
4875   }
4876   *numCoveredPoints = joinSize;
4877   *coveredPoints    = join[i];
4878   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_FALSE, NULL, &closures[p]));
4879   PetscCall(PetscFree(closures));
4880   PetscCall(DMRestoreWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
4881   PetscCall(DMRestoreWorkArray(dm, ms, MPIU_INT, &join[1 - i]));
4882   PetscFunctionReturn(PETSC_SUCCESS);
4883 }
4884 
4885 /*@C
4886   DMPlexGetMeet - Get an array for the meet of the set of points
4887 
4888   Not Collective
4889 
4890   Input Parameters:
4891 + dm        - The `DMPLEX` object
4892 . numPoints - The number of input points for the meet
4893 - points    - The input points
4894 
4895   Output Parameters:
4896 + numCoveringPoints - The number of points in the meet
4897 - coveringPoints    - The points in the meet
4898 
4899   Level: intermediate
4900 
4901   Note:
4902   Currently, this is restricted to a single level meet
4903 
4904   Fortran Notes:
4905   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4906 
4907 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
4908 @*/
4909 PetscErrorCode DMPlexGetMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveringPoints, const PetscInt **coveringPoints)
4910 {
4911   DM_Plex  *mesh = (DM_Plex *)dm->data;
4912   PetscInt *meet[2];
4913   PetscInt  meetSize, i = 0;
4914   PetscInt  dof, off, p, c, m;
4915   PetscInt  maxConeSize;
4916 
4917   PetscFunctionBegin;
4918   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4919   PetscAssertPointer(points, 3);
4920   PetscAssertPointer(numCoveringPoints, 4);
4921   PetscAssertPointer(coveringPoints, 5);
4922   PetscCall(PetscSectionGetMaxDof(mesh->coneSection, &maxConeSize));
4923   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[0]));
4924   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[1]));
4925   /* Copy in cone of first point */
4926   PetscCall(PetscSectionGetDof(mesh->coneSection, points[0], &dof));
4927   PetscCall(PetscSectionGetOffset(mesh->coneSection, points[0], &off));
4928   for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = mesh->cones[off + meetSize];
4929   /* Check each successive cone */
4930   for (p = 1; p < numPoints; ++p) {
4931     PetscInt newMeetSize = 0;
4932 
4933     PetscCall(PetscSectionGetDof(mesh->coneSection, points[p], &dof));
4934     PetscCall(PetscSectionGetOffset(mesh->coneSection, points[p], &off));
4935     for (c = 0; c < dof; ++c) {
4936       const PetscInt point = mesh->cones[off + c];
4937 
4938       for (m = 0; m < meetSize; ++m) {
4939         if (point == meet[i][m]) {
4940           meet[1 - i][newMeetSize++] = point;
4941           break;
4942         }
4943       }
4944     }
4945     meetSize = newMeetSize;
4946     i        = 1 - i;
4947   }
4948   *numCoveringPoints = meetSize;
4949   *coveringPoints    = meet[i];
4950   PetscCall(DMRestoreWorkArray(dm, maxConeSize, MPIU_INT, &meet[1 - i]));
4951   PetscFunctionReturn(PETSC_SUCCESS);
4952 }
4953 
4954 /*@C
4955   DMPlexRestoreMeet - Restore an array for the meet of the set of points
4956 
4957   Not Collective
4958 
4959   Input Parameters:
4960 + dm        - The `DMPLEX` object
4961 . numPoints - The number of input points for the meet
4962 - points    - The input points
4963 
4964   Output Parameters:
4965 + numCoveredPoints - The number of points in the meet
4966 - coveredPoints    - The points in the meet
4967 
4968   Level: intermediate
4969 
4970   Fortran Notes:
4971   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4972 
4973 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetMeet()`, `DMPlexGetFullMeet()`, `DMPlexGetJoin()`
4974 @*/
4975 PetscErrorCode DMPlexRestoreMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4976 {
4977   PetscFunctionBegin;
4978   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4979   if (points) PetscAssertPointer(points, 3);
4980   if (numCoveredPoints) PetscAssertPointer(numCoveredPoints, 4);
4981   PetscAssertPointer(coveredPoints, 5);
4982   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
4983   if (numCoveredPoints) *numCoveredPoints = 0;
4984   PetscFunctionReturn(PETSC_SUCCESS);
4985 }
4986 
4987 /*@C
4988   DMPlexGetFullMeet - Get an array for the meet of the set of points
4989 
4990   Not Collective
4991 
4992   Input Parameters:
4993 + dm        - The `DMPLEX` object
4994 . numPoints - The number of input points for the meet
4995 - points    - The input points
4996 
4997   Output Parameters:
4998 + numCoveredPoints - The number of points in the meet
4999 - coveredPoints    - The points in the meet
5000 
5001   Level: intermediate
5002 
5003   Fortran Notes:
5004   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
5005 
5006 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetMeet()`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
5007 @*/
5008 PetscErrorCode DMPlexGetFullMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
5009 {
5010   PetscInt *offsets, **closures;
5011   PetscInt *meet[2];
5012   PetscInt  height = 0, maxSize, meetSize = 0, i = 0;
5013   PetscInt  p, h, c, m, mc;
5014 
5015   PetscFunctionBegin;
5016   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5017   PetscAssertPointer(points, 3);
5018   PetscAssertPointer(numCoveredPoints, 4);
5019   PetscAssertPointer(coveredPoints, 5);
5020 
5021   PetscCall(DMPlexGetDepth(dm, &height));
5022   PetscCall(PetscMalloc1(numPoints, &closures));
5023   PetscCall(DMGetWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
5024   PetscCall(DMPlexGetMaxSizes(dm, &mc, NULL));
5025   maxSize = (mc > 1) ? ((PetscPowInt(mc, height + 1) - 1) / (mc - 1)) : height + 1;
5026   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[0]));
5027   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[1]));
5028 
5029   for (p = 0; p < numPoints; ++p) {
5030     PetscInt closureSize;
5031 
5032     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_TRUE, &closureSize, &closures[p]));
5033 
5034     offsets[p * (height + 2) + 0] = 0;
5035     for (h = 0; h < height + 1; ++h) {
5036       PetscInt pStart, pEnd, i;
5037 
5038       PetscCall(DMPlexGetHeightStratum(dm, h, &pStart, &pEnd));
5039       for (i = offsets[p * (height + 2) + h]; i < closureSize; ++i) {
5040         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
5041           offsets[p * (height + 2) + h + 1] = i;
5042           break;
5043         }
5044       }
5045       if (i == closureSize) offsets[p * (height + 2) + h + 1] = i;
5046     }
5047     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);
5048   }
5049   for (h = 0; h < height + 1; ++h) {
5050     PetscInt dof;
5051 
5052     /* Copy in cone of first point */
5053     dof = offsets[h + 1] - offsets[h];
5054     for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = closures[0][(offsets[h] + meetSize) * 2];
5055     /* Check each successive cone */
5056     for (p = 1; p < numPoints && meetSize; ++p) {
5057       PetscInt newMeetSize = 0;
5058 
5059       dof = offsets[p * (height + 2) + h + 1] - offsets[p * (height + 2) + h];
5060       for (c = 0; c < dof; ++c) {
5061         const PetscInt point = closures[p][(offsets[p * (height + 2) + h] + c) * 2];
5062 
5063         for (m = 0; m < meetSize; ++m) {
5064           if (point == meet[i][m]) {
5065             meet[1 - i][newMeetSize++] = point;
5066             break;
5067           }
5068         }
5069       }
5070       meetSize = newMeetSize;
5071       i        = 1 - i;
5072     }
5073     if (meetSize) break;
5074   }
5075   *numCoveredPoints = meetSize;
5076   *coveredPoints    = meet[i];
5077   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_TRUE, NULL, &closures[p]));
5078   PetscCall(PetscFree(closures));
5079   PetscCall(DMRestoreWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
5080   PetscCall(DMRestoreWorkArray(dm, mc, MPIU_INT, &meet[1 - i]));
5081   PetscFunctionReturn(PETSC_SUCCESS);
5082 }
5083 
5084 /*@
5085   DMPlexEqual - Determine if two `DM` have the same topology
5086 
5087   Not Collective
5088 
5089   Input Parameters:
5090 + dmA - A `DMPLEX` object
5091 - dmB - A `DMPLEX` object
5092 
5093   Output Parameter:
5094 . equal - `PETSC_TRUE` if the topologies are identical
5095 
5096   Level: intermediate
5097 
5098   Note:
5099   We are not solving graph isomorphism, so we do not permute.
5100 
5101 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCone()`
5102 @*/
5103 PetscErrorCode DMPlexEqual(DM dmA, DM dmB, PetscBool *equal)
5104 {
5105   PetscInt depth, depthB, pStart, pEnd, pStartB, pEndB, p;
5106 
5107   PetscFunctionBegin;
5108   PetscValidHeaderSpecific(dmA, DM_CLASSID, 1);
5109   PetscValidHeaderSpecific(dmB, DM_CLASSID, 2);
5110   PetscAssertPointer(equal, 3);
5111 
5112   *equal = PETSC_FALSE;
5113   PetscCall(DMPlexGetDepth(dmA, &depth));
5114   PetscCall(DMPlexGetDepth(dmB, &depthB));
5115   if (depth != depthB) PetscFunctionReturn(PETSC_SUCCESS);
5116   PetscCall(DMPlexGetChart(dmA, &pStart, &pEnd));
5117   PetscCall(DMPlexGetChart(dmB, &pStartB, &pEndB));
5118   if ((pStart != pStartB) || (pEnd != pEndB)) PetscFunctionReturn(PETSC_SUCCESS);
5119   for (p = pStart; p < pEnd; ++p) {
5120     const PetscInt *cone, *coneB, *ornt, *orntB, *support, *supportB;
5121     PetscInt        coneSize, coneSizeB, c, supportSize, supportSizeB, s;
5122 
5123     PetscCall(DMPlexGetConeSize(dmA, p, &coneSize));
5124     PetscCall(DMPlexGetCone(dmA, p, &cone));
5125     PetscCall(DMPlexGetConeOrientation(dmA, p, &ornt));
5126     PetscCall(DMPlexGetConeSize(dmB, p, &coneSizeB));
5127     PetscCall(DMPlexGetCone(dmB, p, &coneB));
5128     PetscCall(DMPlexGetConeOrientation(dmB, p, &orntB));
5129     if (coneSize != coneSizeB) PetscFunctionReturn(PETSC_SUCCESS);
5130     for (c = 0; c < coneSize; ++c) {
5131       if (cone[c] != coneB[c]) PetscFunctionReturn(PETSC_SUCCESS);
5132       if (ornt[c] != orntB[c]) PetscFunctionReturn(PETSC_SUCCESS);
5133     }
5134     PetscCall(DMPlexGetSupportSize(dmA, p, &supportSize));
5135     PetscCall(DMPlexGetSupport(dmA, p, &support));
5136     PetscCall(DMPlexGetSupportSize(dmB, p, &supportSizeB));
5137     PetscCall(DMPlexGetSupport(dmB, p, &supportB));
5138     if (supportSize != supportSizeB) PetscFunctionReturn(PETSC_SUCCESS);
5139     for (s = 0; s < supportSize; ++s) {
5140       if (support[s] != supportB[s]) PetscFunctionReturn(PETSC_SUCCESS);
5141     }
5142   }
5143   *equal = PETSC_TRUE;
5144   PetscFunctionReturn(PETSC_SUCCESS);
5145 }
5146 
5147 /*@
5148   DMPlexGetNumFaceVertices - Returns the number of vertices on a face
5149 
5150   Not Collective
5151 
5152   Input Parameters:
5153 + dm         - The `DMPLEX`
5154 . cellDim    - The cell dimension
5155 - numCorners - The number of vertices on a cell
5156 
5157   Output Parameter:
5158 . numFaceVertices - The number of vertices on a face
5159 
5160   Level: developer
5161 
5162   Note:
5163   Of course this can only work for a restricted set of symmetric shapes
5164 
5165 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCone()`
5166 @*/
5167 PetscErrorCode DMPlexGetNumFaceVertices(DM dm, PetscInt cellDim, PetscInt numCorners, PetscInt *numFaceVertices)
5168 {
5169   MPI_Comm comm;
5170 
5171   PetscFunctionBegin;
5172   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
5173   PetscAssertPointer(numFaceVertices, 4);
5174   switch (cellDim) {
5175   case 0:
5176     *numFaceVertices = 0;
5177     break;
5178   case 1:
5179     *numFaceVertices = 1;
5180     break;
5181   case 2:
5182     switch (numCorners) {
5183     case 3:                 /* triangle */
5184       *numFaceVertices = 2; /* Edge has 2 vertices */
5185       break;
5186     case 4:                 /* quadrilateral */
5187       *numFaceVertices = 2; /* Edge has 2 vertices */
5188       break;
5189     case 6:                 /* quadratic triangle, tri and quad cohesive Lagrange cells */
5190       *numFaceVertices = 3; /* Edge has 3 vertices */
5191       break;
5192     case 9:                 /* quadratic quadrilateral, quadratic quad cohesive Lagrange cells */
5193       *numFaceVertices = 3; /* Edge has 3 vertices */
5194       break;
5195     default:
5196       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
5197     }
5198     break;
5199   case 3:
5200     switch (numCorners) {
5201     case 4:                 /* tetradehdron */
5202       *numFaceVertices = 3; /* Face has 3 vertices */
5203       break;
5204     case 6:                 /* tet cohesive cells */
5205       *numFaceVertices = 4; /* Face has 4 vertices */
5206       break;
5207     case 8:                 /* hexahedron */
5208       *numFaceVertices = 4; /* Face has 4 vertices */
5209       break;
5210     case 9:                 /* tet cohesive Lagrange cells */
5211       *numFaceVertices = 6; /* Face has 6 vertices */
5212       break;
5213     case 10:                /* quadratic tetrahedron */
5214       *numFaceVertices = 6; /* Face has 6 vertices */
5215       break;
5216     case 12:                /* hex cohesive Lagrange cells */
5217       *numFaceVertices = 6; /* Face has 6 vertices */
5218       break;
5219     case 18:                /* quadratic tet cohesive Lagrange cells */
5220       *numFaceVertices = 6; /* Face has 6 vertices */
5221       break;
5222     case 27:                /* quadratic hexahedron, quadratic hex cohesive Lagrange cells */
5223       *numFaceVertices = 9; /* Face has 9 vertices */
5224       break;
5225     default:
5226       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
5227     }
5228     break;
5229   default:
5230     SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid cell dimension %" PetscInt_FMT, cellDim);
5231   }
5232   PetscFunctionReturn(PETSC_SUCCESS);
5233 }
5234 
5235 /*@
5236   DMPlexGetDepthLabel - Get the `DMLabel` recording the depth of each point
5237 
5238   Not Collective
5239 
5240   Input Parameter:
5241 . dm - The `DMPLEX` object
5242 
5243   Output Parameter:
5244 . depthLabel - The `DMLabel` recording point depth
5245 
5246   Level: developer
5247 
5248 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepth()`, `DMPlexGetHeightStratum()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`,
5249 @*/
5250 PetscErrorCode DMPlexGetDepthLabel(DM dm, DMLabel *depthLabel)
5251 {
5252   PetscFunctionBegin;
5253   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5254   PetscAssertPointer(depthLabel, 2);
5255   *depthLabel = dm->depthLabel;
5256   PetscFunctionReturn(PETSC_SUCCESS);
5257 }
5258 
5259 /*@
5260   DMPlexGetDepth - Get the depth of the DAG representing this mesh
5261 
5262   Not Collective
5263 
5264   Input Parameter:
5265 . dm - The `DMPLEX` object
5266 
5267   Output Parameter:
5268 . depth - The number of strata (breadth first levels) in the DAG
5269 
5270   Level: developer
5271 
5272   Notes:
5273   This returns maximum of point depths over all points, i.e. maximum value of the label returned by `DMPlexGetDepthLabel()`.
5274 
5275   The point depth is described more in detail in `DMPlexGetDepthStratum()`.
5276 
5277   An empty mesh gives -1.
5278 
5279 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepthLabel()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`
5280 @*/
5281 PetscErrorCode DMPlexGetDepth(DM dm, PetscInt *depth)
5282 {
5283   DM_Plex *mesh = (DM_Plex *)dm->data;
5284   DMLabel  label;
5285   PetscInt d = -1;
5286 
5287   PetscFunctionBegin;
5288   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5289   PetscAssertPointer(depth, 2);
5290   if (mesh->tr) {
5291     PetscCall(DMPlexTransformGetDepth(mesh->tr, depth));
5292   } else {
5293     PetscCall(DMPlexGetDepthLabel(dm, &label));
5294     // Allow missing depths
5295     if (label) PetscCall(DMLabelGetValueBounds(label, NULL, &d));
5296     *depth = d;
5297   }
5298   PetscFunctionReturn(PETSC_SUCCESS);
5299 }
5300 
5301 /*@
5302   DMPlexGetDepthStratum - Get the bounds [`start`, `end`) for all points at a certain depth.
5303 
5304   Not Collective
5305 
5306   Input Parameters:
5307 + dm    - The `DMPLEX` object
5308 - depth - The requested depth
5309 
5310   Output Parameters:
5311 + start - The first point at this `depth`
5312 - end   - One beyond the last point at this `depth`
5313 
5314   Level: developer
5315 
5316   Notes:
5317   Depth indexing is related to topological dimension.  Depth stratum 0 contains the lowest topological dimension points,
5318   often "vertices".  If the mesh is "interpolated" (see `DMPlexInterpolate()`), then depth stratum 1 contains the next
5319   higher dimension, e.g., "edges".
5320 
5321 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetHeightStratum()`, `DMPlexGetCellTypeStratum()`, `DMPlexGetDepth()`, `DMPlexGetDepthLabel()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`, `DMPlexInterpolate()`
5322 @*/
5323 PetscErrorCode DMPlexGetDepthStratum(DM dm, PetscInt depth, PetscInt *start, PetscInt *end)
5324 {
5325   DM_Plex *mesh = (DM_Plex *)dm->data;
5326   DMLabel  label;
5327   PetscInt pStart, pEnd;
5328 
5329   PetscFunctionBegin;
5330   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5331   if (start) {
5332     PetscAssertPointer(start, 3);
5333     *start = 0;
5334   }
5335   if (end) {
5336     PetscAssertPointer(end, 4);
5337     *end = 0;
5338   }
5339   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
5340   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
5341   if (depth < 0) {
5342     if (start) *start = pStart;
5343     if (end) *end = pEnd;
5344     PetscFunctionReturn(PETSC_SUCCESS);
5345   }
5346   if (mesh->tr) {
5347     PetscCall(DMPlexTransformGetDepthStratum(mesh->tr, depth, start, end));
5348   } else {
5349     PetscCall(DMPlexGetDepthLabel(dm, &label));
5350     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named depth was found");
5351     PetscCall(DMLabelGetStratumBounds(label, depth, start, end));
5352   }
5353   PetscFunctionReturn(PETSC_SUCCESS);
5354 }
5355 
5356 /*@
5357   DMPlexGetHeightStratum - Get the bounds [`start`, `end`) for all points at a certain height.
5358 
5359   Not Collective
5360 
5361   Input Parameters:
5362 + dm     - The `DMPLEX` object
5363 - height - The requested height
5364 
5365   Output Parameters:
5366 + start - The first point at this `height`
5367 - end   - One beyond the last point at this `height`
5368 
5369   Level: developer
5370 
5371   Notes:
5372   Height indexing is related to topological codimension.  Height stratum 0 contains the highest topological dimension
5373   points, often called "cells" or "elements".  If the mesh is "interpolated" (see `DMPlexInterpolate()`), then height
5374   stratum 1 contains the boundary of these "cells", often called "faces" or "facets".
5375 
5376 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepthStratum()`, `DMPlexGetCellTypeStratum()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
5377 @*/
5378 PetscErrorCode DMPlexGetHeightStratum(DM dm, PetscInt height, PetscInt *start, PetscInt *end)
5379 {
5380   DMLabel  label;
5381   PetscInt depth, pStart, pEnd;
5382 
5383   PetscFunctionBegin;
5384   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5385   if (start) {
5386     PetscAssertPointer(start, 3);
5387     *start = 0;
5388   }
5389   if (end) {
5390     PetscAssertPointer(end, 4);
5391     *end = 0;
5392   }
5393   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
5394   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
5395   if (height < 0) {
5396     if (start) *start = pStart;
5397     if (end) *end = pEnd;
5398     PetscFunctionReturn(PETSC_SUCCESS);
5399   }
5400   PetscCall(DMPlexGetDepthLabel(dm, &label));
5401   if (label) PetscCall(DMLabelGetNumValues(label, &depth));
5402   else PetscCall(DMGetDimension(dm, &depth));
5403   PetscCheck(depth >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Depth not yet computed");
5404   PetscCall(DMPlexGetDepthStratum(dm, depth - 1 - height, start, end));
5405   PetscFunctionReturn(PETSC_SUCCESS);
5406 }
5407 
5408 /*@
5409   DMPlexGetPointDepth - Get the `depth` of a given point
5410 
5411   Not Collective
5412 
5413   Input Parameters:
5414 + dm    - The `DMPLEX` object
5415 - point - The point
5416 
5417   Output Parameter:
5418 . depth - The depth of the `point`
5419 
5420   Level: intermediate
5421 
5422 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
5423 @*/
5424 PetscErrorCode DMPlexGetPointDepth(DM dm, PetscInt point, PetscInt *depth)
5425 {
5426   PetscFunctionBegin;
5427   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5428   PetscAssertPointer(depth, 3);
5429   PetscCall(DMLabelGetValue(dm->depthLabel, point, depth));
5430   PetscFunctionReturn(PETSC_SUCCESS);
5431 }
5432 
5433 /*@
5434   DMPlexGetPointHeight - Get the `height` of a given point
5435 
5436   Not Collective
5437 
5438   Input Parameters:
5439 + dm    - The `DMPLEX` object
5440 - point - The point
5441 
5442   Output Parameter:
5443 . height - The height of the `point`
5444 
5445   Level: intermediate
5446 
5447 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointDepth()`
5448 @*/
5449 PetscErrorCode DMPlexGetPointHeight(DM dm, PetscInt point, PetscInt *height)
5450 {
5451   PetscInt n, pDepth;
5452 
5453   PetscFunctionBegin;
5454   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5455   PetscAssertPointer(height, 3);
5456   PetscCall(DMLabelGetNumValues(dm->depthLabel, &n));
5457   PetscCall(DMLabelGetValue(dm->depthLabel, point, &pDepth));
5458   *height = n - 1 - pDepth; /* DAG depth is n-1 */
5459   PetscFunctionReturn(PETSC_SUCCESS);
5460 }
5461 
5462 /*@
5463   DMPlexGetCellTypeLabel - Get the `DMLabel` recording the polytope type of each cell
5464 
5465   Not Collective
5466 
5467   Input Parameter:
5468 . dm - The `DMPLEX` object
5469 
5470   Output Parameter:
5471 . celltypeLabel - The `DMLabel` recording cell polytope type
5472 
5473   Level: developer
5474 
5475   Note:
5476   This function will trigger automatica computation of cell types. This can be disabled by calling
5477   `DMCreateLabel`(dm, "celltype") beforehand.
5478 
5479 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMCreateLabel()`
5480 @*/
5481 PetscErrorCode DMPlexGetCellTypeLabel(DM dm, DMLabel *celltypeLabel)
5482 {
5483   PetscFunctionBegin;
5484   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5485   PetscAssertPointer(celltypeLabel, 2);
5486   if (!dm->celltypeLabel) PetscCall(DMPlexComputeCellTypes(dm));
5487   *celltypeLabel = dm->celltypeLabel;
5488   PetscFunctionReturn(PETSC_SUCCESS);
5489 }
5490 
5491 /*@
5492   DMPlexGetCellType - Get the polytope type of a given cell
5493 
5494   Not Collective
5495 
5496   Input Parameters:
5497 + dm   - The `DMPLEX` object
5498 - cell - The cell
5499 
5500   Output Parameter:
5501 . celltype - The polytope type of the cell
5502 
5503   Level: intermediate
5504 
5505 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPolytopeType`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`
5506 @*/
5507 PetscErrorCode DMPlexGetCellType(DM dm, PetscInt cell, DMPolytopeType *celltype)
5508 {
5509   DM_Plex *mesh = (DM_Plex *)dm->data;
5510   DMLabel  label;
5511   PetscInt ct;
5512 
5513   PetscFunctionBegin;
5514   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5515   PetscAssertPointer(celltype, 3);
5516   if (mesh->tr) {
5517     PetscCall(DMPlexTransformGetCellType(mesh->tr, cell, celltype));
5518   } else {
5519     PetscInt pStart, pEnd;
5520 
5521     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, NULL));
5522     if (!mesh->cellTypes) { /* XXX remove? optimize? */
5523       PetscCall(PetscSectionGetChart(mesh->coneSection, NULL, &pEnd));
5524       PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
5525       PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5526       for (PetscInt p = pStart; p < pEnd; p++) {
5527         PetscCall(DMLabelGetValue(label, p, &ct));
5528         mesh->cellTypes[p - pStart].value_as_uint8 = (DMPolytopeType)ct;
5529       }
5530     }
5531     *celltype = (DMPolytopeType)mesh->cellTypes[cell - pStart].value_as_uint8;
5532     if (PetscDefined(USE_DEBUG)) {
5533       PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5534       PetscCall(DMLabelGetValue(label, cell, &ct));
5535       PetscCheck(ct >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Cell %" PetscInt_FMT " has not been assigned a cell type", cell);
5536       PetscCheck(ct == (PetscInt)*celltype, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid cellType for %" PetscInt_FMT ": %d != %" PetscInt_FMT, cell, (int)*celltype, ct);
5537     }
5538   }
5539   PetscFunctionReturn(PETSC_SUCCESS);
5540 }
5541 
5542 /*@
5543   DMPlexSetCellType - Set the polytope type of a given cell
5544 
5545   Not Collective
5546 
5547   Input Parameters:
5548 + dm       - The `DMPLEX` object
5549 . cell     - The cell
5550 - celltype - The polytope type of the cell
5551 
5552   Level: advanced
5553 
5554   Note:
5555   By default, cell types will be automatically computed using `DMPlexComputeCellTypes()` before this function
5556   is executed. This function will override the computed type. However, if automatic classification will not succeed
5557   and a user wants to manually specify all types, the classification must be disabled by calling
5558   DMCreateLabel(dm, "celltype") before getting or setting any cell types.
5559 
5560 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexComputeCellTypes()`, `DMCreateLabel()`
5561 @*/
5562 PetscErrorCode DMPlexSetCellType(DM dm, PetscInt cell, DMPolytopeType celltype)
5563 {
5564   DM_Plex *mesh = (DM_Plex *)dm->data;
5565   DMLabel  label;
5566   PetscInt pStart, pEnd;
5567 
5568   PetscFunctionBegin;
5569   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5570   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
5571   PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5572   PetscCall(DMLabelSetValue(label, cell, celltype));
5573   if (!mesh->cellTypes) PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
5574   mesh->cellTypes[cell - pStart].value_as_uint8 = celltype;
5575   PetscFunctionReturn(PETSC_SUCCESS);
5576 }
5577 
5578 PetscErrorCode DMCreateCoordinateDM_Plex(DM dm, DM *cdm)
5579 {
5580   PetscSection section;
5581   PetscInt     maxHeight;
5582   const char  *prefix;
5583 
5584   PetscFunctionBegin;
5585   PetscCall(DMClone(dm, cdm));
5586   PetscCall(PetscObjectGetOptionsPrefix((PetscObject)dm, &prefix));
5587   PetscCall(PetscObjectSetOptionsPrefix((PetscObject)*cdm, prefix));
5588   PetscCall(PetscObjectAppendOptionsPrefix((PetscObject)*cdm, "cdm_"));
5589   PetscCall(DMPlexGetMaxProjectionHeight(dm, &maxHeight));
5590   PetscCall(DMPlexSetMaxProjectionHeight(*cdm, maxHeight));
5591   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
5592   PetscCall(DMSetLocalSection(*cdm, section));
5593   PetscCall(PetscSectionDestroy(&section));
5594 
5595   PetscCall(DMSetNumFields(*cdm, 1));
5596   PetscCall(DMCreateDS(*cdm));
5597   (*cdm)->cloneOpts = PETSC_TRUE;
5598   if (dm->setfromoptionscalled) PetscCall(DMSetFromOptions(*cdm));
5599   PetscFunctionReturn(PETSC_SUCCESS);
5600 }
5601 
5602 PetscErrorCode DMCreateCoordinateField_Plex(DM dm, DMField *field)
5603 {
5604   Vec coordsLocal, cellCoordsLocal;
5605   DM  coordsDM, cellCoordsDM;
5606 
5607   PetscFunctionBegin;
5608   *field = NULL;
5609   PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
5610   PetscCall(DMGetCoordinateDM(dm, &coordsDM));
5611   PetscCall(DMGetCellCoordinatesLocal(dm, &cellCoordsLocal));
5612   PetscCall(DMGetCellCoordinateDM(dm, &cellCoordsDM));
5613   if (coordsLocal && coordsDM) {
5614     if (cellCoordsLocal && cellCoordsDM) PetscCall(DMFieldCreateDSWithDG(coordsDM, cellCoordsDM, 0, coordsLocal, cellCoordsLocal, field));
5615     else PetscCall(DMFieldCreateDS(coordsDM, 0, coordsLocal, field));
5616   }
5617   PetscFunctionReturn(PETSC_SUCCESS);
5618 }
5619 
5620 /*@
5621   DMPlexGetConeSection - Return a section which describes the layout of cone data
5622 
5623   Not Collective
5624 
5625   Input Parameter:
5626 . dm - The `DMPLEX` object
5627 
5628   Output Parameter:
5629 . section - The `PetscSection` object
5630 
5631   Level: developer
5632 
5633 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSupportSection()`, `DMPlexGetCones()`, `DMPlexGetConeOrientations()`, `PetscSection`
5634 @*/
5635 PetscErrorCode DMPlexGetConeSection(DM dm, PetscSection *section)
5636 {
5637   DM_Plex *mesh = (DM_Plex *)dm->data;
5638 
5639   PetscFunctionBegin;
5640   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5641   if (section) *section = mesh->coneSection;
5642   PetscFunctionReturn(PETSC_SUCCESS);
5643 }
5644 
5645 /*@
5646   DMPlexGetSupportSection - Return a section which describes the layout of support data
5647 
5648   Not Collective
5649 
5650   Input Parameter:
5651 . dm - The `DMPLEX` object
5652 
5653   Output Parameter:
5654 . section - The `PetscSection` object
5655 
5656   Level: developer
5657 
5658 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `PetscSection`
5659 @*/
5660 PetscErrorCode DMPlexGetSupportSection(DM dm, PetscSection *section)
5661 {
5662   DM_Plex *mesh = (DM_Plex *)dm->data;
5663 
5664   PetscFunctionBegin;
5665   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5666   if (section) *section = mesh->supportSection;
5667   PetscFunctionReturn(PETSC_SUCCESS);
5668 }
5669 
5670 /*@C
5671   DMPlexGetCones - Return cone data
5672 
5673   Not Collective
5674 
5675   Input Parameter:
5676 . dm - The `DMPLEX` object
5677 
5678   Output Parameter:
5679 . cones - The cone for each point
5680 
5681   Level: developer
5682 
5683 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`
5684 @*/
5685 PetscErrorCode DMPlexGetCones(DM dm, PetscInt *cones[])
5686 {
5687   DM_Plex *mesh = (DM_Plex *)dm->data;
5688 
5689   PetscFunctionBegin;
5690   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5691   if (cones) *cones = mesh->cones;
5692   PetscFunctionReturn(PETSC_SUCCESS);
5693 }
5694 
5695 /*@C
5696   DMPlexGetConeOrientations - Return cone orientation data
5697 
5698   Not Collective
5699 
5700   Input Parameter:
5701 . dm - The `DMPLEX` object
5702 
5703   Output Parameter:
5704 . coneOrientations - The array of cone orientations for all points
5705 
5706   Level: developer
5707 
5708   Notes:
5709   The `PetscSection` returned by `DMPlexGetConeSection()` partitions coneOrientations into cone orientations of particular points as returned by `DMPlexGetConeOrientation()`.
5710 
5711   The meaning of coneOrientations values is detailed in `DMPlexGetConeOrientation()`.
5712 
5713 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `DMPlexGetConeOrientation()`, `PetscSection`
5714 @*/
5715 PetscErrorCode DMPlexGetConeOrientations(DM dm, PetscInt *coneOrientations[])
5716 {
5717   DM_Plex *mesh = (DM_Plex *)dm->data;
5718 
5719   PetscFunctionBegin;
5720   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5721   if (coneOrientations) *coneOrientations = mesh->coneOrientations;
5722   PetscFunctionReturn(PETSC_SUCCESS);
5723 }
5724 
5725 /******************************** FEM Support **********************************/
5726 
5727 PetscErrorCode DMPlexGetAllCells_Internal(DM plex, IS *cellIS)
5728 {
5729   PetscInt depth;
5730 
5731   PetscFunctionBegin;
5732   PetscCall(DMPlexGetDepth(plex, &depth));
5733   PetscCall(DMGetStratumIS(plex, "dim", depth, cellIS));
5734   if (!*cellIS) PetscCall(DMGetStratumIS(plex, "depth", depth, cellIS));
5735   PetscFunctionReturn(PETSC_SUCCESS);
5736 }
5737 
5738 PetscErrorCode DMPlexGetAllFaces_Internal(DM plex, IS *faceIS)
5739 {
5740   PetscInt depth;
5741 
5742   PetscFunctionBegin;
5743   PetscCall(DMPlexGetDepth(plex, &depth));
5744   PetscCall(DMGetStratumIS(plex, "dim", depth - 1, faceIS));
5745   if (!*faceIS) PetscCall(DMGetStratumIS(plex, "depth", depth - 1, faceIS));
5746   PetscFunctionReturn(PETSC_SUCCESS);
5747 }
5748 
5749 /*
5750  Returns number of components and tensor degree for the field.  For interpolated meshes, line should be a point
5751  representing a line in the section.
5752 */
5753 static PetscErrorCode PetscSectionFieldGetTensorDegree_Private(DM dm, PetscSection section, PetscInt field, PetscInt line, PetscInt *Nc, PetscInt *k, PetscBool *continuous, PetscBool *tensor)
5754 {
5755   PetscObject  obj;
5756   PetscClassId id;
5757   PetscFE      fe = NULL;
5758 
5759   PetscFunctionBeginHot;
5760   PetscCall(PetscSectionGetFieldComponents(section, field, Nc));
5761   PetscCall(DMGetField(dm, field, NULL, &obj));
5762   PetscCall(PetscObjectGetClassId(obj, &id));
5763   if (id == PETSCFE_CLASSID) fe = (PetscFE)obj;
5764 
5765   if (!fe) {
5766     /* Assume the full interpolated mesh is in the chart; lines in particular */
5767     /* An order k SEM disc has k-1 dofs on an edge */
5768     PetscCall(PetscSectionGetFieldDof(section, line, field, k));
5769     *k = *k / *Nc + 1;
5770   } else {
5771     PetscInt       dual_space_size, dim;
5772     PetscDualSpace dsp;
5773 
5774     PetscCall(DMGetDimension(dm, &dim));
5775     PetscCall(PetscFEGetDualSpace(fe, &dsp));
5776     PetscCall(PetscDualSpaceGetDimension(dsp, &dual_space_size));
5777     *k = (PetscInt)PetscCeilReal(PetscPowReal(dual_space_size / *Nc, 1.0 / dim)) - 1;
5778     PetscCall(PetscDualSpaceLagrangeGetContinuity(dsp, continuous));
5779     PetscCall(PetscDualSpaceLagrangeGetTensor(dsp, tensor));
5780   }
5781   PetscFunctionReturn(PETSC_SUCCESS);
5782 }
5783 
5784 static PetscErrorCode GetFieldSize_Private(PetscInt dim, PetscInt k, PetscBool tensor, PetscInt *dof)
5785 {
5786   PetscFunctionBeginHot;
5787   if (tensor) {
5788     *dof = PetscPowInt(k + 1, dim);
5789   } else {
5790     switch (dim) {
5791     case 1:
5792       *dof = k + 1;
5793       break;
5794     case 2:
5795       *dof = ((k + 1) * (k + 2)) / 2;
5796       break;
5797     case 3:
5798       *dof = ((k + 1) * (k + 2) * (k + 3)) / 6;
5799       break;
5800     default:
5801       *dof = 0;
5802     }
5803   }
5804   PetscFunctionReturn(PETSC_SUCCESS);
5805 }
5806 
5807 /*@
5808 
5809   DMPlexSetClosurePermutationTensor - Create a permutation from the default (BFS) point ordering in the closure, to a
5810   lexicographic ordering over the tensor product cell (i.e., line, quad, hex, etc.), and set this permutation in the
5811   section provided (or the section of the `DM`).
5812 
5813   Input Parameters:
5814 + dm      - The `DM`
5815 . point   - Either a cell (highest dim point) or an edge (dim 1 point), or `PETSC_DETERMINE`
5816 - section - The `PetscSection` to reorder, or `NULL` for the default section
5817 
5818   Example:
5819   A typical interpolated single-quad mesh might order points as
5820 .vb
5821   [c0, v1, v2, v3, v4, e5, e6, e7, e8]
5822 
5823   v4 -- e6 -- v3
5824   |           |
5825   e7    c0    e8
5826   |           |
5827   v1 -- e5 -- v2
5828 .ve
5829 
5830   (There is no significance to the ordering described here.)  The default section for a Q3 quad might typically assign
5831   dofs in the order of points, e.g.,
5832 .vb
5833     c0 -> [0,1,2,3]
5834     v1 -> [4]
5835     ...
5836     e5 -> [8, 9]
5837 .ve
5838 
5839   which corresponds to the dofs
5840 .vb
5841     6   10  11  7
5842     13  2   3   15
5843     12  0   1   14
5844     4   8   9   5
5845 .ve
5846 
5847   The closure in BFS ordering works through height strata (cells, edges, vertices) to produce the ordering
5848 .vb
5849   0 1 2 3 8 9 14 15 11 10 13 12 4 5 7 6
5850 .ve
5851 
5852   After calling DMPlexSetClosurePermutationTensor(), the closure will be ordered lexicographically,
5853 .vb
5854    4 8 9 5 12 0 1 14 13 2 3 15 6 10 11 7
5855 .ve
5856 
5857   Level: developer
5858 
5859   Notes:
5860   The point is used to determine the number of dofs/field on an edge. For SEM, this is related to the polynomial
5861   degree of the basis.
5862 
5863   This is required to run with libCEED.
5864 
5865 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMGetLocalSection()`, `PetscSectionSetClosurePermutation()`, `DMSetGlobalSection()`
5866 @*/
5867 PetscErrorCode DMPlexSetClosurePermutationTensor(DM dm, PetscInt point, PetscSection section)
5868 {
5869   DMLabel   label;
5870   PetscInt  dim, depth = -1, eStart = -1, Nf;
5871   PetscBool continuous = PETSC_TRUE, tensor = PETSC_TRUE;
5872 
5873   PetscFunctionBegin;
5874   PetscCall(DMGetDimension(dm, &dim));
5875   if (dim < 1) PetscFunctionReturn(PETSC_SUCCESS);
5876   if (point < 0) {
5877     PetscInt sStart, sEnd;
5878 
5879     PetscCall(DMPlexGetDepthStratum(dm, 1, &sStart, &sEnd));
5880     point = sEnd - sStart ? sStart : point;
5881   }
5882   PetscCall(DMPlexGetDepthLabel(dm, &label));
5883   if (point >= 0) PetscCall(DMLabelGetValue(label, point, &depth));
5884   if (!section) PetscCall(DMGetLocalSection(dm, &section));
5885   if (depth == 1) {
5886     eStart = point;
5887   } else if (depth == dim) {
5888     const PetscInt *cone;
5889 
5890     PetscCall(DMPlexGetCone(dm, point, &cone));
5891     if (dim == 2) eStart = cone[0];
5892     else if (dim == 3) {
5893       const PetscInt *cone2;
5894       PetscCall(DMPlexGetCone(dm, cone[0], &cone2));
5895       eStart = cone2[0];
5896     } 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);
5897   } 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);
5898 
5899   PetscCall(PetscSectionGetNumFields(section, &Nf));
5900   for (PetscInt d = 1; d <= dim; d++) {
5901     PetscInt  k, f, Nc, c, i, j, size = 0, offset = 0, foffset = 0;
5902     PetscInt *perm;
5903 
5904     for (f = 0; f < Nf; ++f) {
5905       PetscInt dof;
5906 
5907       PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
5908       PetscCheck(dim == 1 || tensor || !continuous, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Continuous field %" PetscInt_FMT " must have a tensor product discretization", f);
5909       if (!continuous && d < dim) continue;
5910       PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
5911       size += dof * Nc;
5912     }
5913     PetscCall(PetscMalloc1(size, &perm));
5914     for (f = 0; f < Nf; ++f) {
5915       switch (d) {
5916       case 1:
5917         PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
5918         if (!continuous && d < dim) continue;
5919         /*
5920          Original ordering is [ edge of length k-1; vtx0; vtx1 ]
5921          We want              [ vtx0; edge of length k-1; vtx1 ]
5922          */
5923         if (continuous) {
5924           for (c = 0; c < Nc; c++, offset++) perm[offset] = (k - 1) * Nc + c + foffset;
5925           for (i = 0; i < k - 1; i++)
5926             for (c = 0; c < Nc; c++, offset++) perm[offset] = i * Nc + c + foffset;
5927           for (c = 0; c < Nc; c++, offset++) perm[offset] = k * Nc + c + foffset;
5928           foffset = offset;
5929         } else {
5930           PetscInt dof;
5931 
5932           PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
5933           for (i = 0; i < dof * Nc; ++i, ++offset) perm[offset] = i + foffset;
5934           foffset = offset;
5935         }
5936         break;
5937       case 2:
5938         /* The original quad closure is oriented clockwise, {f, e_b, e_r, e_t, e_l, v_lb, v_rb, v_tr, v_tl} */
5939         PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
5940         if (!continuous && d < dim) continue;
5941         /* The SEM order is
5942 
5943          v_lb, {e_b}, v_rb,
5944          e^{(k-1)-i}_l, {f^{i*(k-1)}}, e^i_r,
5945          v_lt, reverse {e_t}, v_rt
5946          */
5947         if (continuous) {
5948           const PetscInt of   = 0;
5949           const PetscInt oeb  = of + PetscSqr(k - 1);
5950           const PetscInt oer  = oeb + (k - 1);
5951           const PetscInt oet  = oer + (k - 1);
5952           const PetscInt oel  = oet + (k - 1);
5953           const PetscInt ovlb = oel + (k - 1);
5954           const PetscInt ovrb = ovlb + 1;
5955           const PetscInt ovrt = ovrb + 1;
5956           const PetscInt ovlt = ovrt + 1;
5957           PetscInt       o;
5958 
5959           /* bottom */
5960           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlb * Nc + c + foffset;
5961           for (o = oeb; o < oer; ++o)
5962             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5963           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrb * Nc + c + foffset;
5964           /* middle */
5965           for (i = 0; i < k - 1; ++i) {
5966             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oel + (k - 2) - i) * Nc + c + foffset;
5967             for (o = of + (k - 1) * i; o < of + (k - 1) * (i + 1); ++o)
5968               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5969             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oer + i) * Nc + c + foffset;
5970           }
5971           /* top */
5972           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlt * Nc + c + foffset;
5973           for (o = oel - 1; o >= oet; --o)
5974             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5975           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrt * Nc + c + foffset;
5976           foffset = offset;
5977         } else {
5978           PetscInt dof;
5979 
5980           PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
5981           for (i = 0; i < dof * Nc; ++i, ++offset) perm[offset] = i + foffset;
5982           foffset = offset;
5983         }
5984         break;
5985       case 3:
5986         /* The original hex closure is
5987 
5988          {c,
5989          f_b, f_t, f_f, f_b, f_r, f_l,
5990          e_bl, e_bb, e_br, e_bf,  e_tf, e_tr, e_tb, e_tl,  e_rf, e_lf, e_lb, e_rb,
5991          v_blf, v_blb, v_brb, v_brf, v_tlf, v_trf, v_trb, v_tlb}
5992          */
5993         PetscCall(PetscSectionFieldGetTensorDegree_Private(dm, section, f, eStart, &Nc, &k, &continuous, &tensor));
5994         if (!continuous && d < dim) continue;
5995         /* The SEM order is
5996          Bottom Slice
5997          v_blf, {e^{(k-1)-n}_bf}, v_brf,
5998          e^{i}_bl, f^{n*(k-1)+(k-1)-i}_b, e^{(k-1)-i}_br,
5999          v_blb, {e_bb}, v_brb,
6000 
6001          Middle Slice (j)
6002          {e^{(k-1)-j}_lf}, {f^{j*(k-1)+n}_f}, e^j_rf,
6003          f^{i*(k-1)+j}_l, {c^{(j*(k-1) + i)*(k-1)+n}_t}, f^{j*(k-1)+i}_r,
6004          e^j_lb, {f^{j*(k-1)+(k-1)-n}_b}, e^{(k-1)-j}_rb,
6005 
6006          Top Slice
6007          v_tlf, {e_tf}, v_trf,
6008          e^{(k-1)-i}_tl, {f^{i*(k-1)}_t}, e^{i}_tr,
6009          v_tlb, {e^{(k-1)-n}_tb}, v_trb,
6010          */
6011         if (continuous) {
6012           const PetscInt oc    = 0;
6013           const PetscInt ofb   = oc + PetscSqr(k - 1) * (k - 1);
6014           const PetscInt oft   = ofb + PetscSqr(k - 1);
6015           const PetscInt off   = oft + PetscSqr(k - 1);
6016           const PetscInt ofk   = off + PetscSqr(k - 1);
6017           const PetscInt ofr   = ofk + PetscSqr(k - 1);
6018           const PetscInt ofl   = ofr + PetscSqr(k - 1);
6019           const PetscInt oebl  = ofl + PetscSqr(k - 1);
6020           const PetscInt oebb  = oebl + (k - 1);
6021           const PetscInt oebr  = oebb + (k - 1);
6022           const PetscInt oebf  = oebr + (k - 1);
6023           const PetscInt oetf  = oebf + (k - 1);
6024           const PetscInt oetr  = oetf + (k - 1);
6025           const PetscInt oetb  = oetr + (k - 1);
6026           const PetscInt oetl  = oetb + (k - 1);
6027           const PetscInt oerf  = oetl + (k - 1);
6028           const PetscInt oelf  = oerf + (k - 1);
6029           const PetscInt oelb  = oelf + (k - 1);
6030           const PetscInt oerb  = oelb + (k - 1);
6031           const PetscInt ovblf = oerb + (k - 1);
6032           const PetscInt ovblb = ovblf + 1;
6033           const PetscInt ovbrb = ovblb + 1;
6034           const PetscInt ovbrf = ovbrb + 1;
6035           const PetscInt ovtlf = ovbrf + 1;
6036           const PetscInt ovtrf = ovtlf + 1;
6037           const PetscInt ovtrb = ovtrf + 1;
6038           const PetscInt ovtlb = ovtrb + 1;
6039           PetscInt       o, n;
6040 
6041           /* Bottom Slice */
6042           /*   bottom */
6043           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblf * Nc + c + foffset;
6044           for (o = oetf - 1; o >= oebf; --o)
6045             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6046           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrf * Nc + c + foffset;
6047           /*   middle */
6048           for (i = 0; i < k - 1; ++i) {
6049             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebl + i) * Nc + c + foffset;
6050             for (n = 0; n < k - 1; ++n) {
6051               o = ofb + n * (k - 1) + i;
6052               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6053             }
6054             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebr + (k - 2) - i) * Nc + c + foffset;
6055           }
6056           /*   top */
6057           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblb * Nc + c + foffset;
6058           for (o = oebb; o < oebr; ++o)
6059             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6060           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrb * Nc + c + foffset;
6061 
6062           /* Middle Slice */
6063           for (j = 0; j < k - 1; ++j) {
6064             /*   bottom */
6065             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelf + (k - 2) - j) * Nc + c + foffset;
6066             for (o = off + j * (k - 1); o < off + (j + 1) * (k - 1); ++o)
6067               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6068             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerf + j) * Nc + c + foffset;
6069             /*   middle */
6070             for (i = 0; i < k - 1; ++i) {
6071               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofl + i * (k - 1) + j) * Nc + c + foffset;
6072               for (n = 0; n < k - 1; ++n)
6073                 for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oc + (j * (k - 1) + i) * (k - 1) + n) * Nc + c + foffset;
6074               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofr + j * (k - 1) + i) * Nc + c + foffset;
6075             }
6076             /*   top */
6077             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelb + j) * Nc + c + foffset;
6078             for (o = ofk + j * (k - 1) + (k - 2); o >= ofk + j * (k - 1); --o)
6079               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6080             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerb + (k - 2) - j) * Nc + c + foffset;
6081           }
6082 
6083           /* Top Slice */
6084           /*   bottom */
6085           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlf * Nc + c + foffset;
6086           for (o = oetf; o < oetr; ++o)
6087             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6088           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrf * Nc + c + foffset;
6089           /*   middle */
6090           for (i = 0; i < k - 1; ++i) {
6091             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetl + (k - 2) - i) * Nc + c + foffset;
6092             for (n = 0; n < k - 1; ++n)
6093               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oft + i * (k - 1) + n) * Nc + c + foffset;
6094             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetr + i) * Nc + c + foffset;
6095           }
6096           /*   top */
6097           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlb * Nc + c + foffset;
6098           for (o = oetl - 1; o >= oetb; --o)
6099             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
6100           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrb * Nc + c + foffset;
6101 
6102           foffset = offset;
6103         } else {
6104           PetscInt dof;
6105 
6106           PetscCall(GetFieldSize_Private(d, k, tensor, &dof));
6107           for (i = 0; i < dof * Nc; ++i, ++offset) perm[offset] = i + foffset;
6108           foffset = offset;
6109         }
6110         break;
6111       default:
6112         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "No spectral ordering for dimension %" PetscInt_FMT, d);
6113       }
6114     }
6115     PetscCheck(offset == size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Number of permutation entries %" PetscInt_FMT " != %" PetscInt_FMT, offset, size);
6116     /* Check permutation */
6117     {
6118       PetscInt *check;
6119 
6120       PetscCall(PetscMalloc1(size, &check));
6121       for (i = 0; i < size; ++i) {
6122         check[i] = -1;
6123         PetscCheck(perm[i] >= 0 && perm[i] < size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid permutation index p[%" PetscInt_FMT "] = %" PetscInt_FMT, i, perm[i]);
6124       }
6125       for (i = 0; i < size; ++i) check[perm[i]] = i;
6126       for (i = 0; i < size; ++i) PetscCheck(check[i] >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Missing permutation index %" PetscInt_FMT, i);
6127       PetscCall(PetscFree(check));
6128     }
6129     PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size, PETSC_OWN_POINTER, perm));
6130     if (d == dim) { // Add permutation for localized (in case this is a coordinate DM)
6131       PetscInt *loc_perm;
6132       PetscCall(PetscMalloc1(size * 2, &loc_perm));
6133       for (PetscInt i = 0; i < size; i++) {
6134         loc_perm[i]        = perm[i];
6135         loc_perm[size + i] = size + perm[i];
6136       }
6137       PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size * 2, PETSC_OWN_POINTER, loc_perm));
6138     }
6139   }
6140   PetscFunctionReturn(PETSC_SUCCESS);
6141 }
6142 
6143 PetscErrorCode DMPlexGetPointDualSpaceFEM(DM dm, PetscInt point, PetscInt field, PetscDualSpace *dspace)
6144 {
6145   PetscDS  prob;
6146   PetscInt depth, Nf, h;
6147   DMLabel  label;
6148 
6149   PetscFunctionBeginHot;
6150   PetscCall(DMGetDS(dm, &prob));
6151   Nf      = prob->Nf;
6152   label   = dm->depthLabel;
6153   *dspace = NULL;
6154   if (field < Nf) {
6155     PetscObject disc = prob->disc[field];
6156 
6157     if (disc->classid == PETSCFE_CLASSID) {
6158       PetscDualSpace dsp;
6159 
6160       PetscCall(PetscFEGetDualSpace((PetscFE)disc, &dsp));
6161       PetscCall(DMLabelGetNumValues(label, &depth));
6162       PetscCall(DMLabelGetValue(label, point, &h));
6163       h = depth - 1 - h;
6164       if (h) {
6165         PetscCall(PetscDualSpaceGetHeightSubspace(dsp, h, dspace));
6166       } else {
6167         *dspace = dsp;
6168       }
6169     }
6170   }
6171   PetscFunctionReturn(PETSC_SUCCESS);
6172 }
6173 
6174 static inline PetscErrorCode DMPlexVecGetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6175 {
6176   PetscScalar       *array;
6177   const PetscScalar *vArray;
6178   const PetscInt    *cone, *coneO;
6179   PetscInt           pStart, pEnd, p, numPoints, size = 0, offset = 0;
6180 
6181   PetscFunctionBeginHot;
6182   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6183   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
6184   PetscCall(DMPlexGetCone(dm, point, &cone));
6185   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
6186   if (!values || !*values) {
6187     if ((point >= pStart) && (point < pEnd)) {
6188       PetscInt dof;
6189 
6190       PetscCall(PetscSectionGetDof(section, point, &dof));
6191       size += dof;
6192     }
6193     for (p = 0; p < numPoints; ++p) {
6194       const PetscInt cp = cone[p];
6195       PetscInt       dof;
6196 
6197       if ((cp < pStart) || (cp >= pEnd)) continue;
6198       PetscCall(PetscSectionGetDof(section, cp, &dof));
6199       size += dof;
6200     }
6201     if (!values) {
6202       if (csize) *csize = size;
6203       PetscFunctionReturn(PETSC_SUCCESS);
6204     }
6205     PetscCall(DMGetWorkArray(dm, size, MPIU_SCALAR, &array));
6206   } else {
6207     array = *values;
6208   }
6209   size = 0;
6210   PetscCall(VecGetArrayRead(v, &vArray));
6211   if ((point >= pStart) && (point < pEnd)) {
6212     PetscInt           dof, off, d;
6213     const PetscScalar *varr;
6214 
6215     PetscCall(PetscSectionGetDof(section, point, &dof));
6216     PetscCall(PetscSectionGetOffset(section, point, &off));
6217     varr = PetscSafePointerPlusOffset(vArray, off);
6218     for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
6219     size += dof;
6220   }
6221   for (p = 0; p < numPoints; ++p) {
6222     const PetscInt     cp = cone[p];
6223     PetscInt           o  = coneO[p];
6224     PetscInt           dof, off, d;
6225     const PetscScalar *varr;
6226 
6227     if ((cp < pStart) || (cp >= pEnd)) continue;
6228     PetscCall(PetscSectionGetDof(section, cp, &dof));
6229     PetscCall(PetscSectionGetOffset(section, cp, &off));
6230     varr = PetscSafePointerPlusOffset(vArray, off);
6231     if (o >= 0) {
6232       for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
6233     } else {
6234       for (d = dof - 1; d >= 0; --d, ++offset) array[offset] = varr[d];
6235     }
6236     size += dof;
6237   }
6238   PetscCall(VecRestoreArrayRead(v, &vArray));
6239   if (!*values) {
6240     if (csize) *csize = size;
6241     *values = array;
6242   } else {
6243     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
6244     *csize = size;
6245   }
6246   PetscFunctionReturn(PETSC_SUCCESS);
6247 }
6248 
6249 /* Compress out points not in the section */
6250 static inline PetscErrorCode CompressPoints_Private(PetscSection section, PetscInt *numPoints, PetscInt points[])
6251 {
6252   const PetscInt np = *numPoints;
6253   PetscInt       pStart, pEnd, p, q;
6254 
6255   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6256   for (p = 0, q = 0; p < np; ++p) {
6257     const PetscInt r = points[p * 2];
6258     if ((r >= pStart) && (r < pEnd)) {
6259       points[q * 2]     = r;
6260       points[q * 2 + 1] = points[p * 2 + 1];
6261       ++q;
6262     }
6263   }
6264   *numPoints = q;
6265   return PETSC_SUCCESS;
6266 }
6267 
6268 /* Compressed closure does not apply closure permutation */
6269 PetscErrorCode DMPlexGetCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt ornt, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
6270 {
6271   const PetscInt *cla = NULL;
6272   PetscInt        np, *pts = NULL;
6273 
6274   PetscFunctionBeginHot;
6275   PetscCall(PetscSectionGetClosureIndex(section, (PetscObject)dm, clSec, clPoints));
6276   if (!ornt && *clPoints) {
6277     PetscInt dof, off;
6278 
6279     PetscCall(PetscSectionGetDof(*clSec, point, &dof));
6280     PetscCall(PetscSectionGetOffset(*clSec, point, &off));
6281     PetscCall(ISGetIndices(*clPoints, &cla));
6282     np  = dof / 2;
6283     pts = PetscSafePointerPlusOffset((PetscInt *)cla, off);
6284   } else {
6285     PetscCall(DMPlexGetTransitiveClosure_Internal(dm, point, ornt, PETSC_TRUE, &np, &pts));
6286     PetscCall(CompressPoints_Private(section, &np, pts));
6287   }
6288   *numPoints = np;
6289   *points    = pts;
6290   *clp       = cla;
6291   PetscFunctionReturn(PETSC_SUCCESS);
6292 }
6293 
6294 PetscErrorCode DMPlexRestoreCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
6295 {
6296   PetscFunctionBeginHot;
6297   if (!*clPoints) {
6298     PetscCall(DMPlexRestoreTransitiveClosure(dm, point, PETSC_TRUE, numPoints, points));
6299   } else {
6300     PetscCall(ISRestoreIndices(*clPoints, clp));
6301   }
6302   *numPoints = 0;
6303   *points    = NULL;
6304   *clSec     = NULL;
6305   *clPoints  = NULL;
6306   *clp       = NULL;
6307   PetscFunctionReturn(PETSC_SUCCESS);
6308 }
6309 
6310 static inline PetscErrorCode DMPlexVecGetClosure_Static(DM dm, PetscSection section, PetscInt numPoints, const PetscInt points[], const PetscInt clperm[], const PetscScalar vArray[], PetscInt *size, PetscScalar array[])
6311 {
6312   PetscInt            offset = 0, p;
6313   const PetscInt    **perms  = NULL;
6314   const PetscScalar **flips  = NULL;
6315 
6316   PetscFunctionBeginHot;
6317   *size = 0;
6318   PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
6319   for (p = 0; p < numPoints; p++) {
6320     const PetscInt     point = points[2 * p];
6321     const PetscInt    *perm  = perms ? perms[p] : NULL;
6322     const PetscScalar *flip  = flips ? flips[p] : NULL;
6323     PetscInt           dof, off, d;
6324     const PetscScalar *varr;
6325 
6326     PetscCall(PetscSectionGetDof(section, point, &dof));
6327     PetscCall(PetscSectionGetOffset(section, point, &off));
6328     varr = PetscSafePointerPlusOffset(vArray, off);
6329     if (clperm) {
6330       if (perm) {
6331         for (d = 0; d < dof; d++) array[clperm[offset + perm[d]]] = varr[d];
6332       } else {
6333         for (d = 0; d < dof; d++) array[clperm[offset + d]] = varr[d];
6334       }
6335       if (flip) {
6336         for (d = 0; d < dof; d++) array[clperm[offset + d]] *= flip[d];
6337       }
6338     } else {
6339       if (perm) {
6340         for (d = 0; d < dof; d++) array[offset + perm[d]] = varr[d];
6341       } else {
6342         for (d = 0; d < dof; d++) array[offset + d] = varr[d];
6343       }
6344       if (flip) {
6345         for (d = 0; d < dof; d++) array[offset + d] *= flip[d];
6346       }
6347     }
6348     offset += dof;
6349   }
6350   PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
6351   *size = offset;
6352   PetscFunctionReturn(PETSC_SUCCESS);
6353 }
6354 
6355 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[])
6356 {
6357   PetscInt offset = 0, f;
6358 
6359   PetscFunctionBeginHot;
6360   *size = 0;
6361   for (f = 0; f < numFields; ++f) {
6362     PetscInt            p;
6363     const PetscInt    **perms = NULL;
6364     const PetscScalar **flips = NULL;
6365 
6366     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6367     for (p = 0; p < numPoints; p++) {
6368       const PetscInt     point = points[2 * p];
6369       PetscInt           fdof, foff, b;
6370       const PetscScalar *varr;
6371       const PetscInt    *perm = perms ? perms[p] : NULL;
6372       const PetscScalar *flip = flips ? flips[p] : NULL;
6373 
6374       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6375       PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6376       varr = &vArray[foff];
6377       if (clperm) {
6378         if (perm) {
6379           for (b = 0; b < fdof; b++) array[clperm[offset + perm[b]]] = varr[b];
6380         } else {
6381           for (b = 0; b < fdof; b++) array[clperm[offset + b]] = varr[b];
6382         }
6383         if (flip) {
6384           for (b = 0; b < fdof; b++) array[clperm[offset + b]] *= flip[b];
6385         }
6386       } else {
6387         if (perm) {
6388           for (b = 0; b < fdof; b++) array[offset + perm[b]] = varr[b];
6389         } else {
6390           for (b = 0; b < fdof; b++) array[offset + b] = varr[b];
6391         }
6392         if (flip) {
6393           for (b = 0; b < fdof; b++) array[offset + b] *= flip[b];
6394         }
6395       }
6396       offset += fdof;
6397     }
6398     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6399   }
6400   *size = offset;
6401   PetscFunctionReturn(PETSC_SUCCESS);
6402 }
6403 
6404 PetscErrorCode DMPlexVecGetOrientedClosure_Internal(DM dm, PetscSection section, PetscBool useClPerm, Vec v, PetscInt point, PetscInt ornt, PetscInt *csize, PetscScalar *values[])
6405 {
6406   PetscSection    clSection;
6407   IS              clPoints;
6408   PetscInt       *points = NULL;
6409   const PetscInt *clp, *perm = NULL;
6410   PetscInt        depth, numFields, numPoints, asize;
6411 
6412   PetscFunctionBeginHot;
6413   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6414   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6415   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6416   PetscValidHeaderSpecific(v, VEC_CLASSID, 4);
6417   PetscCall(DMPlexGetDepth(dm, &depth));
6418   PetscCall(PetscSectionGetNumFields(section, &numFields));
6419   if (depth == 1 && numFields < 2) {
6420     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6421     PetscFunctionReturn(PETSC_SUCCESS);
6422   }
6423   /* Get points */
6424   PetscCall(DMPlexGetCompressedClosure(dm, section, point, ornt, &numPoints, &points, &clSection, &clPoints, &clp));
6425   /* Get sizes */
6426   asize = 0;
6427   for (PetscInt p = 0; p < numPoints * 2; p += 2) {
6428     PetscInt dof;
6429     PetscCall(PetscSectionGetDof(section, points[p], &dof));
6430     asize += dof;
6431   }
6432   if (values) {
6433     const PetscScalar *vArray;
6434     PetscInt           size;
6435 
6436     if (*values) {
6437       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);
6438     } else PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, values));
6439     if (useClPerm) PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, asize, &perm));
6440     PetscCall(VecGetArrayRead(v, &vArray));
6441     /* Get values */
6442     if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, numPoints, points, numFields, perm, vArray, &size, *values));
6443     else PetscCall(DMPlexVecGetClosure_Static(dm, section, numPoints, points, perm, vArray, &size, *values));
6444     PetscCheck(asize == size, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Section size %" PetscInt_FMT " does not match Vec closure size %" PetscInt_FMT, asize, size);
6445     /* Cleanup array */
6446     PetscCall(VecRestoreArrayRead(v, &vArray));
6447   }
6448   if (csize) *csize = asize;
6449   /* Cleanup points */
6450   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6451   PetscFunctionReturn(PETSC_SUCCESS);
6452 }
6453 
6454 /*@C
6455   DMPlexVecGetClosure - Get an array of the values on the closure of 'point'
6456 
6457   Not collective
6458 
6459   Input Parameters:
6460 + dm      - The `DM`
6461 . section - The section describing the layout in `v`, or `NULL` to use the default section
6462 . v       - The local vector
6463 - point   - The point in the `DM`
6464 
6465   Input/Output Parameters:
6466 + csize  - The size of the input values array, or `NULL`; on output the number of values in the closure
6467 - values - An array to use for the values, or `NULL` to have it allocated automatically;
6468            if the user provided `NULL`, it is a borrowed array and should not be freed
6469 
6470   Level: intermediate
6471 
6472   Notes:
6473   `DMPlexVecGetClosure()`/`DMPlexVecRestoreClosure()` only allocates the values array if it set to `NULL` in the
6474   calling function. This is because `DMPlexVecGetClosure()` is typically called in the inner loop of a `Vec` or `Mat`
6475   assembly function, and a user may already have allocated storage for this operation.
6476 
6477   A typical use could be
6478 .vb
6479    values = NULL;
6480    PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
6481    for (cl = 0; cl < clSize; ++cl) {
6482      <Compute on closure>
6483    }
6484    PetscCall(DMPlexVecRestoreClosure(dm, NULL, v, p, &clSize, &values));
6485 .ve
6486   or
6487 .vb
6488    PetscMalloc1(clMaxSize, &values);
6489    for (p = pStart; p < pEnd; ++p) {
6490      clSize = clMaxSize;
6491      PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
6492      for (cl = 0; cl < clSize; ++cl) {
6493        <Compute on closure>
6494      }
6495    }
6496    PetscFree(values);
6497 .ve
6498 
6499   Fortran Notes:
6500   The `csize` argument is not present in the Fortran binding since it is internal to the array.
6501 
6502 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecRestoreClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6503 @*/
6504 PetscErrorCode DMPlexVecGetClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6505 {
6506   PetscFunctionBeginHot;
6507   PetscCall(DMPlexVecGetOrientedClosure_Internal(dm, section, PETSC_TRUE, v, point, 0, csize, values));
6508   PetscFunctionReturn(PETSC_SUCCESS);
6509 }
6510 
6511 PetscErrorCode DMPlexVecGetClosureAtDepth_Internal(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt depth, PetscInt *csize, PetscScalar *values[])
6512 {
6513   DMLabel            depthLabel;
6514   PetscSection       clSection;
6515   IS                 clPoints;
6516   PetscScalar       *array;
6517   const PetscScalar *vArray;
6518   PetscInt          *points = NULL;
6519   const PetscInt    *clp, *perm = NULL;
6520   PetscInt           mdepth, numFields, numPoints, Np = 0, p, clsize, size;
6521 
6522   PetscFunctionBeginHot;
6523   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6524   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6525   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6526   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6527   PetscCall(DMPlexGetDepth(dm, &mdepth));
6528   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
6529   PetscCall(PetscSectionGetNumFields(section, &numFields));
6530   if (mdepth == 1 && numFields < 2) {
6531     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6532     PetscFunctionReturn(PETSC_SUCCESS);
6533   }
6534   /* Get points */
6535   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
6536   for (clsize = 0, p = 0; p < Np; p++) {
6537     PetscInt dof;
6538     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
6539     clsize += dof;
6540   }
6541   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &perm));
6542   /* Filter points */
6543   for (p = 0; p < numPoints * 2; p += 2) {
6544     PetscInt dep;
6545 
6546     PetscCall(DMLabelGetValue(depthLabel, points[p], &dep));
6547     if (dep != depth) continue;
6548     points[Np * 2 + 0] = points[p];
6549     points[Np * 2 + 1] = points[p + 1];
6550     ++Np;
6551   }
6552   /* Get array */
6553   if (!values || !*values) {
6554     PetscInt asize = 0, dof;
6555 
6556     for (p = 0; p < Np * 2; p += 2) {
6557       PetscCall(PetscSectionGetDof(section, points[p], &dof));
6558       asize += dof;
6559     }
6560     if (!values) {
6561       PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6562       if (csize) *csize = asize;
6563       PetscFunctionReturn(PETSC_SUCCESS);
6564     }
6565     PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, &array));
6566   } else {
6567     array = *values;
6568   }
6569   PetscCall(VecGetArrayRead(v, &vArray));
6570   /* Get values */
6571   if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, Np, points, numFields, perm, vArray, &size, array));
6572   else PetscCall(DMPlexVecGetClosure_Static(dm, section, Np, points, perm, vArray, &size, array));
6573   /* Cleanup points */
6574   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6575   /* Cleanup array */
6576   PetscCall(VecRestoreArrayRead(v, &vArray));
6577   if (!*values) {
6578     if (csize) *csize = size;
6579     *values = array;
6580   } else {
6581     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
6582     *csize = size;
6583   }
6584   PetscFunctionReturn(PETSC_SUCCESS);
6585 }
6586 
6587 /*@C
6588   DMPlexVecRestoreClosure - Restore the array of the values on the closure of 'point'
6589 
6590   Not collective
6591 
6592   Input Parameters:
6593 + dm      - The `DM`
6594 . section - The section describing the layout in `v`, or `NULL` to use the default section
6595 . v       - The local vector
6596 . point   - The point in the `DM`
6597 . csize   - The number of values in the closure, or `NULL`
6598 - values  - The array of values, which is a borrowed array and should not be freed
6599 
6600   Level: intermediate
6601 
6602   Note:
6603   The array values are discarded and not copied back into `v`. In order to copy values back to `v`, use `DMPlexVecSetClosure()`
6604 
6605   Fortran Notes:
6606   The `csize` argument is not present in the Fortran binding since it is internal to the array.
6607 
6608 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6609 @*/
6610 PetscErrorCode DMPlexVecRestoreClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6611 {
6612   PetscInt size = 0;
6613 
6614   PetscFunctionBegin;
6615   /* Should work without recalculating size */
6616   PetscCall(DMRestoreWorkArray(dm, size, MPIU_SCALAR, (void *)values));
6617   *values = NULL;
6618   PetscFunctionReturn(PETSC_SUCCESS);
6619 }
6620 
6621 static inline void add(PetscScalar *x, PetscScalar y)
6622 {
6623   *x += y;
6624 }
6625 static inline void insert(PetscScalar *x, PetscScalar y)
6626 {
6627   *x = y;
6628 }
6629 
6630 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[])
6631 {
6632   PetscInt        cdof;  /* The number of constraints on this point */
6633   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6634   PetscScalar    *a;
6635   PetscInt        off, cind = 0, k;
6636 
6637   PetscFunctionBegin;
6638   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6639   PetscCall(PetscSectionGetOffset(section, point, &off));
6640   a = &array[off];
6641   if (!cdof || setBC) {
6642     if (clperm) {
6643       if (perm) {
6644         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6645       } else {
6646         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6647       }
6648     } else {
6649       if (perm) {
6650         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6651       } else {
6652         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6653       }
6654     }
6655   } else {
6656     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6657     if (clperm) {
6658       if (perm) {
6659         for (k = 0; k < dof; ++k) {
6660           if ((cind < cdof) && (k == cdofs[cind])) {
6661             ++cind;
6662             continue;
6663           }
6664           fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6665         }
6666       } else {
6667         for (k = 0; k < dof; ++k) {
6668           if ((cind < cdof) && (k == cdofs[cind])) {
6669             ++cind;
6670             continue;
6671           }
6672           fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6673         }
6674       }
6675     } else {
6676       if (perm) {
6677         for (k = 0; k < dof; ++k) {
6678           if ((cind < cdof) && (k == cdofs[cind])) {
6679             ++cind;
6680             continue;
6681           }
6682           fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6683         }
6684       } else {
6685         for (k = 0; k < dof; ++k) {
6686           if ((cind < cdof) && (k == cdofs[cind])) {
6687             ++cind;
6688             continue;
6689           }
6690           fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6691         }
6692       }
6693     }
6694   }
6695   PetscFunctionReturn(PETSC_SUCCESS);
6696 }
6697 
6698 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[])
6699 {
6700   PetscInt        cdof;  /* The number of constraints on this point */
6701   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6702   PetscScalar    *a;
6703   PetscInt        off, cind = 0, k;
6704 
6705   PetscFunctionBegin;
6706   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6707   PetscCall(PetscSectionGetOffset(section, point, &off));
6708   a = &array[off];
6709   if (cdof) {
6710     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6711     if (clperm) {
6712       if (perm) {
6713         for (k = 0; k < dof; ++k) {
6714           if ((cind < cdof) && (k == cdofs[cind])) {
6715             fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6716             cind++;
6717           }
6718         }
6719       } else {
6720         for (k = 0; k < dof; ++k) {
6721           if ((cind < cdof) && (k == cdofs[cind])) {
6722             fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6723             cind++;
6724           }
6725         }
6726       }
6727     } else {
6728       if (perm) {
6729         for (k = 0; k < dof; ++k) {
6730           if ((cind < cdof) && (k == cdofs[cind])) {
6731             fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6732             cind++;
6733           }
6734         }
6735       } else {
6736         for (k = 0; k < dof; ++k) {
6737           if ((cind < cdof) && (k == cdofs[cind])) {
6738             fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6739             cind++;
6740           }
6741         }
6742       }
6743     }
6744   }
6745   PetscFunctionReturn(PETSC_SUCCESS);
6746 }
6747 
6748 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[])
6749 {
6750   PetscScalar    *a;
6751   PetscInt        fdof, foff, fcdof, foffset = *offset;
6752   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6753   PetscInt        cind = 0, b;
6754 
6755   PetscFunctionBegin;
6756   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6757   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6758   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6759   a = &array[foff];
6760   if (!fcdof || setBC) {
6761     if (clperm) {
6762       if (perm) {
6763         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6764       } else {
6765         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6766       }
6767     } else {
6768       if (perm) {
6769         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6770       } else {
6771         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6772       }
6773     }
6774   } else {
6775     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6776     if (clperm) {
6777       if (perm) {
6778         for (b = 0; b < fdof; b++) {
6779           if ((cind < fcdof) && (b == fcdofs[cind])) {
6780             ++cind;
6781             continue;
6782           }
6783           fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6784         }
6785       } else {
6786         for (b = 0; b < fdof; b++) {
6787           if ((cind < fcdof) && (b == fcdofs[cind])) {
6788             ++cind;
6789             continue;
6790           }
6791           fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6792         }
6793       }
6794     } else {
6795       if (perm) {
6796         for (b = 0; b < fdof; b++) {
6797           if ((cind < fcdof) && (b == fcdofs[cind])) {
6798             ++cind;
6799             continue;
6800           }
6801           fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6802         }
6803       } else {
6804         for (b = 0; b < fdof; b++) {
6805           if ((cind < fcdof) && (b == fcdofs[cind])) {
6806             ++cind;
6807             continue;
6808           }
6809           fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6810         }
6811       }
6812     }
6813   }
6814   *offset += fdof;
6815   PetscFunctionReturn(PETSC_SUCCESS);
6816 }
6817 
6818 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[])
6819 {
6820   PetscScalar    *a;
6821   PetscInt        fdof, foff, fcdof, foffset = *offset;
6822   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6823   PetscInt        Nc, cind = 0, ncind = 0, b;
6824   PetscBool       ncSet, fcSet;
6825 
6826   PetscFunctionBegin;
6827   PetscCall(PetscSectionGetFieldComponents(section, f, &Nc));
6828   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6829   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6830   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6831   a = &array[foff];
6832   if (fcdof) {
6833     /* We just override fcdof and fcdofs with Ncc and comps */
6834     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6835     if (clperm) {
6836       if (perm) {
6837         if (comps) {
6838           for (b = 0; b < fdof; b++) {
6839             ncSet = fcSet = PETSC_FALSE;
6840             if (b % Nc == comps[ncind]) {
6841               ncind = (ncind + 1) % Ncc;
6842               ncSet = PETSC_TRUE;
6843             }
6844             if ((cind < fcdof) && (b == fcdofs[cind])) {
6845               ++cind;
6846               fcSet = PETSC_TRUE;
6847             }
6848             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6849           }
6850         } else {
6851           for (b = 0; b < fdof; b++) {
6852             if ((cind < fcdof) && (b == fcdofs[cind])) {
6853               fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6854               ++cind;
6855             }
6856           }
6857         }
6858       } else {
6859         if (comps) {
6860           for (b = 0; b < fdof; b++) {
6861             ncSet = fcSet = PETSC_FALSE;
6862             if (b % Nc == comps[ncind]) {
6863               ncind = (ncind + 1) % Ncc;
6864               ncSet = PETSC_TRUE;
6865             }
6866             if ((cind < fcdof) && (b == fcdofs[cind])) {
6867               ++cind;
6868               fcSet = PETSC_TRUE;
6869             }
6870             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6871           }
6872         } else {
6873           for (b = 0; b < fdof; b++) {
6874             if ((cind < fcdof) && (b == fcdofs[cind])) {
6875               fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6876               ++cind;
6877             }
6878           }
6879         }
6880       }
6881     } else {
6882       if (perm) {
6883         if (comps) {
6884           for (b = 0; b < fdof; b++) {
6885             ncSet = fcSet = PETSC_FALSE;
6886             if (b % Nc == comps[ncind]) {
6887               ncind = (ncind + 1) % Ncc;
6888               ncSet = PETSC_TRUE;
6889             }
6890             if ((cind < fcdof) && (b == fcdofs[cind])) {
6891               ++cind;
6892               fcSet = PETSC_TRUE;
6893             }
6894             if (ncSet && fcSet) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6895           }
6896         } else {
6897           for (b = 0; b < fdof; b++) {
6898             if ((cind < fcdof) && (b == fcdofs[cind])) {
6899               fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6900               ++cind;
6901             }
6902           }
6903         }
6904       } else {
6905         if (comps) {
6906           for (b = 0; b < fdof; b++) {
6907             ncSet = fcSet = PETSC_FALSE;
6908             if (b % Nc == comps[ncind]) {
6909               ncind = (ncind + 1) % Ncc;
6910               ncSet = PETSC_TRUE;
6911             }
6912             if ((cind < fcdof) && (b == fcdofs[cind])) {
6913               ++cind;
6914               fcSet = PETSC_TRUE;
6915             }
6916             if (ncSet && fcSet) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6917           }
6918         } else {
6919           for (b = 0; b < fdof; b++) {
6920             if ((cind < fcdof) && (b == fcdofs[cind])) {
6921               fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6922               ++cind;
6923             }
6924           }
6925         }
6926       }
6927     }
6928   }
6929   *offset += fdof;
6930   PetscFunctionReturn(PETSC_SUCCESS);
6931 }
6932 
6933 static inline PetscErrorCode DMPlexVecSetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6934 {
6935   PetscScalar    *array;
6936   const PetscInt *cone, *coneO;
6937   PetscInt        pStart, pEnd, p, numPoints, off, dof;
6938 
6939   PetscFunctionBeginHot;
6940   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6941   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
6942   PetscCall(DMPlexGetCone(dm, point, &cone));
6943   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
6944   PetscCall(VecGetArray(v, &array));
6945   for (p = 0, off = 0; p <= numPoints; ++p, off += dof) {
6946     const PetscInt cp = !p ? point : cone[p - 1];
6947     const PetscInt o  = !p ? 0 : coneO[p - 1];
6948 
6949     if ((cp < pStart) || (cp >= pEnd)) {
6950       dof = 0;
6951       continue;
6952     }
6953     PetscCall(PetscSectionGetDof(section, cp, &dof));
6954     /* ADD_VALUES */
6955     {
6956       const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6957       PetscScalar    *a;
6958       PetscInt        cdof, coff, cind = 0, k;
6959 
6960       PetscCall(PetscSectionGetConstraintDof(section, cp, &cdof));
6961       PetscCall(PetscSectionGetOffset(section, cp, &coff));
6962       a = &array[coff];
6963       if (!cdof) {
6964         if (o >= 0) {
6965           for (k = 0; k < dof; ++k) a[k] += values[off + k];
6966         } else {
6967           for (k = 0; k < dof; ++k) a[k] += values[off + dof - k - 1];
6968         }
6969       } else {
6970         PetscCall(PetscSectionGetConstraintIndices(section, cp, &cdofs));
6971         if (o >= 0) {
6972           for (k = 0; k < dof; ++k) {
6973             if ((cind < cdof) && (k == cdofs[cind])) {
6974               ++cind;
6975               continue;
6976             }
6977             a[k] += values[off + k];
6978           }
6979         } else {
6980           for (k = 0; k < dof; ++k) {
6981             if ((cind < cdof) && (k == cdofs[cind])) {
6982               ++cind;
6983               continue;
6984             }
6985             a[k] += values[off + dof - k - 1];
6986           }
6987         }
6988       }
6989     }
6990   }
6991   PetscCall(VecRestoreArray(v, &array));
6992   PetscFunctionReturn(PETSC_SUCCESS);
6993 }
6994 
6995 /*@C
6996   DMPlexVecSetClosure - Set an array of the values on the closure of `point`
6997 
6998   Not collective
6999 
7000   Input Parameters:
7001 + dm      - The `DM`
7002 . section - The section describing the layout in `v`, or `NULL` to use the default section
7003 . v       - The local vector
7004 . point   - The point in the `DM`
7005 . values  - The array of values
7006 - mode    - The insert mode. One of `INSERT_ALL_VALUES`, `ADD_ALL_VALUES`, `INSERT_VALUES`, `ADD_VALUES`, `INSERT_BC_VALUES`, and `ADD_BC_VALUES`,
7007          where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions.
7008 
7009   Level: intermediate
7010 
7011 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`
7012 @*/
7013 PetscErrorCode DMPlexVecSetClosure(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
7014 {
7015   PetscSection    clSection;
7016   IS              clPoints;
7017   PetscScalar    *array;
7018   PetscInt       *points = NULL;
7019   const PetscInt *clp, *clperm = NULL;
7020   PetscInt        depth, numFields, numPoints, p, clsize;
7021 
7022   PetscFunctionBeginHot;
7023   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7024   if (!section) PetscCall(DMGetLocalSection(dm, &section));
7025   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7026   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
7027   PetscCall(DMPlexGetDepth(dm, &depth));
7028   PetscCall(PetscSectionGetNumFields(section, &numFields));
7029   if (depth == 1 && numFields < 2 && mode == ADD_VALUES) {
7030     PetscCall(DMPlexVecSetClosure_Depth1_Static(dm, section, v, point, values, mode));
7031     PetscFunctionReturn(PETSC_SUCCESS);
7032   }
7033   /* Get points */
7034   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
7035   for (clsize = 0, p = 0; p < numPoints; p++) {
7036     PetscInt dof;
7037     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
7038     clsize += dof;
7039   }
7040   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
7041   /* Get array */
7042   PetscCall(VecGetArray(v, &array));
7043   /* Get values */
7044   if (numFields > 0) {
7045     PetscInt offset = 0, f;
7046     for (f = 0; f < numFields; ++f) {
7047       const PetscInt    **perms = NULL;
7048       const PetscScalar **flips = NULL;
7049 
7050       PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7051       switch (mode) {
7052       case INSERT_VALUES:
7053         for (p = 0; p < numPoints; p++) {
7054           const PetscInt     point = points[2 * p];
7055           const PetscInt    *perm  = perms ? perms[p] : NULL;
7056           const PetscScalar *flip  = flips ? flips[p] : NULL;
7057           PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, clperm, values, &offset, array));
7058         }
7059         break;
7060       case INSERT_ALL_VALUES:
7061         for (p = 0; p < numPoints; p++) {
7062           const PetscInt     point = points[2 * p];
7063           const PetscInt    *perm  = perms ? perms[p] : NULL;
7064           const PetscScalar *flip  = flips ? flips[p] : NULL;
7065           PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, clperm, values, &offset, array));
7066         }
7067         break;
7068       case INSERT_BC_VALUES:
7069         for (p = 0; p < numPoints; p++) {
7070           const PetscInt     point = points[2 * p];
7071           const PetscInt    *perm  = perms ? perms[p] : NULL;
7072           const PetscScalar *flip  = flips ? flips[p] : NULL;
7073           PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, insert, clperm, values, &offset, array));
7074         }
7075         break;
7076       case ADD_VALUES:
7077         for (p = 0; p < numPoints; p++) {
7078           const PetscInt     point = points[2 * p];
7079           const PetscInt    *perm  = perms ? perms[p] : NULL;
7080           const PetscScalar *flip  = flips ? flips[p] : NULL;
7081           PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, clperm, values, &offset, array));
7082         }
7083         break;
7084       case ADD_ALL_VALUES:
7085         for (p = 0; p < numPoints; p++) {
7086           const PetscInt     point = points[2 * p];
7087           const PetscInt    *perm  = perms ? perms[p] : NULL;
7088           const PetscScalar *flip  = flips ? flips[p] : NULL;
7089           PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, clperm, values, &offset, array));
7090         }
7091         break;
7092       case ADD_BC_VALUES:
7093         for (p = 0; p < numPoints; p++) {
7094           const PetscInt     point = points[2 * p];
7095           const PetscInt    *perm  = perms ? perms[p] : NULL;
7096           const PetscScalar *flip  = flips ? flips[p] : NULL;
7097           PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, add, clperm, values, &offset, array));
7098         }
7099         break;
7100       default:
7101         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
7102       }
7103       PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7104     }
7105   } else {
7106     PetscInt            dof, off;
7107     const PetscInt    **perms = NULL;
7108     const PetscScalar **flips = NULL;
7109 
7110     PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
7111     switch (mode) {
7112     case INSERT_VALUES:
7113       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7114         const PetscInt     point = points[2 * p];
7115         const PetscInt    *perm  = perms ? perms[p] : NULL;
7116         const PetscScalar *flip  = flips ? flips[p] : NULL;
7117         PetscCall(PetscSectionGetDof(section, point, &dof));
7118         PetscCall(updatePoint_private(section, point, dof, insert, PETSC_FALSE, perm, flip, clperm, values, off, array));
7119       }
7120       break;
7121     case INSERT_ALL_VALUES:
7122       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7123         const PetscInt     point = points[2 * p];
7124         const PetscInt    *perm  = perms ? perms[p] : NULL;
7125         const PetscScalar *flip  = flips ? flips[p] : NULL;
7126         PetscCall(PetscSectionGetDof(section, point, &dof));
7127         PetscCall(updatePoint_private(section, point, dof, insert, PETSC_TRUE, perm, flip, clperm, values, off, array));
7128       }
7129       break;
7130     case INSERT_BC_VALUES:
7131       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7132         const PetscInt     point = points[2 * p];
7133         const PetscInt    *perm  = perms ? perms[p] : NULL;
7134         const PetscScalar *flip  = flips ? flips[p] : NULL;
7135         PetscCall(PetscSectionGetDof(section, point, &dof));
7136         PetscCall(updatePointBC_private(section, point, dof, insert, perm, flip, clperm, values, off, array));
7137       }
7138       break;
7139     case ADD_VALUES:
7140       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7141         const PetscInt     point = points[2 * p];
7142         const PetscInt    *perm  = perms ? perms[p] : NULL;
7143         const PetscScalar *flip  = flips ? flips[p] : NULL;
7144         PetscCall(PetscSectionGetDof(section, point, &dof));
7145         PetscCall(updatePoint_private(section, point, dof, add, PETSC_FALSE, perm, flip, clperm, values, off, array));
7146       }
7147       break;
7148     case ADD_ALL_VALUES:
7149       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7150         const PetscInt     point = points[2 * p];
7151         const PetscInt    *perm  = perms ? perms[p] : NULL;
7152         const PetscScalar *flip  = flips ? flips[p] : NULL;
7153         PetscCall(PetscSectionGetDof(section, point, &dof));
7154         PetscCall(updatePoint_private(section, point, dof, add, PETSC_TRUE, perm, flip, clperm, values, off, array));
7155       }
7156       break;
7157     case ADD_BC_VALUES:
7158       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
7159         const PetscInt     point = points[2 * p];
7160         const PetscInt    *perm  = perms ? perms[p] : NULL;
7161         const PetscScalar *flip  = flips ? flips[p] : NULL;
7162         PetscCall(PetscSectionGetDof(section, point, &dof));
7163         PetscCall(updatePointBC_private(section, point, dof, add, perm, flip, clperm, values, off, array));
7164       }
7165       break;
7166     default:
7167       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
7168     }
7169     PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
7170   }
7171   /* Cleanup points */
7172   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
7173   /* Cleanup array */
7174   PetscCall(VecRestoreArray(v, &array));
7175   PetscFunctionReturn(PETSC_SUCCESS);
7176 }
7177 
7178 /* Check whether the given point is in the label. If not, update the offset to skip this point */
7179 static inline PetscErrorCode CheckPoint_Private(DMLabel label, PetscInt labelId, PetscSection section, PetscInt point, PetscInt f, PetscInt *offset, PetscBool *contains)
7180 {
7181   PetscFunctionBegin;
7182   *contains = PETSC_TRUE;
7183   if (label) {
7184     PetscInt fdof;
7185 
7186     PetscCall(DMLabelStratumHasPoint(label, labelId, point, contains));
7187     if (!*contains) {
7188       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7189       *offset += fdof;
7190       PetscFunctionReturn(PETSC_SUCCESS);
7191     }
7192   }
7193   PetscFunctionReturn(PETSC_SUCCESS);
7194 }
7195 
7196 /* Unlike DMPlexVecSetClosure(), this uses plex-native closure permutation, not a user-specified permutation such as DMPlexSetClosurePermutationTensor(). */
7197 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)
7198 {
7199   PetscSection    clSection;
7200   IS              clPoints;
7201   PetscScalar    *array;
7202   PetscInt       *points = NULL;
7203   const PetscInt *clp;
7204   PetscInt        numFields, numPoints, p;
7205   PetscInt        offset = 0, f;
7206 
7207   PetscFunctionBeginHot;
7208   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7209   if (!section) PetscCall(DMGetLocalSection(dm, &section));
7210   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7211   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
7212   PetscCall(PetscSectionGetNumFields(section, &numFields));
7213   /* Get points */
7214   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
7215   /* Get array */
7216   PetscCall(VecGetArray(v, &array));
7217   /* Get values */
7218   for (f = 0; f < numFields; ++f) {
7219     const PetscInt    **perms = NULL;
7220     const PetscScalar **flips = NULL;
7221     PetscBool           contains;
7222 
7223     if (!fieldActive[f]) {
7224       for (p = 0; p < numPoints * 2; p += 2) {
7225         PetscInt fdof;
7226         PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
7227         offset += fdof;
7228       }
7229       continue;
7230     }
7231     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7232     switch (mode) {
7233     case INSERT_VALUES:
7234       for (p = 0; p < numPoints; p++) {
7235         const PetscInt     point = points[2 * p];
7236         const PetscInt    *perm  = perms ? perms[p] : NULL;
7237         const PetscScalar *flip  = flips ? flips[p] : NULL;
7238         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7239         if (!contains) continue;
7240         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, NULL, values, &offset, array));
7241       }
7242       break;
7243     case INSERT_ALL_VALUES:
7244       for (p = 0; p < numPoints; p++) {
7245         const PetscInt     point = points[2 * p];
7246         const PetscInt    *perm  = perms ? perms[p] : NULL;
7247         const PetscScalar *flip  = flips ? flips[p] : NULL;
7248         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7249         if (!contains) continue;
7250         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, NULL, values, &offset, array));
7251       }
7252       break;
7253     case INSERT_BC_VALUES:
7254       for (p = 0; p < numPoints; p++) {
7255         const PetscInt     point = points[2 * p];
7256         const PetscInt    *perm  = perms ? perms[p] : NULL;
7257         const PetscScalar *flip  = flips ? flips[p] : NULL;
7258         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7259         if (!contains) continue;
7260         PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, Ncc, comps, insert, NULL, values, &offset, array));
7261       }
7262       break;
7263     case ADD_VALUES:
7264       for (p = 0; p < numPoints; p++) {
7265         const PetscInt     point = points[2 * p];
7266         const PetscInt    *perm  = perms ? perms[p] : NULL;
7267         const PetscScalar *flip  = flips ? flips[p] : NULL;
7268         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7269         if (!contains) continue;
7270         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, NULL, values, &offset, array));
7271       }
7272       break;
7273     case ADD_ALL_VALUES:
7274       for (p = 0; p < numPoints; p++) {
7275         const PetscInt     point = points[2 * p];
7276         const PetscInt    *perm  = perms ? perms[p] : NULL;
7277         const PetscScalar *flip  = flips ? flips[p] : NULL;
7278         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
7279         if (!contains) continue;
7280         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, NULL, values, &offset, array));
7281       }
7282       break;
7283     default:
7284       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
7285     }
7286     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
7287   }
7288   /* Cleanup points */
7289   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
7290   /* Cleanup array */
7291   PetscCall(VecRestoreArray(v, &array));
7292   PetscFunctionReturn(PETSC_SUCCESS);
7293 }
7294 
7295 static PetscErrorCode DMPlexPrintMatSetValues(PetscViewer viewer, Mat A, PetscInt point, PetscInt numRIndices, const PetscInt rindices[], PetscInt numCIndices, const PetscInt cindices[], const PetscScalar values[])
7296 {
7297   PetscMPIInt rank;
7298   PetscInt    i, j;
7299 
7300   PetscFunctionBegin;
7301   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7302   PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat for point %" PetscInt_FMT "\n", rank, point));
7303   for (i = 0; i < numRIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat row indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, rindices[i]));
7304   for (i = 0; i < numCIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat col indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, cindices[i]));
7305   numCIndices = numCIndices ? numCIndices : numRIndices;
7306   if (!values) PetscFunctionReturn(PETSC_SUCCESS);
7307   for (i = 0; i < numRIndices; i++) {
7308     PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]", rank));
7309     for (j = 0; j < numCIndices; j++) {
7310 #if defined(PETSC_USE_COMPLEX)
7311       PetscCall(PetscViewerASCIIPrintf(viewer, " (%g,%g)", (double)PetscRealPart(values[i * numCIndices + j]), (double)PetscImaginaryPart(values[i * numCIndices + j])));
7312 #else
7313       PetscCall(PetscViewerASCIIPrintf(viewer, " %g", (double)values[i * numCIndices + j]));
7314 #endif
7315     }
7316     PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
7317   }
7318   PetscFunctionReturn(PETSC_SUCCESS);
7319 }
7320 
7321 /*
7322   DMPlexGetIndicesPoint_Internal - Add the indices for dofs on a point to an index array
7323 
7324   Input Parameters:
7325 + section - The section for this data layout
7326 . islocal - Is the section (and thus indices being requested) local or global?
7327 . point   - The point contributing dofs with these indices
7328 . off     - The global offset of this point
7329 . loff    - The local offset of each field
7330 . setBC   - The flag determining whether to include indices of boundary values
7331 . perm    - A permutation of the dofs on this point, or NULL
7332 - indperm - A permutation of the entire indices array, or NULL
7333 
7334   Output Parameter:
7335 . indices - Indices for dofs on this point
7336 
7337   Level: developer
7338 
7339   Note: The indices could be local or global, depending on the value of 'off'.
7340 */
7341 PetscErrorCode DMPlexGetIndicesPoint_Internal(PetscSection section, PetscBool islocal, PetscInt point, PetscInt off, PetscInt *loff, PetscBool setBC, const PetscInt perm[], const PetscInt indperm[], PetscInt indices[])
7342 {
7343   PetscInt        dof;   /* The number of unknowns on this point */
7344   PetscInt        cdof;  /* The number of constraints on this point */
7345   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
7346   PetscInt        cind = 0, k;
7347 
7348   PetscFunctionBegin;
7349   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
7350   PetscCall(PetscSectionGetDof(section, point, &dof));
7351   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
7352   if (!cdof || setBC) {
7353     for (k = 0; k < dof; ++k) {
7354       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
7355       const PetscInt ind    = indperm ? indperm[preind] : preind;
7356 
7357       indices[ind] = off + k;
7358     }
7359   } else {
7360     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
7361     for (k = 0; k < dof; ++k) {
7362       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
7363       const PetscInt ind    = indperm ? indperm[preind] : preind;
7364 
7365       if ((cind < cdof) && (k == cdofs[cind])) {
7366         /* Insert check for returning constrained indices */
7367         indices[ind] = -(off + k + 1);
7368         ++cind;
7369       } else {
7370         indices[ind] = off + k - (islocal ? 0 : cind);
7371       }
7372     }
7373   }
7374   *loff += dof;
7375   PetscFunctionReturn(PETSC_SUCCESS);
7376 }
7377 
7378 /*
7379  DMPlexGetIndicesPointFields_Internal - gets section indices for a point in its canonical ordering.
7380 
7381  Input Parameters:
7382 + section - a section (global or local)
7383 - islocal - `PETSC_TRUE` if requesting local indices (i.e., section is local); `PETSC_FALSE` for global
7384 . point - point within section
7385 . off - The offset of this point in the (local or global) indexed space - should match islocal and (usually) the section
7386 . foffs - array of length numFields containing the offset in canonical point ordering (the location in indices) of each field
7387 . setBC - identify constrained (boundary condition) points via involution.
7388 . perms - perms[f][permsoff][:] is a permutation of dofs within each field
7389 . permsoff - offset
7390 - indperm - index permutation
7391 
7392  Output Parameter:
7393 . foffs - each entry is incremented by the number of (unconstrained if setBC=FALSE) dofs in that field
7394 . indices - array to hold indices (as defined by section) of each dof associated with point
7395 
7396  Notes:
7397  If section is local and setBC=true, there is no distinction between constrained and unconstrained dofs.
7398  If section is local and setBC=false, the indices for constrained points are the involution -(i+1) of their position
7399  in the local vector.
7400 
7401  If section is global and setBC=false, the indices for constrained points are negative (and their value is not
7402  significant).  It is invalid to call with a global section and setBC=true.
7403 
7404  Developer Note:
7405  The section is only used for field layout, so islocal is technically a statement about the offset (off).  At some point
7406  in the future, global sections may have fields set, in which case we could pass the global section and obtain the
7407  offset could be obtained from the section instead of passing it explicitly as we do now.
7408 
7409  Example:
7410  Suppose a point contains one field with three components, and for which the unconstrained indices are {10, 11, 12}.
7411  When the middle component is constrained, we get the array {10, -12, 12} for (islocal=TRUE, setBC=FALSE).
7412  Note that -12 is the involution of 11, so the user can involute negative indices to recover local indices.
7413  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.
7414 
7415  Level: developer
7416 */
7417 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[])
7418 {
7419   PetscInt numFields, foff, f;
7420 
7421   PetscFunctionBegin;
7422   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
7423   PetscCall(PetscSectionGetNumFields(section, &numFields));
7424   for (f = 0, foff = 0; f < numFields; ++f) {
7425     PetscInt        fdof, cfdof;
7426     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7427     PetscInt        cind = 0, b;
7428     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7429 
7430     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7431     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7432     if (!cfdof || setBC) {
7433       for (b = 0; b < fdof; ++b) {
7434         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7435         const PetscInt ind    = indperm ? indperm[preind] : preind;
7436 
7437         indices[ind] = off + foff + b;
7438       }
7439     } else {
7440       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7441       for (b = 0; b < fdof; ++b) {
7442         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7443         const PetscInt ind    = indperm ? indperm[preind] : preind;
7444 
7445         if ((cind < cfdof) && (b == fcdofs[cind])) {
7446           indices[ind] = -(off + foff + b + 1);
7447           ++cind;
7448         } else {
7449           indices[ind] = off + foff + b - (islocal ? 0 : cind);
7450         }
7451       }
7452     }
7453     foff += (setBC || islocal ? fdof : (fdof - cfdof));
7454     foffs[f] += fdof;
7455   }
7456   PetscFunctionReturn(PETSC_SUCCESS);
7457 }
7458 
7459 /*
7460   This version believes the globalSection offsets for each field, rather than just the point offset
7461 
7462  . foffs - The offset into 'indices' for each field, since it is segregated by field
7463 
7464  Notes:
7465  The semantics of this function relate to that of setBC=FALSE in DMPlexGetIndicesPointFields_Internal.
7466  Since this function uses global indices, setBC=TRUE would be invalid, so no such argument exists.
7467 */
7468 static PetscErrorCode DMPlexGetIndicesPointFieldsSplit_Internal(PetscSection section, PetscSection globalSection, PetscInt point, PetscInt foffs[], const PetscInt ***perms, PetscInt permsoff, const PetscInt indperm[], PetscInt indices[])
7469 {
7470   PetscInt numFields, foff, f;
7471 
7472   PetscFunctionBegin;
7473   PetscCall(PetscSectionGetNumFields(section, &numFields));
7474   for (f = 0; f < numFields; ++f) {
7475     PetscInt        fdof, cfdof;
7476     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7477     PetscInt        cind = 0, b;
7478     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7479 
7480     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7481     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7482     PetscCall(PetscSectionGetFieldOffset(globalSection, point, f, &foff));
7483     if (!cfdof) {
7484       for (b = 0; b < fdof; ++b) {
7485         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7486         const PetscInt ind    = indperm ? indperm[preind] : preind;
7487 
7488         indices[ind] = foff + b;
7489       }
7490     } else {
7491       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7492       for (b = 0; b < fdof; ++b) {
7493         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7494         const PetscInt ind    = indperm ? indperm[preind] : preind;
7495 
7496         if ((cind < cfdof) && (b == fcdofs[cind])) {
7497           indices[ind] = -(foff + b + 1);
7498           ++cind;
7499         } else {
7500           indices[ind] = foff + b - cind;
7501         }
7502       }
7503     }
7504     foffs[f] += fdof;
7505   }
7506   PetscFunctionReturn(PETSC_SUCCESS);
7507 }
7508 
7509 static PetscErrorCode DMPlexAnchorsGetSubMatIndices(PetscInt nPoints, const PetscInt pnts[], PetscSection section, PetscSection cSec, PetscInt tmpIndices[], PetscInt fieldOffsets[], PetscInt indices[], const PetscInt ***perms)
7510 {
7511   PetscInt numFields, sStart, sEnd, cStart, cEnd;
7512 
7513   PetscFunctionBegin;
7514   PetscCall(PetscSectionGetNumFields(section, &numFields));
7515   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
7516   PetscCall(PetscSectionGetChart(cSec, &cStart, &cEnd));
7517   for (PetscInt p = 0; p < nPoints; p++) {
7518     PetscInt     b       = pnts[2 * p];
7519     PetscInt     bSecDof = 0, bOff;
7520     PetscInt     cSecDof = 0;
7521     PetscSection indices_section;
7522 
7523     if (b >= sStart && b < sEnd) PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7524     if (!bSecDof) continue;
7525     if (b >= cStart && b < cEnd) PetscCall(PetscSectionGetDof(cSec, b, &cSecDof));
7526     indices_section = cSecDof > 0 ? cSec : section;
7527     if (numFields) {
7528       PetscInt fStart[32], fEnd[32];
7529 
7530       fStart[0] = 0;
7531       fEnd[0]   = 0;
7532       for (PetscInt f = 0; f < numFields; f++) {
7533         PetscInt fDof = 0;
7534 
7535         PetscCall(PetscSectionGetFieldDof(indices_section, b, f, &fDof));
7536         fStart[f + 1] = fStart[f] + fDof;
7537         fEnd[f + 1]   = fStart[f + 1];
7538       }
7539       PetscCall(PetscSectionGetOffset(indices_section, b, &bOff));
7540       // only apply permutations on one side
7541       PetscCall(DMPlexGetIndicesPointFields_Internal(indices_section, PETSC_TRUE, b, bOff, fEnd, PETSC_TRUE, perms, perms ? p : -1, NULL, tmpIndices));
7542       for (PetscInt f = 0; f < numFields; f++) {
7543         for (PetscInt i = fStart[f]; i < fEnd[f]; i++) { indices[fieldOffsets[f]++] = (cSecDof > 0) ? tmpIndices[i] : -(tmpIndices[i] + 1); }
7544       }
7545     } else {
7546       PetscInt bEnd = 0;
7547 
7548       PetscCall(PetscSectionGetOffset(indices_section, b, &bOff));
7549       PetscCall(DMPlexGetIndicesPoint_Internal(indices_section, PETSC_TRUE, b, bOff, &bEnd, PETSC_TRUE, (perms && perms[0]) ? perms[0][p] : NULL, NULL, tmpIndices));
7550 
7551       for (PetscInt i = 0; i < bEnd; i++) indices[fieldOffsets[0]++] = (cSecDof > 0) ? tmpIndices[i] : -(tmpIndices[i] + 1);
7552     }
7553   }
7554   PetscFunctionReturn(PETSC_SUCCESS);
7555 }
7556 
7557 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[])
7558 {
7559   Mat             cMat;
7560   PetscSection    aSec, cSec;
7561   IS              aIS;
7562   PetscInt        aStart = -1, aEnd = -1;
7563   PetscInt        sStart = -1, sEnd = -1;
7564   PetscInt        cStart = -1, cEnd = -1;
7565   const PetscInt *anchors;
7566   PetscInt        numFields, p;
7567   PetscInt        newNumPoints = 0, newNumIndices = 0;
7568   PetscInt       *newPoints, *indices, *newIndices, *tmpIndices, *tmpNewIndices;
7569   PetscInt        oldOffsets[32];
7570   PetscInt        newOffsets[32];
7571   PetscInt        oldOffsetsCopy[32];
7572   PetscInt        newOffsetsCopy[32];
7573   PetscScalar    *modMat         = NULL;
7574   PetscBool       anyConstrained = PETSC_FALSE;
7575 
7576   PetscFunctionBegin;
7577   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7578   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7579   PetscCall(PetscSectionGetNumFields(section, &numFields));
7580 
7581   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
7582   /* if there are point-to-point constraints */
7583   if (aSec) {
7584     PetscCall(PetscArrayzero(newOffsets, 32));
7585     PetscCall(PetscArrayzero(oldOffsets, 32));
7586     PetscCall(ISGetIndices(aIS, &anchors));
7587     PetscCall(PetscSectionGetChart(aSec, &aStart, &aEnd));
7588     PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
7589     /* figure out how many points are going to be in the new element matrix
7590      * (we allow double counting, because it's all just going to be summed
7591      * into the global matrix anyway) */
7592     for (p = 0; p < 2 * numPoints; p += 2) {
7593       PetscInt b    = points[p];
7594       PetscInt bDof = 0, bSecDof = 0;
7595 
7596       if (b >= sStart && b < sEnd) PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7597       if (!bSecDof) continue;
7598 
7599       for (PetscInt f = 0; f < numFields; f++) {
7600         PetscInt fDof = 0;
7601 
7602         PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7603         oldOffsets[f + 1] += fDof;
7604       }
7605       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7606       if (bDof) {
7607         /* this point is constrained */
7608         /* it is going to be replaced by its anchors */
7609         PetscInt bOff, q;
7610 
7611         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7612         for (q = 0; q < bDof; q++) {
7613           PetscInt a    = anchors[bOff + q];
7614           PetscInt aDof = 0;
7615 
7616           if (a >= sStart && a < sEnd) PetscCall(PetscSectionGetDof(section, a, &aDof));
7617           if (aDof) {
7618             anyConstrained = PETSC_TRUE;
7619             newNumPoints += 1;
7620           }
7621           newNumIndices += aDof;
7622           for (PetscInt f = 0; f < numFields; ++f) {
7623             PetscInt fDof = 0;
7624 
7625             if (a >= sStart && a < sEnd) PetscCall(PetscSectionGetFieldDof(section, a, f, &fDof));
7626             newOffsets[f + 1] += fDof;
7627           }
7628         }
7629       } else {
7630         /* this point is not constrained */
7631         newNumPoints++;
7632         newNumIndices += bSecDof;
7633         for (PetscInt f = 0; f < numFields; ++f) {
7634           PetscInt fDof;
7635 
7636           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7637           newOffsets[f + 1] += fDof;
7638         }
7639       }
7640     }
7641   }
7642   if (!anyConstrained) {
7643     if (outNumPoints) *outNumPoints = 0;
7644     if (outNumIndices) *outNumIndices = 0;
7645     if (outPoints) *outPoints = NULL;
7646     if (outMat) *outMat = NULL;
7647     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7648     PetscFunctionReturn(PETSC_SUCCESS);
7649   }
7650 
7651   if (outNumPoints) *outNumPoints = newNumPoints;
7652   if (outNumIndices) *outNumIndices = newNumIndices;
7653 
7654   for (PetscInt f = 0; f < numFields; ++f) newOffsets[f + 1] += newOffsets[f];
7655   for (PetscInt f = 0; f < numFields; ++f) oldOffsets[f + 1] += oldOffsets[f];
7656 
7657   if (!outPoints && !outMat) {
7658     if (offsets) {
7659       for (PetscInt f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7660     }
7661     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7662     PetscFunctionReturn(PETSC_SUCCESS);
7663   }
7664 
7665   PetscCheck(!numFields || newOffsets[numFields] == newNumIndices, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, newOffsets[numFields], newNumIndices);
7666   PetscCheck(!numFields || oldOffsets[numFields] == numIndices, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, oldOffsets[numFields], numIndices);
7667 
7668   PetscCall(DMGetDefaultConstraints(dm, &cSec, &cMat, NULL));
7669   PetscCall(PetscSectionGetChart(cSec, &cStart, &cEnd));
7670 
7671   /* output arrays */
7672   PetscCall(DMGetWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7673   PetscCall(PetscArrayzero(newPoints, 2 * newNumPoints));
7674 
7675   // get the new Points
7676   for (PetscInt p = 0, newP = 0; p < numPoints; p++) {
7677     PetscInt b    = points[2 * p];
7678     PetscInt bDof = 0, bSecDof = 0, bOff;
7679 
7680     if (b >= sStart && b < sEnd) PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7681     if (!bSecDof) continue;
7682     if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7683     if (bDof) {
7684       PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7685       for (PetscInt q = 0; q < bDof; q++) {
7686         PetscInt a = anchors[bOff + q], aDof = 0;
7687 
7688         if (a >= sStart && a < sEnd) PetscCall(PetscSectionGetDof(section, a, &aDof));
7689         if (aDof) {
7690           newPoints[2 * newP]     = a;
7691           newPoints[2 * newP + 1] = 0; // orientations are accounted for in constructing the matrix, newly added points are in default orientation
7692           newP++;
7693         }
7694       }
7695     } else {
7696       newPoints[2 * newP]     = b;
7697       newPoints[2 * newP + 1] = points[2 * p + 1];
7698       newP++;
7699     }
7700   }
7701 
7702   if (outMat) {
7703     PetscScalar *tmpMat;
7704     PetscCall(PetscArraycpy(oldOffsetsCopy, oldOffsets, 32));
7705     PetscCall(PetscArraycpy(newOffsetsCopy, newOffsets, 32));
7706 
7707     PetscCall(DMGetWorkArray(dm, numIndices, MPIU_INT, &indices));
7708     PetscCall(DMGetWorkArray(dm, numIndices, MPIU_INT, &tmpIndices));
7709     PetscCall(DMGetWorkArray(dm, newNumIndices, MPIU_INT, &newIndices));
7710     PetscCall(DMGetWorkArray(dm, newNumIndices, MPIU_INT, &tmpNewIndices));
7711 
7712     for (PetscInt i = 0; i < numIndices; i++) indices[i] = -1;
7713     for (PetscInt i = 0; i < newNumIndices; i++) newIndices[i] = -1;
7714 
7715     PetscCall(DMPlexAnchorsGetSubMatIndices(numPoints, points, section, cSec, tmpIndices, oldOffsetsCopy, indices, perms));
7716     PetscCall(DMPlexAnchorsGetSubMatIndices(newNumPoints, newPoints, section, section, tmpNewIndices, newOffsetsCopy, newIndices, NULL));
7717 
7718     PetscCall(DMGetWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &modMat));
7719     PetscCall(DMGetWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &tmpMat));
7720     PetscCall(PetscArrayzero(modMat, newNumIndices * numIndices));
7721     // for each field, insert the anchor modification into modMat
7722     for (PetscInt f = 0; f < PetscMax(1, numFields); f++) {
7723       PetscInt fStart    = oldOffsets[f];
7724       PetscInt fNewStart = newOffsets[f];
7725       for (PetscInt p = 0, newP = 0, o = fStart, oNew = fNewStart; p < numPoints; p++) {
7726         PetscInt b    = points[2 * p];
7727         PetscInt bDof = 0, bSecDof = 0, bOff;
7728 
7729         if (b >= sStart && b < sEnd) {
7730           if (numFields) {
7731             PetscCall(PetscSectionGetFieldDof(section, b, f, &bSecDof));
7732           } else {
7733             PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7734           }
7735         }
7736         if (!bSecDof) continue;
7737         if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7738         if (bDof) {
7739           PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7740           for (PetscInt q = 0; q < bDof; q++, newP++) {
7741             PetscInt a = anchors[bOff + q], aDof = 0;
7742 
7743             if (a >= sStart && a < sEnd) {
7744               if (numFields) {
7745                 PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
7746               } else {
7747                 PetscCall(PetscSectionGetDof(section, a, &aDof));
7748               }
7749             }
7750             if (aDof) {
7751               PetscCall(MatGetValues(cMat, bSecDof, &indices[o], aDof, &newIndices[oNew], tmpMat));
7752               for (PetscInt d = 0; d < bSecDof; d++) {
7753                 for (PetscInt e = 0; e < aDof; e++) modMat[(o + d) * newNumIndices + oNew + e] = tmpMat[d * aDof + e];
7754               }
7755             }
7756             oNew += aDof;
7757           }
7758         } else {
7759           // Insert the identity matrix in this block
7760           for (PetscInt d = 0; d < bSecDof; d++) modMat[(o + d) * newNumIndices + oNew + d] = 1;
7761           oNew += bSecDof;
7762           newP++;
7763         }
7764         o += bSecDof;
7765       }
7766     }
7767 
7768     *outMat = modMat;
7769 
7770     PetscCall(DMRestoreWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &tmpMat));
7771     PetscCall(DMRestoreWorkArray(dm, newNumIndices, MPIU_INT, &tmpNewIndices));
7772     PetscCall(DMRestoreWorkArray(dm, newNumIndices, MPIU_INT, &newIndices));
7773     PetscCall(DMRestoreWorkArray(dm, numIndices, MPIU_INT, &tmpIndices));
7774     PetscCall(DMRestoreWorkArray(dm, numIndices, MPIU_INT, &indices));
7775   }
7776   PetscCall(ISRestoreIndices(aIS, &anchors));
7777 
7778   /* output */
7779   if (outPoints) {
7780     *outPoints = newPoints;
7781   } else {
7782     PetscCall(DMRestoreWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7783   }
7784   for (PetscInt f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7785   PetscFunctionReturn(PETSC_SUCCESS);
7786 }
7787 
7788 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)
7789 {
7790   PetscScalar *modMat        = NULL;
7791   PetscInt     newNumIndices = -1;
7792 
7793   PetscFunctionBegin;
7794   /* 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.
7795      modMat is that matrix C */
7796   PetscCall(DMPlexAnchorsGetSubMatModification(dm, section, numPoints, numIndices, points, perms, outNumPoints, &newNumIndices, outPoints, offsets, outValues ? &modMat : NULL));
7797   if (outNumIndices) *outNumIndices = newNumIndices;
7798   if (modMat) {
7799     const PetscScalar *newValues = values;
7800 
7801     if (multiplyRight) {
7802       PetscScalar *newNewValues = NULL;
7803       PetscBLASInt M            = newNumIndices;
7804       PetscBLASInt N            = numRows;
7805       PetscBLASInt K            = numIndices;
7806       PetscScalar  a = 1.0, b = 0.0;
7807 
7808       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);
7809 
7810       PetscCall(DMGetWorkArray(dm, numRows * newNumIndices, MPIU_SCALAR, &newNewValues));
7811       // row-major to column-major conversion, right multiplication becomes left multiplication
7812       PetscCallBLAS("BLASgemm", BLASgemm_("N", "N", &M, &N, &K, &a, modMat, &M, newValues, &K, &b, newNewValues, &M));
7813 
7814       numCols   = newNumIndices;
7815       newValues = newNewValues;
7816     }
7817 
7818     if (multiplyLeft) {
7819       PetscScalar *newNewValues = NULL;
7820       PetscBLASInt M            = numCols;
7821       PetscBLASInt N            = newNumIndices;
7822       PetscBLASInt K            = numIndices;
7823       PetscScalar  a = 1.0, b = 0.0;
7824 
7825       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);
7826 
7827       PetscCall(DMGetWorkArray(dm, newNumIndices * numCols, MPIU_SCALAR, &newNewValues));
7828       // row-major to column-major conversion, left multiplication becomes right multiplication
7829       PetscCallBLAS("BLASgemm", BLASgemm_("N", "T", &M, &N, &K, &a, newValues, &M, modMat, &N, &b, newNewValues, &M));
7830       if (newValues != values) PetscCall(DMRestoreWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &newValues));
7831       newValues = newNewValues;
7832     }
7833     *outValues = (PetscScalar *)newValues;
7834     PetscCall(DMRestoreWorkArray(dm, numIndices * newNumIndices, MPIU_SCALAR, &modMat));
7835   }
7836   PetscFunctionReturn(PETSC_SUCCESS);
7837 }
7838 
7839 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)
7840 {
7841   PetscFunctionBegin;
7842   PetscCall(DMPlexAnchorsModifyMat_Internal(dm, section, numPoints, numIndices, points, perms, numIndices, numIndices, values, outNumPoints, outNumIndices, outPoints, outValues, offsets, PETSC_TRUE, multiplyLeft));
7843   PetscFunctionReturn(PETSC_SUCCESS);
7844 }
7845 
7846 static PetscErrorCode DMPlexGetClosureIndicesSize_Internal(DM dm, PetscSection section, PetscInt point, PetscInt *closureSize)
7847 {
7848   /* Closure ordering */
7849   PetscSection    clSection;
7850   IS              clPoints;
7851   const PetscInt *clp;
7852   PetscInt       *points;
7853   PetscInt        Ncl, Ni = 0;
7854 
7855   PetscFunctionBeginHot;
7856   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &Ncl, &points, &clSection, &clPoints, &clp));
7857   for (PetscInt p = 0; p < Ncl * 2; p += 2) {
7858     PetscInt dof;
7859 
7860     PetscCall(PetscSectionGetDof(section, points[p], &dof));
7861     Ni += dof;
7862   }
7863   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7864   *closureSize = Ni;
7865   PetscFunctionReturn(PETSC_SUCCESS);
7866 }
7867 
7868 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)
7869 {
7870   /* Closure ordering */
7871   PetscSection    clSection;
7872   IS              clPoints;
7873   const PetscInt *clp;
7874   PetscInt       *points;
7875   const PetscInt *clperm = NULL;
7876   /* Dof permutation and sign flips */
7877   const PetscInt    **perms[32] = {NULL};
7878   const PetscScalar **flips[32] = {NULL};
7879   PetscScalar        *valCopy   = NULL;
7880   /* Hanging node constraints */
7881   PetscInt    *pointsC = NULL;
7882   PetscScalar *valuesC = NULL;
7883   PetscInt     NclC, NiC;
7884 
7885   PetscInt *idx;
7886   PetscInt  Nf, Ncl, Ni = 0, offsets[32], p, f;
7887   PetscBool isLocal = (section == idxSection) ? PETSC_TRUE : PETSC_FALSE;
7888   PetscInt  idxStart, idxEnd;
7889   PetscInt  nRows, nCols;
7890 
7891   PetscFunctionBeginHot;
7892   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7893   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7894   PetscValidHeaderSpecific(idxSection, PETSC_SECTION_CLASSID, 3);
7895   PetscAssertPointer(numRows, 6);
7896   PetscAssertPointer(numCols, 7);
7897   if (indices) PetscAssertPointer(indices, 8);
7898   if (outOffsets) PetscAssertPointer(outOffsets, 9);
7899   if (values) PetscAssertPointer(values, 10);
7900   PetscCall(PetscSectionGetNumFields(section, &Nf));
7901   PetscCheck(Nf <= 31, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", Nf);
7902   PetscCall(PetscArrayzero(offsets, 32));
7903   /* 1) Get points in closure */
7904   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &Ncl, &points, &clSection, &clPoints, &clp));
7905   if (useClPerm) {
7906     PetscInt depth, clsize;
7907     PetscCall(DMPlexGetPointDepth(dm, point, &depth));
7908     for (clsize = 0, p = 0; p < Ncl; p++) {
7909       PetscInt dof;
7910       PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
7911       clsize += dof;
7912     }
7913     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
7914   }
7915   /* 2) Get number of indices on these points and field offsets from section */
7916   for (p = 0; p < Ncl * 2; p += 2) {
7917     PetscInt dof, fdof;
7918 
7919     PetscCall(PetscSectionGetDof(section, points[p], &dof));
7920     for (f = 0; f < Nf; ++f) {
7921       PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
7922       offsets[f + 1] += fdof;
7923     }
7924     Ni += dof;
7925   }
7926   if (*numRows == -1) *numRows = Ni;
7927   if (*numCols == -1) *numCols = Ni;
7928   nRows = *numRows;
7929   nCols = *numCols;
7930   for (f = 1; f < Nf; ++f) offsets[f + 1] += offsets[f];
7931   PetscCheck(!Nf || offsets[Nf] == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, offsets[Nf], Ni);
7932   /* 3) Get symmetries and sign flips. Apply sign flips to values if passed in (only works for square values matrix) */
7933   if (multiplyRight) PetscCheck(nCols == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Expected %" PetscInt_FMT " columns, got %" PetscInt_FMT, Ni, nCols);
7934   if (multiplyLeft) PetscCheck(nRows == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Expected %" PetscInt_FMT " rows, got %" PetscInt_FMT, Ni, nRows);
7935   for (f = 0; f < PetscMax(1, Nf); ++f) {
7936     if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7937     else PetscCall(PetscSectionGetPointSyms(section, Ncl, points, &perms[f], &flips[f]));
7938     /* may need to apply sign changes to the element matrix */
7939     if (values && flips[f]) {
7940       PetscInt foffset = offsets[f];
7941 
7942       for (p = 0; p < Ncl; ++p) {
7943         PetscInt           pnt  = points[2 * p], fdof;
7944         const PetscScalar *flip = flips[f] ? flips[f][p] : NULL;
7945 
7946         if (!Nf) PetscCall(PetscSectionGetDof(section, pnt, &fdof));
7947         else PetscCall(PetscSectionGetFieldDof(section, pnt, f, &fdof));
7948         if (flip) {
7949           PetscInt i, j, k;
7950 
7951           if (!valCopy) {
7952             PetscCall(DMGetWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
7953             for (j = 0; j < Ni * Ni; ++j) valCopy[j] = (*values)[j];
7954             *values = valCopy;
7955           }
7956           for (i = 0; i < fdof; ++i) {
7957             PetscScalar fval = flip[i];
7958 
7959             if (multiplyRight) {
7960               for (k = 0; k < nRows; ++k) { valCopy[Ni * k + (foffset + i)] *= fval; }
7961             }
7962             if (multiplyLeft) {
7963               for (k = 0; k < nCols; ++k) { valCopy[nCols * (foffset + i) + k] *= fval; }
7964             }
7965           }
7966         }
7967         foffset += fdof;
7968       }
7969     }
7970   }
7971   /* 4) Apply hanging node constraints. Get new symmetries and replace all storage with constrained storage */
7972   PetscCall(DMPlexAnchorsModifyMat_Internal(dm, section, Ncl, Ni, points, perms, nRows, nCols, values ? *values : NULL, &NclC, &NiC, &pointsC, values ? &valuesC : NULL, offsets, multiplyRight, multiplyLeft));
7973   if (NclC) {
7974     if (multiplyRight) { *numCols = nCols = NiC; }
7975     if (multiplyLeft) { *numRows = nRows = NiC; }
7976     if (valCopy) PetscCall(DMRestoreWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
7977     for (f = 0; f < PetscMax(1, Nf); ++f) {
7978       if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7979       else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
7980     }
7981     for (f = 0; f < PetscMax(1, Nf); ++f) {
7982       if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, NclC, pointsC, &perms[f], &flips[f]));
7983       else PetscCall(PetscSectionGetPointSyms(section, NclC, pointsC, &perms[f], &flips[f]));
7984     }
7985     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7986     Ncl    = NclC;
7987     Ni     = NiC;
7988     points = pointsC;
7989     if (values) *values = valuesC;
7990   }
7991   /* 5) Calculate indices */
7992   PetscCall(DMGetWorkArray(dm, Ni, MPIU_INT, &idx));
7993   PetscCall(PetscSectionGetChart(idxSection, &idxStart, &idxEnd));
7994   if (Nf) {
7995     PetscInt  idxOff;
7996     PetscBool useFieldOffsets;
7997 
7998     if (outOffsets) {
7999       for (f = 0; f <= Nf; f++) outOffsets[f] = offsets[f];
8000     }
8001     PetscCall(PetscSectionGetUseFieldOffsets(idxSection, &useFieldOffsets));
8002     if (useFieldOffsets) {
8003       for (p = 0; p < Ncl; ++p) {
8004         const PetscInt pnt = points[p * 2];
8005 
8006         PetscCall(DMPlexGetIndicesPointFieldsSplit_Internal(section, idxSection, pnt, offsets, perms, p, clperm, idx));
8007       }
8008     } else {
8009       for (p = 0; p < Ncl; ++p) {
8010         const PetscInt pnt = points[p * 2];
8011 
8012         if (pnt < idxStart || pnt >= idxEnd) continue;
8013         PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
8014         /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
8015          * not (at the time of this writing) have fields set. They probably should, in which case we would pass the
8016          * global section. */
8017         PetscCall(DMPlexGetIndicesPointFields_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, offsets, PETSC_FALSE, perms, p, clperm, idx));
8018       }
8019     }
8020   } else {
8021     PetscInt off = 0, idxOff;
8022 
8023     for (p = 0; p < Ncl; ++p) {
8024       const PetscInt  pnt  = points[p * 2];
8025       const PetscInt *perm = perms[0] ? perms[0][p] : NULL;
8026 
8027       if (pnt < idxStart || pnt >= idxEnd) continue;
8028       PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
8029       /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
8030        * not (at the time of this writing) have fields set. They probably should, in which case we would pass the global section. */
8031       PetscCall(DMPlexGetIndicesPoint_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, &off, PETSC_FALSE, perm, clperm, idx));
8032     }
8033   }
8034   /* 6) Cleanup */
8035   for (f = 0; f < PetscMax(1, Nf); ++f) {
8036     if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
8037     else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
8038   }
8039   if (NclC) {
8040     PetscCall(DMRestoreWorkArray(dm, NclC * 2, MPIU_INT, &pointsC));
8041   } else {
8042     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
8043   }
8044 
8045   if (indices) *indices = idx;
8046   PetscFunctionReturn(PETSC_SUCCESS);
8047 }
8048 
8049 /*@C
8050   DMPlexGetClosureIndices - Gets the global dof indices associated with the closure of the given point within the provided sections.
8051 
8052   Not collective
8053 
8054   Input Parameters:
8055 + dm         - The `DM`
8056 . section    - The `PetscSection` describing the points (a local section)
8057 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
8058 . point      - The point defining the closure
8059 - useClPerm  - Use the closure point permutation if available
8060 
8061   Output Parameters:
8062 + numIndices - The number of dof indices in the closure of point with the input sections
8063 . indices    - The dof indices
8064 . outOffsets - Array to write the field offsets into, or `NULL`
8065 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or `NULL`
8066 
8067   Level: advanced
8068 
8069   Notes:
8070   Must call `DMPlexRestoreClosureIndices()` to free allocated memory
8071 
8072   If `idxSection` is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
8073   of those indices is not significant.  If `idxSection` is local, the constrained dofs will yield the involution -(idx+1)
8074   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
8075   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when `idxSection` == section, otherwise global
8076   indices (with the above semantics) are implied.
8077 
8078 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`,
8079           `PetscSection`, `DMGetGlobalSection()`
8080 @*/
8081 PetscErrorCode DMPlexGetClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
8082 {
8083   PetscInt numRows = -1, numCols = -1;
8084 
8085   PetscFunctionBeginHot;
8086   PetscCall(DMPlexGetClosureIndices_Internal(dm, section, idxSection, point, useClPerm, &numRows, &numCols, indices, outOffsets, values, PETSC_TRUE, PETSC_TRUE));
8087   PetscCheck(numRows == numCols, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Symmetric matrix transformation produces rectangular dimensions (%" PetscInt_FMT ", %" PetscInt_FMT ")", numRows, numCols);
8088   *numIndices = numRows;
8089   PetscFunctionReturn(PETSC_SUCCESS);
8090 }
8091 
8092 /*@C
8093   DMPlexRestoreClosureIndices - Restores the global dof indices associated with the closure of the given point within the provided sections.
8094 
8095   Not collective
8096 
8097   Input Parameters:
8098 + dm         - The `DM`
8099 . section    - The `PetscSection` describing the points (a local section)
8100 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
8101 . point      - The point defining the closure
8102 - useClPerm  - Use the closure point permutation if available
8103 
8104   Output Parameters:
8105 + numIndices - The number of dof indices in the closure of point with the input sections
8106 . indices    - The dof indices
8107 . outOffsets - Array to write the field offsets into, or `NULL`
8108 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or `NULL`
8109 
8110   Level: advanced
8111 
8112   Notes:
8113   If values were modified, the user is responsible for calling `DMRestoreWorkArray`(dm, 0, `MPIU_SCALAR`, &values).
8114 
8115   If idxSection is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
8116   of those indices is not significant.  If idxSection is local, the constrained dofs will yield the involution -(idx+1)
8117   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
8118   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when idxSection == section, otherwise global
8119   indices (with the above semantics) are implied.
8120 
8121 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`, `DMGetGlobalSection()`
8122 @*/
8123 PetscErrorCode DMPlexRestoreClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
8124 {
8125   PetscFunctionBegin;
8126   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8127   PetscAssertPointer(indices, 7);
8128   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, indices));
8129   PetscFunctionReturn(PETSC_SUCCESS);
8130 }
8131 
8132 PetscErrorCode DMPlexMatSetClosure_Internal(DM dm, PetscSection section, PetscSection globalSection, PetscBool useClPerm, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8133 {
8134   DM_Plex           *mesh = (DM_Plex *)dm->data;
8135   PetscInt          *indices;
8136   PetscInt           numIndices;
8137   const PetscScalar *valuesOrig = values;
8138   PetscErrorCode     ierr;
8139 
8140   PetscFunctionBegin;
8141   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8142   if (!section) PetscCall(DMGetLocalSection(dm, &section));
8143   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
8144   if (!globalSection) PetscCall(DMGetGlobalSection(dm, &globalSection));
8145   PetscValidHeaderSpecific(globalSection, PETSC_SECTION_CLASSID, 3);
8146   PetscValidHeaderSpecific(A, MAT_CLASSID, 5);
8147 
8148   PetscCall(DMPlexGetClosureIndices(dm, section, globalSection, point, useClPerm, &numIndices, &indices, NULL, (PetscScalar **)&values));
8149 
8150   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndices, indices, 0, NULL, values));
8151   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8152   ierr = MatSetValues(A, numIndices, indices, numIndices, indices, values, mode);
8153   if (ierr) {
8154     PetscMPIInt rank;
8155 
8156     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8157     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8158     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndices, indices, 0, NULL, values));
8159     PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
8160     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
8161     SETERRQ(PetscObjectComm((PetscObject)dm), ierr, "Not possible to set matrix values");
8162   }
8163   if (mesh->printFEM > 1) {
8164     PetscInt i;
8165     PetscCall(PetscPrintf(PETSC_COMM_SELF, "  Indices:"));
8166     for (i = 0; i < numIndices; ++i) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, indices[i]));
8167     PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8168   }
8169 
8170   PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
8171   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
8172   PetscFunctionReturn(PETSC_SUCCESS);
8173 }
8174 
8175 /*@C
8176   DMPlexMatSetClosure - Set an array of the values on the closure of 'point'
8177 
8178   Not collective
8179 
8180   Input Parameters:
8181 + dm            - The `DM`
8182 . section       - The section describing the layout in `v`, or `NULL` to use the default section
8183 . globalSection - The section describing the layout in `v`, or `NULL` to use the default global section
8184 . A             - The matrix
8185 . point         - The point in the `DM`
8186 . values        - The array of values
8187 - mode          - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
8188 
8189   Level: intermediate
8190 
8191 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosureGeneral()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
8192 @*/
8193 PetscErrorCode DMPlexMatSetClosure(DM dm, PetscSection section, PetscSection globalSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8194 {
8195   PetscFunctionBegin;
8196   PetscCall(DMPlexMatSetClosure_Internal(dm, section, globalSection, PETSC_TRUE, A, point, values, mode));
8197   PetscFunctionReturn(PETSC_SUCCESS);
8198 }
8199 
8200 /*@C
8201   DMPlexMatSetClosureGeneral - Set an array of the values on the closure of 'point' using a different row and column section
8202 
8203   Not collective
8204 
8205   Input Parameters:
8206 + dmRow            - The `DM` for the row fields
8207 . sectionRow       - The section describing the layout, or `NULL` to use the default section in `dmRow`
8208 . useRowPerm       - The flag to use the closure permutation of the `dmRow` if available
8209 . globalSectionRow - The section describing the layout, or `NULL` to use the default global section in `dmRow`
8210 . dmCol            - The `DM` for the column fields
8211 . sectionCol       - The section describing the layout, or `NULL` to use the default section in `dmCol`
8212 . useColPerm       - The flag to use the closure permutation of the `dmCol` if available
8213 . globalSectionCol - The section describing the layout, or `NULL` to use the default global section in `dmCol`
8214 . A                - The matrix
8215 . point            - The point in the `DM`
8216 . values           - The array of values
8217 - mode             - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
8218 
8219   Level: intermediate
8220 
8221 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosure()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
8222 @*/
8223 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)
8224 {
8225   DM_Plex           *mesh = (DM_Plex *)dmRow->data;
8226   PetscInt          *indicesRow, *indicesCol;
8227   PetscInt           numIndicesRow = -1, numIndicesCol = -1;
8228   const PetscScalar *valuesV0 = values, *valuesV1, *valuesV2;
8229 
8230   PetscErrorCode ierr;
8231 
8232   PetscFunctionBegin;
8233   PetscValidHeaderSpecific(dmRow, DM_CLASSID, 1);
8234   if (!sectionRow) PetscCall(DMGetLocalSection(dmRow, &sectionRow));
8235   PetscValidHeaderSpecific(sectionRow, PETSC_SECTION_CLASSID, 2);
8236   if (!globalSectionRow) PetscCall(DMGetGlobalSection(dmRow, &globalSectionRow));
8237   PetscValidHeaderSpecific(globalSectionRow, PETSC_SECTION_CLASSID, 3);
8238   PetscValidHeaderSpecific(dmCol, DM_CLASSID, 5);
8239   if (!sectionCol) PetscCall(DMGetLocalSection(dmCol, &sectionCol));
8240   PetscValidHeaderSpecific(sectionCol, PETSC_SECTION_CLASSID, 6);
8241   if (!globalSectionCol) PetscCall(DMGetGlobalSection(dmCol, &globalSectionCol));
8242   PetscValidHeaderSpecific(globalSectionCol, PETSC_SECTION_CLASSID, 7);
8243   PetscValidHeaderSpecific(A, MAT_CLASSID, 9);
8244 
8245   PetscCall(DMPlexGetClosureIndicesSize_Internal(dmRow, sectionRow, point, &numIndicesRow));
8246   PetscCall(DMPlexGetClosureIndicesSize_Internal(dmCol, sectionCol, point, &numIndicesCol));
8247   valuesV1 = valuesV0;
8248   PetscCall(DMPlexGetClosureIndices_Internal(dmRow, sectionRow, globalSectionRow, point, useRowPerm, &numIndicesRow, &numIndicesCol, &indicesRow, NULL, (PetscScalar **)&valuesV1, PETSC_FALSE, PETSC_TRUE));
8249   valuesV2 = valuesV1;
8250   PetscCall(DMPlexGetClosureIndices_Internal(dmCol, sectionCol, globalSectionCol, point, useColPerm, &numIndicesRow, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&valuesV2, PETSC_TRUE, PETSC_FALSE));
8251 
8252   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, valuesV2));
8253   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8254   ierr = MatSetValues(A, numIndicesRow, indicesRow, numIndicesCol, indicesCol, valuesV2, mode);
8255   if (ierr) {
8256     PetscMPIInt rank;
8257 
8258     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8259     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8260     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
8261     PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesRow, NULL, (PetscScalar **)&valuesV2));
8262     PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&valuesV1));
8263     if (valuesV2 != valuesV1) PetscCall(DMRestoreWorkArray(dmCol, 0, MPIU_SCALAR, &valuesV2));
8264     if (valuesV1 != valuesV0) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &valuesV1));
8265   }
8266 
8267   PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, useColPerm, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&valuesV2));
8268   PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, useRowPerm, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&valuesV1));
8269   if (valuesV2 != valuesV1) PetscCall(DMRestoreWorkArray(dmCol, 0, MPIU_SCALAR, &valuesV2));
8270   if (valuesV1 != valuesV0) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &valuesV1));
8271   PetscFunctionReturn(PETSC_SUCCESS);
8272 }
8273 
8274 PetscErrorCode DMPlexMatSetClosureRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8275 {
8276   DM_Plex        *mesh    = (DM_Plex *)dmf->data;
8277   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
8278   PetscInt       *cpoints = NULL;
8279   PetscInt       *findices, *cindices;
8280   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
8281   PetscInt        foffsets[32], coffsets[32];
8282   DMPolytopeType  ct;
8283   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
8284   PetscErrorCode  ierr;
8285 
8286   PetscFunctionBegin;
8287   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
8288   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
8289   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
8290   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
8291   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
8292   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
8293   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
8294   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
8295   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
8296   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
8297   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
8298   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
8299   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
8300   PetscCall(PetscArrayzero(foffsets, 32));
8301   PetscCall(PetscArrayzero(coffsets, 32));
8302   /* Column indices */
8303   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8304   maxFPoints = numCPoints;
8305   /* Compress out points not in the section */
8306   /*   TODO: Squeeze out points with 0 dof as well */
8307   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
8308   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
8309     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
8310       cpoints[q * 2]     = cpoints[p];
8311       cpoints[q * 2 + 1] = cpoints[p + 1];
8312       ++q;
8313     }
8314   }
8315   numCPoints = q;
8316   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
8317     PetscInt fdof;
8318 
8319     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
8320     if (!dof) continue;
8321     for (f = 0; f < numFields; ++f) {
8322       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
8323       coffsets[f + 1] += fdof;
8324     }
8325     numCIndices += dof;
8326   }
8327   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
8328   /* Row indices */
8329   PetscCall(DMPlexGetCellType(dmc, point, &ct));
8330   {
8331     DMPlexTransform tr;
8332     DMPolytopeType *rct;
8333     PetscInt       *rsize, *rcone, *rornt, Nt;
8334 
8335     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8336     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8337     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8338     numSubcells = rsize[Nt - 1];
8339     PetscCall(DMPlexTransformDestroy(&tr));
8340   }
8341   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8342   for (r = 0, q = 0; r < numSubcells; ++r) {
8343     /* TODO Map from coarse to fine cells */
8344     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8345     /* Compress out points not in the section */
8346     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8347     for (p = 0; p < numFPoints * 2; p += 2) {
8348       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8349         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8350         if (!dof) continue;
8351         for (s = 0; s < q; ++s)
8352           if (fpoints[p] == ftotpoints[s * 2]) break;
8353         if (s < q) continue;
8354         ftotpoints[q * 2]     = fpoints[p];
8355         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8356         ++q;
8357       }
8358     }
8359     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8360   }
8361   numFPoints = q;
8362   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8363     PetscInt fdof;
8364 
8365     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8366     if (!dof) continue;
8367     for (f = 0; f < numFields; ++f) {
8368       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8369       foffsets[f + 1] += fdof;
8370     }
8371     numFIndices += dof;
8372   }
8373   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8374 
8375   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8376   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8377   PetscCall(DMGetWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8378   PetscCall(DMGetWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8379   if (numFields) {
8380     const PetscInt **permsF[32] = {NULL};
8381     const PetscInt **permsC[32] = {NULL};
8382 
8383     for (f = 0; f < numFields; f++) {
8384       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8385       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8386     }
8387     for (p = 0; p < numFPoints; p++) {
8388       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8389       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8390     }
8391     for (p = 0; p < numCPoints; p++) {
8392       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8393       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8394     }
8395     for (f = 0; f < numFields; f++) {
8396       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8397       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8398     }
8399   } else {
8400     const PetscInt **permsF = NULL;
8401     const PetscInt **permsC = NULL;
8402 
8403     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8404     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8405     for (p = 0, off = 0; p < numFPoints; p++) {
8406       const PetscInt *perm = permsF ? permsF[p] : NULL;
8407 
8408       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8409       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8410     }
8411     for (p = 0, off = 0; p < numCPoints; p++) {
8412       const PetscInt *perm = permsC ? permsC[p] : NULL;
8413 
8414       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8415       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8416     }
8417     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8418     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8419   }
8420   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8421   /* TODO: flips */
8422   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8423   ierr = MatSetValues(A, numFIndices, findices, numCIndices, cindices, values, mode);
8424   if (ierr) {
8425     PetscMPIInt rank;
8426 
8427     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8428     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8429     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8430     PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8431     PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8432   }
8433   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8434   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8435   PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8436   PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8437   PetscFunctionReturn(PETSC_SUCCESS);
8438 }
8439 
8440 PetscErrorCode DMPlexMatGetClosureIndicesRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, PetscInt point, PetscInt cindices[], PetscInt findices[])
8441 {
8442   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
8443   PetscInt       *cpoints      = NULL;
8444   PetscInt        foffsets[32] = {0}, coffsets[32] = {0};
8445   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
8446   DMPolytopeType  ct;
8447   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
8448 
8449   PetscFunctionBegin;
8450   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
8451   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
8452   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
8453   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
8454   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
8455   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
8456   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
8457   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
8458   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
8459   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
8460   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
8461   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
8462   /* Column indices */
8463   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8464   maxFPoints = numCPoints;
8465   /* Compress out points not in the section */
8466   /*   TODO: Squeeze out points with 0 dof as well */
8467   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
8468   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
8469     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
8470       cpoints[q * 2]     = cpoints[p];
8471       cpoints[q * 2 + 1] = cpoints[p + 1];
8472       ++q;
8473     }
8474   }
8475   numCPoints = q;
8476   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
8477     PetscInt fdof;
8478 
8479     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
8480     if (!dof) continue;
8481     for (f = 0; f < numFields; ++f) {
8482       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
8483       coffsets[f + 1] += fdof;
8484     }
8485     numCIndices += dof;
8486   }
8487   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
8488   /* Row indices */
8489   PetscCall(DMPlexGetCellType(dmc, point, &ct));
8490   {
8491     DMPlexTransform tr;
8492     DMPolytopeType *rct;
8493     PetscInt       *rsize, *rcone, *rornt, Nt;
8494 
8495     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8496     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8497     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8498     numSubcells = rsize[Nt - 1];
8499     PetscCall(DMPlexTransformDestroy(&tr));
8500   }
8501   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8502   for (r = 0, q = 0; r < numSubcells; ++r) {
8503     /* TODO Map from coarse to fine cells */
8504     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8505     /* Compress out points not in the section */
8506     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8507     for (p = 0; p < numFPoints * 2; p += 2) {
8508       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8509         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8510         if (!dof) continue;
8511         for (s = 0; s < q; ++s)
8512           if (fpoints[p] == ftotpoints[s * 2]) break;
8513         if (s < q) continue;
8514         ftotpoints[q * 2]     = fpoints[p];
8515         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8516         ++q;
8517       }
8518     }
8519     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8520   }
8521   numFPoints = q;
8522   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8523     PetscInt fdof;
8524 
8525     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8526     if (!dof) continue;
8527     for (f = 0; f < numFields; ++f) {
8528       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8529       foffsets[f + 1] += fdof;
8530     }
8531     numFIndices += dof;
8532   }
8533   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8534 
8535   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8536   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8537   if (numFields) {
8538     const PetscInt **permsF[32] = {NULL};
8539     const PetscInt **permsC[32] = {NULL};
8540 
8541     for (f = 0; f < numFields; f++) {
8542       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8543       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8544     }
8545     for (p = 0; p < numFPoints; p++) {
8546       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8547       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8548     }
8549     for (p = 0; p < numCPoints; p++) {
8550       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8551       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8552     }
8553     for (f = 0; f < numFields; f++) {
8554       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8555       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8556     }
8557   } else {
8558     const PetscInt **permsF = NULL;
8559     const PetscInt **permsC = NULL;
8560 
8561     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8562     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8563     for (p = 0, off = 0; p < numFPoints; p++) {
8564       const PetscInt *perm = permsF ? permsF[p] : NULL;
8565 
8566       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8567       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8568     }
8569     for (p = 0, off = 0; p < numCPoints; p++) {
8570       const PetscInt *perm = permsC ? permsC[p] : NULL;
8571 
8572       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8573       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8574     }
8575     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8576     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8577   }
8578   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8579   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8580   PetscFunctionReturn(PETSC_SUCCESS);
8581 }
8582 
8583 /*@
8584   DMPlexGetVTKCellHeight - Returns the height in the DAG used to determine which points are cells (normally 0)
8585 
8586   Input Parameter:
8587 . dm - The `DMPLEX` object
8588 
8589   Output Parameter:
8590 . cellHeight - The height of a cell
8591 
8592   Level: developer
8593 
8594 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetVTKCellHeight()`
8595 @*/
8596 PetscErrorCode DMPlexGetVTKCellHeight(DM dm, PetscInt *cellHeight)
8597 {
8598   DM_Plex *mesh = (DM_Plex *)dm->data;
8599 
8600   PetscFunctionBegin;
8601   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8602   PetscAssertPointer(cellHeight, 2);
8603   *cellHeight = mesh->vtkCellHeight;
8604   PetscFunctionReturn(PETSC_SUCCESS);
8605 }
8606 
8607 /*@
8608   DMPlexSetVTKCellHeight - Sets the height in the DAG used to determine which points are cells (normally 0)
8609 
8610   Input Parameters:
8611 + dm         - The `DMPLEX` object
8612 - cellHeight - The height of a cell
8613 
8614   Level: developer
8615 
8616 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetVTKCellHeight()`
8617 @*/
8618 PetscErrorCode DMPlexSetVTKCellHeight(DM dm, PetscInt cellHeight)
8619 {
8620   DM_Plex *mesh = (DM_Plex *)dm->data;
8621 
8622   PetscFunctionBegin;
8623   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8624   mesh->vtkCellHeight = cellHeight;
8625   PetscFunctionReturn(PETSC_SUCCESS);
8626 }
8627 
8628 /*@
8629   DMPlexGetCellTypeStratum - Get the range of cells of a given celltype
8630 
8631   Input Parameters:
8632 + dm - The `DMPLEX` object
8633 - ct - The `DMPolytopeType` of the cell
8634 
8635   Output Parameters:
8636 + start - The first cell of this type, or `NULL`
8637 - end   - The upper bound on this celltype, or `NULL`
8638 
8639   Level: advanced
8640 
8641 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexConstructGhostCells()`, `DMPlexGetDepthStratum()`, `DMPlexGetHeightStratum()`
8642 @*/
8643 PetscErrorCode DMPlexGetCellTypeStratum(DM dm, DMPolytopeType ct, PetscInt *start, PetscInt *end)
8644 {
8645   DM_Plex *mesh = (DM_Plex *)dm->data;
8646   DMLabel  label;
8647   PetscInt pStart, pEnd;
8648 
8649   PetscFunctionBegin;
8650   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8651   if (start) {
8652     PetscAssertPointer(start, 3);
8653     *start = 0;
8654   }
8655   if (end) {
8656     PetscAssertPointer(end, 4);
8657     *end = 0;
8658   }
8659   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8660   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
8661   if (mesh->tr) {
8662     PetscCall(DMPlexTransformGetCellTypeStratum(mesh->tr, ct, start, end));
8663   } else {
8664     PetscCall(DMPlexGetCellTypeLabel(dm, &label));
8665     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named celltype was found");
8666     PetscCall(DMLabelGetStratumBounds(label, ct, start, end));
8667   }
8668   PetscFunctionReturn(PETSC_SUCCESS);
8669 }
8670 
8671 PetscErrorCode DMPlexCreateNumbering_Plex(DM dm, PetscInt pStart, PetscInt pEnd, PetscInt shift, PetscInt *globalSize, PetscSF sf, IS *numbering)
8672 {
8673   PetscSection section, globalSection;
8674   PetscInt    *numbers, p;
8675 
8676   PetscFunctionBegin;
8677   if (PetscDefined(USE_DEBUG)) PetscCall(DMPlexCheckPointSF(dm, sf, PETSC_TRUE));
8678   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
8679   PetscCall(PetscSectionSetChart(section, pStart, pEnd));
8680   for (p = pStart; p < pEnd; ++p) PetscCall(PetscSectionSetDof(section, p, 1));
8681   PetscCall(PetscSectionSetUp(section));
8682   PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &globalSection));
8683   PetscCall(PetscMalloc1(pEnd - pStart, &numbers));
8684   for (p = pStart; p < pEnd; ++p) {
8685     PetscCall(PetscSectionGetOffset(globalSection, p, &numbers[p - pStart]));
8686     if (numbers[p - pStart] < 0) numbers[p - pStart] -= shift;
8687     else numbers[p - pStart] += shift;
8688   }
8689   PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), pEnd - pStart, numbers, PETSC_OWN_POINTER, numbering));
8690   if (globalSize) {
8691     PetscLayout layout;
8692     PetscCall(PetscSectionGetPointLayout(PetscObjectComm((PetscObject)dm), globalSection, &layout));
8693     PetscCall(PetscLayoutGetSize(layout, globalSize));
8694     PetscCall(PetscLayoutDestroy(&layout));
8695   }
8696   PetscCall(PetscSectionDestroy(&section));
8697   PetscCall(PetscSectionDestroy(&globalSection));
8698   PetscFunctionReturn(PETSC_SUCCESS);
8699 }
8700 
8701 PetscErrorCode DMPlexCreateCellNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalCellNumbers)
8702 {
8703   PetscInt cellHeight, cStart, cEnd;
8704 
8705   PetscFunctionBegin;
8706   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8707   if (includeHybrid) PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8708   else PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd));
8709   PetscCall(DMPlexCreateNumbering_Plex(dm, cStart, cEnd, 0, NULL, dm->sf, globalCellNumbers));
8710   PetscFunctionReturn(PETSC_SUCCESS);
8711 }
8712 
8713 /*@
8714   DMPlexGetCellNumbering - Get a global cell numbering for all cells on this process
8715 
8716   Input Parameter:
8717 . dm - The `DMPLEX` object
8718 
8719   Output Parameter:
8720 . globalCellNumbers - Global cell numbers for all cells on this process
8721 
8722   Level: developer
8723 
8724 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetVertexNumbering()`
8725 @*/
8726 PetscErrorCode DMPlexGetCellNumbering(DM dm, IS *globalCellNumbers)
8727 {
8728   DM_Plex *mesh = (DM_Plex *)dm->data;
8729 
8730   PetscFunctionBegin;
8731   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8732   if (!mesh->globalCellNumbers) PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_FALSE, &mesh->globalCellNumbers));
8733   *globalCellNumbers = mesh->globalCellNumbers;
8734   PetscFunctionReturn(PETSC_SUCCESS);
8735 }
8736 
8737 PetscErrorCode DMPlexCreateVertexNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalVertexNumbers)
8738 {
8739   PetscInt vStart, vEnd;
8740 
8741   PetscFunctionBegin;
8742   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8743   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8744   PetscCall(DMPlexCreateNumbering_Plex(dm, vStart, vEnd, 0, NULL, dm->sf, globalVertexNumbers));
8745   PetscFunctionReturn(PETSC_SUCCESS);
8746 }
8747 
8748 /*@
8749   DMPlexGetVertexNumbering - Get a global vertex numbering for all vertices on this process
8750 
8751   Input Parameter:
8752 . dm - The `DMPLEX` object
8753 
8754   Output Parameter:
8755 . globalVertexNumbers - Global vertex numbers for all vertices on this process
8756 
8757   Level: developer
8758 
8759 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
8760 @*/
8761 PetscErrorCode DMPlexGetVertexNumbering(DM dm, IS *globalVertexNumbers)
8762 {
8763   DM_Plex *mesh = (DM_Plex *)dm->data;
8764 
8765   PetscFunctionBegin;
8766   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8767   if (!mesh->globalVertexNumbers) PetscCall(DMPlexCreateVertexNumbering_Internal(dm, PETSC_FALSE, &mesh->globalVertexNumbers));
8768   *globalVertexNumbers = mesh->globalVertexNumbers;
8769   PetscFunctionReturn(PETSC_SUCCESS);
8770 }
8771 
8772 /*@
8773   DMPlexCreatePointNumbering - Create a global numbering for all points.
8774 
8775   Collective
8776 
8777   Input Parameter:
8778 . dm - The `DMPLEX` object
8779 
8780   Output Parameter:
8781 . globalPointNumbers - Global numbers for all points on this process
8782 
8783   Level: developer
8784 
8785   Notes:
8786   The point numbering `IS` is parallel, with local portion indexed by local points (see `DMGetLocalSection()`). The global
8787   points are taken as stratified, with each MPI rank owning a contiguous subset of each stratum. In the IS, owned points
8788   will have their non-negative value while points owned by different ranks will be involuted -(idx+1). As an example,
8789   consider a parallel mesh in which the first two elements and first two vertices are owned by rank 0.
8790 
8791   The partitioned mesh is
8792   ```
8793   (2)--0--(3)--1--(4)    (1)--0--(2)
8794   ```
8795   and its global numbering is
8796   ```
8797   (3)--0--(4)--1--(5)--2--(6)
8798   ```
8799   Then the global numbering is provided as
8800   ```
8801   [0] Number of indices in set 5
8802   [0] 0 0
8803   [0] 1 1
8804   [0] 2 3
8805   [0] 3 4
8806   [0] 4 -6
8807   [1] Number of indices in set 3
8808   [1] 0 2
8809   [1] 1 5
8810   [1] 2 6
8811   ```
8812 
8813 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
8814 @*/
8815 PetscErrorCode DMPlexCreatePointNumbering(DM dm, IS *globalPointNumbers)
8816 {
8817   IS        nums[4];
8818   PetscInt  depths[4], gdepths[4], starts[4];
8819   PetscInt  depth, d, shift = 0;
8820   PetscBool empty = PETSC_FALSE;
8821 
8822   PetscFunctionBegin;
8823   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8824   PetscCall(DMPlexGetDepth(dm, &depth));
8825   // For unstratified meshes use dim instead of depth
8826   if (depth < 0) PetscCall(DMGetDimension(dm, &depth));
8827   // If any stratum is empty, we must mark all empty
8828   for (d = 0; d <= depth; ++d) {
8829     PetscInt end;
8830 
8831     depths[d] = depth - d;
8832     PetscCall(DMPlexGetDepthStratum(dm, depths[d], &starts[d], &end));
8833     if (!(starts[d] - end)) empty = PETSC_TRUE;
8834   }
8835   if (empty)
8836     for (d = 0; d <= depth; ++d) {
8837       depths[d] = -1;
8838       starts[d] = -1;
8839     }
8840   else PetscCall(PetscSortIntWithArray(depth + 1, starts, depths));
8841   PetscCall(MPIU_Allreduce(depths, gdepths, depth + 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
8842   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]);
8843   // Note here that 'shift' is collective, so that the numbering is stratified by depth
8844   for (d = 0; d <= depth; ++d) {
8845     PetscInt pStart, pEnd, gsize;
8846 
8847     PetscCall(DMPlexGetDepthStratum(dm, gdepths[d], &pStart, &pEnd));
8848     PetscCall(DMPlexCreateNumbering_Plex(dm, pStart, pEnd, shift, &gsize, dm->sf, &nums[d]));
8849     shift += gsize;
8850   }
8851   PetscCall(ISConcatenate(PETSC_COMM_SELF, depth + 1, nums, globalPointNumbers));
8852   for (d = 0; d <= depth; ++d) PetscCall(ISDestroy(&nums[d]));
8853   PetscFunctionReturn(PETSC_SUCCESS);
8854 }
8855 
8856 /*@
8857   DMPlexCreateEdgeNumbering - Create a global numbering for edges.
8858 
8859   Collective
8860 
8861   Input Parameter:
8862 . dm - The `DMPLEX` object
8863 
8864   Output Parameter:
8865 . globalEdgeNumbers - Global numbers for all edges on this process
8866 
8867   Level: developer
8868 
8869   Notes:
8870   The point numbering `IS` is parallel, with local portion indexed by local points (see `DMGetLocalSection()`). In the IS, owned edges will have their non-negative value while edges owned by different ranks will be involuted -(idx+1).
8871 
8872 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`, `DMPlexGetVertexNumbering()`, `DMPlexCreatePointNumbering()`
8873 @*/
8874 PetscErrorCode DMPlexCreateEdgeNumbering(DM dm, IS *globalEdgeNumbers)
8875 {
8876   PetscSF  sf;
8877   PetscInt eStart, eEnd;
8878 
8879   PetscFunctionBegin;
8880   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8881   PetscCall(DMGetPointSF(dm, &sf));
8882   PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
8883   PetscCall(DMPlexCreateNumbering_Plex(dm, eStart, eEnd, 0, NULL, sf, globalEdgeNumbers));
8884   PetscFunctionReturn(PETSC_SUCCESS);
8885 }
8886 
8887 /*@
8888   DMPlexCreateRankField - Create a cell field whose value is the rank of the owner
8889 
8890   Input Parameter:
8891 . dm - The `DMPLEX` object
8892 
8893   Output Parameter:
8894 . ranks - The rank field
8895 
8896   Options Database Key:
8897 . -dm_partition_view - Adds the rank field into the `DM` output from `-dm_view` using the same viewer
8898 
8899   Level: intermediate
8900 
8901 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`
8902 @*/
8903 PetscErrorCode DMPlexCreateRankField(DM dm, Vec *ranks)
8904 {
8905   DM             rdm;
8906   PetscFE        fe;
8907   PetscScalar   *r;
8908   PetscMPIInt    rank;
8909   DMPolytopeType ct;
8910   PetscInt       dim, cStart, cEnd, c;
8911   PetscBool      simplex;
8912 
8913   PetscFunctionBeginUser;
8914   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8915   PetscAssertPointer(ranks, 2);
8916   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
8917   PetscCall(DMClone(dm, &rdm));
8918   PetscCall(DMGetDimension(rdm, &dim));
8919   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
8920   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
8921   simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct) + 1 ? PETSC_TRUE : PETSC_FALSE;
8922   PetscCall(PetscFECreateDefault(PETSC_COMM_SELF, dim, 1, simplex, "PETSc___rank_", -1, &fe));
8923   PetscCall(PetscObjectSetName((PetscObject)fe, "rank"));
8924   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
8925   PetscCall(PetscFEDestroy(&fe));
8926   PetscCall(DMCreateDS(rdm));
8927   PetscCall(DMCreateGlobalVector(rdm, ranks));
8928   PetscCall(PetscObjectSetName((PetscObject)*ranks, "partition"));
8929   PetscCall(VecGetArray(*ranks, &r));
8930   for (c = cStart; c < cEnd; ++c) {
8931     PetscScalar *lr;
8932 
8933     PetscCall(DMPlexPointGlobalRef(rdm, c, r, &lr));
8934     if (lr) *lr = rank;
8935   }
8936   PetscCall(VecRestoreArray(*ranks, &r));
8937   PetscCall(DMDestroy(&rdm));
8938   PetscFunctionReturn(PETSC_SUCCESS);
8939 }
8940 
8941 /*@
8942   DMPlexCreateLabelField - Create a field whose value is the label value for that point
8943 
8944   Input Parameters:
8945 + dm    - The `DMPLEX`
8946 - label - The `DMLabel`
8947 
8948   Output Parameter:
8949 . val - The label value field
8950 
8951   Options Database Key:
8952 . -dm_label_view - Adds the label value field into the `DM` output from `-dm_view` using the same viewer
8953 
8954   Level: intermediate
8955 
8956 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`
8957 @*/
8958 PetscErrorCode DMPlexCreateLabelField(DM dm, DMLabel label, Vec *val)
8959 {
8960   DM             rdm, plex;
8961   Vec            lval;
8962   PetscSection   section;
8963   PetscFE        fe;
8964   PetscScalar   *v;
8965   PetscInt       dim, pStart, pEnd, p, cStart;
8966   DMPolytopeType ct;
8967   char           name[PETSC_MAX_PATH_LEN];
8968   const char    *lname, *prefix;
8969 
8970   PetscFunctionBeginUser;
8971   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8972   PetscAssertPointer(label, 2);
8973   PetscAssertPointer(val, 3);
8974   PetscCall(DMClone(dm, &rdm));
8975   PetscCall(DMConvert(rdm, DMPLEX, &plex));
8976   PetscCall(DMPlexGetHeightStratum(plex, 0, &cStart, NULL));
8977   PetscCall(DMPlexGetCellType(plex, cStart, &ct));
8978   PetscCall(DMDestroy(&plex));
8979   PetscCall(DMGetDimension(rdm, &dim));
8980   PetscCall(DMGetOptionsPrefix(dm, &prefix));
8981   PetscCall(PetscObjectGetName((PetscObject)label, &lname));
8982   PetscCall(PetscSNPrintf(name, sizeof(name), "%s%s_", prefix ? prefix : "", lname));
8983   PetscCall(PetscFECreateByCell(PETSC_COMM_SELF, dim, 1, ct, name, -1, &fe));
8984   PetscCall(PetscObjectSetName((PetscObject)fe, ""));
8985   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
8986   PetscCall(PetscFEDestroy(&fe));
8987   PetscCall(DMCreateDS(rdm));
8988   PetscCall(DMCreateGlobalVector(rdm, val));
8989   PetscCall(DMCreateLocalVector(rdm, &lval));
8990   PetscCall(PetscObjectSetName((PetscObject)*val, lname));
8991   PetscCall(DMGetLocalSection(rdm, &section));
8992   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
8993   PetscCall(VecGetArray(lval, &v));
8994   for (p = pStart; p < pEnd; ++p) {
8995     PetscInt cval, dof, off;
8996 
8997     PetscCall(PetscSectionGetDof(section, p, &dof));
8998     if (!dof) continue;
8999     PetscCall(DMLabelGetValue(label, p, &cval));
9000     PetscCall(PetscSectionGetOffset(section, p, &off));
9001     for (PetscInt d = 0; d < dof; d++) v[off + d] = cval;
9002   }
9003   PetscCall(VecRestoreArray(lval, &v));
9004   PetscCall(DMLocalToGlobal(rdm, lval, INSERT_VALUES, *val));
9005   PetscCall(VecDestroy(&lval));
9006   PetscCall(DMDestroy(&rdm));
9007   PetscFunctionReturn(PETSC_SUCCESS);
9008 }
9009 
9010 /*@
9011   DMPlexCheckSymmetry - Check that the adjacency information in the mesh is symmetric.
9012 
9013   Input Parameter:
9014 . dm - The `DMPLEX` object
9015 
9016   Level: developer
9017 
9018   Notes:
9019   This is a useful diagnostic when creating meshes programmatically.
9020 
9021   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9022 
9023 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9024 @*/
9025 PetscErrorCode DMPlexCheckSymmetry(DM dm)
9026 {
9027   PetscSection    coneSection, supportSection;
9028   const PetscInt *cone, *support;
9029   PetscInt        coneSize, c, supportSize, s;
9030   PetscInt        pStart, pEnd, p, pp, csize, ssize;
9031   PetscBool       storagecheck = PETSC_TRUE;
9032 
9033   PetscFunctionBegin;
9034   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9035   PetscCall(DMViewFromOptions(dm, NULL, "-sym_dm_view"));
9036   PetscCall(DMPlexGetConeSection(dm, &coneSection));
9037   PetscCall(DMPlexGetSupportSection(dm, &supportSection));
9038   /* Check that point p is found in the support of its cone points, and vice versa */
9039   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
9040   for (p = pStart; p < pEnd; ++p) {
9041     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
9042     PetscCall(DMPlexGetCone(dm, p, &cone));
9043     for (c = 0; c < coneSize; ++c) {
9044       PetscBool dup = PETSC_FALSE;
9045       PetscInt  d;
9046       for (d = c - 1; d >= 0; --d) {
9047         if (cone[c] == cone[d]) {
9048           dup = PETSC_TRUE;
9049           break;
9050         }
9051       }
9052       PetscCall(DMPlexGetSupportSize(dm, cone[c], &supportSize));
9053       PetscCall(DMPlexGetSupport(dm, cone[c], &support));
9054       for (s = 0; s < supportSize; ++s) {
9055         if (support[s] == p) break;
9056       }
9057       if ((s >= supportSize) || (dup && (support[s + 1] != p))) {
9058         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", p));
9059         for (s = 0; s < coneSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[s]));
9060         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9061         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", cone[c]));
9062         for (s = 0; s < supportSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[s]));
9063         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9064         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]);
9065         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in support of cone point %" PetscInt_FMT, p, cone[c]);
9066       }
9067     }
9068     PetscCall(DMPlexGetTreeParent(dm, p, &pp, NULL));
9069     if (p != pp) {
9070       storagecheck = PETSC_FALSE;
9071       continue;
9072     }
9073     PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
9074     PetscCall(DMPlexGetSupport(dm, p, &support));
9075     for (s = 0; s < supportSize; ++s) {
9076       PetscCall(DMPlexGetConeSize(dm, support[s], &coneSize));
9077       PetscCall(DMPlexGetCone(dm, support[s], &cone));
9078       for (c = 0; c < coneSize; ++c) {
9079         PetscCall(DMPlexGetTreeParent(dm, cone[c], &pp, NULL));
9080         if (cone[c] != pp) {
9081           c = 0;
9082           break;
9083         }
9084         if (cone[c] == p) break;
9085       }
9086       if (c >= coneSize) {
9087         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", p));
9088         for (c = 0; c < supportSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[c]));
9089         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9090         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", support[s]));
9091         for (c = 0; c < coneSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[c]));
9092         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9093         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in cone of support point %" PetscInt_FMT, p, support[s]);
9094       }
9095     }
9096   }
9097   if (storagecheck) {
9098     PetscCall(PetscSectionGetStorageSize(coneSection, &csize));
9099     PetscCall(PetscSectionGetStorageSize(supportSection, &ssize));
9100     PetscCheck(csize == ssize, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Total cone size %" PetscInt_FMT " != Total support size %" PetscInt_FMT, csize, ssize);
9101   }
9102   PetscFunctionReturn(PETSC_SUCCESS);
9103 }
9104 
9105 /*
9106   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.
9107 */
9108 static PetscErrorCode DMPlexCellUnsplitVertices_Private(DM dm, PetscInt c, DMPolytopeType ct, PetscInt *unsplit)
9109 {
9110   DMPolytopeType  cct;
9111   PetscInt        ptpoints[4];
9112   const PetscInt *cone, *ccone, *ptcone;
9113   PetscInt        coneSize, cp, cconeSize, ccp, npt = 0, pt;
9114 
9115   PetscFunctionBegin;
9116   *unsplit = 0;
9117   switch (ct) {
9118   case DM_POLYTOPE_POINT_PRISM_TENSOR:
9119     ptpoints[npt++] = c;
9120     break;
9121   case DM_POLYTOPE_SEG_PRISM_TENSOR:
9122     PetscCall(DMPlexGetCone(dm, c, &cone));
9123     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9124     for (cp = 0; cp < coneSize; ++cp) {
9125       PetscCall(DMPlexGetCellType(dm, cone[cp], &cct));
9126       if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) ptpoints[npt++] = cone[cp];
9127     }
9128     break;
9129   case DM_POLYTOPE_TRI_PRISM_TENSOR:
9130   case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9131     PetscCall(DMPlexGetCone(dm, c, &cone));
9132     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9133     for (cp = 0; cp < coneSize; ++cp) {
9134       PetscCall(DMPlexGetCone(dm, cone[cp], &ccone));
9135       PetscCall(DMPlexGetConeSize(dm, cone[cp], &cconeSize));
9136       for (ccp = 0; ccp < cconeSize; ++ccp) {
9137         PetscCall(DMPlexGetCellType(dm, ccone[ccp], &cct));
9138         if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) {
9139           PetscInt p;
9140           for (p = 0; p < npt; ++p)
9141             if (ptpoints[p] == ccone[ccp]) break;
9142           if (p == npt) ptpoints[npt++] = ccone[ccp];
9143         }
9144       }
9145     }
9146     break;
9147   default:
9148     break;
9149   }
9150   for (pt = 0; pt < npt; ++pt) {
9151     PetscCall(DMPlexGetCone(dm, ptpoints[pt], &ptcone));
9152     if (ptcone[0] == ptcone[1]) ++(*unsplit);
9153   }
9154   PetscFunctionReturn(PETSC_SUCCESS);
9155 }
9156 
9157 /*@
9158   DMPlexCheckSkeleton - Check that each cell has the correct number of vertices
9159 
9160   Input Parameters:
9161 + dm         - The `DMPLEX` object
9162 - cellHeight - Normally 0
9163 
9164   Level: developer
9165 
9166   Notes:
9167   This is a useful diagnostic when creating meshes programmatically.
9168   Currently applicable only to homogeneous simplex or tensor meshes.
9169 
9170   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9171 
9172 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9173 @*/
9174 PetscErrorCode DMPlexCheckSkeleton(DM dm, PetscInt cellHeight)
9175 {
9176   DMPlexInterpolatedFlag interp;
9177   DMPolytopeType         ct;
9178   PetscInt               vStart, vEnd, cStart, cEnd, c;
9179 
9180   PetscFunctionBegin;
9181   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9182   PetscCall(DMPlexIsInterpolated(dm, &interp));
9183   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9184   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
9185   for (c = cStart; c < cEnd; ++c) {
9186     PetscInt *closure = NULL;
9187     PetscInt  coneSize, closureSize, cl, Nv = 0;
9188 
9189     PetscCall(DMPlexGetCellType(dm, c, &ct));
9190     PetscCheck((PetscInt)ct >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell %" PetscInt_FMT " has no cell type", c);
9191     if (ct == DM_POLYTOPE_UNKNOWN) continue;
9192     if (interp == DMPLEX_INTERPOLATED_FULL) {
9193       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9194       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));
9195     }
9196     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9197     for (cl = 0; cl < closureSize * 2; cl += 2) {
9198       const PetscInt p = closure[cl];
9199       if ((p >= vStart) && (p < vEnd)) ++Nv;
9200     }
9201     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9202     /* Special Case: Tensor faces with identified vertices */
9203     if (Nv < DMPolytopeTypeGetNumVertices(ct)) {
9204       PetscInt unsplit;
9205 
9206       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9207       if (Nv + unsplit == DMPolytopeTypeGetNumVertices(ct)) continue;
9208     }
9209     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));
9210   }
9211   PetscFunctionReturn(PETSC_SUCCESS);
9212 }
9213 
9214 /*@
9215   DMPlexCheckFaces - Check that the faces of each cell give a vertex order this is consistent with what we expect from the cell type
9216 
9217   Collective
9218 
9219   Input Parameters:
9220 + dm         - The `DMPLEX` object
9221 - cellHeight - Normally 0
9222 
9223   Level: developer
9224 
9225   Notes:
9226   This is a useful diagnostic when creating meshes programmatically.
9227   This routine is only relevant for meshes that are fully interpolated across all ranks.
9228   It will error out if a partially interpolated mesh is given on some rank.
9229   It will do nothing for locally uninterpolated mesh (as there is nothing to check).
9230 
9231   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9232 
9233 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMPlexGetVTKCellHeight()`, `DMSetFromOptions()`
9234 @*/
9235 PetscErrorCode DMPlexCheckFaces(DM dm, PetscInt cellHeight)
9236 {
9237   PetscInt               dim, depth, vStart, vEnd, cStart, cEnd, c, h;
9238   DMPlexInterpolatedFlag interpEnum;
9239 
9240   PetscFunctionBegin;
9241   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9242   PetscCall(DMPlexIsInterpolatedCollective(dm, &interpEnum));
9243   if (interpEnum == DMPLEX_INTERPOLATED_NONE) PetscFunctionReturn(PETSC_SUCCESS);
9244   if (interpEnum != DMPLEX_INTERPOLATED_FULL) {
9245     PetscCall(PetscPrintf(PetscObjectComm((PetscObject)dm), "DMPlexCheckFaces() warning: Mesh is only partially interpolated, this is currently not supported"));
9246     PetscFunctionReturn(PETSC_SUCCESS);
9247   }
9248 
9249   PetscCall(DMGetDimension(dm, &dim));
9250   PetscCall(DMPlexGetDepth(dm, &depth));
9251   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
9252   for (h = cellHeight; h < PetscMin(depth, dim); ++h) {
9253     PetscCall(DMPlexGetHeightStratum(dm, h, &cStart, &cEnd));
9254     for (c = cStart; c < cEnd; ++c) {
9255       const PetscInt       *cone, *ornt, *faceSizes, *faces;
9256       const DMPolytopeType *faceTypes;
9257       DMPolytopeType        ct;
9258       PetscInt              numFaces, coneSize, f;
9259       PetscInt             *closure = NULL, closureSize, cl, numCorners = 0, fOff = 0, unsplit;
9260 
9261       PetscCall(DMPlexGetCellType(dm, c, &ct));
9262       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9263       if (unsplit) continue;
9264       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
9265       PetscCall(DMPlexGetCone(dm, c, &cone));
9266       PetscCall(DMPlexGetConeOrientation(dm, c, &ornt));
9267       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9268       for (cl = 0; cl < closureSize * 2; cl += 2) {
9269         const PetscInt p = closure[cl];
9270         if ((p >= vStart) && (p < vEnd)) closure[numCorners++] = p;
9271       }
9272       PetscCall(DMPlexGetRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
9273       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);
9274       for (f = 0; f < numFaces; ++f) {
9275         DMPolytopeType fct;
9276         PetscInt      *fclosure = NULL, fclosureSize, cl, fnumCorners = 0, v;
9277 
9278         PetscCall(DMPlexGetCellType(dm, cone[f], &fct));
9279         PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[f], ornt[f], PETSC_TRUE, &fclosureSize, &fclosure));
9280         for (cl = 0; cl < fclosureSize * 2; cl += 2) {
9281           const PetscInt p = fclosure[cl];
9282           if ((p >= vStart) && (p < vEnd)) fclosure[fnumCorners++] = p;
9283         }
9284         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]);
9285         for (v = 0; v < fnumCorners; ++v) {
9286           if (fclosure[v] != faces[fOff + v]) {
9287             PetscInt v1;
9288 
9289             PetscCall(PetscPrintf(PETSC_COMM_SELF, "face closure:"));
9290             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, fclosure[v1]));
9291             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\ncell face:"));
9292             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, faces[fOff + v1]));
9293             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
9294             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]);
9295           }
9296         }
9297         PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[f], PETSC_TRUE, &fclosureSize, &fclosure));
9298         fOff += faceSizes[f];
9299       }
9300       PetscCall(DMPlexRestoreRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
9301       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
9302     }
9303   }
9304   PetscFunctionReturn(PETSC_SUCCESS);
9305 }
9306 
9307 /*@
9308   DMPlexCheckGeometry - Check the geometry of mesh cells
9309 
9310   Input Parameter:
9311 . dm - The `DMPLEX` object
9312 
9313   Level: developer
9314 
9315   Notes:
9316   This is a useful diagnostic when creating meshes programmatically.
9317 
9318   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9319 
9320 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9321 @*/
9322 PetscErrorCode DMPlexCheckGeometry(DM dm)
9323 {
9324   Vec       coordinates;
9325   PetscReal detJ, J[9], refVol = 1.0;
9326   PetscReal vol;
9327   PetscInt  dim, depth, dE, d, cStart, cEnd, c;
9328 
9329   PetscFunctionBegin;
9330   PetscCall(DMGetDimension(dm, &dim));
9331   PetscCall(DMGetCoordinateDim(dm, &dE));
9332   if (dim != dE) PetscFunctionReturn(PETSC_SUCCESS);
9333   PetscCall(DMPlexGetDepth(dm, &depth));
9334   for (d = 0; d < dim; ++d) refVol *= 2.0;
9335   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
9336   /* Make sure local coordinates are created, because that step is collective */
9337   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
9338   if (!coordinates) PetscFunctionReturn(PETSC_SUCCESS);
9339   for (c = cStart; c < cEnd; ++c) {
9340     DMPolytopeType ct;
9341     PetscInt       unsplit;
9342     PetscBool      ignoreZeroVol = PETSC_FALSE;
9343 
9344     PetscCall(DMPlexGetCellType(dm, c, &ct));
9345     switch (ct) {
9346     case DM_POLYTOPE_SEG_PRISM_TENSOR:
9347     case DM_POLYTOPE_TRI_PRISM_TENSOR:
9348     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9349       ignoreZeroVol = PETSC_TRUE;
9350       break;
9351     default:
9352       break;
9353     }
9354     switch (ct) {
9355     case DM_POLYTOPE_TRI_PRISM:
9356     case DM_POLYTOPE_TRI_PRISM_TENSOR:
9357     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9358     case DM_POLYTOPE_PYRAMID:
9359       continue;
9360     default:
9361       break;
9362     }
9363     PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9364     if (unsplit) continue;
9365     PetscCall(DMPlexComputeCellGeometryFEM(dm, c, NULL, NULL, J, NULL, &detJ));
9366     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);
9367     PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FEM Volume %g\n", c, (double)(detJ * refVol)));
9368     /* This should work with periodicity since DG coordinates should be used */
9369     if (depth > 1) {
9370       PetscCall(DMPlexComputeCellGeometryFVM(dm, c, &vol, NULL, NULL));
9371       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);
9372       PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FVM Volume %g\n", c, (double)vol));
9373     }
9374   }
9375   PetscFunctionReturn(PETSC_SUCCESS);
9376 }
9377 
9378 /*@
9379   DMPlexCheckPointSF - Check that several necessary conditions are met for the point `PetscSF` of this plex.
9380 
9381   Collective
9382 
9383   Input Parameters:
9384 + dm              - The `DMPLEX` object
9385 . pointSF         - The `PetscSF`, or `NULL` for `PointSF` attached to `DM`
9386 - allowExtraRoots - Flag to allow extra points not present in the `DM`
9387 
9388   Level: developer
9389 
9390   Notes:
9391   This is mainly intended for debugging/testing purposes.
9392 
9393   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9394 
9395   Extra roots can come from periodic cuts, where additional points appear on the boundary
9396 
9397 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMGetPointSF()`, `DMSetFromOptions()`
9398 @*/
9399 PetscErrorCode DMPlexCheckPointSF(DM dm, PetscSF pointSF, PetscBool allowExtraRoots)
9400 {
9401   PetscInt           l, nleaves, nroots, overlap;
9402   const PetscInt    *locals;
9403   const PetscSFNode *remotes;
9404   PetscBool          distributed;
9405   MPI_Comm           comm;
9406   PetscMPIInt        rank;
9407 
9408   PetscFunctionBegin;
9409   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9410   if (pointSF) PetscValidHeaderSpecific(pointSF, PETSCSF_CLASSID, 2);
9411   else pointSF = dm->sf;
9412   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9413   PetscCheck(pointSF, comm, PETSC_ERR_ARG_WRONGSTATE, "DMPlex must have Point SF attached");
9414   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9415   {
9416     PetscMPIInt mpiFlag;
9417 
9418     PetscCallMPI(MPI_Comm_compare(comm, PetscObjectComm((PetscObject)pointSF), &mpiFlag));
9419     PetscCheck(mpiFlag == MPI_CONGRUENT || mpiFlag == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "DM and Point SF have different communicators (flag %d)", mpiFlag);
9420   }
9421   PetscCall(PetscSFGetGraph(pointSF, &nroots, &nleaves, &locals, &remotes));
9422   PetscCall(DMPlexIsDistributed(dm, &distributed));
9423   if (!distributed) {
9424     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);
9425     PetscFunctionReturn(PETSC_SUCCESS);
9426   }
9427   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);
9428   PetscCall(DMPlexGetOverlap(dm, &overlap));
9429 
9430   /* Check SF graph is compatible with DMPlex chart */
9431   {
9432     PetscInt pStart, pEnd, maxLeaf;
9433 
9434     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
9435     PetscCall(PetscSFGetLeafRange(pointSF, NULL, &maxLeaf));
9436     PetscCheck(allowExtraRoots || pEnd - pStart == nroots, PETSC_COMM_SELF, PETSC_ERR_PLIB, "pEnd - pStart = %" PetscInt_FMT " != nroots = %" PetscInt_FMT, pEnd - pStart, nroots);
9437     PetscCheck(maxLeaf < pEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "maxLeaf = %" PetscInt_FMT " >= pEnd = %" PetscInt_FMT, maxLeaf, pEnd);
9438   }
9439 
9440   /* Check Point SF has no local points referenced */
9441   for (l = 0; l < nleaves; l++) {
9442     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);
9443   }
9444 
9445   /* Check there are no cells in interface */
9446   if (!overlap) {
9447     PetscInt cellHeight, cStart, cEnd;
9448 
9449     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9450     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9451     for (l = 0; l < nleaves; ++l) {
9452       const PetscInt point = locals ? locals[l] : l;
9453 
9454       PetscCheck(point < cStart || point >= cEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " which is a cell", point);
9455     }
9456   }
9457 
9458   /* If some point is in interface, then all its cone points must be also in interface (either as leaves or roots) */
9459   {
9460     const PetscInt *rootdegree;
9461 
9462     PetscCall(PetscSFComputeDegreeBegin(pointSF, &rootdegree));
9463     PetscCall(PetscSFComputeDegreeEnd(pointSF, &rootdegree));
9464     for (l = 0; l < nleaves; ++l) {
9465       const PetscInt  point = locals ? locals[l] : l;
9466       const PetscInt *cone;
9467       PetscInt        coneSize, c, idx;
9468 
9469       PetscCall(DMPlexGetConeSize(dm, point, &coneSize));
9470       PetscCall(DMPlexGetCone(dm, point, &cone));
9471       for (c = 0; c < coneSize; ++c) {
9472         if (!rootdegree[cone[c]]) {
9473           if (locals) {
9474             PetscCall(PetscFindInt(cone[c], nleaves, locals, &idx));
9475           } else {
9476             idx = (cone[c] < nleaves) ? cone[c] : -1;
9477           }
9478           PetscCheck(idx >= 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " but not %" PetscInt_FMT " from its cone", point, cone[c]);
9479         }
9480       }
9481     }
9482   }
9483   PetscFunctionReturn(PETSC_SUCCESS);
9484 }
9485 
9486 /*@
9487   DMPlexCheckOrphanVertices - Check that no vertices are disconnected from the mesh, unless the mesh only consists of disconnected vertices.
9488 
9489   Collective
9490 
9491   Input Parameter:
9492 . dm - The `DMPLEX` object
9493 
9494   Level: developer
9495 
9496   Notes:
9497   This is mainly intended for debugging/testing purposes.
9498 
9499   Other cell types which are disconnected would be caught by the symmetry and face checks.
9500 
9501   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9502 
9503 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCheck()`, `DMSetFromOptions()`
9504 @*/
9505 PetscErrorCode DMPlexCheckOrphanVertices(DM dm)
9506 {
9507   PetscInt pStart, pEnd, vStart, vEnd;
9508 
9509   PetscFunctionBegin;
9510   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
9511   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
9512   if (pStart == vStart && pEnd == vEnd) PetscFunctionReturn(PETSC_SUCCESS);
9513   for (PetscInt v = vStart; v < vEnd; ++v) {
9514     PetscInt suppSize;
9515 
9516     PetscCall(DMPlexGetSupportSize(dm, v, &suppSize));
9517     PetscCheck(suppSize, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Vertex %" PetscInt_FMT " is disconnected from the mesh", v);
9518   }
9519   PetscFunctionReturn(PETSC_SUCCESS);
9520 }
9521 
9522 /*@
9523   DMPlexCheck - Perform various checks of `DMPLEX` sanity
9524 
9525   Input Parameter:
9526 . dm - The `DMPLEX` object
9527 
9528   Level: developer
9529 
9530   Notes:
9531   This is a useful diagnostic when creating meshes programmatically.
9532 
9533   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9534 
9535   Currently does not include `DMPlexCheckCellShape()`.
9536 
9537 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9538 @*/
9539 PetscErrorCode DMPlexCheck(DM dm)
9540 {
9541   PetscInt cellHeight;
9542 
9543   PetscFunctionBegin;
9544   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9545   PetscCall(DMPlexCheckSymmetry(dm));
9546   PetscCall(DMPlexCheckSkeleton(dm, cellHeight));
9547   PetscCall(DMPlexCheckFaces(dm, cellHeight));
9548   PetscCall(DMPlexCheckGeometry(dm));
9549   PetscCall(DMPlexCheckPointSF(dm, NULL, PETSC_FALSE));
9550   PetscCall(DMPlexCheckInterfaceCones(dm));
9551   PetscCall(DMPlexCheckOrphanVertices(dm));
9552   PetscFunctionReturn(PETSC_SUCCESS);
9553 }
9554 
9555 typedef struct cell_stats {
9556   PetscReal min, max, sum, squaresum;
9557   PetscInt  count;
9558 } cell_stats_t;
9559 
9560 static void MPIAPI cell_stats_reduce(void *a, void *b, int *len, MPI_Datatype *datatype)
9561 {
9562   PetscInt i, N = *len;
9563 
9564   for (i = 0; i < N; i++) {
9565     cell_stats_t *A = (cell_stats_t *)a;
9566     cell_stats_t *B = (cell_stats_t *)b;
9567 
9568     B->min = PetscMin(A->min, B->min);
9569     B->max = PetscMax(A->max, B->max);
9570     B->sum += A->sum;
9571     B->squaresum += A->squaresum;
9572     B->count += A->count;
9573   }
9574 }
9575 
9576 /*@
9577   DMPlexCheckCellShape - Checks the Jacobian of the mapping from reference to real cells and computes some minimal statistics.
9578 
9579   Collective
9580 
9581   Input Parameters:
9582 + dm        - The `DMPLEX` object
9583 . output    - If true, statistics will be displayed on `stdout`
9584 - condLimit - Display all cells above this condition number, or `PETSC_DETERMINE` for no cell output
9585 
9586   Level: developer
9587 
9588   Notes:
9589   This is mainly intended for debugging/testing purposes.
9590 
9591   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9592 
9593 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexComputeOrthogonalQuality()`
9594 @*/
9595 PetscErrorCode DMPlexCheckCellShape(DM dm, PetscBool output, PetscReal condLimit)
9596 {
9597   DM           dmCoarse;
9598   cell_stats_t stats, globalStats;
9599   MPI_Comm     comm = PetscObjectComm((PetscObject)dm);
9600   PetscReal   *J, *invJ, min = 0, max = 0, mean = 0, stdev = 0;
9601   PetscReal    limit = condLimit > 0 ? condLimit : PETSC_MAX_REAL;
9602   PetscInt     cdim, cStart, cEnd, c, eStart, eEnd, count = 0;
9603   PetscMPIInt  rank, size;
9604 
9605   PetscFunctionBegin;
9606   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9607   stats.min = PETSC_MAX_REAL;
9608   stats.max = PETSC_MIN_REAL;
9609   stats.sum = stats.squaresum = 0.;
9610   stats.count                 = 0;
9611 
9612   PetscCallMPI(MPI_Comm_size(comm, &size));
9613   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9614   PetscCall(DMGetCoordinateDim(dm, &cdim));
9615   PetscCall(PetscMalloc2(PetscSqr(cdim), &J, PetscSqr(cdim), &invJ));
9616   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
9617   PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
9618   for (c = cStart; c < cEnd; c++) {
9619     PetscInt  i;
9620     PetscReal frobJ = 0., frobInvJ = 0., cond2, cond, detJ;
9621 
9622     PetscCall(DMPlexComputeCellGeometryAffineFEM(dm, c, NULL, J, invJ, &detJ));
9623     PetscCheck(detJ >= 0.0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Mesh cell %" PetscInt_FMT " is inverted", c);
9624     for (i = 0; i < PetscSqr(cdim); ++i) {
9625       frobJ += J[i] * J[i];
9626       frobInvJ += invJ[i] * invJ[i];
9627     }
9628     cond2 = frobJ * frobInvJ;
9629     cond  = PetscSqrtReal(cond2);
9630 
9631     stats.min = PetscMin(stats.min, cond);
9632     stats.max = PetscMax(stats.max, cond);
9633     stats.sum += cond;
9634     stats.squaresum += cond2;
9635     stats.count++;
9636     if (output && cond > limit) {
9637       PetscSection coordSection;
9638       Vec          coordsLocal;
9639       PetscScalar *coords = NULL;
9640       PetscInt     Nv, d, clSize, cl, *closure = NULL;
9641 
9642       PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
9643       PetscCall(DMGetCoordinateSection(dm, &coordSection));
9644       PetscCall(DMPlexVecGetClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9645       PetscCall(PetscSynchronizedPrintf(comm, "[%d] Cell %" PetscInt_FMT " cond %g\n", rank, c, (double)cond));
9646       for (i = 0; i < Nv / cdim; ++i) {
9647         PetscCall(PetscSynchronizedPrintf(comm, "  Vertex %" PetscInt_FMT ": (", i));
9648         for (d = 0; d < cdim; ++d) {
9649           if (d > 0) PetscCall(PetscSynchronizedPrintf(comm, ", "));
9650           PetscCall(PetscSynchronizedPrintf(comm, "%g", (double)PetscRealPart(coords[i * cdim + d])));
9651         }
9652         PetscCall(PetscSynchronizedPrintf(comm, ")\n"));
9653       }
9654       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9655       for (cl = 0; cl < clSize * 2; cl += 2) {
9656         const PetscInt edge = closure[cl];
9657 
9658         if ((edge >= eStart) && (edge < eEnd)) {
9659           PetscReal len;
9660 
9661           PetscCall(DMPlexComputeCellGeometryFVM(dm, edge, &len, NULL, NULL));
9662           PetscCall(PetscSynchronizedPrintf(comm, "  Edge %" PetscInt_FMT ": length %g\n", edge, (double)len));
9663         }
9664       }
9665       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9666       PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9667     }
9668   }
9669   if (output) PetscCall(PetscSynchronizedFlush(comm, NULL));
9670 
9671   if (size > 1) {
9672     PetscMPIInt  blockLengths[2] = {4, 1};
9673     MPI_Aint     blockOffsets[2] = {offsetof(cell_stats_t, min), offsetof(cell_stats_t, count)};
9674     MPI_Datatype blockTypes[2]   = {MPIU_REAL, MPIU_INT}, statType;
9675     MPI_Op       statReduce;
9676 
9677     PetscCallMPI(MPI_Type_create_struct(2, blockLengths, blockOffsets, blockTypes, &statType));
9678     PetscCallMPI(MPI_Type_commit(&statType));
9679     PetscCallMPI(MPI_Op_create(cell_stats_reduce, PETSC_TRUE, &statReduce));
9680     PetscCallMPI(MPI_Reduce(&stats, &globalStats, 1, statType, statReduce, 0, comm));
9681     PetscCallMPI(MPI_Op_free(&statReduce));
9682     PetscCallMPI(MPI_Type_free(&statType));
9683   } else {
9684     PetscCall(PetscArraycpy(&globalStats, &stats, 1));
9685   }
9686   if (rank == 0) {
9687     count = globalStats.count;
9688     min   = globalStats.min;
9689     max   = globalStats.max;
9690     mean  = globalStats.sum / globalStats.count;
9691     stdev = globalStats.count > 1 ? PetscSqrtReal(PetscMax((globalStats.squaresum - globalStats.count * mean * mean) / (globalStats.count - 1), 0)) : 0.0;
9692   }
9693 
9694   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));
9695   PetscCall(PetscFree2(J, invJ));
9696 
9697   PetscCall(DMGetCoarseDM(dm, &dmCoarse));
9698   if (dmCoarse) {
9699     PetscBool isplex;
9700 
9701     PetscCall(PetscObjectTypeCompare((PetscObject)dmCoarse, DMPLEX, &isplex));
9702     if (isplex) PetscCall(DMPlexCheckCellShape(dmCoarse, output, condLimit));
9703   }
9704   PetscFunctionReturn(PETSC_SUCCESS);
9705 }
9706 
9707 /*@
9708   DMPlexComputeOrthogonalQuality - Compute cell-wise orthogonal quality mesh statistic. Optionally tags all cells with
9709   orthogonal quality below given tolerance.
9710 
9711   Collective
9712 
9713   Input Parameters:
9714 + dm   - The `DMPLEX` object
9715 . fv   - Optional `PetscFV` object for pre-computed cell/face centroid information
9716 - atol - [0, 1] Absolute tolerance for tagging cells.
9717 
9718   Output Parameters:
9719 + OrthQual      - `Vec` containing orthogonal quality per cell
9720 - OrthQualLabel - `DMLabel` tagging cells below atol with `DM_ADAPT_REFINE`
9721 
9722   Options Database Keys:
9723 + -dm_plex_orthogonal_quality_label_view - view OrthQualLabel if label is requested. Currently only `PETSCVIEWERASCII` is supported.
9724 - -dm_plex_orthogonal_quality_vec_view   - view OrthQual vector.
9725 
9726   Level: intermediate
9727 
9728   Notes:
9729   Orthogonal quality is given by the following formula\:
9730 
9731   $ \min \left[ \frac{A_i \cdot f_i}{\|A_i\| \|f_i\|} , \frac{A_i \cdot c_i}{\|A_i\| \|c_i\|} \right]$
9732 
9733   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
9734   is the vector from the current cells centroid to the centroid of its i'th neighbor (which shares a face with the
9735   current cell). This computes the vector similarity between each cell face and its corresponding neighbor centroid by
9736   calculating the cosine of the angle between these vectors.
9737 
9738   Orthogonal quality ranges from 1 (best) to 0 (worst).
9739 
9740   This routine is mainly useful for FVM, however is not restricted to only FVM. The `PetscFV` object is optionally used to check for
9741   pre-computed FVM cell data, but if it is not passed in then this data will be computed.
9742 
9743   Cells are tagged if they have an orthogonal quality less than or equal to the absolute tolerance.
9744 
9745 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCheckCellShape()`, `DMCreateLabel()`, `PetscFV`, `DMLabel`, `Vec`
9746 @*/
9747 PetscErrorCode DMPlexComputeOrthogonalQuality(DM dm, PetscFV fv, PetscReal atol, Vec *OrthQual, DMLabel *OrthQualLabel)
9748 {
9749   PetscInt               nc, cellHeight, cStart, cEnd, cell, cellIter = 0;
9750   PetscInt              *idx;
9751   PetscScalar           *oqVals;
9752   const PetscScalar     *cellGeomArr, *faceGeomArr;
9753   PetscReal             *ci, *fi, *Ai;
9754   MPI_Comm               comm;
9755   Vec                    cellgeom, facegeom;
9756   DM                     dmFace, dmCell;
9757   IS                     glob;
9758   ISLocalToGlobalMapping ltog;
9759   PetscViewer            vwr;
9760 
9761   PetscFunctionBegin;
9762   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9763   if (fv) PetscValidHeaderSpecific(fv, PETSCFV_CLASSID, 2);
9764   PetscAssertPointer(OrthQual, 4);
9765   PetscCheck(atol >= 0.0 && atol <= 1.0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Absolute tolerance %g not in [0,1]", (double)atol);
9766   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9767   PetscCall(DMGetDimension(dm, &nc));
9768   PetscCheck(nc >= 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must have dimension >= 2 (current %" PetscInt_FMT ")", nc);
9769   {
9770     DMPlexInterpolatedFlag interpFlag;
9771 
9772     PetscCall(DMPlexIsInterpolated(dm, &interpFlag));
9773     if (interpFlag != DMPLEX_INTERPOLATED_FULL) {
9774       PetscMPIInt rank;
9775 
9776       PetscCallMPI(MPI_Comm_rank(comm, &rank));
9777       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must be fully interpolated, DM on rank %d is not fully interpolated", rank);
9778     }
9779   }
9780   if (OrthQualLabel) {
9781     PetscAssertPointer(OrthQualLabel, 5);
9782     PetscCall(DMCreateLabel(dm, "Orthogonal_Quality"));
9783     PetscCall(DMGetLabel(dm, "Orthogonal_Quality", OrthQualLabel));
9784   } else {
9785     *OrthQualLabel = NULL;
9786   }
9787   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9788   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9789   PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_TRUE, &glob));
9790   PetscCall(ISLocalToGlobalMappingCreateIS(glob, &ltog));
9791   PetscCall(ISLocalToGlobalMappingSetType(ltog, ISLOCALTOGLOBALMAPPINGHASH));
9792   PetscCall(VecCreate(comm, OrthQual));
9793   PetscCall(VecSetType(*OrthQual, VECSTANDARD));
9794   PetscCall(VecSetSizes(*OrthQual, cEnd - cStart, PETSC_DETERMINE));
9795   PetscCall(VecSetLocalToGlobalMapping(*OrthQual, ltog));
9796   PetscCall(VecSetUp(*OrthQual));
9797   PetscCall(ISDestroy(&glob));
9798   PetscCall(ISLocalToGlobalMappingDestroy(&ltog));
9799   PetscCall(DMPlexGetDataFVM(dm, fv, &cellgeom, &facegeom, NULL));
9800   PetscCall(VecGetArrayRead(cellgeom, &cellGeomArr));
9801   PetscCall(VecGetArrayRead(facegeom, &faceGeomArr));
9802   PetscCall(VecGetDM(cellgeom, &dmCell));
9803   PetscCall(VecGetDM(facegeom, &dmFace));
9804   PetscCall(PetscMalloc5(cEnd - cStart, &idx, cEnd - cStart, &oqVals, nc, &ci, nc, &fi, nc, &Ai));
9805   for (cell = cStart; cell < cEnd; cellIter++, cell++) {
9806     PetscInt         cellneigh, cellneighiter = 0, adjSize = PETSC_DETERMINE;
9807     PetscInt         cellarr[2], *adj = NULL;
9808     PetscScalar     *cArr, *fArr;
9809     PetscReal        minvalc = 1.0, minvalf = 1.0;
9810     PetscFVCellGeom *cg;
9811 
9812     idx[cellIter] = cell - cStart;
9813     cellarr[0]    = cell;
9814     /* Make indexing into cellGeom easier */
9815     PetscCall(DMPlexPointLocalRead(dmCell, cell, cellGeomArr, &cg));
9816     PetscCall(DMPlexGetAdjacency_Internal(dm, cell, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &adjSize, &adj));
9817     /* Technically 1 too big, but easier than fiddling with empty adjacency array */
9818     PetscCall(PetscCalloc2(adjSize, &cArr, adjSize, &fArr));
9819     for (cellneigh = 0; cellneigh < adjSize; cellneighiter++, cellneigh++) {
9820       PetscInt         i;
9821       const PetscInt   neigh  = adj[cellneigh];
9822       PetscReal        normci = 0, normfi = 0, normai = 0;
9823       PetscFVCellGeom *cgneigh;
9824       PetscFVFaceGeom *fg;
9825 
9826       /* Don't count ourselves in the neighbor list */
9827       if (neigh == cell) continue;
9828       PetscCall(DMPlexPointLocalRead(dmCell, neigh, cellGeomArr, &cgneigh));
9829       cellarr[1] = neigh;
9830       {
9831         PetscInt        numcovpts;
9832         const PetscInt *covpts;
9833 
9834         PetscCall(DMPlexGetMeet(dm, 2, cellarr, &numcovpts, &covpts));
9835         PetscCall(DMPlexPointLocalRead(dmFace, covpts[0], faceGeomArr, &fg));
9836         PetscCall(DMPlexRestoreMeet(dm, 2, cellarr, &numcovpts, &covpts));
9837       }
9838 
9839       /* Compute c_i, f_i and their norms */
9840       for (i = 0; i < nc; i++) {
9841         ci[i] = cgneigh->centroid[i] - cg->centroid[i];
9842         fi[i] = fg->centroid[i] - cg->centroid[i];
9843         Ai[i] = fg->normal[i];
9844         normci += PetscPowReal(ci[i], 2);
9845         normfi += PetscPowReal(fi[i], 2);
9846         normai += PetscPowReal(Ai[i], 2);
9847       }
9848       normci = PetscSqrtReal(normci);
9849       normfi = PetscSqrtReal(normfi);
9850       normai = PetscSqrtReal(normai);
9851 
9852       /* Normalize and compute for each face-cell-normal pair */
9853       for (i = 0; i < nc; i++) {
9854         ci[i] = ci[i] / normci;
9855         fi[i] = fi[i] / normfi;
9856         Ai[i] = Ai[i] / normai;
9857         /* PetscAbs because I don't know if normals are guaranteed to point out */
9858         cArr[cellneighiter] += PetscAbs(Ai[i] * ci[i]);
9859         fArr[cellneighiter] += PetscAbs(Ai[i] * fi[i]);
9860       }
9861       if (PetscRealPart(cArr[cellneighiter]) < minvalc) minvalc = PetscRealPart(cArr[cellneighiter]);
9862       if (PetscRealPart(fArr[cellneighiter]) < minvalf) minvalf = PetscRealPart(fArr[cellneighiter]);
9863     }
9864     PetscCall(PetscFree(adj));
9865     PetscCall(PetscFree2(cArr, fArr));
9866     /* Defer to cell if they're equal */
9867     oqVals[cellIter] = PetscMin(minvalf, minvalc);
9868     if (OrthQualLabel) {
9869       if (PetscRealPart(oqVals[cellIter]) <= atol) PetscCall(DMLabelSetValue(*OrthQualLabel, cell, DM_ADAPT_REFINE));
9870     }
9871   }
9872   PetscCall(VecSetValuesLocal(*OrthQual, cEnd - cStart, idx, oqVals, INSERT_VALUES));
9873   PetscCall(VecAssemblyBegin(*OrthQual));
9874   PetscCall(VecAssemblyEnd(*OrthQual));
9875   PetscCall(VecRestoreArrayRead(cellgeom, &cellGeomArr));
9876   PetscCall(VecRestoreArrayRead(facegeom, &faceGeomArr));
9877   PetscCall(PetscOptionsGetViewer(comm, NULL, NULL, "-dm_plex_orthogonal_quality_label_view", &vwr, NULL, NULL));
9878   if (OrthQualLabel) {
9879     if (vwr) PetscCall(DMLabelView(*OrthQualLabel, vwr));
9880   }
9881   PetscCall(PetscFree5(idx, oqVals, ci, fi, Ai));
9882   PetscCall(PetscOptionsRestoreViewer(&vwr));
9883   PetscCall(VecViewFromOptions(*OrthQual, NULL, "-dm_plex_orthogonal_quality_vec_view"));
9884   PetscFunctionReturn(PETSC_SUCCESS);
9885 }
9886 
9887 /* this is here instead of DMGetOutputDM because output DM still has constraints in the local indices that affect
9888  * interpolator construction */
9889 static PetscErrorCode DMGetFullDM(DM dm, DM *odm)
9890 {
9891   PetscSection section, newSection, gsection;
9892   PetscSF      sf;
9893   PetscBool    hasConstraints, ghasConstraints;
9894 
9895   PetscFunctionBegin;
9896   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9897   PetscAssertPointer(odm, 2);
9898   PetscCall(DMGetLocalSection(dm, &section));
9899   PetscCall(PetscSectionHasConstraints(section, &hasConstraints));
9900   PetscCall(MPIU_Allreduce(&hasConstraints, &ghasConstraints, 1, MPIU_BOOL, MPI_LOR, PetscObjectComm((PetscObject)dm)));
9901   if (!ghasConstraints) {
9902     PetscCall(PetscObjectReference((PetscObject)dm));
9903     *odm = dm;
9904     PetscFunctionReturn(PETSC_SUCCESS);
9905   }
9906   PetscCall(DMClone(dm, odm));
9907   PetscCall(DMCopyFields(dm, *odm));
9908   PetscCall(DMGetLocalSection(*odm, &newSection));
9909   PetscCall(DMGetPointSF(*odm, &sf));
9910   PetscCall(PetscSectionCreateGlobalSection(newSection, sf, PETSC_TRUE, PETSC_TRUE, PETSC_FALSE, &gsection));
9911   PetscCall(DMSetGlobalSection(*odm, gsection));
9912   PetscCall(PetscSectionDestroy(&gsection));
9913   PetscFunctionReturn(PETSC_SUCCESS);
9914 }
9915 
9916 static PetscErrorCode DMCreateAffineInterpolationCorrection_Plex(DM dmc, DM dmf, Vec *shift)
9917 {
9918   DM        dmco, dmfo;
9919   Mat       interpo;
9920   Vec       rscale;
9921   Vec       cglobalo, clocal;
9922   Vec       fglobal, fglobalo, flocal;
9923   PetscBool regular;
9924 
9925   PetscFunctionBegin;
9926   PetscCall(DMGetFullDM(dmc, &dmco));
9927   PetscCall(DMGetFullDM(dmf, &dmfo));
9928   PetscCall(DMSetCoarseDM(dmfo, dmco));
9929   PetscCall(DMPlexGetRegularRefinement(dmf, &regular));
9930   PetscCall(DMPlexSetRegularRefinement(dmfo, regular));
9931   PetscCall(DMCreateInterpolation(dmco, dmfo, &interpo, &rscale));
9932   PetscCall(DMCreateGlobalVector(dmco, &cglobalo));
9933   PetscCall(DMCreateLocalVector(dmc, &clocal));
9934   PetscCall(VecSet(cglobalo, 0.));
9935   PetscCall(VecSet(clocal, 0.));
9936   PetscCall(DMCreateGlobalVector(dmf, &fglobal));
9937   PetscCall(DMCreateGlobalVector(dmfo, &fglobalo));
9938   PetscCall(DMCreateLocalVector(dmf, &flocal));
9939   PetscCall(VecSet(fglobal, 0.));
9940   PetscCall(VecSet(fglobalo, 0.));
9941   PetscCall(VecSet(flocal, 0.));
9942   PetscCall(DMPlexInsertBoundaryValues(dmc, PETSC_TRUE, clocal, 0., NULL, NULL, NULL));
9943   PetscCall(DMLocalToGlobalBegin(dmco, clocal, INSERT_VALUES, cglobalo));
9944   PetscCall(DMLocalToGlobalEnd(dmco, clocal, INSERT_VALUES, cglobalo));
9945   PetscCall(MatMult(interpo, cglobalo, fglobalo));
9946   PetscCall(DMGlobalToLocalBegin(dmfo, fglobalo, INSERT_VALUES, flocal));
9947   PetscCall(DMGlobalToLocalEnd(dmfo, fglobalo, INSERT_VALUES, flocal));
9948   PetscCall(DMLocalToGlobalBegin(dmf, flocal, INSERT_VALUES, fglobal));
9949   PetscCall(DMLocalToGlobalEnd(dmf, flocal, INSERT_VALUES, fglobal));
9950   *shift = fglobal;
9951   PetscCall(VecDestroy(&flocal));
9952   PetscCall(VecDestroy(&fglobalo));
9953   PetscCall(VecDestroy(&clocal));
9954   PetscCall(VecDestroy(&cglobalo));
9955   PetscCall(VecDestroy(&rscale));
9956   PetscCall(MatDestroy(&interpo));
9957   PetscCall(DMDestroy(&dmfo));
9958   PetscCall(DMDestroy(&dmco));
9959   PetscFunctionReturn(PETSC_SUCCESS);
9960 }
9961 
9962 PETSC_INTERN PetscErrorCode DMInterpolateSolution_Plex(DM coarse, DM fine, Mat interp, Vec coarseSol, Vec fineSol)
9963 {
9964   PetscObject shifto;
9965   Vec         shift;
9966 
9967   PetscFunctionBegin;
9968   if (!interp) {
9969     Vec rscale;
9970 
9971     PetscCall(DMCreateInterpolation(coarse, fine, &interp, &rscale));
9972     PetscCall(VecDestroy(&rscale));
9973   } else {
9974     PetscCall(PetscObjectReference((PetscObject)interp));
9975   }
9976   PetscCall(PetscObjectQuery((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", &shifto));
9977   if (!shifto) {
9978     PetscCall(DMCreateAffineInterpolationCorrection_Plex(coarse, fine, &shift));
9979     PetscCall(PetscObjectCompose((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", (PetscObject)shift));
9980     shifto = (PetscObject)shift;
9981     PetscCall(VecDestroy(&shift));
9982   }
9983   shift = (Vec)shifto;
9984   PetscCall(MatInterpolate(interp, coarseSol, fineSol));
9985   PetscCall(VecAXPY(fineSol, 1.0, shift));
9986   PetscCall(MatDestroy(&interp));
9987   PetscFunctionReturn(PETSC_SUCCESS);
9988 }
9989 
9990 /* Pointwise interpolation
9991      Just code FEM for now
9992      u^f = I u^c
9993      sum_k u^f_k phi^f_k = I sum_j u^c_j phi^c_j
9994      u^f_i = sum_j psi^f_i I phi^c_j u^c_j
9995      I_{ij} = psi^f_i phi^c_j
9996 */
9997 PetscErrorCode DMCreateInterpolation_Plex(DM dmCoarse, DM dmFine, Mat *interpolation, Vec *scaling)
9998 {
9999   PetscSection gsc, gsf;
10000   PetscInt     m, n;
10001   void        *ctx;
10002   DM           cdm;
10003   PetscBool    regular, ismatis, isRefined = dmCoarse->data == dmFine->data ? PETSC_FALSE : PETSC_TRUE;
10004 
10005   PetscFunctionBegin;
10006   PetscCall(DMGetGlobalSection(dmFine, &gsf));
10007   PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
10008   PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
10009   PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
10010 
10011   PetscCall(PetscStrcmp(dmCoarse->mattype, MATIS, &ismatis));
10012   PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), interpolation));
10013   PetscCall(MatSetSizes(*interpolation, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
10014   PetscCall(MatSetType(*interpolation, ismatis ? MATAIJ : dmCoarse->mattype));
10015   PetscCall(DMGetApplicationContext(dmFine, &ctx));
10016 
10017   PetscCall(DMGetCoarseDM(dmFine, &cdm));
10018   PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
10019   if (!isRefined || (regular && cdm == dmCoarse)) PetscCall(DMPlexComputeInterpolatorNested(dmCoarse, dmFine, isRefined, *interpolation, ctx));
10020   else PetscCall(DMPlexComputeInterpolatorGeneral(dmCoarse, dmFine, *interpolation, ctx));
10021   PetscCall(MatViewFromOptions(*interpolation, NULL, "-interp_mat_view"));
10022   if (scaling) {
10023     /* Use naive scaling */
10024     PetscCall(DMCreateInterpolationScale(dmCoarse, dmFine, *interpolation, scaling));
10025   }
10026   PetscFunctionReturn(PETSC_SUCCESS);
10027 }
10028 
10029 PetscErrorCode DMCreateInjection_Plex(DM dmCoarse, DM dmFine, Mat *mat)
10030 {
10031   VecScatter ctx;
10032 
10033   PetscFunctionBegin;
10034   PetscCall(DMPlexComputeInjectorFEM(dmCoarse, dmFine, &ctx, NULL));
10035   PetscCall(MatCreateScatter(PetscObjectComm((PetscObject)ctx), ctx, mat));
10036   PetscCall(VecScatterDestroy(&ctx));
10037   PetscFunctionReturn(PETSC_SUCCESS);
10038 }
10039 
10040 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[])
10041 {
10042   const PetscInt Nc = uOff[1] - uOff[0];
10043   PetscInt       c;
10044   for (c = 0; c < Nc; ++c) g0[c * Nc + c] = 1.0;
10045 }
10046 
10047 PetscErrorCode DMCreateMassMatrixLumped_Plex(DM dm, Vec *mass)
10048 {
10049   DM           dmc;
10050   PetscDS      ds;
10051   Vec          ones, locmass;
10052   IS           cellIS;
10053   PetscFormKey key;
10054   PetscInt     depth;
10055 
10056   PetscFunctionBegin;
10057   PetscCall(DMClone(dm, &dmc));
10058   PetscCall(DMCopyDisc(dm, dmc));
10059   PetscCall(DMGetDS(dmc, &ds));
10060   PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
10061   PetscCall(DMCreateGlobalVector(dmc, mass));
10062   PetscCall(DMGetLocalVector(dmc, &ones));
10063   PetscCall(DMGetLocalVector(dmc, &locmass));
10064   PetscCall(DMPlexGetDepth(dmc, &depth));
10065   PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
10066   PetscCall(VecSet(locmass, 0.0));
10067   PetscCall(VecSet(ones, 1.0));
10068   key.label = NULL;
10069   key.value = 0;
10070   key.field = 0;
10071   key.part  = 0;
10072   PetscCall(DMPlexComputeJacobian_Action_Internal(dmc, key, cellIS, 0.0, 0.0, ones, NULL, ones, locmass, NULL));
10073   PetscCall(ISDestroy(&cellIS));
10074   PetscCall(VecSet(*mass, 0.0));
10075   PetscCall(DMLocalToGlobalBegin(dmc, locmass, ADD_VALUES, *mass));
10076   PetscCall(DMLocalToGlobalEnd(dmc, locmass, ADD_VALUES, *mass));
10077   PetscCall(DMRestoreLocalVector(dmc, &ones));
10078   PetscCall(DMRestoreLocalVector(dmc, &locmass));
10079   PetscCall(DMDestroy(&dmc));
10080   PetscFunctionReturn(PETSC_SUCCESS);
10081 }
10082 
10083 PetscErrorCode DMCreateMassMatrix_Plex(DM dmCoarse, DM dmFine, Mat *mass)
10084 {
10085   PetscSection gsc, gsf;
10086   PetscInt     m, n;
10087   void        *ctx;
10088   DM           cdm;
10089   PetscBool    regular;
10090 
10091   PetscFunctionBegin;
10092   if (dmFine == dmCoarse) {
10093     DM            dmc;
10094     PetscDS       ds;
10095     PetscWeakForm wf;
10096     Vec           u;
10097     IS            cellIS;
10098     PetscFormKey  key;
10099     PetscInt      depth;
10100 
10101     PetscCall(DMClone(dmFine, &dmc));
10102     PetscCall(DMCopyDisc(dmFine, dmc));
10103     PetscCall(DMGetDS(dmc, &ds));
10104     PetscCall(PetscDSGetWeakForm(ds, &wf));
10105     PetscCall(PetscWeakFormClear(wf));
10106     PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
10107     PetscCall(DMCreateMatrix(dmc, mass));
10108     PetscCall(DMGetLocalVector(dmc, &u));
10109     PetscCall(DMPlexGetDepth(dmc, &depth));
10110     PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
10111     PetscCall(MatZeroEntries(*mass));
10112     key.label = NULL;
10113     key.value = 0;
10114     key.field = 0;
10115     key.part  = 0;
10116     PetscCall(DMPlexComputeJacobian_Internal(dmc, key, cellIS, 0.0, 0.0, u, NULL, *mass, *mass, NULL));
10117     PetscCall(ISDestroy(&cellIS));
10118     PetscCall(DMRestoreLocalVector(dmc, &u));
10119     PetscCall(DMDestroy(&dmc));
10120   } else {
10121     PetscCall(DMGetGlobalSection(dmFine, &gsf));
10122     PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
10123     PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
10124     PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
10125 
10126     PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), mass));
10127     PetscCall(MatSetSizes(*mass, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
10128     PetscCall(MatSetType(*mass, dmCoarse->mattype));
10129     PetscCall(DMGetApplicationContext(dmFine, &ctx));
10130 
10131     PetscCall(DMGetCoarseDM(dmFine, &cdm));
10132     PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
10133     if (regular && cdm == dmCoarse) PetscCall(DMPlexComputeMassMatrixNested(dmCoarse, dmFine, *mass, ctx));
10134     else PetscCall(DMPlexComputeMassMatrixGeneral(dmCoarse, dmFine, *mass, ctx));
10135   }
10136   PetscCall(MatViewFromOptions(*mass, NULL, "-mass_mat_view"));
10137   PetscFunctionReturn(PETSC_SUCCESS);
10138 }
10139 
10140 /*@
10141   DMPlexGetRegularRefinement - Get the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
10142 
10143   Input Parameter:
10144 . dm - The `DMPLEX` object
10145 
10146   Output Parameter:
10147 . regular - The flag
10148 
10149   Level: intermediate
10150 
10151 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetRegularRefinement()`
10152 @*/
10153 PetscErrorCode DMPlexGetRegularRefinement(DM dm, PetscBool *regular)
10154 {
10155   PetscFunctionBegin;
10156   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10157   PetscAssertPointer(regular, 2);
10158   *regular = ((DM_Plex *)dm->data)->regularRefinement;
10159   PetscFunctionReturn(PETSC_SUCCESS);
10160 }
10161 
10162 /*@
10163   DMPlexSetRegularRefinement - Set the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
10164 
10165   Input Parameters:
10166 + dm      - The `DMPLEX` object
10167 - regular - The flag
10168 
10169   Level: intermediate
10170 
10171 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetRegularRefinement()`
10172 @*/
10173 PetscErrorCode DMPlexSetRegularRefinement(DM dm, PetscBool regular)
10174 {
10175   PetscFunctionBegin;
10176   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10177   ((DM_Plex *)dm->data)->regularRefinement = regular;
10178   PetscFunctionReturn(PETSC_SUCCESS);
10179 }
10180 
10181 /*@
10182   DMPlexGetAnchors - Get the layout of the anchor (point-to-point) constraints.  Typically, the user will not have to
10183   call DMPlexGetAnchors() directly: if there are anchors, then `DMPlexGetAnchors()` is called during `DMGetDefaultConstraints()`.
10184 
10185   Not Collective
10186 
10187   Input Parameter:
10188 . dm - The `DMPLEX` object
10189 
10190   Output Parameters:
10191 + anchorSection - If not `NULL`, set to the section describing which points anchor the constrained points.
10192 - anchorIS      - If not `NULL`, set to the list of anchors indexed by `anchorSection`
10193 
10194   Level: intermediate
10195 
10196 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`, `IS`, `PetscSection`
10197 @*/
10198 PetscErrorCode DMPlexGetAnchors(DM dm, PetscSection *anchorSection, IS *anchorIS)
10199 {
10200   DM_Plex *plex = (DM_Plex *)dm->data;
10201 
10202   PetscFunctionBegin;
10203   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10204   if (!plex->anchorSection && !plex->anchorIS && plex->createanchors) PetscCall((*plex->createanchors)(dm));
10205   if (anchorSection) *anchorSection = plex->anchorSection;
10206   if (anchorIS) *anchorIS = plex->anchorIS;
10207   PetscFunctionReturn(PETSC_SUCCESS);
10208 }
10209 
10210 /*@
10211   DMPlexSetAnchors - Set the layout of the local anchor (point-to-point) constraints.
10212 
10213   Collective
10214 
10215   Input Parameters:
10216 + dm            - The `DMPLEX` object
10217 . anchorSection - The section that describes the mapping from constrained points to the anchor points listed in anchorIS.
10218                   Must have a local communicator (`PETSC_COMM_SELF` or derivative).
10219 - anchorIS      - The list of all anchor points.  Must have a local communicator (`PETSC_COMM_SELF` or derivative).
10220 
10221   Level: intermediate
10222 
10223   Notes:
10224   Unlike boundary conditions, when a point's degrees of freedom in a section are constrained to
10225   an outside value, the anchor constraints set a point's degrees of freedom to be a linear
10226   combination of other points' degrees of freedom.
10227 
10228   After specifying the layout of constraints with `DMPlexSetAnchors()`, one specifies the constraints by calling
10229   `DMGetDefaultConstraints()` and filling in the entries in the constraint matrix.
10230 
10231   The reference counts of `anchorSection` and `anchorIS` are incremented.
10232 
10233 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`
10234 @*/
10235 PetscErrorCode DMPlexSetAnchors(DM dm, PetscSection anchorSection, IS anchorIS)
10236 {
10237   DM_Plex    *plex = (DM_Plex *)dm->data;
10238   PetscMPIInt result;
10239 
10240   PetscFunctionBegin;
10241   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10242   if (anchorSection) {
10243     PetscValidHeaderSpecific(anchorSection, PETSC_SECTION_CLASSID, 2);
10244     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorSection), &result));
10245     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor section must have local communicator");
10246   }
10247   if (anchorIS) {
10248     PetscValidHeaderSpecific(anchorIS, IS_CLASSID, 3);
10249     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorIS), &result));
10250     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor IS must have local communicator");
10251   }
10252 
10253   PetscCall(PetscObjectReference((PetscObject)anchorSection));
10254   PetscCall(PetscSectionDestroy(&plex->anchorSection));
10255   plex->anchorSection = anchorSection;
10256 
10257   PetscCall(PetscObjectReference((PetscObject)anchorIS));
10258   PetscCall(ISDestroy(&plex->anchorIS));
10259   plex->anchorIS = anchorIS;
10260 
10261   if (PetscUnlikelyDebug(anchorIS && anchorSection)) {
10262     PetscInt        size, a, pStart, pEnd;
10263     const PetscInt *anchors;
10264 
10265     PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
10266     PetscCall(ISGetLocalSize(anchorIS, &size));
10267     PetscCall(ISGetIndices(anchorIS, &anchors));
10268     for (a = 0; a < size; a++) {
10269       PetscInt p;
10270 
10271       p = anchors[a];
10272       if (p >= pStart && p < pEnd) {
10273         PetscInt dof;
10274 
10275         PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
10276         if (dof) {
10277           PetscCall(ISRestoreIndices(anchorIS, &anchors));
10278           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Point %" PetscInt_FMT " cannot be constrained and an anchor", p);
10279         }
10280       }
10281     }
10282     PetscCall(ISRestoreIndices(anchorIS, &anchors));
10283   }
10284   /* reset the generic constraints */
10285   PetscCall(DMSetDefaultConstraints(dm, NULL, NULL, NULL));
10286   PetscFunctionReturn(PETSC_SUCCESS);
10287 }
10288 
10289 static PetscErrorCode DMPlexCreateConstraintSection_Anchors(DM dm, PetscSection section, PetscSection *cSec)
10290 {
10291   PetscSection anchorSection;
10292   PetscInt     pStart, pEnd, sStart, sEnd, p, dof, numFields, f;
10293 
10294   PetscFunctionBegin;
10295   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10296   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
10297   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, cSec));
10298   PetscCall(PetscSectionGetNumFields(section, &numFields));
10299   if (numFields) {
10300     PetscInt f;
10301     PetscCall(PetscSectionSetNumFields(*cSec, numFields));
10302 
10303     for (f = 0; f < numFields; f++) {
10304       PetscInt numComp;
10305 
10306       PetscCall(PetscSectionGetFieldComponents(section, f, &numComp));
10307       PetscCall(PetscSectionSetFieldComponents(*cSec, f, numComp));
10308     }
10309   }
10310   PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
10311   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
10312   pStart = PetscMax(pStart, sStart);
10313   pEnd   = PetscMin(pEnd, sEnd);
10314   pEnd   = PetscMax(pStart, pEnd);
10315   PetscCall(PetscSectionSetChart(*cSec, pStart, pEnd));
10316   for (p = pStart; p < pEnd; p++) {
10317     PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
10318     if (dof) {
10319       PetscCall(PetscSectionGetDof(section, p, &dof));
10320       PetscCall(PetscSectionSetDof(*cSec, p, dof));
10321       for (f = 0; f < numFields; f++) {
10322         PetscCall(PetscSectionGetFieldDof(section, p, f, &dof));
10323         PetscCall(PetscSectionSetFieldDof(*cSec, p, f, dof));
10324       }
10325     }
10326   }
10327   PetscCall(PetscSectionSetUp(*cSec));
10328   PetscCall(PetscObjectSetName((PetscObject)*cSec, "Constraint Section"));
10329   PetscFunctionReturn(PETSC_SUCCESS);
10330 }
10331 
10332 static PetscErrorCode DMPlexCreateConstraintMatrix_Anchors(DM dm, PetscSection section, PetscSection cSec, Mat *cMat)
10333 {
10334   PetscSection    aSec;
10335   PetscInt        pStart, pEnd, p, sStart, sEnd, dof, aDof, aOff, off, nnz, annz, m, n, q, a, offset, *i, *j;
10336   const PetscInt *anchors;
10337   PetscInt        numFields, f;
10338   IS              aIS;
10339   MatType         mtype;
10340   PetscBool       iscuda, iskokkos;
10341 
10342   PetscFunctionBegin;
10343   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10344   PetscCall(PetscSectionGetStorageSize(cSec, &m));
10345   PetscCall(PetscSectionGetStorageSize(section, &n));
10346   PetscCall(MatCreate(PETSC_COMM_SELF, cMat));
10347   PetscCall(MatSetSizes(*cMat, m, n, m, n));
10348   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJCUSPARSE, &iscuda));
10349   if (!iscuda) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJCUSPARSE, &iscuda));
10350   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJKOKKOS, &iskokkos));
10351   if (!iskokkos) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJKOKKOS, &iskokkos));
10352   if (iscuda) mtype = MATSEQAIJCUSPARSE;
10353   else if (iskokkos) mtype = MATSEQAIJKOKKOS;
10354   else mtype = MATSEQAIJ;
10355   PetscCall(MatSetType(*cMat, mtype));
10356   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
10357   PetscCall(ISGetIndices(aIS, &anchors));
10358   /* cSec will be a subset of aSec and section */
10359   PetscCall(PetscSectionGetChart(cSec, &pStart, &pEnd));
10360   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
10361   PetscCall(PetscMalloc1(m + 1, &i));
10362   i[0] = 0;
10363   PetscCall(PetscSectionGetNumFields(section, &numFields));
10364   for (p = pStart; p < pEnd; p++) {
10365     PetscInt rDof, rOff, r;
10366 
10367     PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10368     if (!rDof) continue;
10369     PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10370     if (numFields) {
10371       for (f = 0; f < numFields; f++) {
10372         annz = 0;
10373         for (r = 0; r < rDof; r++) {
10374           a = anchors[rOff + r];
10375           if (a < sStart || a >= sEnd) continue;
10376           PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
10377           annz += aDof;
10378         }
10379         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
10380         PetscCall(PetscSectionGetFieldOffset(cSec, p, f, &off));
10381         for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
10382       }
10383     } else {
10384       annz = 0;
10385       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10386       for (q = 0; q < dof; q++) {
10387         a = anchors[rOff + q];
10388         if (a < sStart || a >= sEnd) continue;
10389         PetscCall(PetscSectionGetDof(section, a, &aDof));
10390         annz += aDof;
10391       }
10392       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10393       PetscCall(PetscSectionGetOffset(cSec, p, &off));
10394       for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
10395     }
10396   }
10397   nnz = i[m];
10398   PetscCall(PetscMalloc1(nnz, &j));
10399   offset = 0;
10400   for (p = pStart; p < pEnd; p++) {
10401     if (numFields) {
10402       for (f = 0; f < numFields; f++) {
10403         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
10404         for (q = 0; q < dof; q++) {
10405           PetscInt rDof, rOff, r;
10406           PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10407           PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10408           for (r = 0; r < rDof; r++) {
10409             PetscInt s;
10410 
10411             a = anchors[rOff + r];
10412             if (a < sStart || a >= sEnd) continue;
10413             PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
10414             PetscCall(PetscSectionGetFieldOffset(section, a, f, &aOff));
10415             for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
10416           }
10417         }
10418       }
10419     } else {
10420       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10421       for (q = 0; q < dof; q++) {
10422         PetscInt rDof, rOff, r;
10423         PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10424         PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10425         for (r = 0; r < rDof; r++) {
10426           PetscInt s;
10427 
10428           a = anchors[rOff + r];
10429           if (a < sStart || a >= sEnd) continue;
10430           PetscCall(PetscSectionGetDof(section, a, &aDof));
10431           PetscCall(PetscSectionGetOffset(section, a, &aOff));
10432           for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
10433         }
10434       }
10435     }
10436   }
10437   PetscCall(MatSeqAIJSetPreallocationCSR(*cMat, i, j, NULL));
10438   PetscCall(PetscFree(i));
10439   PetscCall(PetscFree(j));
10440   PetscCall(ISRestoreIndices(aIS, &anchors));
10441   PetscFunctionReturn(PETSC_SUCCESS);
10442 }
10443 
10444 PetscErrorCode DMCreateDefaultConstraints_Plex(DM dm)
10445 {
10446   DM_Plex     *plex = (DM_Plex *)dm->data;
10447   PetscSection anchorSection, section, cSec;
10448   Mat          cMat;
10449 
10450   PetscFunctionBegin;
10451   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10452   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
10453   if (anchorSection) {
10454     PetscInt Nf;
10455 
10456     PetscCall(DMGetLocalSection(dm, &section));
10457     PetscCall(DMPlexCreateConstraintSection_Anchors(dm, section, &cSec));
10458     PetscCall(DMPlexCreateConstraintMatrix_Anchors(dm, section, cSec, &cMat));
10459     PetscCall(DMGetNumFields(dm, &Nf));
10460     if (Nf && plex->computeanchormatrix) PetscCall((*plex->computeanchormatrix)(dm, section, cSec, cMat));
10461     PetscCall(DMSetDefaultConstraints(dm, cSec, cMat, NULL));
10462     PetscCall(PetscSectionDestroy(&cSec));
10463     PetscCall(MatDestroy(&cMat));
10464   }
10465   PetscFunctionReturn(PETSC_SUCCESS);
10466 }
10467 
10468 PetscErrorCode DMCreateSubDomainDM_Plex(DM dm, DMLabel label, PetscInt value, IS *is, DM *subdm)
10469 {
10470   IS           subis;
10471   PetscSection section, subsection;
10472 
10473   PetscFunctionBegin;
10474   PetscCall(DMGetLocalSection(dm, &section));
10475   PetscCheck(section, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set default section for DM before splitting subdomain");
10476   PetscCheck(subdm, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set output subDM for splitting subdomain");
10477   /* Create subdomain */
10478   PetscCall(DMPlexFilter(dm, label, value, PETSC_FALSE, PETSC_FALSE, NULL, subdm));
10479   /* Create submodel */
10480   PetscCall(DMPlexGetSubpointIS(*subdm, &subis));
10481   PetscCall(PetscSectionCreateSubmeshSection(section, subis, &subsection));
10482   PetscCall(DMSetLocalSection(*subdm, subsection));
10483   PetscCall(PetscSectionDestroy(&subsection));
10484   PetscCall(DMCopyDisc(dm, *subdm));
10485   /* Create map from submodel to global model */
10486   if (is) {
10487     PetscSection    sectionGlobal, subsectionGlobal;
10488     IS              spIS;
10489     const PetscInt *spmap;
10490     PetscInt       *subIndices;
10491     PetscInt        subSize = 0, subOff = 0, pStart, pEnd, p;
10492     PetscInt        Nf, f, bs = -1, bsLocal[2], bsMinMax[2];
10493 
10494     PetscCall(DMPlexGetSubpointIS(*subdm, &spIS));
10495     PetscCall(ISGetIndices(spIS, &spmap));
10496     PetscCall(PetscSectionGetNumFields(section, &Nf));
10497     PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
10498     PetscCall(DMGetGlobalSection(*subdm, &subsectionGlobal));
10499     PetscCall(PetscSectionGetChart(subsection, &pStart, &pEnd));
10500     for (p = pStart; p < pEnd; ++p) {
10501       PetscInt gdof, pSubSize = 0;
10502 
10503       PetscCall(PetscSectionGetDof(sectionGlobal, p, &gdof));
10504       if (gdof > 0) {
10505         for (f = 0; f < Nf; ++f) {
10506           PetscInt fdof, fcdof;
10507 
10508           PetscCall(PetscSectionGetFieldDof(subsection, p, f, &fdof));
10509           PetscCall(PetscSectionGetFieldConstraintDof(subsection, p, f, &fcdof));
10510           pSubSize += fdof - fcdof;
10511         }
10512         subSize += pSubSize;
10513         if (pSubSize) {
10514           if (bs < 0) {
10515             bs = pSubSize;
10516           } else if (bs != pSubSize) {
10517             /* Layout does not admit a pointwise block size */
10518             bs = 1;
10519           }
10520         }
10521       }
10522     }
10523     /* Must have same blocksize on all procs (some might have no points) */
10524     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs;
10525     bsLocal[1] = bs;
10526     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
10527     if (bsMinMax[0] != bsMinMax[1]) {
10528       bs = 1;
10529     } else {
10530       bs = bsMinMax[0];
10531     }
10532     PetscCall(PetscMalloc1(subSize, &subIndices));
10533     for (p = pStart; p < pEnd; ++p) {
10534       PetscInt gdof, goff;
10535 
10536       PetscCall(PetscSectionGetDof(subsectionGlobal, p, &gdof));
10537       if (gdof > 0) {
10538         const PetscInt point = spmap[p];
10539 
10540         PetscCall(PetscSectionGetOffset(sectionGlobal, point, &goff));
10541         for (f = 0; f < Nf; ++f) {
10542           PetscInt fdof, fcdof, fc, f2, poff = 0;
10543 
10544           /* Can get rid of this loop by storing field information in the global section */
10545           for (f2 = 0; f2 < f; ++f2) {
10546             PetscCall(PetscSectionGetFieldDof(section, p, f2, &fdof));
10547             PetscCall(PetscSectionGetFieldConstraintDof(section, p, f2, &fcdof));
10548             poff += fdof - fcdof;
10549           }
10550           PetscCall(PetscSectionGetFieldDof(section, p, f, &fdof));
10551           PetscCall(PetscSectionGetFieldConstraintDof(section, p, f, &fcdof));
10552           for (fc = 0; fc < fdof - fcdof; ++fc, ++subOff) subIndices[subOff] = goff + poff + fc;
10553         }
10554       }
10555     }
10556     PetscCall(ISRestoreIndices(spIS, &spmap));
10557     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), subSize, subIndices, PETSC_OWN_POINTER, is));
10558     if (bs > 1) {
10559       /* We need to check that the block size does not come from non-contiguous fields */
10560       PetscInt i, j, set = 1;
10561       for (i = 0; i < subSize; i += bs) {
10562         for (j = 0; j < bs; ++j) {
10563           if (subIndices[i + j] != subIndices[i] + j) {
10564             set = 0;
10565             break;
10566           }
10567         }
10568       }
10569       if (set) PetscCall(ISSetBlockSize(*is, bs));
10570     }
10571     /* Attach nullspace */
10572     for (f = 0; f < Nf; ++f) {
10573       (*subdm)->nullspaceConstructors[f] = dm->nullspaceConstructors[f];
10574       if ((*subdm)->nullspaceConstructors[f]) break;
10575     }
10576     if (f < Nf) {
10577       MatNullSpace nullSpace;
10578       PetscCall((*(*subdm)->nullspaceConstructors[f])(*subdm, f, f, &nullSpace));
10579 
10580       PetscCall(PetscObjectCompose((PetscObject)*is, "nullspace", (PetscObject)nullSpace));
10581       PetscCall(MatNullSpaceDestroy(&nullSpace));
10582     }
10583   }
10584   PetscFunctionReturn(PETSC_SUCCESS);
10585 }
10586 
10587 /*@
10588   DMPlexMonitorThroughput - Report the cell throughput of FE integration
10589 
10590   Input Parameters:
10591 + dm    - The `DM`
10592 - dummy - unused argument
10593 
10594   Options Database Key:
10595 . -dm_plex_monitor_throughput - Activate the monitor
10596 
10597   Level: developer
10598 
10599 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexCreate()`
10600 @*/
10601 PetscErrorCode DMPlexMonitorThroughput(DM dm, void *dummy)
10602 {
10603   PetscLogHandler default_handler;
10604 
10605   PetscFunctionBegin;
10606   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10607   PetscCall(PetscLogGetDefaultHandler(&default_handler));
10608   if (default_handler) {
10609     PetscLogEvent      event;
10610     PetscEventPerfInfo eventInfo;
10611     PetscReal          cellRate, flopRate;
10612     PetscInt           cStart, cEnd, Nf, N;
10613     const char        *name;
10614 
10615     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
10616     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
10617     PetscCall(DMGetNumFields(dm, &Nf));
10618     PetscCall(PetscLogEventGetId("DMPlexResidualFE", &event));
10619     PetscCall(PetscLogEventGetPerfInfo(PETSC_DEFAULT, event, &eventInfo));
10620     N        = (cEnd - cStart) * Nf * eventInfo.count;
10621     flopRate = eventInfo.flops / eventInfo.time;
10622     cellRate = N / eventInfo.time;
10623     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)));
10624   } else {
10625     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.");
10626   }
10627   PetscFunctionReturn(PETSC_SUCCESS);
10628 }
10629