xref: /petsc/src/dm/impls/plex/plex.c (revision 0619917b5a674bb687c64e7daba2ab22be99af31)
1 #include <petsc/private/dmpleximpl.h> /*I      "petscdmplex.h"   I*/
2 #include <petsc/private/dmlabelimpl.h>
3 #include <petsc/private/isimpl.h>
4 #include <petsc/private/vecimpl.h>
5 #include <petsc/private/glvisvecimpl.h>
6 #include <petscsf.h>
7 #include <petscds.h>
8 #include <petscdraw.h>
9 #include <petscdmfield.h>
10 #include <petscdmplextransform.h>
11 
12 /* Logging support */
13 PetscLogEvent DMPLEX_Interpolate, DMPLEX_Partition, DMPLEX_Distribute, DMPLEX_DistributeCones, DMPLEX_DistributeLabels, DMPLEX_DistributeSF, DMPLEX_DistributeOverlap, DMPLEX_DistributeField, DMPLEX_DistributeData, DMPLEX_Migrate, DMPLEX_InterpolateSF, DMPLEX_GlobalToNaturalBegin, DMPLEX_GlobalToNaturalEnd, DMPLEX_NaturalToGlobalBegin, DMPLEX_NaturalToGlobalEnd, DMPLEX_Stratify, DMPLEX_Symmetrize, DMPLEX_Preallocate, DMPLEX_ResidualFEM, DMPLEX_JacobianFEM, DMPLEX_InterpolatorFEM, DMPLEX_InjectorFEM, DMPLEX_IntegralFEM, DMPLEX_CreateGmsh, DMPLEX_RebalanceSharedPoints, DMPLEX_PartSelf, DMPLEX_PartLabelInvert, DMPLEX_PartLabelCreateSF, DMPLEX_PartStratSF, DMPLEX_CreatePointSF, DMPLEX_LocatePoints, DMPLEX_TopologyView, DMPLEX_LabelsView, DMPLEX_CoordinatesView, DMPLEX_SectionView, DMPLEX_GlobalVectorView, DMPLEX_LocalVectorView, DMPLEX_TopologyLoad, DMPLEX_LabelsLoad, DMPLEX_CoordinatesLoad, DMPLEX_SectionLoad, DMPLEX_GlobalVectorLoad, DMPLEX_LocalVectorLoad;
14 PetscLogEvent DMPLEX_RebalBuildGraph, DMPLEX_RebalRewriteSF, DMPLEX_RebalGatherGraph, DMPLEX_RebalPartition, DMPLEX_RebalScatterPart, DMPLEX_Generate, DMPLEX_Transform, DMPLEX_GetLocalOffsets, DMPLEX_Uninterpolate;
15 
16 PETSC_EXTERN PetscErrorCode VecView_MPI(Vec, PetscViewer);
17 
18 /*@
19   DMPlexIsSimplex - Is the first cell in this mesh a simplex?
20 
21   Input Parameter:
22 . dm - The `DMPLEX` object
23 
24   Output Parameter:
25 . simplex - Flag checking for a simplex
26 
27   Level: intermediate
28 
29   Note:
30   This just gives the first range of cells found. If the mesh has several cell types, it will only give the first.
31   If the mesh has no cells, this returns `PETSC_FALSE`.
32 
33 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSimplexOrBoxCells()`, `DMPlexGetCellType()`, `DMPlexGetHeightStratum()`, `DMPolytopeTypeGetNumVertices()`
34 @*/
35 PetscErrorCode DMPlexIsSimplex(DM dm, PetscBool *simplex)
36 {
37   DMPolytopeType ct;
38   PetscInt       cStart, cEnd;
39 
40   PetscFunctionBegin;
41   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
42   if (cEnd <= cStart) {
43     *simplex = PETSC_FALSE;
44     PetscFunctionReturn(PETSC_SUCCESS);
45   }
46   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
47   *simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct) + 1 ? PETSC_TRUE : PETSC_FALSE;
48   PetscFunctionReturn(PETSC_SUCCESS);
49 }
50 
51 /*@
52   DMPlexGetSimplexOrBoxCells - Get the range of cells which are neither prisms nor ghost FV cells
53 
54   Input Parameters:
55 + dm     - The `DMPLEX` object
56 - height - The cell height in the Plex, 0 is the default
57 
58   Output Parameters:
59 + cStart - The first "normal" cell
60 - cEnd   - The upper bound on "normal"" cells
61 
62   Level: developer
63 
64   Note:
65   This just gives the first range of cells found. If the mesh has several cell types, it will only give the first.
66 
67 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexConstructGhostCells()`, `DMPlexGetCellTypeStratum()`
68 @*/
69 PetscErrorCode DMPlexGetSimplexOrBoxCells(DM dm, PetscInt height, PetscInt *cStart, PetscInt *cEnd)
70 {
71   DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
72   PetscInt       cS, cE, c;
73 
74   PetscFunctionBegin;
75   PetscCall(DMPlexGetHeightStratum(dm, PetscMax(height, 0), &cS, &cE));
76   for (c = cS; c < cE; ++c) {
77     DMPolytopeType cct;
78 
79     PetscCall(DMPlexGetCellType(dm, c, &cct));
80     if ((PetscInt)cct < 0) break;
81     switch (cct) {
82     case DM_POLYTOPE_POINT:
83     case DM_POLYTOPE_SEGMENT:
84     case DM_POLYTOPE_TRIANGLE:
85     case DM_POLYTOPE_QUADRILATERAL:
86     case DM_POLYTOPE_TETRAHEDRON:
87     case DM_POLYTOPE_HEXAHEDRON:
88       ct = cct;
89       break;
90     default:
91       break;
92     }
93     if (ct != DM_POLYTOPE_UNKNOWN) break;
94   }
95   if (ct != DM_POLYTOPE_UNKNOWN) {
96     DMLabel ctLabel;
97 
98     PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
99     PetscCall(DMLabelGetStratumBounds(ctLabel, ct, &cS, &cE));
100     // Reset label for fast lookup
101     PetscCall(DMLabelMakeAllInvalid_Internal(ctLabel));
102   }
103   if (cStart) *cStart = cS;
104   if (cEnd) *cEnd = cE;
105   PetscFunctionReturn(PETSC_SUCCESS);
106 }
107 
108 PetscErrorCode DMPlexGetFieldType_Internal(DM dm, PetscSection section, PetscInt field, PetscInt *sStart, PetscInt *sEnd, PetscViewerVTKFieldType *ft)
109 {
110   PetscInt cdim, pStart, pEnd, vStart, vEnd, cStart, cEnd;
111   PetscInt vcdof[2] = {0, 0}, globalvcdof[2];
112 
113   PetscFunctionBegin;
114   *ft = PETSC_VTK_INVALID;
115   PetscCall(DMGetCoordinateDim(dm, &cdim));
116   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
117   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
118   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
119   if (field >= 0) {
120     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, vStart, field, &vcdof[0]));
121     if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetFieldDof(section, cStart, field, &vcdof[1]));
122   } else {
123     if ((vStart >= pStart) && (vStart < pEnd)) PetscCall(PetscSectionGetDof(section, vStart, &vcdof[0]));
124     if ((cStart >= pStart) && (cStart < pEnd)) PetscCall(PetscSectionGetDof(section, cStart, &vcdof[1]));
125   }
126   PetscCall(MPIU_Allreduce(vcdof, globalvcdof, 2, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
127   if (globalvcdof[0]) {
128     *sStart = vStart;
129     *sEnd   = vEnd;
130     if (globalvcdof[0] == cdim) *ft = PETSC_VTK_POINT_VECTOR_FIELD;
131     else *ft = PETSC_VTK_POINT_FIELD;
132   } else if (globalvcdof[1]) {
133     *sStart = cStart;
134     *sEnd   = cEnd;
135     if (globalvcdof[1] == cdim) *ft = PETSC_VTK_CELL_VECTOR_FIELD;
136     else *ft = PETSC_VTK_CELL_FIELD;
137   } else {
138     if (field >= 0) {
139       const char *fieldname;
140 
141       PetscCall(PetscSectionGetFieldName(section, field, &fieldname));
142       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section field %" PetscInt_FMT " \"%s\"\n", field, fieldname));
143     } else {
144       PetscCall(PetscInfo((PetscObject)dm, "Could not classify VTK output type of section\n"));
145     }
146   }
147   PetscFunctionReturn(PETSC_SUCCESS);
148 }
149 
150 /*@
151   DMPlexVecView1D - Plot many 1D solutions on the same line graph
152 
153   Collective
154 
155   Input Parameters:
156 + dm     - The `DMPLEX` object
157 . n      - The number of vectors
158 . u      - The array of local vectors
159 - viewer - The `PetscViewer`
160 
161   Level: advanced
162 
163 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `VecViewFromOptions()`, `VecView()`
164 @*/
165 PetscErrorCode DMPlexVecView1D(DM dm, PetscInt n, Vec u[], PetscViewer viewer)
166 {
167   PetscDS            ds;
168   PetscDraw          draw = NULL;
169   PetscDrawLG        lg;
170   Vec                coordinates;
171   const PetscScalar *coords, **sol;
172   PetscReal         *vals;
173   PetscInt          *Nc;
174   PetscInt           Nf, f, c, Nl, l, i, vStart, vEnd, v;
175   char             **names;
176 
177   PetscFunctionBegin;
178   PetscCall(DMGetDS(dm, &ds));
179   PetscCall(PetscDSGetNumFields(ds, &Nf));
180   PetscCall(PetscDSGetTotalComponents(ds, &Nl));
181   PetscCall(PetscDSGetComponents(ds, &Nc));
182 
183   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
184   if (!draw) PetscFunctionReturn(PETSC_SUCCESS);
185   PetscCall(PetscDrawLGCreate(draw, n * Nl, &lg));
186 
187   PetscCall(PetscMalloc3(n, &sol, n * Nl, &names, n * Nl, &vals));
188   for (i = 0, l = 0; i < n; ++i) {
189     const char *vname;
190 
191     PetscCall(PetscObjectGetName((PetscObject)u[i], &vname));
192     for (f = 0; f < Nf; ++f) {
193       PetscObject disc;
194       const char *fname;
195       char        tmpname[PETSC_MAX_PATH_LEN];
196 
197       PetscCall(PetscDSGetDiscretization(ds, f, &disc));
198       /* TODO Create names for components */
199       for (c = 0; c < Nc[f]; ++c, ++l) {
200         PetscCall(PetscObjectGetName(disc, &fname));
201         PetscCall(PetscStrncpy(tmpname, vname, sizeof(tmpname)));
202         PetscCall(PetscStrlcat(tmpname, ":", sizeof(tmpname)));
203         PetscCall(PetscStrlcat(tmpname, fname, sizeof(tmpname)));
204         PetscCall(PetscStrallocpy(tmpname, &names[l]));
205       }
206     }
207   }
208   PetscCall(PetscDrawLGSetLegend(lg, (const char *const *)names));
209   /* Just add P_1 support for now */
210   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
211   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
212   PetscCall(VecGetArrayRead(coordinates, &coords));
213   for (i = 0; i < n; ++i) PetscCall(VecGetArrayRead(u[i], &sol[i]));
214   for (v = vStart; v < vEnd; ++v) {
215     PetscScalar *x, *svals;
216 
217     PetscCall(DMPlexPointLocalRead(dm, v, coords, &x));
218     for (i = 0; i < n; ++i) {
219       PetscCall(DMPlexPointLocalRead(dm, v, sol[i], &svals));
220       for (l = 0; l < Nl; ++l) vals[i * Nl + l] = PetscRealPart(svals[l]);
221     }
222     PetscCall(PetscDrawLGAddCommonPoint(lg, PetscRealPart(x[0]), vals));
223   }
224   PetscCall(VecRestoreArrayRead(coordinates, &coords));
225   for (i = 0; i < n; ++i) PetscCall(VecRestoreArrayRead(u[i], &sol[i]));
226   for (l = 0; l < n * Nl; ++l) PetscCall(PetscFree(names[l]));
227   PetscCall(PetscFree3(sol, names, vals));
228 
229   PetscCall(PetscDrawLGDraw(lg));
230   PetscCall(PetscDrawLGDestroy(&lg));
231   PetscFunctionReturn(PETSC_SUCCESS);
232 }
233 
234 static PetscErrorCode VecView_Plex_Local_Draw_1D(Vec u, PetscViewer viewer)
235 {
236   DM dm;
237 
238   PetscFunctionBegin;
239   PetscCall(VecGetDM(u, &dm));
240   PetscCall(DMPlexVecView1D(dm, 1, &u, viewer));
241   PetscFunctionReturn(PETSC_SUCCESS);
242 }
243 
244 static PetscErrorCode VecView_Plex_Local_Draw_2D(Vec v, PetscViewer viewer)
245 {
246   DM                 dm;
247   PetscSection       s;
248   PetscDraw          draw, popup;
249   DM                 cdm;
250   PetscSection       coordSection;
251   Vec                coordinates;
252   const PetscScalar *array;
253   PetscReal          lbound[3], ubound[3];
254   PetscReal          vbound[2], time;
255   PetscBool          flg;
256   PetscInt           dim, Nf, f, Nc, comp, vStart, vEnd, cStart, cEnd, c, N, level, step, w = 0;
257   const char        *name;
258   char               title[PETSC_MAX_PATH_LEN];
259 
260   PetscFunctionBegin;
261   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
262   PetscCall(VecGetDM(v, &dm));
263   PetscCall(DMGetCoordinateDim(dm, &dim));
264   PetscCall(DMGetLocalSection(dm, &s));
265   PetscCall(PetscSectionGetNumFields(s, &Nf));
266   PetscCall(DMGetCoarsenLevel(dm, &level));
267   PetscCall(DMGetCoordinateDM(dm, &cdm));
268   PetscCall(DMGetLocalSection(cdm, &coordSection));
269   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
270   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
271   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
272 
273   PetscCall(PetscObjectGetName((PetscObject)v, &name));
274   PetscCall(DMGetOutputSequenceNumber(dm, &step, &time));
275 
276   PetscCall(VecGetLocalSize(coordinates, &N));
277   PetscCall(DMGetBoundingBox(dm, lbound, ubound));
278   PetscCall(PetscDrawClear(draw));
279 
280   /* Could implement something like DMDASelectFields() */
281   for (f = 0; f < Nf; ++f) {
282     DM          fdm = dm;
283     Vec         fv  = v;
284     IS          fis;
285     char        prefix[PETSC_MAX_PATH_LEN];
286     const char *fname;
287 
288     PetscCall(PetscSectionGetFieldComponents(s, f, &Nc));
289     PetscCall(PetscSectionGetFieldName(s, f, &fname));
290 
291     if (v->hdr.prefix) PetscCall(PetscStrncpy(prefix, v->hdr.prefix, sizeof(prefix)));
292     else prefix[0] = '\0';
293     if (Nf > 1) {
294       PetscCall(DMCreateSubDM(dm, 1, &f, &fis, &fdm));
295       PetscCall(VecGetSubVector(v, fis, &fv));
296       PetscCall(PetscStrlcat(prefix, fname, sizeof(prefix)));
297       PetscCall(PetscStrlcat(prefix, "_", sizeof(prefix)));
298     }
299     for (comp = 0; comp < Nc; ++comp, ++w) {
300       PetscInt nmax = 2;
301 
302       PetscCall(PetscViewerDrawGetDraw(viewer, w, &draw));
303       if (Nc > 1) PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s_%" PetscInt_FMT " Step: %" PetscInt_FMT " Time: %.4g", name, fname, comp, step, (double)time));
304       else PetscCall(PetscSNPrintf(title, sizeof(title), "%s:%s Step: %" PetscInt_FMT " Time: %.4g", name, fname, step, (double)time));
305       PetscCall(PetscDrawSetTitle(draw, title));
306 
307       /* TODO Get max and min only for this component */
308       PetscCall(PetscOptionsGetRealArray(NULL, prefix, "-vec_view_bounds", vbound, &nmax, &flg));
309       if (!flg) {
310         PetscCall(VecMin(fv, NULL, &vbound[0]));
311         PetscCall(VecMax(fv, NULL, &vbound[1]));
312         if (vbound[1] <= vbound[0]) vbound[1] = vbound[0] + 1.0;
313       }
314 
315       PetscCall(PetscDrawGetPopup(draw, &popup));
316       PetscCall(PetscDrawScalePopup(popup, vbound[0], vbound[1]));
317       PetscCall(PetscDrawSetCoordinates(draw, lbound[0], lbound[1], ubound[0], ubound[1]));
318       PetscCall(VecGetArrayRead(fv, &array));
319       for (c = cStart; c < cEnd; ++c) {
320         PetscScalar       *coords = NULL, *a = NULL;
321         const PetscScalar *coords_arr;
322         PetscBool          isDG;
323         PetscInt           numCoords, color[4] = {-1, -1, -1, -1};
324 
325         PetscCall(DMPlexPointLocalRead(fdm, c, array, &a));
326         if (a) {
327           color[0] = PetscDrawRealToColor(PetscRealPart(a[comp]), vbound[0], vbound[1]);
328           color[1] = color[2] = color[3] = color[0];
329         } else {
330           PetscScalar *vals = NULL;
331           PetscInt     numVals, va;
332 
333           PetscCall(DMPlexVecGetClosure(fdm, NULL, fv, c, &numVals, &vals));
334           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);
335           switch (numVals / Nc) {
336           case 3: /* P1 Triangle */
337           case 4: /* P1 Quadrangle */
338             for (va = 0; va < numVals / Nc; ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp]), vbound[0], vbound[1]);
339             break;
340           case 6: /* P2 Triangle */
341           case 8: /* P2 Quadrangle */
342             for (va = 0; va < numVals / (Nc * 2); ++va) color[va] = PetscDrawRealToColor(PetscRealPart(vals[va * Nc + comp + numVals / (Nc * 2)]), vbound[0], vbound[1]);
343             break;
344           default:
345             SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Number of values for cell closure %" PetscInt_FMT " cannot be handled", numVals / Nc);
346           }
347           PetscCall(DMPlexVecRestoreClosure(fdm, NULL, fv, c, &numVals, &vals));
348         }
349         PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
350         switch (numCoords) {
351         case 6:
352         case 12: /* Localized triangle */
353           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]));
354           break;
355         case 8:
356         case 16: /* Localized quadrilateral */
357           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]));
358           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]));
359           break;
360         default:
361           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells with %" PetscInt_FMT " coordinates", numCoords);
362         }
363         PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
364       }
365       PetscCall(VecRestoreArrayRead(fv, &array));
366       PetscCall(PetscDrawFlush(draw));
367       PetscCall(PetscDrawPause(draw));
368       PetscCall(PetscDrawSave(draw));
369     }
370     if (Nf > 1) {
371       PetscCall(VecRestoreSubVector(v, fis, &fv));
372       PetscCall(ISDestroy(&fis));
373       PetscCall(DMDestroy(&fdm));
374     }
375   }
376   PetscFunctionReturn(PETSC_SUCCESS);
377 }
378 
379 static PetscErrorCode VecView_Plex_Local_Draw(Vec v, PetscViewer viewer)
380 {
381   DM        dm;
382   PetscDraw draw;
383   PetscInt  dim;
384   PetscBool isnull;
385 
386   PetscFunctionBegin;
387   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
388   PetscCall(PetscDrawIsNull(draw, &isnull));
389   if (isnull) PetscFunctionReturn(PETSC_SUCCESS);
390 
391   PetscCall(VecGetDM(v, &dm));
392   PetscCall(DMGetCoordinateDim(dm, &dim));
393   switch (dim) {
394   case 1:
395     PetscCall(VecView_Plex_Local_Draw_1D(v, viewer));
396     break;
397   case 2:
398     PetscCall(VecView_Plex_Local_Draw_2D(v, viewer));
399     break;
400   default:
401     SETERRQ(PetscObjectComm((PetscObject)v), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT ". Try PETSCVIEWERGLVIS", dim);
402   }
403   PetscFunctionReturn(PETSC_SUCCESS);
404 }
405 
406 static PetscErrorCode VecView_Plex_Local_VTK(Vec v, PetscViewer viewer)
407 {
408   DM                      dm;
409   Vec                     locv;
410   const char             *name;
411   PetscSection            section;
412   PetscInt                pStart, pEnd;
413   PetscInt                numFields;
414   PetscViewerVTKFieldType ft;
415 
416   PetscFunctionBegin;
417   PetscCall(VecGetDM(v, &dm));
418   PetscCall(DMCreateLocalVector(dm, &locv)); /* VTK viewer requires exclusive ownership of the vector */
419   PetscCall(PetscObjectGetName((PetscObject)v, &name));
420   PetscCall(PetscObjectSetName((PetscObject)locv, name));
421   PetscCall(VecCopy(v, locv));
422   PetscCall(DMGetLocalSection(dm, &section));
423   PetscCall(PetscSectionGetNumFields(section, &numFields));
424   if (!numFields) {
425     PetscCall(DMPlexGetFieldType_Internal(dm, section, PETSC_DETERMINE, &pStart, &pEnd, &ft));
426     PetscCall(PetscViewerVTKAddField(viewer, (PetscObject)dm, DMPlexVTKWriteAll, PETSC_DEFAULT, ft, PETSC_TRUE, (PetscObject)locv));
427   } else {
428     PetscInt f;
429 
430     for (f = 0; f < numFields; f++) {
431       PetscCall(DMPlexGetFieldType_Internal(dm, section, f, &pStart, &pEnd, &ft));
432       if (ft == PETSC_VTK_INVALID) continue;
433       PetscCall(PetscObjectReference((PetscObject)locv));
434       PetscCall(PetscViewerVTKAddField(viewer, (PetscObject)dm, DMPlexVTKWriteAll, f, ft, PETSC_TRUE, (PetscObject)locv));
435     }
436     PetscCall(VecDestroy(&locv));
437   }
438   PetscFunctionReturn(PETSC_SUCCESS);
439 }
440 
441 PetscErrorCode VecView_Plex_Local(Vec v, PetscViewer viewer)
442 {
443   DM        dm;
444   PetscBool isvtk, ishdf5, isdraw, isglvis, iscgns;
445 
446   PetscFunctionBegin;
447   PetscCall(VecGetDM(v, &dm));
448   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
449   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
450   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
451   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
452   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
453   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
454   if (isvtk || ishdf5 || isdraw || isglvis || iscgns) {
455     PetscInt    i, numFields;
456     PetscObject fe;
457     PetscBool   fem  = PETSC_FALSE;
458     Vec         locv = v;
459     const char *name;
460     PetscInt    step;
461     PetscReal   time;
462 
463     PetscCall(DMGetNumFields(dm, &numFields));
464     for (i = 0; i < numFields; i++) {
465       PetscCall(DMGetField(dm, i, NULL, &fe));
466       if (fe->classid == PETSCFE_CLASSID) {
467         fem = PETSC_TRUE;
468         break;
469       }
470     }
471     if (fem) {
472       PetscObject isZero;
473 
474       PetscCall(DMGetLocalVector(dm, &locv));
475       PetscCall(PetscObjectGetName((PetscObject)v, &name));
476       PetscCall(PetscObjectSetName((PetscObject)locv, name));
477       PetscCall(PetscObjectQuery((PetscObject)v, "__Vec_bc_zero__", &isZero));
478       PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", isZero));
479       PetscCall(VecCopy(v, locv));
480       PetscCall(DMGetOutputSequenceNumber(dm, NULL, &time));
481       PetscCall(DMPlexInsertBoundaryValues(dm, PETSC_TRUE, locv, time, NULL, NULL, NULL));
482     }
483     if (isvtk) {
484       PetscCall(VecView_Plex_Local_VTK(locv, viewer));
485     } else if (ishdf5) {
486 #if defined(PETSC_HAVE_HDF5)
487       PetscCall(VecView_Plex_Local_HDF5_Internal(locv, viewer));
488 #else
489       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
490 #endif
491     } else if (isdraw) {
492       PetscCall(VecView_Plex_Local_Draw(locv, viewer));
493     } else if (isglvis) {
494       PetscCall(DMGetOutputSequenceNumber(dm, &step, NULL));
495       PetscCall(PetscViewerGLVisSetSnapId(viewer, step));
496       PetscCall(VecView_GLVis(locv, viewer));
497     } else if (iscgns) {
498 #if defined(PETSC_HAVE_CGNS)
499       PetscCall(VecView_Plex_Local_CGNS(locv, viewer));
500 #else
501       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "CGNS not supported in this build.\nPlease reconfigure using --download-cgns");
502 #endif
503     }
504     if (fem) {
505       PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", NULL));
506       PetscCall(DMRestoreLocalVector(dm, &locv));
507     }
508   } else {
509     PetscBool isseq;
510 
511     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
512     if (isseq) PetscCall(VecView_Seq(v, viewer));
513     else PetscCall(VecView_MPI(v, viewer));
514   }
515   PetscFunctionReturn(PETSC_SUCCESS);
516 }
517 
518 PetscErrorCode VecView_Plex(Vec v, PetscViewer viewer)
519 {
520   DM        dm;
521   PetscBool isvtk, ishdf5, isdraw, isglvis, isexodusii, iscgns;
522 
523   PetscFunctionBegin;
524   PetscCall(VecGetDM(v, &dm));
525   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
526   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
527   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
528   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
529   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
530   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
531   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodusii));
532   if (isvtk || isdraw || isglvis || iscgns) {
533     Vec         locv;
534     PetscObject isZero;
535     const char *name;
536 
537     PetscCall(DMGetLocalVector(dm, &locv));
538     PetscCall(PetscObjectGetName((PetscObject)v, &name));
539     PetscCall(PetscObjectSetName((PetscObject)locv, name));
540     PetscCall(DMGlobalToLocalBegin(dm, v, INSERT_VALUES, locv));
541     PetscCall(DMGlobalToLocalEnd(dm, v, INSERT_VALUES, locv));
542     PetscCall(PetscObjectQuery((PetscObject)v, "__Vec_bc_zero__", &isZero));
543     PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", isZero));
544     PetscCall(VecView_Plex_Local(locv, viewer));
545     PetscCall(PetscObjectCompose((PetscObject)locv, "__Vec_bc_zero__", NULL));
546     PetscCall(DMRestoreLocalVector(dm, &locv));
547   } else if (ishdf5) {
548 #if defined(PETSC_HAVE_HDF5)
549     PetscCall(VecView_Plex_HDF5_Internal(v, viewer));
550 #else
551     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
552 #endif
553   } else if (isexodusii) {
554 #if defined(PETSC_HAVE_EXODUSII)
555     PetscCall(VecView_PlexExodusII_Internal(v, viewer));
556 #else
557     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
558 #endif
559   } else {
560     PetscBool isseq;
561 
562     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
563     if (isseq) PetscCall(VecView_Seq(v, viewer));
564     else PetscCall(VecView_MPI(v, viewer));
565   }
566   PetscFunctionReturn(PETSC_SUCCESS);
567 }
568 
569 PetscErrorCode VecView_Plex_Native(Vec originalv, PetscViewer viewer)
570 {
571   DM                dm;
572   MPI_Comm          comm;
573   PetscViewerFormat format;
574   Vec               v;
575   PetscBool         isvtk, ishdf5;
576 
577   PetscFunctionBegin;
578   PetscCall(VecGetDM(originalv, &dm));
579   PetscCall(PetscObjectGetComm((PetscObject)originalv, &comm));
580   PetscCheck(dm, comm, PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
581   PetscCall(PetscViewerGetFormat(viewer, &format));
582   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
583   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
584   if (format == PETSC_VIEWER_NATIVE) {
585     /* Natural ordering is the common case for DMDA, NATIVE means plain vector, for PLEX is the opposite */
586     /* this need a better fix */
587     if (dm->useNatural) {
588       if (dm->sfNatural) {
589         const char *vecname;
590         PetscInt    n, nroots;
591 
592         PetscCall(VecGetLocalSize(originalv, &n));
593         PetscCall(PetscSFGetGraph(dm->sfNatural, &nroots, NULL, NULL, NULL));
594         if (n == nroots) {
595           PetscCall(DMPlexCreateNaturalVector(dm, &v));
596           PetscCall(DMPlexGlobalToNaturalBegin(dm, originalv, v));
597           PetscCall(DMPlexGlobalToNaturalEnd(dm, originalv, v));
598           PetscCall(PetscObjectGetName((PetscObject)originalv, &vecname));
599           PetscCall(PetscObjectSetName((PetscObject)v, vecname));
600         } else SETERRQ(comm, PETSC_ERR_ARG_WRONG, "DM global to natural SF only handles global vectors");
601       } else SETERRQ(comm, PETSC_ERR_ARG_WRONGSTATE, "DM global to natural SF was not created");
602     } else v = originalv;
603   } else v = originalv;
604 
605   if (ishdf5) {
606 #if defined(PETSC_HAVE_HDF5)
607     PetscCall(VecView_Plex_HDF5_Native_Internal(v, viewer));
608 #else
609     SETERRQ(comm, PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
610 #endif
611   } else if (isvtk) {
612     SETERRQ(comm, PETSC_ERR_SUP, "VTK format does not support viewing in natural order. Please switch to HDF5.");
613   } else {
614     PetscBool isseq;
615 
616     PetscCall(PetscObjectTypeCompare((PetscObject)v, VECSEQ, &isseq));
617     if (isseq) PetscCall(VecView_Seq(v, viewer));
618     else PetscCall(VecView_MPI(v, viewer));
619   }
620   if (v != originalv) PetscCall(VecDestroy(&v));
621   PetscFunctionReturn(PETSC_SUCCESS);
622 }
623 
624 PetscErrorCode VecLoad_Plex_Local(Vec v, PetscViewer viewer)
625 {
626   DM        dm;
627   PetscBool ishdf5;
628 
629   PetscFunctionBegin;
630   PetscCall(VecGetDM(v, &dm));
631   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
632   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
633   if (ishdf5) {
634     DM          dmBC;
635     Vec         gv;
636     const char *name;
637 
638     PetscCall(DMGetOutputDM(dm, &dmBC));
639     PetscCall(DMGetGlobalVector(dmBC, &gv));
640     PetscCall(PetscObjectGetName((PetscObject)v, &name));
641     PetscCall(PetscObjectSetName((PetscObject)gv, name));
642     PetscCall(VecLoad_Default(gv, viewer));
643     PetscCall(DMGlobalToLocalBegin(dmBC, gv, INSERT_VALUES, v));
644     PetscCall(DMGlobalToLocalEnd(dmBC, gv, INSERT_VALUES, v));
645     PetscCall(DMRestoreGlobalVector(dmBC, &gv));
646   } else PetscCall(VecLoad_Default(v, viewer));
647   PetscFunctionReturn(PETSC_SUCCESS);
648 }
649 
650 PetscErrorCode VecLoad_Plex(Vec v, PetscViewer viewer)
651 {
652   DM        dm;
653   PetscBool ishdf5, isexodusii;
654 
655   PetscFunctionBegin;
656   PetscCall(VecGetDM(v, &dm));
657   PetscCheck(dm, PetscObjectComm((PetscObject)v), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
658   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
659   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodusii));
660   if (ishdf5) {
661 #if defined(PETSC_HAVE_HDF5)
662     PetscCall(VecLoad_Plex_HDF5_Internal(v, viewer));
663 #else
664     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
665 #endif
666   } else if (isexodusii) {
667 #if defined(PETSC_HAVE_EXODUSII)
668     PetscCall(VecLoad_PlexExodusII_Internal(v, viewer));
669 #else
670     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "ExodusII not supported in this build.\nPlease reconfigure using --download-exodusii");
671 #endif
672   } else PetscCall(VecLoad_Default(v, viewer));
673   PetscFunctionReturn(PETSC_SUCCESS);
674 }
675 
676 PetscErrorCode VecLoad_Plex_Native(Vec originalv, PetscViewer viewer)
677 {
678   DM                dm;
679   PetscViewerFormat format;
680   PetscBool         ishdf5;
681 
682   PetscFunctionBegin;
683   PetscCall(VecGetDM(originalv, &dm));
684   PetscCheck(dm, PetscObjectComm((PetscObject)originalv), PETSC_ERR_ARG_WRONG, "Vector not generated from a DM");
685   PetscCall(PetscViewerGetFormat(viewer, &format));
686   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
687   if (format == PETSC_VIEWER_NATIVE) {
688     if (dm->useNatural) {
689       if (dm->sfNatural) {
690         if (ishdf5) {
691 #if defined(PETSC_HAVE_HDF5)
692           Vec         v;
693           const char *vecname;
694 
695           PetscCall(DMPlexCreateNaturalVector(dm, &v));
696           PetscCall(PetscObjectGetName((PetscObject)originalv, &vecname));
697           PetscCall(PetscObjectSetName((PetscObject)v, vecname));
698           PetscCall(VecLoad_Plex_HDF5_Native_Internal(v, viewer));
699           PetscCall(DMPlexNaturalToGlobalBegin(dm, v, originalv));
700           PetscCall(DMPlexNaturalToGlobalEnd(dm, v, originalv));
701           PetscCall(VecDestroy(&v));
702 #else
703           SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
704 #endif
705         } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Reading in natural order is not supported for anything but HDF5.");
706       }
707     } else PetscCall(VecLoad_Default(originalv, viewer));
708   }
709   PetscFunctionReturn(PETSC_SUCCESS);
710 }
711 
712 PETSC_UNUSED static PetscErrorCode DMPlexView_Ascii_Geometry(DM dm, PetscViewer viewer)
713 {
714   PetscSection       coordSection;
715   Vec                coordinates;
716   DMLabel            depthLabel, celltypeLabel;
717   const char        *name[4];
718   const PetscScalar *a;
719   PetscInt           dim, pStart, pEnd, cStart, cEnd, c;
720 
721   PetscFunctionBegin;
722   PetscCall(DMGetDimension(dm, &dim));
723   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
724   PetscCall(DMGetCoordinateSection(dm, &coordSection));
725   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
726   PetscCall(DMPlexGetCellTypeLabel(dm, &celltypeLabel));
727   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
728   PetscCall(PetscSectionGetChart(coordSection, &pStart, &pEnd));
729   PetscCall(VecGetArrayRead(coordinates, &a));
730   name[0]       = "vertex";
731   name[1]       = "edge";
732   name[dim - 1] = "face";
733   name[dim]     = "cell";
734   for (c = cStart; c < cEnd; ++c) {
735     PetscInt *closure = NULL;
736     PetscInt  closureSize, cl, ct;
737 
738     PetscCall(DMLabelGetValue(celltypeLabel, c, &ct));
739     PetscCall(PetscViewerASCIIPrintf(viewer, "Geometry for cell %" PetscInt_FMT " polytope type %s:\n", c, DMPolytopeTypes[ct]));
740     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
741     PetscCall(PetscViewerASCIIPushTab(viewer));
742     for (cl = 0; cl < closureSize * 2; cl += 2) {
743       PetscInt point = closure[cl], depth, dof, off, d, p;
744 
745       if ((point < pStart) || (point >= pEnd)) continue;
746       PetscCall(PetscSectionGetDof(coordSection, point, &dof));
747       if (!dof) continue;
748       PetscCall(DMLabelGetValue(depthLabel, point, &depth));
749       PetscCall(PetscSectionGetOffset(coordSection, point, &off));
750       PetscCall(PetscViewerASCIIPrintf(viewer, "%s %" PetscInt_FMT " coords:", name[depth], point));
751       for (p = 0; p < dof / dim; ++p) {
752         PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
753         for (d = 0; d < dim; ++d) {
754           if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
755           PetscCall(PetscViewerASCIIPrintf(viewer, "%g", (double)PetscRealPart(a[off + p * dim + d])));
756         }
757         PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
758       }
759       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
760     }
761     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
762     PetscCall(PetscViewerASCIIPopTab(viewer));
763   }
764   PetscCall(VecRestoreArrayRead(coordinates, &a));
765   PetscFunctionReturn(PETSC_SUCCESS);
766 }
767 
768 typedef enum {
769   CS_CARTESIAN,
770   CS_POLAR,
771   CS_CYLINDRICAL,
772   CS_SPHERICAL
773 } CoordSystem;
774 const char *CoordSystems[] = {"cartesian", "polar", "cylindrical", "spherical", "CoordSystem", "CS_", NULL};
775 
776 static PetscErrorCode DMPlexView_Ascii_Coordinates(PetscViewer viewer, CoordSystem cs, PetscInt dim, const PetscScalar x[])
777 {
778   PetscInt i;
779 
780   PetscFunctionBegin;
781   if (dim > 3) {
782     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double)PetscRealPart(x[i])));
783   } else {
784     PetscReal coords[3], trcoords[3] = {0., 0., 0.};
785 
786     for (i = 0; i < dim; ++i) coords[i] = PetscRealPart(x[i]);
787     switch (cs) {
788     case CS_CARTESIAN:
789       for (i = 0; i < dim; ++i) trcoords[i] = coords[i];
790       break;
791     case CS_POLAR:
792       PetscCheck(dim == 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Polar coordinates are for 2 dimension, not %" PetscInt_FMT, dim);
793       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
794       trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
795       break;
796     case CS_CYLINDRICAL:
797       PetscCheck(dim == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cylindrical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
798       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]));
799       trcoords[1] = PetscAtan2Real(coords[1], coords[0]);
800       trcoords[2] = coords[2];
801       break;
802     case CS_SPHERICAL:
803       PetscCheck(dim == 3, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Spherical coordinates are for 3 dimension, not %" PetscInt_FMT, dim);
804       trcoords[0] = PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1]) + PetscSqr(coords[2]));
805       trcoords[1] = PetscAtan2Real(PetscSqrtReal(PetscSqr(coords[0]) + PetscSqr(coords[1])), coords[2]);
806       trcoords[2] = PetscAtan2Real(coords[1], coords[0]);
807       break;
808     }
809     for (i = 0; i < dim; ++i) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " %g", (double)trcoords[i]));
810   }
811   PetscFunctionReturn(PETSC_SUCCESS);
812 }
813 
814 static PetscErrorCode DMPlexView_Ascii(DM dm, PetscViewer viewer)
815 {
816   DM_Plex          *mesh = (DM_Plex *)dm->data;
817   DM                cdm, cdmCell;
818   PetscSection      coordSection, coordSectionCell;
819   Vec               coordinates, coordinatesCell;
820   PetscViewerFormat format;
821 
822   PetscFunctionBegin;
823   PetscCall(PetscViewerGetFormat(viewer, &format));
824   if (format == PETSC_VIEWER_ASCII_INFO_DETAIL) {
825     const char *name;
826     PetscInt    dim, cellHeight, maxConeSize, maxSupportSize;
827     PetscInt    pStart, pEnd, p, numLabels, l;
828     PetscMPIInt rank, size;
829 
830     PetscCall(DMGetCoordinateDM(dm, &cdm));
831     PetscCall(DMGetCoordinateSection(dm, &coordSection));
832     PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
833     PetscCall(DMGetCellCoordinateDM(dm, &cdmCell));
834     PetscCall(DMGetCellCoordinateSection(dm, &coordSectionCell));
835     PetscCall(DMGetCellCoordinatesLocal(dm, &coordinatesCell));
836     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
837     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
838     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
839     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
840     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
841     PetscCall(DMGetDimension(dm, &dim));
842     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
843     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
844     else PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
845     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
846     PetscCall(PetscViewerASCIIPrintf(viewer, "Supports:\n"));
847     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
848     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max support size: %" PetscInt_FMT "\n", rank, maxSupportSize));
849     for (p = pStart; p < pEnd; ++p) {
850       PetscInt dof, off, s;
851 
852       PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
853       PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
854       for (s = off; s < off + dof; ++s) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d]: %" PetscInt_FMT " ----> %" PetscInt_FMT "\n", rank, p, mesh->supports[s]));
855     }
856     PetscCall(PetscViewerFlush(viewer));
857     PetscCall(PetscViewerASCIIPrintf(viewer, "Cones:\n"));
858     PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "[%d] Max cone size: %" PetscInt_FMT "\n", rank, maxConeSize));
859     for (p = pStart; p < pEnd; ++p) {
860       PetscInt dof, off, c;
861 
862       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
863       PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
864       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]));
865     }
866     PetscCall(PetscViewerFlush(viewer));
867     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
868     if (coordSection && coordinates) {
869       CoordSystem        cs = CS_CARTESIAN;
870       const PetscScalar *array, *arrayCell = NULL;
871       PetscInt           Nf, Nc, pvStart, pvEnd, pcStart = PETSC_MAX_INT, pcEnd = PETSC_MIN_INT, pStart, pEnd, p;
872       PetscMPIInt        rank;
873       const char        *name;
874 
875       PetscCall(PetscOptionsGetEnum(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_coord_system", CoordSystems, (PetscEnum *)&cs, NULL));
876       PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)viewer), &rank));
877       PetscCall(PetscSectionGetNumFields(coordSection, &Nf));
878       PetscCheck(Nf == 1, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Coordinate section should have 1 field, not %" PetscInt_FMT, Nf);
879       PetscCall(PetscSectionGetFieldComponents(coordSection, 0, &Nc));
880       PetscCall(PetscSectionGetChart(coordSection, &pvStart, &pvEnd));
881       if (coordSectionCell) PetscCall(PetscSectionGetChart(coordSectionCell, &pcStart, &pcEnd));
882       pStart = PetscMin(pvStart, pcStart);
883       pEnd   = PetscMax(pvEnd, pcEnd);
884       PetscCall(PetscObjectGetName((PetscObject)coordinates, &name));
885       PetscCall(PetscViewerASCIIPrintf(viewer, "%s with %" PetscInt_FMT " fields\n", name, Nf));
886       PetscCall(PetscViewerASCIIPrintf(viewer, "  field 0 with %" PetscInt_FMT " components\n", Nc));
887       if (cs != CS_CARTESIAN) PetscCall(PetscViewerASCIIPrintf(viewer, "  output coordinate system: %s\n", CoordSystems[cs]));
888 
889       PetscCall(VecGetArrayRead(coordinates, &array));
890       if (coordinatesCell) PetscCall(VecGetArrayRead(coordinatesCell, &arrayCell));
891       PetscCall(PetscViewerASCIIPushSynchronized(viewer));
892       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "Process %d:\n", rank));
893       for (p = pStart; p < pEnd; ++p) {
894         PetscInt dof, off;
895 
896         if (p >= pvStart && p < pvEnd) {
897           PetscCall(PetscSectionGetDof(coordSection, p, &dof));
898           PetscCall(PetscSectionGetOffset(coordSection, p, &off));
899           if (dof) {
900             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dim %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
901             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &array[off]));
902             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
903           }
904         }
905         if (cdmCell && p >= pcStart && p < pcEnd) {
906           PetscCall(PetscSectionGetDof(coordSectionCell, p, &dof));
907           PetscCall(PetscSectionGetOffset(coordSectionCell, p, &off));
908           if (dof) {
909             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "  (%4" PetscInt_FMT ") dim %2" PetscInt_FMT " offset %3" PetscInt_FMT, p, dof, off));
910             PetscCall(DMPlexView_Ascii_Coordinates(viewer, cs, dof, &arrayCell[off]));
911             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\n"));
912           }
913         }
914       }
915       PetscCall(PetscViewerFlush(viewer));
916       PetscCall(PetscViewerASCIIPopSynchronized(viewer));
917       PetscCall(VecRestoreArrayRead(coordinates, &array));
918       if (coordinatesCell) PetscCall(VecRestoreArrayRead(coordinatesCell, &arrayCell));
919     }
920     PetscCall(DMGetNumLabels(dm, &numLabels));
921     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
922     for (l = 0; l < numLabels; ++l) {
923       DMLabel     label;
924       PetscBool   isdepth;
925       const char *name;
926 
927       PetscCall(DMGetLabelName(dm, l, &name));
928       PetscCall(PetscStrcmp(name, "depth", &isdepth));
929       if (isdepth) continue;
930       PetscCall(DMGetLabel(dm, name, &label));
931       PetscCall(DMLabelView(label, viewer));
932     }
933     if (size > 1) {
934       PetscSF sf;
935 
936       PetscCall(DMGetPointSF(dm, &sf));
937       PetscCall(PetscSFView(sf, viewer));
938     }
939     if (mesh->periodic.face_sf) PetscCall(PetscSFView(mesh->periodic.face_sf, viewer));
940     PetscCall(PetscViewerFlush(viewer));
941   } else if (format == PETSC_VIEWER_ASCII_LATEX) {
942     const char  *name, *color;
943     const char  *defcolors[3]  = {"gray", "orange", "green"};
944     const char  *deflcolors[4] = {"blue", "cyan", "red", "magenta"};
945     char         lname[PETSC_MAX_PATH_LEN];
946     PetscReal    scale      = 2.0;
947     PetscReal    tikzscale  = 1.0;
948     PetscBool    useNumbers = PETSC_TRUE, drawNumbers[4], drawColors[4], useLabels, useColors, plotEdges, drawHasse = PETSC_FALSE;
949     double       tcoords[3];
950     PetscScalar *coords;
951     PetscInt     numLabels, l, numColors, numLColors, dim, d, depth, cStart, cEnd, c, vStart, vEnd, v, eStart = 0, eEnd = 0, e, p, n;
952     PetscMPIInt  rank, size;
953     char       **names, **colors, **lcolors;
954     PetscBool    flg, lflg;
955     PetscBT      wp = NULL;
956     PetscInt     pEnd, pStart;
957 
958     PetscCall(DMGetCoordinateDM(dm, &cdm));
959     PetscCall(DMGetCoordinateSection(dm, &coordSection));
960     PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
961     PetscCall(DMGetCellCoordinateDM(dm, &cdmCell));
962     PetscCall(DMGetCellCoordinateSection(dm, &coordSectionCell));
963     PetscCall(DMGetCellCoordinatesLocal(dm, &coordinatesCell));
964     PetscCall(DMGetDimension(dm, &dim));
965     PetscCall(DMPlexGetDepth(dm, &depth));
966     PetscCall(DMGetNumLabels(dm, &numLabels));
967     numLabels  = PetscMax(numLabels, 10);
968     numColors  = 10;
969     numLColors = 10;
970     PetscCall(PetscCalloc3(numLabels, &names, numColors, &colors, numLColors, &lcolors));
971     PetscCall(PetscOptionsGetReal(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_scale", &scale, NULL));
972     PetscCall(PetscOptionsGetReal(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_tikzscale", &tikzscale, NULL));
973     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_numbers", &useNumbers, NULL));
974     for (d = 0; d < 4; ++d) drawNumbers[d] = useNumbers;
975     for (d = 0; d < 4; ++d) drawColors[d] = PETSC_TRUE;
976     n = 4;
977     PetscCall(PetscOptionsGetBoolArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_numbers_depth", drawNumbers, &n, &flg));
978     PetscCheck(!flg || n == dim + 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim + 1);
979     PetscCall(PetscOptionsGetBoolArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_colors_depth", drawColors, &n, &flg));
980     PetscCheck(!flg || n == dim + 1, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_SIZ, "Number of flags %" PetscInt_FMT " != %" PetscInt_FMT " dim+1", n, dim + 1);
981     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_labels", names, &numLabels, &useLabels));
982     if (!useLabels) numLabels = 0;
983     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_colors", colors, &numColors, &useColors));
984     if (!useColors) {
985       numColors = 3;
986       for (c = 0; c < numColors; ++c) PetscCall(PetscStrallocpy(defcolors[c], &colors[c]));
987     }
988     PetscCall(PetscOptionsGetStringArray(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_lcolors", lcolors, &numLColors, &useColors));
989     if (!useColors) {
990       numLColors = 4;
991       for (c = 0; c < numLColors; ++c) PetscCall(PetscStrallocpy(deflcolors[c], &lcolors[c]));
992     }
993     PetscCall(PetscOptionsGetString(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_label_filter", lname, sizeof(lname), &lflg));
994     plotEdges = (PetscBool)(depth > 1 && drawNumbers[1] && dim < 3);
995     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_edges", &plotEdges, &flg));
996     PetscCheck(!flg || !plotEdges || depth >= dim, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Mesh must be interpolated");
997     if (depth < dim) plotEdges = PETSC_FALSE;
998     PetscCall(PetscOptionsGetBool(((PetscObject)viewer)->options, ((PetscObject)viewer)->prefix, "-dm_plex_view_hasse", &drawHasse, NULL));
999 
1000     /* filter points with labelvalue != labeldefaultvalue */
1001     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
1002     PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1003     PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
1004     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1005     if (lflg) {
1006       DMLabel lbl;
1007 
1008       PetscCall(DMGetLabel(dm, lname, &lbl));
1009       if (lbl) {
1010         PetscInt val, defval;
1011 
1012         PetscCall(DMLabelGetDefaultValue(lbl, &defval));
1013         PetscCall(PetscBTCreate(pEnd - pStart, &wp));
1014         for (c = pStart; c < pEnd; c++) {
1015           PetscInt *closure = NULL;
1016           PetscInt  closureSize;
1017 
1018           PetscCall(DMLabelGetValue(lbl, c, &val));
1019           if (val == defval) continue;
1020 
1021           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1022           for (p = 0; p < closureSize * 2; p += 2) PetscCall(PetscBTSet(wp, closure[p] - pStart));
1023           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1024         }
1025       }
1026     }
1027 
1028     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1029     PetscCallMPI(MPI_Comm_size(PetscObjectComm((PetscObject)dm), &size));
1030     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1031     PetscCall(PetscViewerASCIIPrintf(viewer, "\
1032 \\documentclass[tikz]{standalone}\n\n\
1033 \\usepackage{pgflibraryshapes}\n\
1034 \\usetikzlibrary{backgrounds}\n\
1035 \\usetikzlibrary{arrows}\n\
1036 \\begin{document}\n"));
1037     if (size > 1) {
1038       PetscCall(PetscViewerASCIIPrintf(viewer, "%s for process ", name));
1039       for (p = 0; p < size; ++p) {
1040         if (p) PetscCall(PetscViewerASCIIPrintf(viewer, (p == size - 1) ? ", and " : ", "));
1041         PetscCall(PetscViewerASCIIPrintf(viewer, "{\\textcolor{%s}%" PetscInt_FMT "}", colors[p % numColors], p));
1042       }
1043       PetscCall(PetscViewerASCIIPrintf(viewer, ".\n\n\n"));
1044     }
1045     if (drawHasse) {
1046       PetscInt maxStratum = PetscMax(vEnd - vStart, PetscMax(eEnd - eStart, cEnd - cStart));
1047 
1048       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vStart}{%" PetscInt_FMT "}\n", vStart));
1049       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vEnd}{%" PetscInt_FMT "}\n", vEnd - 1));
1050       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numVertices}{%" PetscInt_FMT "}\n", vEnd - vStart));
1051       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\vShift}{%.2f}\n", 3 + (maxStratum - (vEnd - vStart)) / 2.));
1052       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eStart}{%" PetscInt_FMT "}\n", eStart));
1053       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eEnd}{%" PetscInt_FMT "}\n", eEnd - 1));
1054       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\eShift}{%.2f}\n", 3 + (maxStratum - (eEnd - eStart)) / 2.));
1055       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numEdges}{%" PetscInt_FMT "}\n", eEnd - eStart));
1056       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cStart}{%" PetscInt_FMT "}\n", cStart));
1057       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cEnd}{%" PetscInt_FMT "}\n", cEnd - 1));
1058       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\numCells}{%" PetscInt_FMT "}\n", cEnd - cStart));
1059       PetscCall(PetscViewerASCIIPrintf(viewer, "\\newcommand{\\cShift}{%.2f}\n", 3 + (maxStratum - (cEnd - cStart)) / 2.));
1060     }
1061     PetscCall(PetscViewerASCIIPrintf(viewer, "\\begin{tikzpicture}[scale = %g,font=\\fontsize{8}{8}\\selectfont]\n", (double)tikzscale));
1062 
1063     /* Plot vertices */
1064     PetscCall(VecGetArray(coordinates, &coords));
1065     PetscCall(PetscViewerASCIIPushSynchronized(viewer));
1066     for (v = vStart; v < vEnd; ++v) {
1067       PetscInt  off, dof, d;
1068       PetscBool isLabeled = PETSC_FALSE;
1069 
1070       if (wp && !PetscBTLookup(wp, v - pStart)) continue;
1071       PetscCall(PetscSectionGetDof(coordSection, v, &dof));
1072       PetscCall(PetscSectionGetOffset(coordSection, v, &off));
1073       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1074       PetscCheck(dof <= 3, PETSC_COMM_SELF, PETSC_ERR_PLIB, "coordSection vertex %" PetscInt_FMT " has dof %" PetscInt_FMT " > 3", v, dof);
1075       for (d = 0; d < dof; ++d) {
1076         tcoords[d] = (double)(scale * PetscRealPart(coords[off + d]));
1077         tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1078       }
1079       /* Rotate coordinates since PGF makes z point out of the page instead of up */
1080       if (dim == 3) {
1081         PetscReal tmp = tcoords[1];
1082         tcoords[1]    = tcoords[2];
1083         tcoords[2]    = -tmp;
1084       }
1085       for (d = 0; d < dof; ++d) {
1086         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1087         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)tcoords[d]));
1088       }
1089       if (drawHasse) color = colors[0 % numColors];
1090       else color = colors[rank % numColors];
1091       for (l = 0; l < numLabels; ++l) {
1092         PetscInt val;
1093         PetscCall(DMGetLabelValue(dm, names[l], v, &val));
1094         if (val >= 0) {
1095           color     = lcolors[l % numLColors];
1096           isLabeled = PETSC_TRUE;
1097           break;
1098         }
1099       }
1100       if (drawNumbers[0]) {
1101         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", v, rank, color, v));
1102       } else if (drawColors[0]) {
1103         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", v, rank, !isLabeled ? 1 : 2, color));
1104       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", v, rank));
1105     }
1106     PetscCall(VecRestoreArray(coordinates, &coords));
1107     PetscCall(PetscViewerFlush(viewer));
1108     /* Plot edges */
1109     if (plotEdges) {
1110       PetscCall(VecGetArray(coordinates, &coords));
1111       PetscCall(PetscViewerASCIIPrintf(viewer, "\\path\n"));
1112       for (e = eStart; e < eEnd; ++e) {
1113         const PetscInt *cone;
1114         PetscInt        coneSize, offA, offB, dof, d;
1115 
1116         if (wp && !PetscBTLookup(wp, e - pStart)) continue;
1117         PetscCall(DMPlexGetConeSize(dm, e, &coneSize));
1118         PetscCheck(coneSize == 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Edge %" PetscInt_FMT " cone should have two vertices, not %" PetscInt_FMT, e, coneSize);
1119         PetscCall(DMPlexGetCone(dm, e, &cone));
1120         PetscCall(PetscSectionGetDof(coordSection, cone[0], &dof));
1121         PetscCall(PetscSectionGetOffset(coordSection, cone[0], &offA));
1122         PetscCall(PetscSectionGetOffset(coordSection, cone[1], &offB));
1123         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "("));
1124         for (d = 0; d < dof; ++d) {
1125           tcoords[d] = (double)(0.5 * scale * PetscRealPart(coords[offA + d] + coords[offB + d]));
1126           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1127         }
1128         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1129         if (dim == 3) {
1130           PetscReal tmp = tcoords[1];
1131           tcoords[1]    = tcoords[2];
1132           tcoords[2]    = -tmp;
1133         }
1134         for (d = 0; d < dof; ++d) {
1135           if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1136           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)tcoords[d]));
1137         }
1138         if (drawHasse) color = colors[1 % numColors];
1139         else color = colors[rank % numColors];
1140         for (l = 0; l < numLabels; ++l) {
1141           PetscInt val;
1142           PetscCall(DMGetLabelValue(dm, names[l], v, &val));
1143           if (val >= 0) {
1144             color = lcolors[l % numLColors];
1145             break;
1146           }
1147         }
1148         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "} --\n", e, rank, color, e));
1149       }
1150       PetscCall(VecRestoreArray(coordinates, &coords));
1151       PetscCall(PetscViewerFlush(viewer));
1152       PetscCall(PetscViewerASCIIPrintf(viewer, "(0,0);\n"));
1153     }
1154     /* Plot cells */
1155     if (dim == 3 || !drawNumbers[1]) {
1156       for (e = eStart; e < eEnd; ++e) {
1157         const PetscInt *cone;
1158 
1159         if (wp && !PetscBTLookup(wp, e - pStart)) continue;
1160         color = colors[rank % numColors];
1161         for (l = 0; l < numLabels; ++l) {
1162           PetscInt val;
1163           PetscCall(DMGetLabelValue(dm, names[l], e, &val));
1164           if (val >= 0) {
1165             color = lcolors[l % numLColors];
1166             break;
1167           }
1168         }
1169         PetscCall(DMPlexGetCone(dm, e, &cone));
1170         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", color, cone[0], rank, cone[1], rank));
1171       }
1172     } else {
1173       DMPolytopeType ct;
1174 
1175       /* Drawing a 2D polygon */
1176       for (c = cStart; c < cEnd; ++c) {
1177         if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1178         PetscCall(DMPlexGetCellType(dm, c, &ct));
1179         if (ct == DM_POLYTOPE_SEG_PRISM_TENSOR || ct == DM_POLYTOPE_TRI_PRISM_TENSOR || ct == DM_POLYTOPE_QUAD_PRISM_TENSOR) {
1180           const PetscInt *cone;
1181           PetscInt        coneSize, e;
1182 
1183           PetscCall(DMPlexGetCone(dm, c, &cone));
1184           PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
1185           for (e = 0; e < coneSize; ++e) {
1186             const PetscInt *econe;
1187 
1188             PetscCall(DMPlexGetCone(dm, cone[e], &econe));
1189             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));
1190           }
1191         } else {
1192           PetscInt *closure = NULL;
1193           PetscInt  closureSize, Nv = 0, v;
1194 
1195           PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1196           for (p = 0; p < closureSize * 2; p += 2) {
1197             const PetscInt point = closure[p];
1198 
1199             if ((point >= vStart) && (point < vEnd)) closure[Nv++] = point;
1200           }
1201           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\draw[color=%s] ", colors[rank % numColors]));
1202           for (v = 0; v <= Nv; ++v) {
1203             const PetscInt vertex = closure[v % Nv];
1204 
1205             if (v > 0) {
1206               if (plotEdges) {
1207                 const PetscInt *edge;
1208                 PetscInt        endpoints[2], ne;
1209 
1210                 endpoints[0] = closure[v - 1];
1211                 endpoints[1] = vertex;
1212                 PetscCall(DMPlexGetJoin(dm, 2, endpoints, &ne, &edge));
1213                 PetscCheck(ne == 1, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Could not find edge for vertices %" PetscInt_FMT ", %" PetscInt_FMT, endpoints[0], endpoints[1]);
1214                 PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- (%" PetscInt_FMT "_%d) -- ", edge[0], rank));
1215                 PetscCall(DMPlexRestoreJoin(dm, 2, endpoints, &ne, &edge));
1216               } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, " -- "));
1217             }
1218             PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "(%" PetscInt_FMT "_%d)", vertex, rank));
1219           }
1220           PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ";\n"));
1221           PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
1222         }
1223       }
1224     }
1225     for (c = cStart; c < cEnd; ++c) {
1226       double             ccoords[3] = {0.0, 0.0, 0.0};
1227       PetscBool          isLabeled  = PETSC_FALSE;
1228       PetscScalar       *cellCoords = NULL;
1229       const PetscScalar *array;
1230       PetscInt           numCoords, cdim, d;
1231       PetscBool          isDG;
1232 
1233       if (wp && !PetscBTLookup(wp, c - pStart)) continue;
1234       PetscCall(DMGetCoordinateDim(dm, &cdim));
1235       PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1236       PetscCheck(!(numCoords % cdim), PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "coordinate dim %" PetscInt_FMT " does not divide numCoords %" PetscInt_FMT, cdim, numCoords);
1237       PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "\\path ("));
1238       for (p = 0; p < numCoords / cdim; ++p) {
1239         for (d = 0; d < cdim; ++d) {
1240           tcoords[d] = (double)(scale * PetscRealPart(cellCoords[p * cdim + d]));
1241           tcoords[d] = PetscAbs(tcoords[d]) < 1e-10 ? 0.0 : tcoords[d];
1242         }
1243         /* Rotate coordinates since PGF makes z point out of the page instead of up */
1244         if (cdim == 3) {
1245           PetscReal tmp = tcoords[1];
1246           tcoords[1]    = tcoords[2];
1247           tcoords[2]    = -tmp;
1248         }
1249         for (d = 0; d < dim; ++d) ccoords[d] += tcoords[d];
1250       }
1251       for (d = 0; d < cdim; ++d) ccoords[d] /= (numCoords / cdim);
1252       PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &array, &cellCoords));
1253       for (d = 0; d < cdim; ++d) {
1254         if (d > 0) PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ","));
1255         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, "%g", (double)ccoords[d]));
1256       }
1257       if (drawHasse) color = colors[depth % numColors];
1258       else color = colors[rank % numColors];
1259       for (l = 0; l < numLabels; ++l) {
1260         PetscInt val;
1261         PetscCall(DMGetLabelValue(dm, names[l], c, &val));
1262         if (val >= 0) {
1263           color     = lcolors[l % numLColors];
1264           isLabeled = PETSC_TRUE;
1265           break;
1266         }
1267       }
1268       if (drawNumbers[dim]) {
1269         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [draw,shape=circle,color=%s] {%" PetscInt_FMT "};\n", c, rank, color, c));
1270       } else if (drawColors[dim]) {
1271         PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [fill,inner sep=%dpt,shape=circle,color=%s] {};\n", c, rank, !isLabeled ? 1 : 2, color));
1272       } else PetscCall(PetscViewerASCIISynchronizedPrintf(viewer, ") node(%" PetscInt_FMT "_%d) [] {};\n", c, rank));
1273     }
1274     if (drawHasse) {
1275       color = colors[depth % numColors];
1276       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Cells\n"));
1277       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\c in {\\cStart,...,\\cEnd}\n"));
1278       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1279       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\c_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\cShift+\\c-\\cStart,0) {\\c};\n", rank, color));
1280       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1281 
1282       color = colors[1 % numColors];
1283       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Edges\n"));
1284       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\e in {\\eStart,...,\\eEnd}\n"));
1285       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1286       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\e_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\eShift+\\e-\\eStart,1) {\\e};\n", rank, color));
1287       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1288 
1289       color = colors[0 % numColors];
1290       PetscCall(PetscViewerASCIIPrintf(viewer, "%% Vertices\n"));
1291       PetscCall(PetscViewerASCIIPrintf(viewer, "\\foreach \\v in {\\vStart,...,\\vEnd}\n"));
1292       PetscCall(PetscViewerASCIIPrintf(viewer, "{\n"));
1293       PetscCall(PetscViewerASCIIPrintf(viewer, "  \\node(\\v_%d) [draw,shape=circle,color=%s,minimum size = 6mm] at (\\vShift+\\v-\\vStart,2) {\\v};\n", rank, color));
1294       PetscCall(PetscViewerASCIIPrintf(viewer, "}\n"));
1295 
1296       for (p = pStart; p < pEnd; ++p) {
1297         const PetscInt *cone;
1298         PetscInt        coneSize, cp;
1299 
1300         PetscCall(DMPlexGetCone(dm, p, &cone));
1301         PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
1302         for (cp = 0; cp < coneSize; ++cp) PetscCall(PetscViewerASCIIPrintf(viewer, "\\draw[->, shorten >=1pt] (%" PetscInt_FMT "_%d) -- (%" PetscInt_FMT "_%d);\n", cone[cp], rank, p, rank));
1303       }
1304     }
1305     PetscCall(PetscViewerFlush(viewer));
1306     PetscCall(PetscViewerASCIIPopSynchronized(viewer));
1307     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{tikzpicture}\n"));
1308     PetscCall(PetscViewerASCIIPrintf(viewer, "\\end{document}\n"));
1309     for (l = 0; l < numLabels; ++l) PetscCall(PetscFree(names[l]));
1310     for (c = 0; c < numColors; ++c) PetscCall(PetscFree(colors[c]));
1311     for (c = 0; c < numLColors; ++c) PetscCall(PetscFree(lcolors[c]));
1312     PetscCall(PetscFree3(names, colors, lcolors));
1313     PetscCall(PetscBTDestroy(&wp));
1314   } else if (format == PETSC_VIEWER_LOAD_BALANCE) {
1315     Vec                    cown, acown;
1316     VecScatter             sct;
1317     ISLocalToGlobalMapping g2l;
1318     IS                     gid, acis;
1319     MPI_Comm               comm, ncomm = MPI_COMM_NULL;
1320     MPI_Group              ggroup, ngroup;
1321     PetscScalar           *array, nid;
1322     const PetscInt        *idxs;
1323     PetscInt              *idxs2, *start, *adjacency, *work;
1324     PetscInt64             lm[3], gm[3];
1325     PetscInt               i, c, cStart, cEnd, cum, numVertices, ect, ectn, cellHeight;
1326     PetscMPIInt            d1, d2, rank;
1327 
1328     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1329     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1330 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1331     PetscCallMPI(MPI_Comm_split_type(comm, MPI_COMM_TYPE_SHARED, rank, MPI_INFO_NULL, &ncomm));
1332 #endif
1333     if (ncomm != MPI_COMM_NULL) {
1334       PetscCallMPI(MPI_Comm_group(comm, &ggroup));
1335       PetscCallMPI(MPI_Comm_group(ncomm, &ngroup));
1336       d1 = 0;
1337       PetscCallMPI(MPI_Group_translate_ranks(ngroup, 1, &d1, ggroup, &d2));
1338       nid = d2;
1339       PetscCallMPI(MPI_Group_free(&ggroup));
1340       PetscCallMPI(MPI_Group_free(&ngroup));
1341       PetscCallMPI(MPI_Comm_free(&ncomm));
1342     } else nid = 0.0;
1343 
1344     /* Get connectivity */
1345     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1346     PetscCall(DMPlexCreatePartitionerGraph(dm, cellHeight, &numVertices, &start, &adjacency, &gid));
1347 
1348     /* filter overlapped local cells */
1349     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
1350     PetscCall(ISGetIndices(gid, &idxs));
1351     PetscCall(ISGetLocalSize(gid, &cum));
1352     PetscCall(PetscMalloc1(cum, &idxs2));
1353     for (c = cStart, cum = 0; c < cEnd; c++) {
1354       if (idxs[c - cStart] < 0) continue;
1355       idxs2[cum++] = idxs[c - cStart];
1356     }
1357     PetscCall(ISRestoreIndices(gid, &idxs));
1358     PetscCheck(numVertices == cum, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Unexpected %" PetscInt_FMT " != %" PetscInt_FMT, numVertices, cum);
1359     PetscCall(ISDestroy(&gid));
1360     PetscCall(ISCreateGeneral(comm, numVertices, idxs2, PETSC_OWN_POINTER, &gid));
1361 
1362     /* support for node-aware cell locality */
1363     PetscCall(ISCreateGeneral(comm, start[numVertices], adjacency, PETSC_USE_POINTER, &acis));
1364     PetscCall(VecCreateSeq(PETSC_COMM_SELF, start[numVertices], &acown));
1365     PetscCall(VecCreateMPI(comm, numVertices, PETSC_DECIDE, &cown));
1366     PetscCall(VecGetArray(cown, &array));
1367     for (c = 0; c < numVertices; c++) array[c] = nid;
1368     PetscCall(VecRestoreArray(cown, &array));
1369     PetscCall(VecScatterCreate(cown, acis, acown, NULL, &sct));
1370     PetscCall(VecScatterBegin(sct, cown, acown, INSERT_VALUES, SCATTER_FORWARD));
1371     PetscCall(VecScatterEnd(sct, cown, acown, INSERT_VALUES, SCATTER_FORWARD));
1372     PetscCall(ISDestroy(&acis));
1373     PetscCall(VecScatterDestroy(&sct));
1374     PetscCall(VecDestroy(&cown));
1375 
1376     /* compute edgeCut */
1377     for (c = 0, cum = 0; c < numVertices; c++) cum = PetscMax(cum, start[c + 1] - start[c]);
1378     PetscCall(PetscMalloc1(cum, &work));
1379     PetscCall(ISLocalToGlobalMappingCreateIS(gid, &g2l));
1380     PetscCall(ISLocalToGlobalMappingSetType(g2l, ISLOCALTOGLOBALMAPPINGHASH));
1381     PetscCall(ISDestroy(&gid));
1382     PetscCall(VecGetArray(acown, &array));
1383     for (c = 0, ect = 0, ectn = 0; c < numVertices; c++) {
1384       PetscInt totl;
1385 
1386       totl = start[c + 1] - start[c];
1387       PetscCall(ISGlobalToLocalMappingApply(g2l, IS_GTOLM_MASK, totl, adjacency + start[c], NULL, work));
1388       for (i = 0; i < totl; i++) {
1389         if (work[i] < 0) {
1390           ect += 1;
1391           ectn += (array[i + start[c]] != nid) ? 0 : 1;
1392         }
1393       }
1394     }
1395     PetscCall(PetscFree(work));
1396     PetscCall(VecRestoreArray(acown, &array));
1397     lm[0] = numVertices > 0 ? numVertices : PETSC_MAX_INT;
1398     lm[1] = -numVertices;
1399     PetscCall(MPIU_Allreduce(lm, gm, 2, MPIU_INT64, MPI_MIN, comm));
1400     PetscCall(PetscViewerASCIIPrintf(viewer, "  Cell balance: %.2f (max %" PetscInt_FMT ", min %" PetscInt_FMT, -((double)gm[1]) / ((double)gm[0]), -(PetscInt)gm[1], (PetscInt)gm[0]));
1401     lm[0] = ect;                     /* edgeCut */
1402     lm[1] = ectn;                    /* node-aware edgeCut */
1403     lm[2] = numVertices > 0 ? 0 : 1; /* empty processes */
1404     PetscCall(MPIU_Allreduce(lm, gm, 3, MPIU_INT64, MPI_SUM, comm));
1405     PetscCall(PetscViewerASCIIPrintf(viewer, ", empty %" PetscInt_FMT ")\n", (PetscInt)gm[2]));
1406 #if defined(PETSC_HAVE_MPI_PROCESS_SHARED_MEMORY)
1407     PetscCall(PetscViewerASCIIPrintf(viewer, "  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n", (PetscInt)(gm[0] / 2), gm[0] ? ((double)(gm[1])) / ((double)gm[0]) : 1.));
1408 #else
1409     PetscCall(PetscViewerASCIIPrintf(viewer, "  Edge Cut: %" PetscInt_FMT " (on node %.3f)\n", (PetscInt)(gm[0] / 2), 0.0));
1410 #endif
1411     PetscCall(ISLocalToGlobalMappingDestroy(&g2l));
1412     PetscCall(PetscFree(start));
1413     PetscCall(PetscFree(adjacency));
1414     PetscCall(VecDestroy(&acown));
1415   } else {
1416     const char    *name;
1417     PetscInt      *sizes, *hybsizes, *ghostsizes;
1418     PetscInt       locDepth, depth, cellHeight, dim, d;
1419     PetscInt       pStart, pEnd, p, gcStart, gcEnd, gcNum;
1420     PetscInt       numLabels, l, maxSize = 17;
1421     DMPolytopeType ct0 = DM_POLYTOPE_UNKNOWN;
1422     MPI_Comm       comm;
1423     PetscMPIInt    size, rank;
1424 
1425     PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
1426     PetscCallMPI(MPI_Comm_size(comm, &size));
1427     PetscCallMPI(MPI_Comm_rank(comm, &rank));
1428     PetscCall(DMGetDimension(dm, &dim));
1429     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
1430     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
1431     if (name) PetscCall(PetscViewerASCIIPrintf(viewer, "%s in %" PetscInt_FMT " dimension%s:\n", name, dim, dim == 1 ? "" : "s"));
1432     else PetscCall(PetscViewerASCIIPrintf(viewer, "Mesh in %" PetscInt_FMT " dimension%s:\n", dim, dim == 1 ? "" : "s"));
1433     if (cellHeight) PetscCall(PetscViewerASCIIPrintf(viewer, "  Cells are at height %" PetscInt_FMT "\n", cellHeight));
1434     PetscCall(DMPlexGetDepth(dm, &locDepth));
1435     PetscCall(MPIU_Allreduce(&locDepth, &depth, 1, MPIU_INT, MPI_MAX, comm));
1436     PetscCall(DMPlexGetCellTypeStratum(dm, DM_POLYTOPE_FV_GHOST, &gcStart, &gcEnd));
1437     gcNum = gcEnd - gcStart;
1438     if (size < maxSize) PetscCall(PetscCalloc3(size, &sizes, size, &hybsizes, size, &ghostsizes));
1439     else PetscCall(PetscCalloc3(3, &sizes, 3, &hybsizes, 3, &ghostsizes));
1440     for (d = 0; d <= depth; d++) {
1441       PetscInt Nc[2] = {0, 0}, ict;
1442 
1443       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
1444       if (pStart < pEnd) PetscCall(DMPlexGetCellType(dm, pStart, &ct0));
1445       ict = ct0;
1446       PetscCallMPI(MPI_Bcast(&ict, 1, MPIU_INT, 0, comm));
1447       ct0 = (DMPolytopeType)ict;
1448       for (p = pStart; p < pEnd; ++p) {
1449         DMPolytopeType ct;
1450 
1451         PetscCall(DMPlexGetCellType(dm, p, &ct));
1452         if (ct == ct0) ++Nc[0];
1453         else ++Nc[1];
1454       }
1455       if (size < maxSize) {
1456         PetscCallMPI(MPI_Gather(&Nc[0], 1, MPIU_INT, sizes, 1, MPIU_INT, 0, comm));
1457         PetscCallMPI(MPI_Gather(&Nc[1], 1, MPIU_INT, hybsizes, 1, MPIU_INT, 0, comm));
1458         if (d == depth) PetscCallMPI(MPI_Gather(&gcNum, 1, MPIU_INT, ghostsizes, 1, MPIU_INT, 0, comm));
1459         PetscCall(PetscViewerASCIIPrintf(viewer, "  Number of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1460         for (p = 0; p < size; ++p) {
1461           if (rank == 0) {
1462             PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT, sizes[p] + hybsizes[p]));
1463             if (hybsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT ")", hybsizes[p]));
1464             if (ghostsizes[p] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "]", ghostsizes[p]));
1465           }
1466         }
1467       } else {
1468         PetscInt locMinMax[2];
1469 
1470         locMinMax[0] = Nc[0] + Nc[1];
1471         locMinMax[1] = Nc[0] + Nc[1];
1472         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, sizes));
1473         locMinMax[0] = Nc[1];
1474         locMinMax[1] = Nc[1];
1475         PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, hybsizes));
1476         if (d == depth) {
1477           locMinMax[0] = gcNum;
1478           locMinMax[1] = gcNum;
1479           PetscCall(PetscGlobalMinMaxInt(comm, locMinMax, ghostsizes));
1480         }
1481         PetscCall(PetscViewerASCIIPrintf(viewer, "  Min/Max of %" PetscInt_FMT "-cells per rank:", (depth == 1) && d ? dim : d));
1482         PetscCall(PetscViewerASCIIPrintf(viewer, " %" PetscInt_FMT "/%" PetscInt_FMT, sizes[0], sizes[1]));
1483         if (hybsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " (%" PetscInt_FMT "/%" PetscInt_FMT ")", hybsizes[0], hybsizes[1]));
1484         if (ghostsizes[0] > 0) PetscCall(PetscViewerASCIIPrintf(viewer, " [%" PetscInt_FMT "/%" PetscInt_FMT "]", ghostsizes[0], ghostsizes[1]));
1485       }
1486       PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
1487     }
1488     PetscCall(PetscFree3(sizes, hybsizes, ghostsizes));
1489     {
1490       const PetscReal *maxCell;
1491       const PetscReal *L;
1492       PetscBool        localized;
1493 
1494       PetscCall(DMGetPeriodicity(dm, &maxCell, NULL, &L));
1495       PetscCall(DMGetCoordinatesLocalized(dm, &localized));
1496       if (L || localized) {
1497         PetscCall(PetscViewerASCIIPrintf(viewer, "Periodic mesh"));
1498         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1499         if (L) {
1500           PetscCall(PetscViewerASCIIPrintf(viewer, " ("));
1501           for (d = 0; d < dim; ++d) {
1502             if (d > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1503             PetscCall(PetscViewerASCIIPrintf(viewer, "%s", L[d] > 0.0 ? "PERIODIC" : "NONE"));
1504           }
1505           PetscCall(PetscViewerASCIIPrintf(viewer, ")"));
1506         }
1507         PetscCall(PetscViewerASCIIPrintf(viewer, " coordinates %s\n", localized ? "localized" : "not localized"));
1508         PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1509       }
1510     }
1511     PetscCall(DMGetNumLabels(dm, &numLabels));
1512     if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Labels:\n"));
1513     for (l = 0; l < numLabels; ++l) {
1514       DMLabel         label;
1515       const char     *name;
1516       IS              valueIS;
1517       const PetscInt *values;
1518       PetscInt        numValues, v;
1519 
1520       PetscCall(DMGetLabelName(dm, l, &name));
1521       PetscCall(DMGetLabel(dm, name, &label));
1522       PetscCall(DMLabelGetNumValues(label, &numValues));
1523       PetscCall(PetscViewerASCIIPrintf(viewer, "  %s: %" PetscInt_FMT " strata with value/size (", name, numValues));
1524       PetscCall(DMLabelGetValueIS(label, &valueIS));
1525       PetscCall(ISGetIndices(valueIS, &values));
1526       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_FALSE));
1527       for (v = 0; v < numValues; ++v) {
1528         PetscInt size;
1529 
1530         PetscCall(DMLabelGetStratumSize(label, values[v], &size));
1531         if (v > 0) PetscCall(PetscViewerASCIIPrintf(viewer, ", "));
1532         PetscCall(PetscViewerASCIIPrintf(viewer, "%" PetscInt_FMT " (%" PetscInt_FMT ")", values[v], size));
1533       }
1534       PetscCall(PetscViewerASCIIPrintf(viewer, ")\n"));
1535       PetscCall(PetscViewerASCIIUseTabs(viewer, PETSC_TRUE));
1536       PetscCall(ISRestoreIndices(valueIS, &values));
1537       PetscCall(ISDestroy(&valueIS));
1538     }
1539     {
1540       char    **labelNames;
1541       PetscInt  Nl = numLabels;
1542       PetscBool flg;
1543 
1544       PetscCall(PetscMalloc1(Nl, &labelNames));
1545       PetscCall(PetscOptionsGetStringArray(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_plex_view_labels", labelNames, &Nl, &flg));
1546       for (l = 0; l < Nl; ++l) {
1547         DMLabel label;
1548 
1549         PetscCall(DMHasLabel(dm, labelNames[l], &flg));
1550         if (flg) {
1551           PetscCall(DMGetLabel(dm, labelNames[l], &label));
1552           PetscCall(DMLabelView(label, viewer));
1553         }
1554         PetscCall(PetscFree(labelNames[l]));
1555       }
1556       PetscCall(PetscFree(labelNames));
1557     }
1558     /* If no fields are specified, people do not want to see adjacency */
1559     if (dm->Nf) {
1560       PetscInt f;
1561 
1562       for (f = 0; f < dm->Nf; ++f) {
1563         const char *name;
1564 
1565         PetscCall(PetscObjectGetName(dm->fields[f].disc, &name));
1566         if (numLabels) PetscCall(PetscViewerASCIIPrintf(viewer, "Field %s:\n", name));
1567         PetscCall(PetscViewerASCIIPushTab(viewer));
1568         if (dm->fields[f].label) PetscCall(DMLabelView(dm->fields[f].label, viewer));
1569         if (dm->fields[f].adjacency[0]) {
1570           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM++\n"));
1571           else PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FVM\n"));
1572         } else {
1573           if (dm->fields[f].adjacency[1]) PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FEM\n"));
1574           else PetscCall(PetscViewerASCIIPrintf(viewer, "adjacency FUNKY\n"));
1575         }
1576         PetscCall(PetscViewerASCIIPopTab(viewer));
1577       }
1578     }
1579     PetscCall(DMGetCoarseDM(dm, &cdm));
1580     if (cdm) {
1581       PetscCall(PetscViewerASCIIPushTab(viewer));
1582       PetscCall(PetscViewerASCIIPrintf(viewer, "Defined by transform from:\n"));
1583       PetscCall(DMPlexView_Ascii(cdm, viewer));
1584       PetscCall(PetscViewerASCIIPopTab(viewer));
1585     }
1586   }
1587   PetscFunctionReturn(PETSC_SUCCESS);
1588 }
1589 
1590 static PetscErrorCode DMPlexDrawCell(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[])
1591 {
1592   DMPolytopeType ct;
1593   PetscMPIInt    rank;
1594   PetscInt       cdim;
1595 
1596   PetscFunctionBegin;
1597   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1598   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1599   PetscCall(DMGetCoordinateDim(dm, &cdim));
1600   switch (ct) {
1601   case DM_POLYTOPE_SEGMENT:
1602   case DM_POLYTOPE_POINT_PRISM_TENSOR:
1603     switch (cdim) {
1604     case 1: {
1605       const PetscReal y  = 0.5;  /* TODO Put it in the middle of the viewport */
1606       const PetscReal dy = 0.05; /* TODO Make it a fraction of the total length */
1607 
1608       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y, PetscRealPart(coords[1]), y, PETSC_DRAW_BLACK));
1609       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), y + dy, PetscRealPart(coords[0]), y - dy, PETSC_DRAW_BLACK));
1610       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[1]), y + dy, PetscRealPart(coords[1]), y - dy, PETSC_DRAW_BLACK));
1611     } break;
1612     case 2: {
1613       const PetscReal dx = (PetscRealPart(coords[3]) - PetscRealPart(coords[1]));
1614       const PetscReal dy = (PetscRealPart(coords[2]) - PetscRealPart(coords[0]));
1615       const PetscReal l  = 0.1 / PetscSqrtReal(dx * dx + dy * dy);
1616 
1617       PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1618       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));
1619       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));
1620     } break;
1621     default:
1622       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of dimension %" PetscInt_FMT, cdim);
1623     }
1624     break;
1625   case DM_POLYTOPE_TRIANGLE:
1626     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));
1627     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1628     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1629     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1630     break;
1631   case DM_POLYTOPE_QUADRILATERAL:
1632     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));
1633     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));
1634     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1635     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1636     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_BLACK));
1637     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1638     break;
1639   case DM_POLYTOPE_SEG_PRISM_TENSOR:
1640     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));
1641     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));
1642     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[0]), PetscRealPart(coords[1]), PetscRealPart(coords[2]), PetscRealPart(coords[3]), PETSC_DRAW_BLACK));
1643     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[2]), PetscRealPart(coords[3]), PetscRealPart(coords[6]), PetscRealPart(coords[7]), PETSC_DRAW_BLACK));
1644     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[6]), PetscRealPart(coords[7]), PetscRealPart(coords[4]), PetscRealPart(coords[5]), PETSC_DRAW_BLACK));
1645     PetscCall(PetscDrawLine(draw, PetscRealPart(coords[4]), PetscRealPart(coords[5]), PetscRealPart(coords[0]), PetscRealPart(coords[1]), PETSC_DRAW_BLACK));
1646     break;
1647   case DM_POLYTOPE_FV_GHOST:
1648     break;
1649   default:
1650     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1651   }
1652   PetscFunctionReturn(PETSC_SUCCESS);
1653 }
1654 
1655 static PetscErrorCode DMPlexDrawCellHighOrder(DM dm, PetscDraw draw, PetscInt cell, const PetscScalar coords[], PetscInt edgeDiv, PetscReal refCoords[], PetscReal edgeCoords[])
1656 {
1657   DMPolytopeType ct;
1658   PetscReal      centroid[2] = {0., 0.};
1659   PetscMPIInt    rank;
1660   PetscInt       fillColor, v, e, d;
1661 
1662   PetscFunctionBegin;
1663   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
1664   PetscCall(DMPlexGetCellType(dm, cell, &ct));
1665   fillColor = PETSC_DRAW_WHITE + rank % (PETSC_DRAW_BASIC_COLORS - 2) + 2;
1666   switch (ct) {
1667   case DM_POLYTOPE_TRIANGLE: {
1668     PetscReal refVertices[6] = {-1., -1., 1., -1., -1., 1.};
1669 
1670     for (v = 0; v < 3; ++v) {
1671       centroid[0] += PetscRealPart(coords[v * 2 + 0]) / 3.;
1672       centroid[1] += PetscRealPart(coords[v * 2 + 1]) / 3.;
1673     }
1674     for (e = 0; e < 3; ++e) {
1675       refCoords[0] = refVertices[e * 2 + 0];
1676       refCoords[1] = refVertices[e * 2 + 1];
1677       for (d = 1; d <= edgeDiv; ++d) {
1678         refCoords[d * 2 + 0] = refCoords[0] + (refVertices[(e + 1) % 3 * 2 + 0] - refCoords[0]) * d / edgeDiv;
1679         refCoords[d * 2 + 1] = refCoords[1] + (refVertices[(e + 1) % 3 * 2 + 1] - refCoords[1]) * d / edgeDiv;
1680       }
1681       PetscCall(DMPlexReferenceToCoordinates(dm, cell, edgeDiv + 1, refCoords, edgeCoords));
1682       for (d = 0; d < edgeDiv; ++d) {
1683         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));
1684         PetscCall(PetscDrawLine(draw, edgeCoords[d * 2 + 0], edgeCoords[d * 2 + 1], edgeCoords[(d + 1) * 2 + 0], edgeCoords[(d + 1) * 2 + 1], PETSC_DRAW_BLACK));
1685       }
1686     }
1687   } break;
1688   default:
1689     SETERRQ(PETSC_COMM_SELF, PETSC_ERR_SUP, "Cannot draw cells of type %s", DMPolytopeTypes[ct]);
1690   }
1691   PetscFunctionReturn(PETSC_SUCCESS);
1692 }
1693 
1694 static PetscErrorCode DMPlexView_Draw(DM dm, PetscViewer viewer)
1695 {
1696   PetscDraw    draw;
1697   DM           cdm;
1698   PetscSection coordSection;
1699   Vec          coordinates;
1700   PetscReal    xyl[3], xyr[3];
1701   PetscReal   *refCoords, *edgeCoords;
1702   PetscBool    isnull, drawAffine = PETSC_TRUE;
1703   PetscInt     dim, vStart, vEnd, cStart, cEnd, c, edgeDiv = 4;
1704 
1705   PetscFunctionBegin;
1706   PetscCall(DMGetCoordinateDim(dm, &dim));
1707   PetscCheck(dim <= 2, PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Cannot draw meshes of dimension %" PetscInt_FMT, dim);
1708   PetscCall(PetscOptionsGetBool(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_view_draw_affine", &drawAffine, NULL));
1709   if (!drawAffine) PetscCall(PetscMalloc2((edgeDiv + 1) * dim, &refCoords, (edgeDiv + 1) * dim, &edgeCoords));
1710   PetscCall(DMGetCoordinateDM(dm, &cdm));
1711   PetscCall(DMGetLocalSection(cdm, &coordSection));
1712   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
1713   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
1714   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1715 
1716   PetscCall(PetscViewerDrawGetDraw(viewer, 0, &draw));
1717   PetscCall(PetscDrawIsNull(draw, &isnull));
1718   if (isnull) PetscFunctionReturn(PETSC_SUCCESS);
1719   PetscCall(PetscDrawSetTitle(draw, "Mesh"));
1720 
1721   PetscCall(DMGetBoundingBox(dm, xyl, xyr));
1722   PetscCall(PetscDrawSetCoordinates(draw, xyl[0], xyl[1], xyr[0], xyr[1]));
1723   PetscCall(PetscDrawClear(draw));
1724 
1725   for (c = cStart; c < cEnd; ++c) {
1726     PetscScalar       *coords = NULL;
1727     const PetscScalar *coords_arr;
1728     PetscInt           numCoords;
1729     PetscBool          isDG;
1730 
1731     PetscCall(DMPlexGetCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
1732     if (drawAffine) PetscCall(DMPlexDrawCell(dm, draw, c, coords));
1733     else PetscCall(DMPlexDrawCellHighOrder(dm, draw, c, coords, edgeDiv, refCoords, edgeCoords));
1734     PetscCall(DMPlexRestoreCellCoordinates(dm, c, &isDG, &numCoords, &coords_arr, &coords));
1735   }
1736   if (!drawAffine) PetscCall(PetscFree2(refCoords, edgeCoords));
1737   PetscCall(PetscDrawFlush(draw));
1738   PetscCall(PetscDrawPause(draw));
1739   PetscCall(PetscDrawSave(draw));
1740   PetscFunctionReturn(PETSC_SUCCESS);
1741 }
1742 
1743 #if defined(PETSC_HAVE_EXODUSII)
1744   #include <exodusII.h>
1745   #include <petscviewerexodusii.h>
1746 #endif
1747 
1748 PetscErrorCode DMView_Plex(DM dm, PetscViewer viewer)
1749 {
1750   PetscBool iascii, ishdf5, isvtk, isdraw, flg, isglvis, isexodus, iscgns;
1751   char      name[PETSC_MAX_PATH_LEN];
1752 
1753   PetscFunctionBegin;
1754   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1755   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1756   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERASCII, &iascii));
1757   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERVTK, &isvtk));
1758   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1759   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERDRAW, &isdraw));
1760   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERGLVIS, &isglvis));
1761   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWEREXODUSII, &isexodus));
1762   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERCGNS, &iscgns));
1763   if (iascii) {
1764     PetscViewerFormat format;
1765     PetscCall(PetscViewerGetFormat(viewer, &format));
1766     if (format == PETSC_VIEWER_ASCII_GLVIS) PetscCall(DMPlexView_GLVis(dm, viewer));
1767     else PetscCall(DMPlexView_Ascii(dm, viewer));
1768   } else if (ishdf5) {
1769 #if defined(PETSC_HAVE_HDF5)
1770     PetscCall(DMPlexView_HDF5_Internal(dm, viewer));
1771 #else
1772     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1773 #endif
1774   } else if (isvtk) {
1775     PetscCall(DMPlexVTKWriteAll((PetscObject)dm, viewer));
1776   } else if (isdraw) {
1777     PetscCall(DMPlexView_Draw(dm, viewer));
1778   } else if (isglvis) {
1779     PetscCall(DMPlexView_GLVis(dm, viewer));
1780 #if defined(PETSC_HAVE_EXODUSII)
1781   } else if (isexodus) {
1782     /*
1783       exodusII requires that all sets be part of exactly one cell set.
1784       If the dm does not have a "Cell Sets" label defined, we create one
1785       with ID 1, containing all cells.
1786       Note that if the Cell Sets label is defined but does not cover all cells,
1787       we may still have a problem. This should probably be checked here or in the viewer;
1788     */
1789     PetscInt numCS;
1790     PetscCall(DMGetLabelSize(dm, "Cell Sets", &numCS));
1791     if (!numCS) {
1792       PetscInt cStart, cEnd, c;
1793       PetscCall(DMCreateLabel(dm, "Cell Sets"));
1794       PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
1795       for (c = cStart; c < cEnd; ++c) PetscCall(DMSetLabelValue(dm, "Cell Sets", c, 1));
1796     }
1797     PetscCall(DMView_PlexExodusII(dm, viewer));
1798 #endif
1799 #if defined(PETSC_HAVE_CGNS)
1800   } else if (iscgns) {
1801     PetscCall(DMView_PlexCGNS(dm, viewer));
1802 #endif
1803   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex writing", ((PetscObject)viewer)->type_name);
1804   /* Optionally view the partition */
1805   PetscCall(PetscOptionsHasName(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_partition_view", &flg));
1806   if (flg) {
1807     Vec ranks;
1808     PetscCall(DMPlexCreateRankField(dm, &ranks));
1809     PetscCall(VecView(ranks, viewer));
1810     PetscCall(VecDestroy(&ranks));
1811   }
1812   /* Optionally view a label */
1813   PetscCall(PetscOptionsGetString(((PetscObject)dm)->options, ((PetscObject)dm)->prefix, "-dm_label_view", name, sizeof(name), &flg));
1814   if (flg) {
1815     DMLabel label;
1816     Vec     val;
1817 
1818     PetscCall(DMGetLabel(dm, name, &label));
1819     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Label %s provided to -dm_label_view does not exist in this DM", name);
1820     PetscCall(DMPlexCreateLabelField(dm, label, &val));
1821     PetscCall(VecView(val, viewer));
1822     PetscCall(VecDestroy(&val));
1823   }
1824   PetscFunctionReturn(PETSC_SUCCESS);
1825 }
1826 
1827 /*@
1828   DMPlexTopologyView - Saves a `DMPLEX` topology into a file
1829 
1830   Collective
1831 
1832   Input Parameters:
1833 + dm     - The `DM` whose topology is to be saved
1834 - viewer - The `PetscViewer` to save it in
1835 
1836   Level: advanced
1837 
1838 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexTopologyLoad()`, `PetscViewer`
1839 @*/
1840 PetscErrorCode DMPlexTopologyView(DM dm, PetscViewer viewer)
1841 {
1842   PetscBool ishdf5;
1843 
1844   PetscFunctionBegin;
1845   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1846   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1847   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1848   PetscCall(PetscLogEventBegin(DMPLEX_TopologyView, viewer, 0, 0, 0));
1849   if (ishdf5) {
1850 #if defined(PETSC_HAVE_HDF5)
1851     PetscViewerFormat format;
1852     PetscCall(PetscViewerGetFormat(viewer, &format));
1853     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
1854       IS globalPointNumbering;
1855 
1856       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
1857       PetscCall(DMPlexTopologyView_HDF5_Internal(dm, globalPointNumbering, viewer));
1858       PetscCall(ISDestroy(&globalPointNumbering));
1859     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
1860 #else
1861     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1862 #endif
1863   }
1864   PetscCall(PetscLogEventEnd(DMPLEX_TopologyView, viewer, 0, 0, 0));
1865   PetscFunctionReturn(PETSC_SUCCESS);
1866 }
1867 
1868 /*@
1869   DMPlexCoordinatesView - Saves `DMPLEX` coordinates into a file
1870 
1871   Collective
1872 
1873   Input Parameters:
1874 + dm     - The `DM` whose coordinates are to be saved
1875 - viewer - The `PetscViewer` for saving
1876 
1877   Level: advanced
1878 
1879 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexLabelsView()`, `DMPlexCoordinatesLoad()`, `PetscViewer`
1880 @*/
1881 PetscErrorCode DMPlexCoordinatesView(DM dm, PetscViewer viewer)
1882 {
1883   PetscBool ishdf5;
1884 
1885   PetscFunctionBegin;
1886   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1887   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1888   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1889   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
1890   if (ishdf5) {
1891 #if defined(PETSC_HAVE_HDF5)
1892     PetscViewerFormat format;
1893     PetscCall(PetscViewerGetFormat(viewer, &format));
1894     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
1895       PetscCall(DMPlexCoordinatesView_HDF5_Internal(dm, viewer));
1896     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 output.", PetscViewerFormats[format]);
1897 #else
1898     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1899 #endif
1900   }
1901   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesView, viewer, 0, 0, 0));
1902   PetscFunctionReturn(PETSC_SUCCESS);
1903 }
1904 
1905 /*@
1906   DMPlexLabelsView - Saves `DMPLEX` labels into a file
1907 
1908   Collective
1909 
1910   Input Parameters:
1911 + dm     - The `DM` whose labels are to be saved
1912 - viewer - The `PetscViewer` for saving
1913 
1914   Level: advanced
1915 
1916 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsLoad()`, `PetscViewer`
1917 @*/
1918 PetscErrorCode DMPlexLabelsView(DM dm, PetscViewer viewer)
1919 {
1920   PetscBool ishdf5;
1921 
1922   PetscFunctionBegin;
1923   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1924   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1925   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1926   PetscCall(PetscLogEventBegin(DMPLEX_LabelsView, viewer, 0, 0, 0));
1927   if (ishdf5) {
1928 #if defined(PETSC_HAVE_HDF5)
1929     IS                globalPointNumbering;
1930     PetscViewerFormat format;
1931 
1932     PetscCall(PetscViewerGetFormat(viewer, &format));
1933     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
1934       PetscCall(DMPlexCreatePointNumbering(dm, &globalPointNumbering));
1935       PetscCall(DMPlexLabelsView_HDF5_Internal(dm, globalPointNumbering, viewer));
1936       PetscCall(ISDestroy(&globalPointNumbering));
1937     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
1938 #else
1939     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1940 #endif
1941   }
1942   PetscCall(PetscLogEventEnd(DMPLEX_LabelsView, viewer, 0, 0, 0));
1943   PetscFunctionReturn(PETSC_SUCCESS);
1944 }
1945 
1946 /*@
1947   DMPlexSectionView - Saves a section associated with a `DMPLEX`
1948 
1949   Collective
1950 
1951   Input Parameters:
1952 + dm        - The `DM` that contains the topology on which the section to be saved is defined
1953 . viewer    - The `PetscViewer` for saving
1954 - sectiondm - The `DM` that contains the section to be saved
1955 
1956   Level: advanced
1957 
1958   Notes:
1959   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.
1960 
1961   In general dm and sectiondm are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
1962 
1963 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`, `DMPlexTopologyView()`, `DMPlexCoordinatesView()`, `DMPlexLabelsView()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`, `PetscSectionView()`, `DMPlexSectionLoad()`, `PetscViewer`
1964 @*/
1965 PetscErrorCode DMPlexSectionView(DM dm, PetscViewer viewer, DM sectiondm)
1966 {
1967   PetscBool ishdf5;
1968 
1969   PetscFunctionBegin;
1970   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
1971   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
1972   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
1973   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
1974   PetscCall(PetscLogEventBegin(DMPLEX_SectionView, viewer, 0, 0, 0));
1975   if (ishdf5) {
1976 #if defined(PETSC_HAVE_HDF5)
1977     PetscCall(DMPlexSectionView_HDF5_Internal(dm, viewer, sectiondm));
1978 #else
1979     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
1980 #endif
1981   }
1982   PetscCall(PetscLogEventEnd(DMPLEX_SectionView, viewer, 0, 0, 0));
1983   PetscFunctionReturn(PETSC_SUCCESS);
1984 }
1985 
1986 /*@
1987   DMPlexGlobalVectorView - Saves a global vector
1988 
1989   Collective
1990 
1991   Input Parameters:
1992 + dm        - The `DM` that represents the topology
1993 . viewer    - The `PetscViewer` to save data with
1994 . sectiondm - The `DM` that contains the global section on which vec is defined
1995 - vec       - The global vector to be saved
1996 
1997   Level: advanced
1998 
1999   Notes:
2000   In general dm and sectiondm are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2001 
2002   Calling sequence:
2003 .vb
2004        DMCreate(PETSC_COMM_WORLD, &dm);
2005        DMSetType(dm, DMPLEX);
2006        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2007        DMClone(dm, &sectiondm);
2008        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2009        PetscSectionCreate(PETSC_COMM_WORLD, &section);
2010        DMPlexGetChart(sectiondm, &pStart, &pEnd);
2011        PetscSectionSetChart(section, pStart, pEnd);
2012        PetscSectionSetUp(section);
2013        DMSetLocalSection(sectiondm, section);
2014        PetscSectionDestroy(&section);
2015        DMGetGlobalVector(sectiondm, &vec);
2016        PetscObjectSetName((PetscObject)vec, "vec_name");
2017        DMPlexTopologyView(dm, viewer);
2018        DMPlexSectionView(dm, viewer, sectiondm);
2019        DMPlexGlobalVectorView(dm, viewer, sectiondm, vec);
2020        DMRestoreGlobalVector(sectiondm, &vec);
2021        DMDestroy(&sectiondm);
2022        DMDestroy(&dm);
2023 .ve
2024 
2025 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexLocalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2026 @*/
2027 PetscErrorCode DMPlexGlobalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2028 {
2029   PetscBool ishdf5;
2030 
2031   PetscFunctionBegin;
2032   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2033   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2034   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2035   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2036   /* Check consistency */
2037   {
2038     PetscSection section;
2039     PetscBool    includesConstraints;
2040     PetscInt     m, m1;
2041 
2042     PetscCall(VecGetLocalSize(vec, &m1));
2043     PetscCall(DMGetGlobalSection(sectiondm, &section));
2044     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2045     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2046     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2047     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2048   }
2049   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2050   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorView, viewer, 0, 0, 0));
2051   if (ishdf5) {
2052 #if defined(PETSC_HAVE_HDF5)
2053     PetscCall(DMPlexGlobalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
2054 #else
2055     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2056 #endif
2057   }
2058   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorView, viewer, 0, 0, 0));
2059   PetscFunctionReturn(PETSC_SUCCESS);
2060 }
2061 
2062 /*@
2063   DMPlexLocalVectorView - Saves a local vector
2064 
2065   Collective
2066 
2067   Input Parameters:
2068 + dm        - The `DM` that represents the topology
2069 . viewer    - The `PetscViewer` to save data with
2070 . sectiondm - The `DM` that contains the local section on which `vec` is defined; may be the same as `dm`
2071 - vec       - The local vector to be saved
2072 
2073   Level: advanced
2074 
2075   Note:
2076   In general `dm` and `sectiondm` are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2077 
2078   Calling sequence:
2079 .vb
2080        DMCreate(PETSC_COMM_WORLD, &dm);
2081        DMSetType(dm, DMPLEX);
2082        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2083        DMClone(dm, &sectiondm);
2084        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2085        PetscSectionCreate(PETSC_COMM_WORLD, &section);
2086        DMPlexGetChart(sectiondm, &pStart, &pEnd);
2087        PetscSectionSetChart(section, pStart, pEnd);
2088        PetscSectionSetUp(section);
2089        DMSetLocalSection(sectiondm, section);
2090        DMGetLocalVector(sectiondm, &vec);
2091        PetscObjectSetName((PetscObject)vec, "vec_name");
2092        DMPlexTopologyView(dm, viewer);
2093        DMPlexSectionView(dm, viewer, sectiondm);
2094        DMPlexLocalVectorView(dm, viewer, sectiondm, vec);
2095        DMRestoreLocalVector(sectiondm, &vec);
2096        DMDestroy(&sectiondm);
2097        DMDestroy(&dm);
2098 .ve
2099 
2100 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyView()`, `DMPlexSectionView()`, `DMPlexGlobalVectorView()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`
2101 @*/
2102 PetscErrorCode DMPlexLocalVectorView(DM dm, PetscViewer viewer, DM sectiondm, Vec vec)
2103 {
2104   PetscBool ishdf5;
2105 
2106   PetscFunctionBegin;
2107   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2108   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2109   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2110   PetscValidHeaderSpecific(vec, VEC_CLASSID, 4);
2111   /* Check consistency */
2112   {
2113     PetscSection section;
2114     PetscBool    includesConstraints;
2115     PetscInt     m, m1;
2116 
2117     PetscCall(VecGetLocalSize(vec, &m1));
2118     PetscCall(DMGetLocalSection(sectiondm, &section));
2119     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2120     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2121     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2122     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2123   }
2124   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2125   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorView, viewer, 0, 0, 0));
2126   if (ishdf5) {
2127 #if defined(PETSC_HAVE_HDF5)
2128     PetscCall(DMPlexLocalVectorView_HDF5_Internal(dm, viewer, sectiondm, vec));
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_LocalVectorView, viewer, 0, 0, 0));
2134   PetscFunctionReturn(PETSC_SUCCESS);
2135 }
2136 
2137 PetscErrorCode DMLoad_Plex(DM dm, PetscViewer viewer)
2138 {
2139   PetscBool ishdf5;
2140 
2141   PetscFunctionBegin;
2142   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2143   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2144   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2145   if (ishdf5) {
2146 #if defined(PETSC_HAVE_HDF5)
2147     PetscViewerFormat format;
2148     PetscCall(PetscViewerGetFormat(viewer, &format));
2149     if (format == PETSC_VIEWER_HDF5_XDMF || format == PETSC_VIEWER_HDF5_VIZ) {
2150       PetscCall(DMPlexLoad_HDF5_Xdmf_Internal(dm, viewer));
2151     } else if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2152       PetscCall(DMPlexLoad_HDF5_Internal(dm, viewer));
2153     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2154     PetscFunctionReturn(PETSC_SUCCESS);
2155 #else
2156     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2157 #endif
2158   } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "Viewer type %s not yet supported for DMPlex loading", ((PetscObject)viewer)->type_name);
2159 }
2160 
2161 /*@
2162   DMPlexTopologyLoad - Loads a topology into a `DMPLEX`
2163 
2164   Collective
2165 
2166   Input Parameters:
2167 + dm     - The `DM` into which the topology is loaded
2168 - viewer - The `PetscViewer` for the saved topology
2169 
2170   Output Parameter:
2171 . 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
2172 
2173   Level: advanced
2174 
2175 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2176           `PetscViewer`, `PetscSF`
2177 @*/
2178 PetscErrorCode DMPlexTopologyLoad(DM dm, PetscViewer viewer, PetscSF *globalToLocalPointSF)
2179 {
2180   PetscBool ishdf5;
2181 
2182   PetscFunctionBegin;
2183   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2184   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2185   if (globalToLocalPointSF) PetscAssertPointer(globalToLocalPointSF, 3);
2186   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2187   PetscCall(PetscLogEventBegin(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2188   if (ishdf5) {
2189 #if defined(PETSC_HAVE_HDF5)
2190     PetscViewerFormat format;
2191     PetscCall(PetscViewerGetFormat(viewer, &format));
2192     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2193       PetscCall(DMPlexTopologyLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2194     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2195 #else
2196     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2197 #endif
2198   }
2199   PetscCall(PetscLogEventEnd(DMPLEX_TopologyLoad, viewer, 0, 0, 0));
2200   PetscFunctionReturn(PETSC_SUCCESS);
2201 }
2202 
2203 /*@
2204   DMPlexCoordinatesLoad - Loads coordinates into a `DMPLEX`
2205 
2206   Collective
2207 
2208   Input Parameters:
2209 + dm                   - The `DM` into which the coordinates are loaded
2210 . viewer               - The `PetscViewer` for the saved coordinates
2211 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad()` when loading dm from viewer
2212 
2213   Level: advanced
2214 
2215 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexLabelsLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2216           `PetscSF`, `PetscViewer`
2217 @*/
2218 PetscErrorCode DMPlexCoordinatesLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2219 {
2220   PetscBool ishdf5;
2221 
2222   PetscFunctionBegin;
2223   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2224   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2225   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2226   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2227   PetscCall(PetscLogEventBegin(DMPLEX_CoordinatesLoad, viewer, 0, 0, 0));
2228   if (ishdf5) {
2229 #if defined(PETSC_HAVE_HDF5)
2230     PetscViewerFormat format;
2231     PetscCall(PetscViewerGetFormat(viewer, &format));
2232     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2233       PetscCall(DMPlexCoordinatesLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2234     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2235 #else
2236     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2237 #endif
2238   }
2239   PetscCall(PetscLogEventEnd(DMPLEX_CoordinatesLoad, viewer, 0, 0, 0));
2240   PetscFunctionReturn(PETSC_SUCCESS);
2241 }
2242 
2243 /*@
2244   DMPlexLabelsLoad - Loads labels into a `DMPLEX`
2245 
2246   Collective
2247 
2248   Input Parameters:
2249 + dm                   - The `DM` into which the labels are loaded
2250 . viewer               - The `PetscViewer` for the saved labels
2251 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad()` when loading `dm` from viewer
2252 
2253   Level: advanced
2254 
2255   Note:
2256   The `PetscSF` argument must not be NULL if the `DM` is distributed, otherwise an error occurs.
2257 
2258 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMView()`, `PetscViewerHDF5Open()`, `PetscViewerPushFormat()`,
2259           `PetscSF`, `PetscViewer`
2260 @*/
2261 PetscErrorCode DMPlexLabelsLoad(DM dm, PetscViewer viewer, PetscSF globalToLocalPointSF)
2262 {
2263   PetscBool ishdf5;
2264 
2265   PetscFunctionBegin;
2266   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2267   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2268   if (globalToLocalPointSF) PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 3);
2269   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2270   PetscCall(PetscLogEventBegin(DMPLEX_LabelsLoad, viewer, 0, 0, 0));
2271   if (ishdf5) {
2272 #if defined(PETSC_HAVE_HDF5)
2273     PetscViewerFormat format;
2274 
2275     PetscCall(PetscViewerGetFormat(viewer, &format));
2276     if (format == PETSC_VIEWER_HDF5_PETSC || format == PETSC_VIEWER_DEFAULT || format == PETSC_VIEWER_NATIVE) {
2277       PetscCall(DMPlexLabelsLoad_HDF5_Internal(dm, viewer, globalToLocalPointSF));
2278     } else SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "PetscViewerFormat %s not supported for HDF5 input.", PetscViewerFormats[format]);
2279 #else
2280     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2281 #endif
2282   }
2283   PetscCall(PetscLogEventEnd(DMPLEX_LabelsLoad, viewer, 0, 0, 0));
2284   PetscFunctionReturn(PETSC_SUCCESS);
2285 }
2286 
2287 /*@
2288   DMPlexSectionLoad - Loads section into a `DMPLEX`
2289 
2290   Collective
2291 
2292   Input Parameters:
2293 + dm                   - The `DM` that represents the topology
2294 . viewer               - The `PetscViewer` that represents the on-disk section (sectionA)
2295 . sectiondm            - The `DM` into which the on-disk section (sectionA) is migrated
2296 - globalToLocalPointSF - The `PetscSF` returned by `DMPlexTopologyLoad(`) when loading dm from viewer
2297 
2298   Output Parameters:
2299 + 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)
2300 - 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)
2301 
2302   Level: advanced
2303 
2304   Notes:
2305   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.
2306 
2307   In general `dm` and `sectiondm` are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2308 
2309   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.
2310 
2311   Example using 2 processes:
2312 .vb
2313   NX (number of points on dm): 4
2314   sectionA                   : the on-disk section
2315   vecA                       : a vector associated with sectionA
2316   sectionB                   : sectiondm's local section constructed in this function
2317   vecB (local)               : a vector associated with sectiondm's local section
2318   vecB (global)              : a vector associated with sectiondm's global section
2319 
2320                                      rank 0    rank 1
2321   vecA (global)                  : [.0 .4 .1 | .2 .3]        <- to be loaded in DMPlexGlobalVectorLoad() or DMPlexLocalVectorLoad()
2322   sectionA->atlasOff             :       0 2 | 1             <- loaded in PetscSectionLoad()
2323   sectionA->atlasDof             :       1 3 | 1             <- loaded in PetscSectionLoad()
2324   sectionA's global point numbers:       0 2 | 3             <- loaded in DMPlexSectionLoad()
2325   [0, NX)                        :       0 1 | 2 3           <- conceptual partition used in globalToLocalPointSF
2326   sectionB's global point numbers:     0 1 3 | 3 2           <- associated with [0, NX) by globalToLocalPointSF
2327   sectionB->atlasDof             :     1 0 1 | 1 3
2328   sectionB->atlasOff (no perm)   :     0 1 1 | 0 1
2329   vecB (local)                   :   [.0 .4] | [.4 .1 .2 .3] <- to be constructed by calling DMPlexLocalVectorLoad() with localDofSF
2330   vecB (global)                  :    [.0 .4 | .1 .2 .3]     <- to be constructed by calling DMPlexGlobalVectorLoad() with globalDofSF
2331 .ve
2332   where "|" represents a partition of loaded data, and global point 3 is assumed to be owned by rank 0.
2333 
2334 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMLoad()`, `DMPlexTopologyLoad()`, `DMPlexCoordinatesLoad()`, `DMPlexLabelsLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexLocalVectorLoad()`, `PetscSectionLoad()`, `DMPlexSectionView()`, `PetscSF`, `PetscViewer`
2335 @*/
2336 PetscErrorCode DMPlexSectionLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF globalToLocalPointSF, PetscSF *globalDofSF, PetscSF *localDofSF)
2337 {
2338   PetscBool ishdf5;
2339 
2340   PetscFunctionBegin;
2341   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2342   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2343   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2344   PetscValidHeaderSpecific(globalToLocalPointSF, PETSCSF_CLASSID, 4);
2345   if (globalDofSF) PetscAssertPointer(globalDofSF, 5);
2346   if (localDofSF) PetscAssertPointer(localDofSF, 6);
2347   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2348   PetscCall(PetscLogEventBegin(DMPLEX_SectionLoad, viewer, 0, 0, 0));
2349   if (ishdf5) {
2350 #if defined(PETSC_HAVE_HDF5)
2351     PetscCall(DMPlexSectionLoad_HDF5_Internal(dm, viewer, sectiondm, globalToLocalPointSF, globalDofSF, localDofSF));
2352 #else
2353     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2354 #endif
2355   }
2356   PetscCall(PetscLogEventEnd(DMPLEX_SectionLoad, viewer, 0, 0, 0));
2357   PetscFunctionReturn(PETSC_SUCCESS);
2358 }
2359 
2360 /*@
2361   DMPlexGlobalVectorLoad - Loads on-disk vector data into a global vector
2362 
2363   Collective
2364 
2365   Input Parameters:
2366 + dm        - The `DM` that represents the topology
2367 . viewer    - The `PetscViewer` that represents the on-disk vector data
2368 . sectiondm - The `DM` that contains the global section on which vec is defined
2369 . sf        - The `PetscSF` that migrates the on-disk vector data into vec
2370 - vec       - The global vector to set values of
2371 
2372   Level: advanced
2373 
2374   Notes:
2375   In general dm and sectiondm are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2376 
2377   Calling sequence:
2378 .vb
2379        DMCreate(PETSC_COMM_WORLD, &dm);
2380        DMSetType(dm, DMPLEX);
2381        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2382        DMPlexTopologyLoad(dm, viewer, &sfX);
2383        DMClone(dm, &sectiondm);
2384        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2385        DMPlexSectionLoad(dm, viewer, sectiondm, sfX, &gsf, NULL);
2386        DMGetGlobalVector(sectiondm, &vec);
2387        PetscObjectSetName((PetscObject)vec, "vec_name");
2388        DMPlexGlobalVectorLoad(dm, viewer, sectiondm, gsf, vec);
2389        DMRestoreGlobalVector(sectiondm, &vec);
2390        PetscSFDestroy(&gsf);
2391        PetscSFDestroy(&sfX);
2392        DMDestroy(&sectiondm);
2393        DMDestroy(&dm);
2394 .ve
2395 
2396 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexLocalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`,
2397           `PetscSF`, `PetscViewer`
2398 @*/
2399 PetscErrorCode DMPlexGlobalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2400 {
2401   PetscBool ishdf5;
2402 
2403   PetscFunctionBegin;
2404   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2405   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2406   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2407   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2408   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2409   /* Check consistency */
2410   {
2411     PetscSection section;
2412     PetscBool    includesConstraints;
2413     PetscInt     m, m1;
2414 
2415     PetscCall(VecGetLocalSize(vec, &m1));
2416     PetscCall(DMGetGlobalSection(sectiondm, &section));
2417     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2418     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2419     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2420     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Global vector size (%" PetscInt_FMT ") != global section storage size (%" PetscInt_FMT ")", m1, m);
2421   }
2422   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2423   PetscCall(PetscLogEventBegin(DMPLEX_GlobalVectorLoad, viewer, 0, 0, 0));
2424   if (ishdf5) {
2425 #if defined(PETSC_HAVE_HDF5)
2426     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2427 #else
2428     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2429 #endif
2430   }
2431   PetscCall(PetscLogEventEnd(DMPLEX_GlobalVectorLoad, viewer, 0, 0, 0));
2432   PetscFunctionReturn(PETSC_SUCCESS);
2433 }
2434 
2435 /*@
2436   DMPlexLocalVectorLoad - Loads on-disk vector data into a local vector
2437 
2438   Collective
2439 
2440   Input Parameters:
2441 + dm        - The `DM` that represents the topology
2442 . viewer    - The `PetscViewer` that represents the on-disk vector data
2443 . sectiondm - The `DM` that contains the local section on which vec is defined
2444 . sf        - The `PetscSF` that migrates the on-disk vector data into vec
2445 - vec       - The local vector to set values of
2446 
2447   Level: advanced
2448 
2449   Notes:
2450   In general `dm` and `sectiondm` are two different objects, the former carrying the topology and the latter carrying the section, and have been given a topology name and a section name, respectively, with `PetscObjectSetName()`. In practice, however, they can be the same object if it carries both topology and section; in that case the name of the object is used as both the topology name and the section name.
2451 
2452   Calling sequence:
2453 .vb
2454        DMCreate(PETSC_COMM_WORLD, &dm);
2455        DMSetType(dm, DMPLEX);
2456        PetscObjectSetName((PetscObject)dm, "topologydm_name");
2457        DMPlexTopologyLoad(dm, viewer, &sfX);
2458        DMClone(dm, &sectiondm);
2459        PetscObjectSetName((PetscObject)sectiondm, "sectiondm_name");
2460        DMPlexSectionLoad(dm, viewer, sectiondm, sfX, NULL, &lsf);
2461        DMGetLocalVector(sectiondm, &vec);
2462        PetscObjectSetName((PetscObject)vec, "vec_name");
2463        DMPlexLocalVectorLoad(dm, viewer, sectiondm, lsf, vec);
2464        DMRestoreLocalVector(sectiondm, &vec);
2465        PetscSFDestroy(&lsf);
2466        PetscSFDestroy(&sfX);
2467        DMDestroy(&sectiondm);
2468        DMDestroy(&dm);
2469 .ve
2470 
2471 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexTopologyLoad()`, `DMPlexSectionLoad()`, `DMPlexGlobalVectorLoad()`, `DMPlexGlobalVectorView()`, `DMPlexLocalVectorView()`,
2472           `PetscSF`, `PetscViewer`
2473 @*/
2474 PetscErrorCode DMPlexLocalVectorLoad(DM dm, PetscViewer viewer, DM sectiondm, PetscSF sf, Vec vec)
2475 {
2476   PetscBool ishdf5;
2477 
2478   PetscFunctionBegin;
2479   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2480   PetscValidHeaderSpecific(viewer, PETSC_VIEWER_CLASSID, 2);
2481   PetscValidHeaderSpecific(sectiondm, DM_CLASSID, 3);
2482   PetscValidHeaderSpecific(sf, PETSCSF_CLASSID, 4);
2483   PetscValidHeaderSpecific(vec, VEC_CLASSID, 5);
2484   /* Check consistency */
2485   {
2486     PetscSection section;
2487     PetscBool    includesConstraints;
2488     PetscInt     m, m1;
2489 
2490     PetscCall(VecGetLocalSize(vec, &m1));
2491     PetscCall(DMGetLocalSection(sectiondm, &section));
2492     PetscCall(PetscSectionGetIncludesConstraints(section, &includesConstraints));
2493     if (includesConstraints) PetscCall(PetscSectionGetStorageSize(section, &m));
2494     else PetscCall(PetscSectionGetConstrainedStorageSize(section, &m));
2495     PetscCheck(m1 == m, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Local vector size (%" PetscInt_FMT ") != local section storage size (%" PetscInt_FMT ")", m1, m);
2496   }
2497   PetscCall(PetscObjectTypeCompare((PetscObject)viewer, PETSCVIEWERHDF5, &ishdf5));
2498   PetscCall(PetscLogEventBegin(DMPLEX_LocalVectorLoad, viewer, 0, 0, 0));
2499   if (ishdf5) {
2500 #if defined(PETSC_HAVE_HDF5)
2501     PetscCall(DMPlexVecLoad_HDF5_Internal(dm, viewer, sectiondm, sf, vec));
2502 #else
2503     SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_SUP, "HDF5 not supported in this build.\nPlease reconfigure using --download-hdf5");
2504 #endif
2505   }
2506   PetscCall(PetscLogEventEnd(DMPLEX_LocalVectorLoad, viewer, 0, 0, 0));
2507   PetscFunctionReturn(PETSC_SUCCESS);
2508 }
2509 
2510 PetscErrorCode DMDestroy_Plex(DM dm)
2511 {
2512   DM_Plex *mesh = (DM_Plex *)dm->data;
2513 
2514   PetscFunctionBegin;
2515   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMSetUpGLVisViewer_C", NULL));
2516   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertBoundaryValues_C", NULL));
2517   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMCreateNeumannOverlap_C", NULL));
2518   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMInterpolateSolution_C", NULL));
2519   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexInsertTimeDerviativeBoundaryValues_C", NULL));
2520   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", NULL));
2521   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeGetDefault_C", NULL));
2522   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexDistributeSetDefault_C", NULL));
2523   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "MatComputeNeumannOverlap_C", NULL));
2524   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderGetDefault_C", NULL));
2525   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexReorderSetDefault_C", NULL));
2526   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexGetOverlap_C", NULL));
2527   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMPlexSetOverlap_C", NULL));
2528   PetscCall(PetscObjectComposeFunction((PetscObject)dm, "DMGetIsoperiodicPointSF_C", NULL));
2529   if (--mesh->refct > 0) PetscFunctionReturn(PETSC_SUCCESS);
2530   PetscCall(PetscSectionDestroy(&mesh->coneSection));
2531   PetscCall(PetscFree(mesh->cones));
2532   PetscCall(PetscFree(mesh->coneOrientations));
2533   PetscCall(PetscSectionDestroy(&mesh->supportSection));
2534   PetscCall(PetscSectionDestroy(&mesh->subdomainSection));
2535   PetscCall(PetscFree(mesh->supports));
2536   PetscCall(PetscFree(mesh->cellTypes));
2537   PetscCall(DMPlexTransformDestroy(&mesh->tr));
2538   PetscCall(PetscFree(mesh->tetgenOpts));
2539   PetscCall(PetscFree(mesh->triangleOpts));
2540   PetscCall(PetscFree(mesh->transformType));
2541   PetscCall(PetscFree(mesh->distributionName));
2542   PetscCall(PetscPartitionerDestroy(&mesh->partitioner));
2543   PetscCall(DMLabelDestroy(&mesh->subpointMap));
2544   PetscCall(ISDestroy(&mesh->subpointIS));
2545   PetscCall(ISDestroy(&mesh->globalVertexNumbers));
2546   PetscCall(ISDestroy(&mesh->globalCellNumbers));
2547   PetscCall(PetscSFDestroy(&mesh->periodic.face_sf));
2548   PetscCall(PetscSFDestroy(&mesh->periodic.composed_sf));
2549   PetscCall(ISDestroy(&mesh->periodic.periodic_points));
2550   PetscCall(PetscSectionDestroy(&mesh->anchorSection));
2551   PetscCall(ISDestroy(&mesh->anchorIS));
2552   PetscCall(PetscSectionDestroy(&mesh->parentSection));
2553   PetscCall(PetscFree(mesh->parents));
2554   PetscCall(PetscFree(mesh->childIDs));
2555   PetscCall(PetscSectionDestroy(&mesh->childSection));
2556   PetscCall(PetscFree(mesh->children));
2557   PetscCall(DMDestroy(&mesh->referenceTree));
2558   PetscCall(PetscGridHashDestroy(&mesh->lbox));
2559   PetscCall(PetscFree(mesh->neighbors));
2560   if (mesh->metricCtx) PetscCall(PetscFree(mesh->metricCtx));
2561   /* This was originally freed in DMDestroy(), but that prevents reference counting of backend objects */
2562   PetscCall(PetscFree(mesh));
2563   PetscFunctionReturn(PETSC_SUCCESS);
2564 }
2565 
2566 PetscErrorCode DMCreateMatrix_Plex(DM dm, Mat *J)
2567 {
2568   PetscSection           sectionGlobal;
2569   PetscInt               bs = -1, mbs;
2570   PetscInt               localSize, localStart = 0;
2571   PetscBool              isShell, isBlock, isSeqBlock, isMPIBlock, isSymBlock, isSymSeqBlock, isSymMPIBlock, isMatIS;
2572   MatType                mtype;
2573   ISLocalToGlobalMapping ltog;
2574 
2575   PetscFunctionBegin;
2576   PetscCall(MatInitializePackage());
2577   mtype = dm->mattype;
2578   PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
2579   /* PetscCall(PetscSectionGetStorageSize(sectionGlobal, &localSize)); */
2580   PetscCall(PetscSectionGetConstrainedStorageSize(sectionGlobal, &localSize));
2581   PetscCallMPI(MPI_Exscan(&localSize, &localStart, 1, MPIU_INT, MPI_SUM, PetscObjectComm((PetscObject)dm)));
2582   PetscCall(MatCreate(PetscObjectComm((PetscObject)dm), J));
2583   PetscCall(MatSetSizes(*J, localSize, localSize, PETSC_DETERMINE, PETSC_DETERMINE));
2584   PetscCall(MatSetType(*J, mtype));
2585   PetscCall(MatSetFromOptions(*J));
2586   PetscCall(MatGetBlockSize(*J, &mbs));
2587   if (mbs > 1) bs = mbs;
2588   PetscCall(PetscStrcmp(mtype, MATSHELL, &isShell));
2589   PetscCall(PetscStrcmp(mtype, MATBAIJ, &isBlock));
2590   PetscCall(PetscStrcmp(mtype, MATSEQBAIJ, &isSeqBlock));
2591   PetscCall(PetscStrcmp(mtype, MATMPIBAIJ, &isMPIBlock));
2592   PetscCall(PetscStrcmp(mtype, MATSBAIJ, &isSymBlock));
2593   PetscCall(PetscStrcmp(mtype, MATSEQSBAIJ, &isSymSeqBlock));
2594   PetscCall(PetscStrcmp(mtype, MATMPISBAIJ, &isSymMPIBlock));
2595   PetscCall(PetscStrcmp(mtype, MATIS, &isMatIS));
2596   if (!isShell) {
2597     PetscBool fillMatrix = (PetscBool)(!dm->prealloc_only && !isMatIS);
2598     PetscInt *dnz, *onz, *dnzu, *onzu, bsLocal[2], bsMinMax[2], *pblocks;
2599     PetscInt  pStart, pEnd, p, dof, cdof, num_fields;
2600 
2601     PetscCall(DMGetLocalToGlobalMapping(dm, &ltog));
2602 
2603     PetscCall(PetscCalloc1(localSize, &pblocks));
2604     PetscCall(PetscSectionGetChart(sectionGlobal, &pStart, &pEnd));
2605     PetscCall(PetscSectionGetNumFields(sectionGlobal, &num_fields));
2606     for (p = pStart; p < pEnd; ++p) {
2607       switch (dm->blocking_type) {
2608       case DM_BLOCKING_TOPOLOGICAL_POINT: { // One block per topological point
2609         PetscInt bdof, offset;
2610 
2611         PetscCall(PetscSectionGetDof(sectionGlobal, p, &dof));
2612         PetscCall(PetscSectionGetOffset(sectionGlobal, p, &offset));
2613         PetscCall(PetscSectionGetConstraintDof(sectionGlobal, p, &cdof));
2614         for (PetscInt i = 0; i < dof - cdof; i++) pblocks[offset - localStart + i] = dof - cdof;
2615         dof  = dof < 0 ? -(dof + 1) : dof;
2616         bdof = cdof && (dof - cdof) ? 1 : dof;
2617         if (dof) {
2618           if (bs < 0) {
2619             bs = bdof;
2620           } else if (bs != bdof) {
2621             bs = 1;
2622           }
2623         }
2624       } break;
2625       case DM_BLOCKING_FIELD_NODE: {
2626         for (PetscInt field = 0; field < num_fields; field++) {
2627           PetscInt num_comp, bdof, offset;
2628           PetscCall(PetscSectionGetFieldComponents(sectionGlobal, field, &num_comp));
2629           PetscCall(PetscSectionGetFieldDof(sectionGlobal, p, field, &dof));
2630           if (dof < 0) continue;
2631           PetscCall(PetscSectionGetFieldOffset(sectionGlobal, p, field, &offset));
2632           PetscCall(PetscSectionGetFieldConstraintDof(sectionGlobal, p, field, &cdof));
2633           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);
2634           PetscInt num_nodes = dof / num_comp;
2635           for (PetscInt i = 0; i < dof - cdof; i++) pblocks[offset - localStart + i] = (dof - cdof) / num_nodes;
2636           // Handle possibly constant block size (unlikely)
2637           bdof = cdof && (dof - cdof) ? 1 : dof;
2638           if (dof) {
2639             if (bs < 0) {
2640               bs = bdof;
2641             } else if (bs != bdof) {
2642               bs = 1;
2643             }
2644           }
2645         }
2646       } break;
2647       }
2648     }
2649     /* Must have same blocksize on all procs (some might have no points) */
2650     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs;
2651     bsLocal[1] = bs;
2652     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
2653     if (bsMinMax[0] != bsMinMax[1]) bs = 1;
2654     else bs = bsMinMax[0];
2655     bs = PetscMax(1, bs);
2656     PetscCall(MatSetLocalToGlobalMapping(*J, ltog, ltog));
2657     if (dm->prealloc_skip) { // User will likely use MatSetPreallocationCOO(), but still set structural parameters
2658       PetscCall(MatSetBlockSize(*J, bs));
2659       PetscCall(MatSetUp(*J));
2660     } else {
2661       PetscCall(PetscCalloc4(localSize / bs, &dnz, localSize / bs, &onz, localSize / bs, &dnzu, localSize / bs, &onzu));
2662       PetscCall(DMPlexPreallocateOperator(dm, bs, dnz, onz, dnzu, onzu, *J, fillMatrix));
2663       PetscCall(PetscFree4(dnz, onz, dnzu, onzu));
2664     }
2665     { // Consolidate blocks
2666       PetscInt nblocks = 0;
2667       for (PetscInt i = 0; i < localSize; i += PetscMax(1, pblocks[i])) {
2668         if (pblocks[i] == 0) continue;
2669         pblocks[nblocks++] = pblocks[i]; // nblocks always <= i
2670         for (PetscInt j = 1; j < pblocks[i]; j++) PetscCheck(pblocks[i + j] == pblocks[i], PETSC_COMM_SELF, PETSC_ERR_PLIB, "Block of size %" PetscInt_FMT " mismatches entry %" PetscInt_FMT, pblocks[i], pblocks[i + j]);
2671       }
2672       PetscCall(MatSetVariableBlockSizes(*J, nblocks, pblocks));
2673     }
2674     PetscCall(PetscFree(pblocks));
2675   }
2676   PetscCall(MatSetDM(*J, dm));
2677   PetscFunctionReturn(PETSC_SUCCESS);
2678 }
2679 
2680 /*@
2681   DMPlexGetSubdomainSection - Returns the section associated with the subdomain
2682 
2683   Not Collective
2684 
2685   Input Parameter:
2686 . dm - The `DMPLEX`
2687 
2688   Output Parameter:
2689 . subsection - The subdomain section
2690 
2691   Level: developer
2692 
2693 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `PetscSection`
2694 @*/
2695 PetscErrorCode DMPlexGetSubdomainSection(DM dm, PetscSection *subsection)
2696 {
2697   DM_Plex *mesh = (DM_Plex *)dm->data;
2698 
2699   PetscFunctionBegin;
2700   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2701   if (!mesh->subdomainSection) {
2702     PetscSection section;
2703     PetscSF      sf;
2704 
2705     PetscCall(PetscSFCreate(PETSC_COMM_SELF, &sf));
2706     PetscCall(DMGetLocalSection(dm, &section));
2707     PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_FALSE, PETSC_TRUE, &mesh->subdomainSection));
2708     PetscCall(PetscSFDestroy(&sf));
2709   }
2710   *subsection = mesh->subdomainSection;
2711   PetscFunctionReturn(PETSC_SUCCESS);
2712 }
2713 
2714 /*@
2715   DMPlexGetChart - Return the interval for all mesh points [`pStart`, `pEnd`)
2716 
2717   Not Collective
2718 
2719   Input Parameter:
2720 . dm - The `DMPLEX`
2721 
2722   Output Parameters:
2723 + pStart - The first mesh point
2724 - pEnd   - The upper bound for mesh points
2725 
2726   Level: beginner
2727 
2728 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetChart()`
2729 @*/
2730 PetscErrorCode DMPlexGetChart(DM dm, PetscInt *pStart, PetscInt *pEnd)
2731 {
2732   DM_Plex *mesh = (DM_Plex *)dm->data;
2733 
2734   PetscFunctionBegin;
2735   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2736   if (mesh->tr) PetscCall(DMPlexTransformGetChart(mesh->tr, pStart, pEnd));
2737   else PetscCall(PetscSectionGetChart(mesh->coneSection, pStart, pEnd));
2738   PetscFunctionReturn(PETSC_SUCCESS);
2739 }
2740 
2741 /*@
2742   DMPlexSetChart - Set the interval for all mesh points [`pStart`, `pEnd`)
2743 
2744   Not Collective
2745 
2746   Input Parameters:
2747 + dm     - The `DMPLEX`
2748 . pStart - The first mesh point
2749 - pEnd   - The upper bound for mesh points
2750 
2751   Level: beginner
2752 
2753 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetChart()`
2754 @*/
2755 PetscErrorCode DMPlexSetChart(DM dm, PetscInt pStart, PetscInt pEnd)
2756 {
2757   DM_Plex *mesh = (DM_Plex *)dm->data;
2758 
2759   PetscFunctionBegin;
2760   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2761   PetscCall(PetscSectionSetChart(mesh->coneSection, pStart, pEnd));
2762   PetscCall(PetscSectionSetChart(mesh->supportSection, pStart, pEnd));
2763   PetscCall(PetscFree(mesh->cellTypes));
2764   PetscFunctionReturn(PETSC_SUCCESS);
2765 }
2766 
2767 /*@
2768   DMPlexGetConeSize - Return the number of in-edges for this point in the DAG
2769 
2770   Not Collective
2771 
2772   Input Parameters:
2773 + dm - The `DMPLEX`
2774 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
2775 
2776   Output Parameter:
2777 . size - The cone size for point `p`
2778 
2779   Level: beginner
2780 
2781 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
2782 @*/
2783 PetscErrorCode DMPlexGetConeSize(DM dm, PetscInt p, PetscInt *size)
2784 {
2785   DM_Plex *mesh = (DM_Plex *)dm->data;
2786 
2787   PetscFunctionBegin;
2788   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2789   PetscAssertPointer(size, 3);
2790   if (mesh->tr) PetscCall(DMPlexTransformGetConeSize(mesh->tr, p, size));
2791   else PetscCall(PetscSectionGetDof(mesh->coneSection, p, size));
2792   PetscFunctionReturn(PETSC_SUCCESS);
2793 }
2794 
2795 /*@
2796   DMPlexSetConeSize - Set the number of in-edges for this point in the DAG
2797 
2798   Not Collective
2799 
2800   Input Parameters:
2801 + dm   - The `DMPLEX`
2802 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
2803 - size - The cone size for point `p`
2804 
2805   Level: beginner
2806 
2807   Note:
2808   This should be called after `DMPlexSetChart()`.
2809 
2810 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetConeSize()`, `DMPlexSetChart()`
2811 @*/
2812 PetscErrorCode DMPlexSetConeSize(DM dm, PetscInt p, PetscInt size)
2813 {
2814   DM_Plex *mesh = (DM_Plex *)dm->data;
2815 
2816   PetscFunctionBegin;
2817   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2818   PetscCheck(!mesh->tr, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Cannot call DMPlexSetConeSize() on a mesh with a transform defined.");
2819   PetscCall(PetscSectionSetDof(mesh->coneSection, p, size));
2820   PetscFunctionReturn(PETSC_SUCCESS);
2821 }
2822 
2823 /*@C
2824   DMPlexGetCone - Return the points on the in-edges for this point in the DAG
2825 
2826   Not Collective
2827 
2828   Input Parameters:
2829 + dm - The `DMPLEX`
2830 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
2831 
2832   Output Parameter:
2833 . cone - An array of points which are on the in-edges for point `p`
2834 
2835   Level: beginner
2836 
2837   Fortran Notes:
2838   You must also call `DMPlexRestoreCone()` after you finish using the returned array.
2839   `DMPlexRestoreCone()` is not needed/available in C.
2840 
2841 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSize()`, `DMPlexSetCone()`, `DMPlexGetConeTuple()`, `DMPlexSetChart()`, `DMPlexRestoreCone()`
2842 @*/
2843 PetscErrorCode DMPlexGetCone(DM dm, PetscInt p, const PetscInt *cone[])
2844 {
2845   DM_Plex *mesh = (DM_Plex *)dm->data;
2846   PetscInt off;
2847 
2848   PetscFunctionBegin;
2849   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2850   PetscAssertPointer(cone, 3);
2851   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
2852   *cone = &mesh->cones[off];
2853   PetscFunctionReturn(PETSC_SUCCESS);
2854 }
2855 
2856 /*@C
2857   DMPlexGetConeTuple - Return the points on the in-edges of several points in the DAG
2858 
2859   Not Collective
2860 
2861   Input Parameters:
2862 + dm - The `DMPLEX`
2863 - p  - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
2864 
2865   Output Parameters:
2866 + pConesSection - `PetscSection` describing the layout of `pCones`
2867 - pCones        - An array of points which are on the in-edges for the point set `p`
2868 
2869   Level: intermediate
2870 
2871 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeRecursive()`, `DMPlexSetChart()`, `PetscSection`, `IS`
2872 @*/
2873 PetscErrorCode DMPlexGetConeTuple(DM dm, IS p, PetscSection *pConesSection, IS *pCones)
2874 {
2875   PetscSection cs, newcs;
2876   PetscInt    *cones;
2877   PetscInt    *newarr = NULL;
2878   PetscInt     n;
2879 
2880   PetscFunctionBegin;
2881   PetscCall(DMPlexGetCones(dm, &cones));
2882   PetscCall(DMPlexGetConeSection(dm, &cs));
2883   PetscCall(PetscSectionExtractDofsFromArray(cs, MPIU_INT, cones, p, &newcs, pCones ? ((void **)&newarr) : NULL));
2884   if (pConesSection) *pConesSection = newcs;
2885   if (pCones) {
2886     PetscCall(PetscSectionGetStorageSize(newcs, &n));
2887     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)p), n, newarr, PETSC_OWN_POINTER, pCones));
2888   }
2889   PetscFunctionReturn(PETSC_SUCCESS);
2890 }
2891 
2892 /*@
2893   DMPlexGetConeRecursiveVertices - Expand each given point into its cone points and do that recursively until we end up just with vertices.
2894 
2895   Not Collective
2896 
2897   Input Parameters:
2898 + dm     - The `DMPLEX`
2899 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
2900 
2901   Output Parameter:
2902 . expandedPoints - An array of vertices recursively expanded from input points
2903 
2904   Level: advanced
2905 
2906   Notes:
2907   Like `DMPlexGetConeRecursive()` but returns only the 0-depth `IS` (i.e. vertices only) and no sections.
2908 
2909   There is no corresponding Restore function, just call `ISDestroy()` on the returned `IS` to deallocate.
2910 
2911 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexRestoreConeRecursive()`,
2912           `DMPlexGetDepth()`, `IS`
2913 @*/
2914 PetscErrorCode DMPlexGetConeRecursiveVertices(DM dm, IS points, IS *expandedPoints)
2915 {
2916   IS      *expandedPointsAll;
2917   PetscInt depth;
2918 
2919   PetscFunctionBegin;
2920   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2921   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
2922   PetscAssertPointer(expandedPoints, 3);
2923   PetscCall(DMPlexGetConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
2924   *expandedPoints = expandedPointsAll[0];
2925   PetscCall(PetscObjectReference((PetscObject)expandedPointsAll[0]));
2926   PetscCall(DMPlexRestoreConeRecursive(dm, points, &depth, &expandedPointsAll, NULL));
2927   PetscFunctionReturn(PETSC_SUCCESS);
2928 }
2929 
2930 /*@
2931   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).
2932 
2933   Not Collective
2934 
2935   Input Parameters:
2936 + dm     - The `DMPLEX`
2937 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
2938 
2939   Output Parameters:
2940 + depth          - (optional) Size of the output arrays, equal to `DMPLEX` depth, returned by `DMPlexGetDepth()`
2941 . expandedPoints - (optional) An array of index sets with recursively expanded cones
2942 - sections       - (optional) An array of sections which describe mappings from points to their cone points
2943 
2944   Level: advanced
2945 
2946   Notes:
2947   Like `DMPlexGetConeTuple()` but recursive.
2948 
2949   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.
2950   For example, for d=0 it contains only vertices, for d=1 it can contain vertices and edges, etc.
2951 
2952   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\:
2953   (1) DAG points in `expandedPoints`[d+1] with `depth` d+1 to their cone points in `expandedPoints`[d];
2954   (2) DAG points in `expandedPoints`[d+1] with `depth` in [0,d] to the same points in `expandedPoints`[d].
2955 
2956 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexRestoreConeRecursive()`, `DMPlexGetConeRecursiveVertices()`,
2957           `DMPlexGetDepth()`, `PetscSection`, `IS`
2958 @*/
2959 PetscErrorCode DMPlexGetConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
2960 {
2961   const PetscInt *arr0 = NULL, *cone = NULL;
2962   PetscInt       *arr = NULL, *newarr = NULL;
2963   PetscInt        d, depth_, i, n, newn, cn, co, start, end;
2964   IS             *expandedPoints_;
2965   PetscSection   *sections_;
2966 
2967   PetscFunctionBegin;
2968   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
2969   PetscValidHeaderSpecific(points, IS_CLASSID, 2);
2970   if (depth) PetscAssertPointer(depth, 3);
2971   if (expandedPoints) PetscAssertPointer(expandedPoints, 4);
2972   if (sections) PetscAssertPointer(sections, 5);
2973   PetscCall(ISGetLocalSize(points, &n));
2974   PetscCall(ISGetIndices(points, &arr0));
2975   PetscCall(DMPlexGetDepth(dm, &depth_));
2976   PetscCall(PetscCalloc1(depth_, &expandedPoints_));
2977   PetscCall(PetscCalloc1(depth_, &sections_));
2978   arr = (PetscInt *)arr0; /* this is ok because first generation of arr is not modified */
2979   for (d = depth_ - 1; d >= 0; d--) {
2980     PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &sections_[d]));
2981     PetscCall(PetscSectionSetChart(sections_[d], 0, n));
2982     for (i = 0; i < n; i++) {
2983       PetscCall(DMPlexGetDepthStratum(dm, d + 1, &start, &end));
2984       if (arr[i] >= start && arr[i] < end) {
2985         PetscCall(DMPlexGetConeSize(dm, arr[i], &cn));
2986         PetscCall(PetscSectionSetDof(sections_[d], i, cn));
2987       } else {
2988         PetscCall(PetscSectionSetDof(sections_[d], i, 1));
2989       }
2990     }
2991     PetscCall(PetscSectionSetUp(sections_[d]));
2992     PetscCall(PetscSectionGetStorageSize(sections_[d], &newn));
2993     PetscCall(PetscMalloc1(newn, &newarr));
2994     for (i = 0; i < n; i++) {
2995       PetscCall(PetscSectionGetDof(sections_[d], i, &cn));
2996       PetscCall(PetscSectionGetOffset(sections_[d], i, &co));
2997       if (cn > 1) {
2998         PetscCall(DMPlexGetCone(dm, arr[i], &cone));
2999         PetscCall(PetscMemcpy(&newarr[co], cone, cn * sizeof(PetscInt)));
3000       } else {
3001         newarr[co] = arr[i];
3002       }
3003     }
3004     PetscCall(ISCreateGeneral(PETSC_COMM_SELF, newn, newarr, PETSC_OWN_POINTER, &expandedPoints_[d]));
3005     arr = newarr;
3006     n   = newn;
3007   }
3008   PetscCall(ISRestoreIndices(points, &arr0));
3009   *depth = depth_;
3010   if (expandedPoints) *expandedPoints = expandedPoints_;
3011   else {
3012     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&expandedPoints_[d]));
3013     PetscCall(PetscFree(expandedPoints_));
3014   }
3015   if (sections) *sections = sections_;
3016   else {
3017     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&sections_[d]));
3018     PetscCall(PetscFree(sections_));
3019   }
3020   PetscFunctionReturn(PETSC_SUCCESS);
3021 }
3022 
3023 /*@
3024   DMPlexRestoreConeRecursive - Deallocates arrays created by `DMPlexGetConeRecursive()`
3025 
3026   Not Collective
3027 
3028   Input Parameters:
3029 + dm     - The `DMPLEX`
3030 - points - The `IS` of points, which must lie in the chart set with `DMPlexSetChart()`
3031 
3032   Output Parameters:
3033 + depth          - (optional) Size of the output arrays, equal to `DMPLEX` depth, returned by `DMPlexGetDepth()`
3034 . expandedPoints - (optional) An array of recursively expanded cones
3035 - sections       - (optional) An array of sections which describe mappings from points to their cone points
3036 
3037   Level: advanced
3038 
3039   Note:
3040   See `DMPlexGetConeRecursive()`
3041 
3042 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexGetConeTuple()`, `DMPlexGetConeRecursive()`, `DMPlexGetConeRecursiveVertices()`,
3043           `DMPlexGetDepth()`, `IS`, `PetscSection`
3044 @*/
3045 PetscErrorCode DMPlexRestoreConeRecursive(DM dm, IS points, PetscInt *depth, IS *expandedPoints[], PetscSection *sections[])
3046 {
3047   PetscInt d, depth_;
3048 
3049   PetscFunctionBegin;
3050   PetscCall(DMPlexGetDepth(dm, &depth_));
3051   PetscCheck(!depth || *depth == depth_, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "depth changed since last call to DMPlexGetConeRecursive");
3052   if (depth) *depth = 0;
3053   if (expandedPoints) {
3054     for (d = 0; d < depth_; d++) PetscCall(ISDestroy(&((*expandedPoints)[d])));
3055     PetscCall(PetscFree(*expandedPoints));
3056   }
3057   if (sections) {
3058     for (d = 0; d < depth_; d++) PetscCall(PetscSectionDestroy(&((*sections)[d])));
3059     PetscCall(PetscFree(*sections));
3060   }
3061   PetscFunctionReturn(PETSC_SUCCESS);
3062 }
3063 
3064 /*@
3065   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
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 - cone - An array of points which are on the in-edges for point `p`
3073 
3074   Level: beginner
3075 
3076   Note:
3077   This should be called after all calls to `DMPlexSetConeSize()` and `DMSetUp()`.
3078 
3079 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`, `DMPlexSetSupport()`, `DMPlexSetSupportSize()`
3080 @*/
3081 PetscErrorCode DMPlexSetCone(DM dm, PetscInt p, const PetscInt cone[])
3082 {
3083   DM_Plex *mesh = (DM_Plex *)dm->data;
3084   PetscInt dof, off, c;
3085 
3086   PetscFunctionBegin;
3087   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3088   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3089   if (dof) PetscAssertPointer(cone, 3);
3090   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3091   if (PetscDefined(USE_DEBUG)) {
3092     PetscInt pStart, pEnd;
3093     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3094     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);
3095     for (c = 0; c < dof; ++c) {
3096       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);
3097       mesh->cones[off + c] = cone[c];
3098     }
3099   } else {
3100     for (c = 0; c < dof; ++c) mesh->cones[off + c] = cone[c];
3101   }
3102   PetscFunctionReturn(PETSC_SUCCESS);
3103 }
3104 
3105 /*@C
3106   DMPlexGetConeOrientation - Return the orientations on the in-edges for this point in the DAG
3107 
3108   Not Collective
3109 
3110   Input Parameters:
3111 + dm - The `DMPLEX`
3112 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3113 
3114   Output Parameter:
3115 . coneOrientation - An array of orientations which are on the in-edges for point `p`. An orientation is an
3116                     integer giving the prescription for cone traversal.
3117 
3118   Level: beginner
3119 
3120   Note:
3121   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3122   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3123   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3124   with the identity.
3125 
3126   Fortran Notes:
3127   You must also call `DMPlexRestoreConeOrientation()` after you finish using the returned array.
3128   `DMPlexRestoreConeOrientation()` is not needed/available in C.
3129 
3130 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPolytopeTypeComposeOrientation()`, `DMPolytopeTypeComposeOrientationInv()`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetCone()`, `DMPlexSetChart()`
3131 @*/
3132 PetscErrorCode DMPlexGetConeOrientation(DM dm, PetscInt p, const PetscInt *coneOrientation[])
3133 {
3134   DM_Plex *mesh = (DM_Plex *)dm->data;
3135   PetscInt off;
3136 
3137   PetscFunctionBegin;
3138   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3139   if (PetscDefined(USE_DEBUG)) {
3140     PetscInt dof;
3141     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3142     if (dof) PetscAssertPointer(coneOrientation, 3);
3143   }
3144   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3145 
3146   *coneOrientation = &mesh->coneOrientations[off];
3147   PetscFunctionReturn(PETSC_SUCCESS);
3148 }
3149 
3150 /*@
3151   DMPlexSetConeOrientation - Set the orientations on the in-edges for this point in the DAG
3152 
3153   Not Collective
3154 
3155   Input Parameters:
3156 + dm              - The `DMPLEX`
3157 . p               - The point, which must lie in the chart set with `DMPlexSetChart()`
3158 - coneOrientation - An array of orientations
3159 
3160   Level: beginner
3161 
3162   Notes:
3163   This should be called after all calls to `DMPlexSetConeSize()` and `DMSetUp()`.
3164 
3165   The meaning of coneOrientation is detailed in `DMPlexGetConeOrientation()`.
3166 
3167 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetConeOrientation()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3168 @*/
3169 PetscErrorCode DMPlexSetConeOrientation(DM dm, PetscInt p, const PetscInt coneOrientation[])
3170 {
3171   DM_Plex *mesh = (DM_Plex *)dm->data;
3172   PetscInt pStart, pEnd;
3173   PetscInt dof, off, c;
3174 
3175   PetscFunctionBegin;
3176   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3177   PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3178   if (dof) PetscAssertPointer(coneOrientation, 3);
3179   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3180   if (PetscDefined(USE_DEBUG)) {
3181     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3182     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);
3183     for (c = 0; c < dof; ++c) {
3184       PetscInt cdof, o = coneOrientation[c];
3185 
3186       PetscCall(PetscSectionGetDof(mesh->coneSection, mesh->cones[off + c], &cdof));
3187       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);
3188       mesh->coneOrientations[off + c] = o;
3189     }
3190   } else {
3191     for (c = 0; c < dof; ++c) mesh->coneOrientations[off + c] = coneOrientation[c];
3192   }
3193   PetscFunctionReturn(PETSC_SUCCESS);
3194 }
3195 
3196 /*@
3197   DMPlexInsertCone - Insert a point into the in-edges for the point p in the DAG
3198 
3199   Not Collective
3200 
3201   Input Parameters:
3202 + dm        - The `DMPLEX`
3203 . p         - The point, which must lie in the chart set with `DMPlexSetChart()`
3204 . conePos   - The local index in the cone where the point should be put
3205 - conePoint - The mesh point to insert
3206 
3207   Level: beginner
3208 
3209 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3210 @*/
3211 PetscErrorCode DMPlexInsertCone(DM dm, PetscInt p, PetscInt conePos, PetscInt conePoint)
3212 {
3213   DM_Plex *mesh = (DM_Plex *)dm->data;
3214   PetscInt pStart, pEnd;
3215   PetscInt dof, off;
3216 
3217   PetscFunctionBegin;
3218   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3219   if (PetscDefined(USE_DEBUG)) {
3220     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3221     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);
3222     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);
3223     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3224     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);
3225   }
3226   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3227   mesh->cones[off + conePos] = conePoint;
3228   PetscFunctionReturn(PETSC_SUCCESS);
3229 }
3230 
3231 /*@
3232   DMPlexInsertConeOrientation - Insert a point orientation for the in-edge for the point p in the DAG
3233 
3234   Not Collective
3235 
3236   Input Parameters:
3237 + dm              - The `DMPLEX`
3238 . p               - The point, which must lie in the chart set with `DMPlexSetChart()`
3239 . conePos         - The local index in the cone where the point should be put
3240 - coneOrientation - The point orientation to insert
3241 
3242   Level: beginner
3243 
3244   Note:
3245   The meaning of coneOrientation values is detailed in `DMPlexGetConeOrientation()`.
3246 
3247 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3248 @*/
3249 PetscErrorCode DMPlexInsertConeOrientation(DM dm, PetscInt p, PetscInt conePos, PetscInt coneOrientation)
3250 {
3251   DM_Plex *mesh = (DM_Plex *)dm->data;
3252   PetscInt pStart, pEnd;
3253   PetscInt dof, off;
3254 
3255   PetscFunctionBegin;
3256   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3257   if (PetscDefined(USE_DEBUG)) {
3258     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
3259     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);
3260     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3261     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);
3262   }
3263   PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3264   mesh->coneOrientations[off + conePos] = coneOrientation;
3265   PetscFunctionReturn(PETSC_SUCCESS);
3266 }
3267 
3268 /*@C
3269   DMPlexGetOrientedCone - Return the points and orientations on the in-edges for this point in the DAG
3270 
3271   Not collective
3272 
3273   Input Parameters:
3274 + dm - The DMPlex
3275 - p  - The point, which must lie in the chart set with DMPlexSetChart()
3276 
3277   Output Parameters:
3278 + cone - An array of points which are on the in-edges for point `p`
3279 - ornt - An array of orientations which are on the in-edges for point `p`. An orientation is an
3280         integer giving the prescription for cone traversal.
3281 
3282   Level: beginner
3283 
3284   Notes:
3285   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3286   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3287   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3288   with the identity.
3289 
3290   Fortran Notes:
3291   You must also call `DMPlexRestoreCone()` after you finish using the returned array.
3292   `DMPlexRestoreCone()` is not needed/available in C.
3293 
3294 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreOrientedCone()`, `DMPlexGetConeSize()`, `DMPlexGetCone()`, `DMPlexGetChart()`
3295 @*/
3296 PetscErrorCode DMPlexGetOrientedCone(DM dm, PetscInt p, const PetscInt *cone[], const PetscInt *ornt[])
3297 {
3298   DM_Plex *mesh = (DM_Plex *)dm->data;
3299 
3300   PetscFunctionBegin;
3301   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3302   if (mesh->tr) {
3303     PetscCall(DMPlexTransformGetCone(mesh->tr, p, cone, ornt));
3304   } else {
3305     PetscInt off;
3306     if (PetscDefined(USE_DEBUG)) {
3307       PetscInt dof;
3308       PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
3309       if (dof) {
3310         if (cone) PetscAssertPointer(cone, 3);
3311         if (ornt) PetscAssertPointer(ornt, 4);
3312       }
3313     }
3314     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
3315     if (cone) *cone = mesh->cones ? mesh->cones + off : NULL; // NULL + 0 is UB
3316     if (ornt) *ornt = mesh->coneOrientations ? mesh->coneOrientations + off : NULL;
3317   }
3318   PetscFunctionReturn(PETSC_SUCCESS);
3319 }
3320 
3321 /*@C
3322   DMPlexRestoreOrientedCone - Restore the points and orientations on the in-edges for this point in the DAG
3323 
3324   Not Collective
3325 
3326   Input Parameters:
3327 + dm   - The DMPlex
3328 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3329 . cone - An array of points which are on the in-edges for point p
3330 - ornt - An array of orientations which are on the in-edges for point `p`. An orientation is an
3331         integer giving the prescription for cone traversal.
3332 
3333   Level: beginner
3334 
3335   Notes:
3336   The number indexes the symmetry transformations for the cell type (see manual). Orientation 0 is always
3337   the identity transformation. Negative orientation indicates reflection so that -(o+1) is the reflection
3338   of o, however it is not necessarily the inverse. To get the inverse, use `DMPolytopeTypeComposeOrientationInv()`
3339   with the identity.
3340 
3341   Fortran Notes:
3342   You must also call `DMPlexRestoreCone()` after you finish using the returned array.
3343   `DMPlexRestoreCone()` is not needed/available in C.
3344 
3345 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetOrientedCone()`, `DMPlexGetConeSize()`, `DMPlexGetCone()`, `DMPlexGetChart()`
3346 @*/
3347 PetscErrorCode DMPlexRestoreOrientedCone(DM dm, PetscInt p, const PetscInt *cone[], const PetscInt *ornt[])
3348 {
3349   DM_Plex *mesh = (DM_Plex *)dm->data;
3350 
3351   PetscFunctionBegin;
3352   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3353   if (mesh->tr) PetscCall(DMPlexTransformRestoreCone(mesh->tr, p, cone, ornt));
3354   PetscFunctionReturn(PETSC_SUCCESS);
3355 }
3356 
3357 /*@
3358   DMPlexGetSupportSize - Return the number of out-edges for this point in the DAG
3359 
3360   Not Collective
3361 
3362   Input Parameters:
3363 + dm - The `DMPLEX`
3364 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3365 
3366   Output Parameter:
3367 . size - The support size for point `p`
3368 
3369   Level: beginner
3370 
3371 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`, `DMPlexGetConeSize()`
3372 @*/
3373 PetscErrorCode DMPlexGetSupportSize(DM dm, PetscInt p, PetscInt *size)
3374 {
3375   DM_Plex *mesh = (DM_Plex *)dm->data;
3376 
3377   PetscFunctionBegin;
3378   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3379   PetscAssertPointer(size, 3);
3380   PetscCall(PetscSectionGetDof(mesh->supportSection, p, size));
3381   PetscFunctionReturn(PETSC_SUCCESS);
3382 }
3383 
3384 /*@
3385   DMPlexSetSupportSize - Set the number of out-edges for this point in the DAG
3386 
3387   Not Collective
3388 
3389   Input Parameters:
3390 + dm   - The `DMPLEX`
3391 . p    - The point, which must lie in the chart set with `DMPlexSetChart()`
3392 - size - The support size for point `p`
3393 
3394   Level: beginner
3395 
3396   Note:
3397   This should be called after `DMPlexSetChart()`.
3398 
3399 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetSupportSize()`, `DMPlexSetChart()`
3400 @*/
3401 PetscErrorCode DMPlexSetSupportSize(DM dm, PetscInt p, PetscInt size)
3402 {
3403   DM_Plex *mesh = (DM_Plex *)dm->data;
3404 
3405   PetscFunctionBegin;
3406   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3407   PetscCall(PetscSectionSetDof(mesh->supportSection, p, size));
3408   PetscFunctionReturn(PETSC_SUCCESS);
3409 }
3410 
3411 /*@C
3412   DMPlexGetSupport - Return the points on the out-edges for this point in the DAG
3413 
3414   Not Collective
3415 
3416   Input Parameters:
3417 + dm - The `DMPLEX`
3418 - p  - The point, which must lie in the chart set with `DMPlexSetChart()`
3419 
3420   Output Parameter:
3421 . support - An array of points which are on the out-edges for point `p`
3422 
3423   Level: beginner
3424 
3425   Fortran Notes:
3426   You must also call `DMPlexRestoreSupport()` after you finish using the returned array.
3427   `DMPlexRestoreSupport()` is not needed/available in C.
3428 
3429 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSupportSize()`, `DMPlexSetSupport()`, `DMPlexGetCone()`, `DMPlexSetChart()`
3430 @*/
3431 PetscErrorCode DMPlexGetSupport(DM dm, PetscInt p, const PetscInt *support[])
3432 {
3433   DM_Plex *mesh = (DM_Plex *)dm->data;
3434   PetscInt off;
3435 
3436   PetscFunctionBegin;
3437   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3438   PetscAssertPointer(support, 3);
3439   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3440   *support = mesh->supports ? mesh->supports + off : NULL; //NULL + 0 is UB
3441   PetscFunctionReturn(PETSC_SUCCESS);
3442 }
3443 
3444 /*@
3445   DMPlexSetSupport - Set the points on the out-edges for this point in the DAG, that is the list of points that this point covers
3446 
3447   Not Collective
3448 
3449   Input Parameters:
3450 + dm      - The `DMPLEX`
3451 . p       - The point, which must lie in the chart set with `DMPlexSetChart()`
3452 - support - An array of points which are on the out-edges for point `p`
3453 
3454   Level: beginner
3455 
3456   Note:
3457   This should be called after all calls to `DMPlexSetSupportSize()` and `DMSetUp()`.
3458 
3459 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetCone()`, `DMPlexSetConeSize()`, `DMPlexCreate()`, `DMPlexGetSupport()`, `DMPlexSetChart()`, `DMPlexSetSupportSize()`, `DMSetUp()`
3460 @*/
3461 PetscErrorCode DMPlexSetSupport(DM dm, PetscInt p, const PetscInt support[])
3462 {
3463   DM_Plex *mesh = (DM_Plex *)dm->data;
3464   PetscInt pStart, pEnd;
3465   PetscInt dof, off, c;
3466 
3467   PetscFunctionBegin;
3468   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3469   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3470   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3471   if (dof) PetscAssertPointer(support, 3);
3472   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3473   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);
3474   for (c = 0; c < dof; ++c) {
3475     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);
3476     mesh->supports[off + c] = support[c];
3477   }
3478   PetscFunctionReturn(PETSC_SUCCESS);
3479 }
3480 
3481 /*@
3482   DMPlexInsertSupport - Insert a point into the out-edges for the point p in the DAG
3483 
3484   Not Collective
3485 
3486   Input Parameters:
3487 + dm           - The `DMPLEX`
3488 . p            - The point, which must lie in the chart set with `DMPlexSetChart()`
3489 . supportPos   - The local index in the cone where the point should be put
3490 - supportPoint - The mesh point to insert
3491 
3492   Level: beginner
3493 
3494 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexGetCone()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMSetUp()`
3495 @*/
3496 PetscErrorCode DMPlexInsertSupport(DM dm, PetscInt p, PetscInt supportPos, PetscInt supportPoint)
3497 {
3498   DM_Plex *mesh = (DM_Plex *)dm->data;
3499   PetscInt pStart, pEnd;
3500   PetscInt dof, off;
3501 
3502   PetscFunctionBegin;
3503   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3504   PetscCall(PetscSectionGetChart(mesh->supportSection, &pStart, &pEnd));
3505   PetscCall(PetscSectionGetDof(mesh->supportSection, p, &dof));
3506   PetscCall(PetscSectionGetOffset(mesh->supportSection, p, &off));
3507   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);
3508   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);
3509   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);
3510   mesh->supports[off + supportPos] = supportPoint;
3511   PetscFunctionReturn(PETSC_SUCCESS);
3512 }
3513 
3514 /* Converts an orientation o in the current numbering to the previous scheme used in Plex */
3515 PetscInt DMPolytopeConvertNewOrientation_Internal(DMPolytopeType ct, PetscInt o)
3516 {
3517   switch (ct) {
3518   case DM_POLYTOPE_SEGMENT:
3519     if (o == -1) return -2;
3520     break;
3521   case DM_POLYTOPE_TRIANGLE:
3522     if (o == -3) return -1;
3523     if (o == -2) return -3;
3524     if (o == -1) return -2;
3525     break;
3526   case DM_POLYTOPE_QUADRILATERAL:
3527     if (o == -4) return -2;
3528     if (o == -3) return -1;
3529     if (o == -2) return -4;
3530     if (o == -1) return -3;
3531     break;
3532   default:
3533     return o;
3534   }
3535   return o;
3536 }
3537 
3538 /* Converts an orientation o in the previous scheme used in Plex to the current numbering */
3539 PetscInt DMPolytopeConvertOldOrientation_Internal(DMPolytopeType ct, PetscInt o)
3540 {
3541   switch (ct) {
3542   case DM_POLYTOPE_SEGMENT:
3543     if ((o == -2) || (o == 1)) return -1;
3544     if (o == -1) return 0;
3545     break;
3546   case DM_POLYTOPE_TRIANGLE:
3547     if (o == -3) return -2;
3548     if (o == -2) return -1;
3549     if (o == -1) return -3;
3550     break;
3551   case DM_POLYTOPE_QUADRILATERAL:
3552     if (o == -4) return -2;
3553     if (o == -3) return -1;
3554     if (o == -2) return -4;
3555     if (o == -1) return -3;
3556     break;
3557   default:
3558     return o;
3559   }
3560   return o;
3561 }
3562 
3563 /* Takes in a mesh whose orientations are in the previous scheme and converts them all to the current numbering */
3564 PetscErrorCode DMPlexConvertOldOrientations_Internal(DM dm)
3565 {
3566   PetscInt pStart, pEnd, p;
3567 
3568   PetscFunctionBegin;
3569   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
3570   for (p = pStart; p < pEnd; ++p) {
3571     const PetscInt *cone, *ornt;
3572     PetscInt        coneSize, c;
3573 
3574     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
3575     PetscCall(DMPlexGetCone(dm, p, &cone));
3576     PetscCall(DMPlexGetConeOrientation(dm, p, &ornt));
3577     for (c = 0; c < coneSize; ++c) {
3578       DMPolytopeType ct;
3579       const PetscInt o = ornt[c];
3580 
3581       PetscCall(DMPlexGetCellType(dm, cone[c], &ct));
3582       switch (ct) {
3583       case DM_POLYTOPE_SEGMENT:
3584         if ((o == -2) || (o == 1)) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3585         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, 0));
3586         break;
3587       case DM_POLYTOPE_TRIANGLE:
3588         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3589         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3590         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3591         break;
3592       case DM_POLYTOPE_QUADRILATERAL:
3593         if (o == -4) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -2));
3594         if (o == -3) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -1));
3595         if (o == -2) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -4));
3596         if (o == -1) PetscCall(DMPlexInsertConeOrientation(dm, p, c, -3));
3597         break;
3598       default:
3599         break;
3600       }
3601     }
3602   }
3603   PetscFunctionReturn(PETSC_SUCCESS);
3604 }
3605 
3606 static inline PetscErrorCode DMPlexGetTransitiveClosure_Hot_Private(DM dm, PetscInt p, PetscBool useCone, PetscInt *size, const PetscInt *arr[], const PetscInt *ornt[])
3607 {
3608   DM_Plex *mesh = (DM_Plex *)dm->data;
3609 
3610   PetscFunctionBeginHot;
3611   if (PetscDefined(USE_DEBUG) || mesh->tr) {
3612     if (useCone) {
3613       PetscCall(DMPlexGetConeSize(dm, p, size));
3614       PetscCall(DMPlexGetOrientedCone(dm, p, arr, ornt));
3615     } else {
3616       PetscCall(DMPlexGetSupportSize(dm, p, size));
3617       PetscCall(DMPlexGetSupport(dm, p, arr));
3618     }
3619   } else {
3620     if (useCone) {
3621       const PetscSection s   = mesh->coneSection;
3622       const PetscInt     ps  = p - s->pStart;
3623       const PetscInt     off = s->atlasOff[ps];
3624 
3625       *size = s->atlasDof[ps];
3626       *arr  = mesh->cones + off;
3627       *ornt = mesh->coneOrientations + off;
3628     } else {
3629       const PetscSection s   = mesh->supportSection;
3630       const PetscInt     ps  = p - s->pStart;
3631       const PetscInt     off = s->atlasOff[ps];
3632 
3633       *size = s->atlasDof[ps];
3634       *arr  = mesh->supports + off;
3635     }
3636   }
3637   PetscFunctionReturn(PETSC_SUCCESS);
3638 }
3639 
3640 static inline PetscErrorCode DMPlexRestoreTransitiveClosure_Hot_Private(DM dm, PetscInt p, PetscBool useCone, PetscInt *size, const PetscInt *arr[], const PetscInt *ornt[])
3641 {
3642   DM_Plex *mesh = (DM_Plex *)dm->data;
3643 
3644   PetscFunctionBeginHot;
3645   if (PetscDefined(USE_DEBUG) || mesh->tr) {
3646     if (useCone) PetscCall(DMPlexRestoreOrientedCone(dm, p, arr, ornt));
3647   }
3648   PetscFunctionReturn(PETSC_SUCCESS);
3649 }
3650 
3651 static PetscErrorCode DMPlexGetTransitiveClosure_Depth1_Private(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3652 {
3653   DMPolytopeType  ct = DM_POLYTOPE_UNKNOWN;
3654   PetscInt       *closure;
3655   const PetscInt *tmp = NULL, *tmpO = NULL;
3656   PetscInt        off = 0, tmpSize, t;
3657 
3658   PetscFunctionBeginHot;
3659   if (ornt) {
3660     PetscCall(DMPlexGetCellType(dm, p, &ct));
3661     if (ct == DM_POLYTOPE_FV_GHOST || ct == DM_POLYTOPE_INTERIOR_GHOST || ct == DM_POLYTOPE_UNKNOWN) ct = DM_POLYTOPE_UNKNOWN;
3662   }
3663   if (*points) {
3664     closure = *points;
3665   } else {
3666     PetscInt maxConeSize, maxSupportSize;
3667     PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3668     PetscCall(DMGetWorkArray(dm, 2 * (PetscMax(maxConeSize, maxSupportSize) + 1), MPIU_INT, &closure));
3669   }
3670   PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, p, useCone, &tmpSize, &tmp, &tmpO));
3671   if (ct == DM_POLYTOPE_UNKNOWN) {
3672     closure[off++] = p;
3673     closure[off++] = 0;
3674     for (t = 0; t < tmpSize; ++t) {
3675       closure[off++] = tmp[t];
3676       closure[off++] = tmpO ? tmpO[t] : 0;
3677     }
3678   } else {
3679     const PetscInt *arr = DMPolytopeTypeGetArrangment(ct, ornt);
3680 
3681     /* We assume that cells with a valid type have faces with a valid type */
3682     closure[off++] = p;
3683     closure[off++] = ornt;
3684     for (t = 0; t < tmpSize; ++t) {
3685       DMPolytopeType ft;
3686 
3687       PetscCall(DMPlexGetCellType(dm, tmp[t], &ft));
3688       closure[off++] = tmp[arr[t]];
3689       closure[off++] = tmpO ? DMPolytopeTypeComposeOrientation(ft, ornt, tmpO[t]) : 0;
3690     }
3691   }
3692   PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, p, useCone, &tmpSize, &tmp, &tmpO));
3693   if (numPoints) *numPoints = tmpSize + 1;
3694   if (points) *points = closure;
3695   PetscFunctionReturn(PETSC_SUCCESS);
3696 }
3697 
3698 /* We need a special tensor version because we want to allow duplicate points in the endcaps for hybrid cells */
3699 static PetscErrorCode DMPlexTransitiveClosure_Tensor_Internal(DM dm, PetscInt point, DMPolytopeType ct, PetscInt o, PetscBool useCone, PetscInt *numPoints, PetscInt **points)
3700 {
3701   const PetscInt *arr = DMPolytopeTypeGetArrangment(ct, o);
3702   const PetscInt *cone, *ornt;
3703   PetscInt       *pts, *closure = NULL;
3704   DMPolytopeType  ft;
3705   PetscInt        maxConeSize, maxSupportSize, coneSeries, supportSeries, maxSize;
3706   PetscInt        dim, coneSize, c, d, clSize, cl;
3707 
3708   PetscFunctionBeginHot;
3709   PetscCall(DMGetDimension(dm, &dim));
3710   PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, point, PETSC_TRUE, &coneSize, &cone, &ornt));
3711   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3712   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, dim + 1) - 1) / (maxConeSize - 1)) : dim + 1;
3713   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, dim + 1) - 1) / (maxSupportSize - 1)) : dim + 1;
3714   maxSize       = PetscMax(coneSeries, supportSeries);
3715   if (*points) {
3716     pts = *points;
3717   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &pts));
3718   c        = 0;
3719   pts[c++] = point;
3720   pts[c++] = o;
3721   PetscCall(DMPlexGetCellType(dm, cone[arr[0 * 2 + 0]], &ft));
3722   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[0 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[0 * 2 + 1], ornt[0]), useCone, &clSize, &closure));
3723   for (cl = 0; cl < clSize * 2; cl += 2) {
3724     pts[c++] = closure[cl];
3725     pts[c++] = closure[cl + 1];
3726   }
3727   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[arr[1 * 2 + 0]], DMPolytopeTypeComposeOrientation(ft, arr[1 * 2 + 1], ornt[1]), useCone, &clSize, &closure));
3728   for (cl = 0; cl < clSize * 2; cl += 2) {
3729     pts[c++] = closure[cl];
3730     pts[c++] = closure[cl + 1];
3731   }
3732   PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[0], useCone, &clSize, &closure));
3733   for (d = 2; d < coneSize; ++d) {
3734     PetscCall(DMPlexGetCellType(dm, cone[arr[d * 2 + 0]], &ft));
3735     pts[c++] = cone[arr[d * 2 + 0]];
3736     pts[c++] = DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]);
3737   }
3738   PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, point, PETSC_TRUE, &coneSize, &cone, &ornt));
3739   if (dim >= 3) {
3740     for (d = 2; d < coneSize; ++d) {
3741       const PetscInt  fpoint = cone[arr[d * 2 + 0]];
3742       const PetscInt *fcone, *fornt;
3743       PetscInt        fconeSize, fc, i;
3744 
3745       PetscCall(DMPlexGetCellType(dm, fpoint, &ft));
3746       const PetscInt *farr = DMPolytopeTypeGetArrangment(ft, DMPolytopeTypeComposeOrientation(ft, arr[d * 2 + 1], ornt[d]));
3747       PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, fpoint, PETSC_TRUE, &fconeSize, &fcone, &fornt));
3748       for (fc = 0; fc < fconeSize; ++fc) {
3749         const PetscInt cp = fcone[farr[fc * 2 + 0]];
3750         const PetscInt co = farr[fc * 2 + 1];
3751 
3752         for (i = 0; i < c; i += 2)
3753           if (pts[i] == cp) break;
3754         if (i == c) {
3755           PetscCall(DMPlexGetCellType(dm, cp, &ft));
3756           pts[c++] = cp;
3757           pts[c++] = DMPolytopeTypeComposeOrientation(ft, co, fornt[farr[fc * 2 + 0]]);
3758         }
3759       }
3760       PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, fpoint, PETSC_TRUE, &fconeSize, &fcone, &fornt));
3761     }
3762   }
3763   *numPoints = c / 2;
3764   *points    = pts;
3765   PetscFunctionReturn(PETSC_SUCCESS);
3766 }
3767 
3768 PetscErrorCode DMPlexGetTransitiveClosure_Internal(DM dm, PetscInt p, PetscInt ornt, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3769 {
3770   DMPolytopeType ct;
3771   PetscInt      *closure, *fifo;
3772   PetscInt       closureSize = 0, fifoStart = 0, fifoSize = 0;
3773   PetscInt       maxConeSize, maxSupportSize, coneSeries, supportSeries;
3774   PetscInt       depth, maxSize;
3775 
3776   PetscFunctionBeginHot;
3777   PetscCall(DMPlexGetDepth(dm, &depth));
3778   if (depth == 1) {
3779     PetscCall(DMPlexGetTransitiveClosure_Depth1_Private(dm, p, ornt, useCone, numPoints, points));
3780     PetscFunctionReturn(PETSC_SUCCESS);
3781   }
3782   PetscCall(DMPlexGetCellType(dm, p, &ct));
3783   if (ct == DM_POLYTOPE_FV_GHOST || ct == DM_POLYTOPE_INTERIOR_GHOST || ct == DM_POLYTOPE_UNKNOWN) ct = DM_POLYTOPE_UNKNOWN;
3784   if (ct == DM_POLYTOPE_SEG_PRISM_TENSOR || ct == DM_POLYTOPE_TRI_PRISM_TENSOR || ct == DM_POLYTOPE_QUAD_PRISM_TENSOR) {
3785     PetscCall(DMPlexTransitiveClosure_Tensor_Internal(dm, p, ct, ornt, useCone, numPoints, points));
3786     PetscFunctionReturn(PETSC_SUCCESS);
3787   }
3788   PetscCall(DMPlexGetMaxSizes(dm, &maxConeSize, &maxSupportSize));
3789   coneSeries    = (maxConeSize > 1) ? ((PetscPowInt(maxConeSize, depth + 1) - 1) / (maxConeSize - 1)) : depth + 1;
3790   supportSeries = (maxSupportSize > 1) ? ((PetscPowInt(maxSupportSize, depth + 1) - 1) / (maxSupportSize - 1)) : depth + 1;
3791   maxSize       = PetscMax(coneSeries, supportSeries);
3792   PetscCall(DMGetWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
3793   if (*points) {
3794     closure = *points;
3795   } else PetscCall(DMGetWorkArray(dm, 2 * maxSize, MPIU_INT, &closure));
3796   closure[closureSize++] = p;
3797   closure[closureSize++] = ornt;
3798   fifo[fifoSize++]       = p;
3799   fifo[fifoSize++]       = ornt;
3800   fifo[fifoSize++]       = ct;
3801   /* Should kick out early when depth is reached, rather than checking all vertices for empty cones */
3802   while (fifoSize - fifoStart) {
3803     const PetscInt       q    = fifo[fifoStart++];
3804     const PetscInt       o    = fifo[fifoStart++];
3805     const DMPolytopeType qt   = (DMPolytopeType)fifo[fifoStart++];
3806     const PetscInt      *qarr = DMPolytopeTypeGetArrangment(qt, o);
3807     const PetscInt      *tmp, *tmpO = NULL;
3808     PetscInt             tmpSize, t;
3809 
3810     if (PetscDefined(USE_DEBUG)) {
3811       PetscInt nO = DMPolytopeTypeGetNumArrangments(qt) / 2;
3812       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);
3813     }
3814     PetscCall(DMPlexGetTransitiveClosure_Hot_Private(dm, q, useCone, &tmpSize, &tmp, &tmpO));
3815     for (t = 0; t < tmpSize; ++t) {
3816       const PetscInt ip = useCone && qarr ? qarr[t * 2] : t;
3817       const PetscInt io = useCone && qarr ? qarr[t * 2 + 1] : 0;
3818       const PetscInt cp = tmp[ip];
3819       PetscCall(DMPlexGetCellType(dm, cp, &ct));
3820       const PetscInt co = tmpO ? DMPolytopeTypeComposeOrientation(ct, io, tmpO[ip]) : 0;
3821       PetscInt       c;
3822 
3823       /* Check for duplicate */
3824       for (c = 0; c < closureSize; c += 2) {
3825         if (closure[c] == cp) break;
3826       }
3827       if (c == closureSize) {
3828         closure[closureSize++] = cp;
3829         closure[closureSize++] = co;
3830         fifo[fifoSize++]       = cp;
3831         fifo[fifoSize++]       = co;
3832         fifo[fifoSize++]       = ct;
3833       }
3834     }
3835     PetscCall(DMPlexRestoreTransitiveClosure_Hot_Private(dm, q, useCone, &tmpSize, &tmp, &tmpO));
3836   }
3837   PetscCall(DMRestoreWorkArray(dm, 3 * maxSize, MPIU_INT, &fifo));
3838   if (numPoints) *numPoints = closureSize / 2;
3839   if (points) *points = closure;
3840   PetscFunctionReturn(PETSC_SUCCESS);
3841 }
3842 
3843 /*@C
3844   DMPlexGetTransitiveClosure - Return the points on the transitive closure of the in-edges or out-edges for this point in the DAG
3845 
3846   Not Collective
3847 
3848   Input Parameters:
3849 + dm      - The `DMPLEX`
3850 . p       - The mesh point
3851 - useCone - `PETSC_TRUE` for the closure, otherwise return the star
3852 
3853   Input/Output Parameter:
3854 . points - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...];
3855            if `NULL` on input, internal storage will be returned, otherwise the provided array is used
3856 
3857   Output Parameter:
3858 . numPoints - The number of points in the closure, so points[] is of size 2*`numPoints`
3859 
3860   Level: beginner
3861 
3862   Note:
3863   If using internal storage (points is `NULL` on input), each call overwrites the last output.
3864 
3865   Fortran Notes:
3866   The `numPoints` argument is not present in the Fortran binding since it is internal to the array.
3867 
3868 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
3869 @*/
3870 PetscErrorCode DMPlexGetTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3871 {
3872   PetscFunctionBeginHot;
3873   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3874   if (numPoints) PetscAssertPointer(numPoints, 4);
3875   if (points) PetscAssertPointer(points, 5);
3876   PetscCall(DMPlexGetTransitiveClosure_Internal(dm, p, 0, useCone, numPoints, points));
3877   PetscFunctionReturn(PETSC_SUCCESS);
3878 }
3879 
3880 /*@C
3881   DMPlexRestoreTransitiveClosure - Restore the array of points on the transitive closure of the in-edges or out-edges for this point in the DAG
3882 
3883   Not Collective
3884 
3885   Input Parameters:
3886 + dm        - The `DMPLEX`
3887 . p         - The mesh point
3888 . useCone   - `PETSC_TRUE` for the closure, otherwise return the star
3889 . numPoints - The number of points in the closure, so points[] is of size 2*`numPoints`
3890 - points    - The points and point orientations, interleaved as pairs [p0, o0, p1, o1, ...]
3891 
3892   Level: beginner
3893 
3894   Note:
3895   If not using internal storage (points is not `NULL` on input), this call is unnecessary
3896 
3897 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetTransitiveClosure()`, `DMPlexCreate()`, `DMPlexSetCone()`, `DMPlexSetChart()`, `DMPlexGetCone()`
3898 @*/
3899 PetscErrorCode DMPlexRestoreTransitiveClosure(DM dm, PetscInt p, PetscBool useCone, PetscInt *numPoints, PetscInt *points[])
3900 {
3901   PetscFunctionBeginHot;
3902   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3903   if (numPoints) *numPoints = 0;
3904   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, points));
3905   PetscFunctionReturn(PETSC_SUCCESS);
3906 }
3907 
3908 /*@
3909   DMPlexGetMaxSizes - Return the maximum number of in-edges (cone) and out-edges (support) for any point in the DAG
3910 
3911   Not Collective
3912 
3913   Input Parameter:
3914 . dm - The `DMPLEX`
3915 
3916   Output Parameters:
3917 + maxConeSize    - The maximum number of in-edges
3918 - maxSupportSize - The maximum number of out-edges
3919 
3920   Level: beginner
3921 
3922 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetConeSize()`, `DMPlexSetChart()`
3923 @*/
3924 PetscErrorCode DMPlexGetMaxSizes(DM dm, PetscInt *maxConeSize, PetscInt *maxSupportSize)
3925 {
3926   DM_Plex *mesh = (DM_Plex *)dm->data;
3927 
3928   PetscFunctionBegin;
3929   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3930   if (maxConeSize) PetscCall(PetscSectionGetMaxDof(mesh->coneSection, maxConeSize));
3931   if (maxSupportSize) PetscCall(PetscSectionGetMaxDof(mesh->supportSection, maxSupportSize));
3932   PetscFunctionReturn(PETSC_SUCCESS);
3933 }
3934 
3935 PetscErrorCode DMSetUp_Plex(DM dm)
3936 {
3937   DM_Plex *mesh = (DM_Plex *)dm->data;
3938   PetscInt size, maxSupportSize;
3939 
3940   PetscFunctionBegin;
3941   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
3942   PetscCall(PetscSectionSetUp(mesh->coneSection));
3943   PetscCall(PetscSectionGetStorageSize(mesh->coneSection, &size));
3944   PetscCall(PetscMalloc1(size, &mesh->cones));
3945   PetscCall(PetscCalloc1(size, &mesh->coneOrientations));
3946   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
3947   if (maxSupportSize) {
3948     PetscCall(PetscSectionSetUp(mesh->supportSection));
3949     PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &size));
3950     PetscCall(PetscMalloc1(size, &mesh->supports));
3951   }
3952   PetscFunctionReturn(PETSC_SUCCESS);
3953 }
3954 
3955 PetscErrorCode DMCreateSubDM_Plex(DM dm, PetscInt numFields, const PetscInt fields[], IS *is, DM *subdm)
3956 {
3957   PetscFunctionBegin;
3958   if (subdm) PetscCall(DMClone(dm, subdm));
3959   PetscCall(DMCreateSectionSubDM(dm, numFields, fields, is, subdm));
3960   if (subdm) (*subdm)->useNatural = dm->useNatural;
3961   if (dm->useNatural && dm->sfMigration) {
3962     PetscSF sfNatural;
3963 
3964     (*subdm)->sfMigration = dm->sfMigration;
3965     PetscCall(PetscObjectReference((PetscObject)dm->sfMigration));
3966     PetscCall(DMPlexCreateGlobalToNaturalSF(*subdm, NULL, (*subdm)->sfMigration, &sfNatural));
3967     (*subdm)->sfNatural = sfNatural;
3968   }
3969   PetscFunctionReturn(PETSC_SUCCESS);
3970 }
3971 
3972 PetscErrorCode DMCreateSuperDM_Plex(DM dms[], PetscInt len, IS **is, DM *superdm)
3973 {
3974   PetscInt i = 0;
3975 
3976   PetscFunctionBegin;
3977   PetscCall(DMClone(dms[0], superdm));
3978   PetscCall(DMCreateSectionSuperDM(dms, len, is, superdm));
3979   (*superdm)->useNatural = PETSC_FALSE;
3980   for (i = 0; i < len; i++) {
3981     if (dms[i]->useNatural && dms[i]->sfMigration) {
3982       PetscSF sfNatural;
3983 
3984       (*superdm)->sfMigration = dms[i]->sfMigration;
3985       PetscCall(PetscObjectReference((PetscObject)dms[i]->sfMigration));
3986       (*superdm)->useNatural = PETSC_TRUE;
3987       PetscCall(DMPlexCreateGlobalToNaturalSF(*superdm, NULL, (*superdm)->sfMigration, &sfNatural));
3988       (*superdm)->sfNatural = sfNatural;
3989       break;
3990     }
3991   }
3992   PetscFunctionReturn(PETSC_SUCCESS);
3993 }
3994 
3995 /*@
3996   DMPlexSymmetrize - Create support (out-edge) information from cone (in-edge) information
3997 
3998   Not Collective
3999 
4000   Input Parameter:
4001 . dm - The `DMPLEX`
4002 
4003   Level: beginner
4004 
4005   Note:
4006   This should be called after all calls to `DMPlexSetCone()`
4007 
4008 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSetChart()`, `DMPlexSetConeSize()`, `DMPlexSetCone()`
4009 @*/
4010 PetscErrorCode DMPlexSymmetrize(DM dm)
4011 {
4012   DM_Plex  *mesh = (DM_Plex *)dm->data;
4013   PetscInt *offsets;
4014   PetscInt  supportSize;
4015   PetscInt  pStart, pEnd, p;
4016 
4017   PetscFunctionBegin;
4018   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4019   PetscCheck(!mesh->supports, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONGSTATE, "Supports were already setup in this DMPlex");
4020   PetscCall(PetscLogEventBegin(DMPLEX_Symmetrize, dm, 0, 0, 0));
4021   /* Calculate support sizes */
4022   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4023   for (p = pStart; p < pEnd; ++p) {
4024     PetscInt dof, off, c;
4025 
4026     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
4027     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
4028     for (c = off; c < off + dof; ++c) PetscCall(PetscSectionAddDof(mesh->supportSection, mesh->cones[c], 1));
4029   }
4030   PetscCall(PetscSectionSetUp(mesh->supportSection));
4031   /* Calculate supports */
4032   PetscCall(PetscSectionGetStorageSize(mesh->supportSection, &supportSize));
4033   PetscCall(PetscMalloc1(supportSize, &mesh->supports));
4034   PetscCall(PetscCalloc1(pEnd - pStart, &offsets));
4035   for (p = pStart; p < pEnd; ++p) {
4036     PetscInt dof, off, c;
4037 
4038     PetscCall(PetscSectionGetDof(mesh->coneSection, p, &dof));
4039     PetscCall(PetscSectionGetOffset(mesh->coneSection, p, &off));
4040     for (c = off; c < off + dof; ++c) {
4041       const PetscInt q = mesh->cones[c];
4042       PetscInt       offS;
4043 
4044       PetscCall(PetscSectionGetOffset(mesh->supportSection, q, &offS));
4045 
4046       mesh->supports[offS + offsets[q]] = p;
4047       ++offsets[q];
4048     }
4049   }
4050   PetscCall(PetscFree(offsets));
4051   PetscCall(PetscLogEventEnd(DMPLEX_Symmetrize, dm, 0, 0, 0));
4052   PetscFunctionReturn(PETSC_SUCCESS);
4053 }
4054 
4055 static PetscErrorCode DMPlexCreateDepthStratum(DM dm, DMLabel label, PetscInt depth, PetscInt pStart, PetscInt pEnd)
4056 {
4057   IS stratumIS;
4058 
4059   PetscFunctionBegin;
4060   if (pStart >= pEnd) PetscFunctionReturn(PETSC_SUCCESS);
4061   if (PetscDefined(USE_DEBUG)) {
4062     PetscInt  qStart, qEnd, numLevels, level;
4063     PetscBool overlap = PETSC_FALSE;
4064     PetscCall(DMLabelGetNumValues(label, &numLevels));
4065     for (level = 0; level < numLevels; level++) {
4066       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4067       if ((pStart >= qStart && pStart < qEnd) || (pEnd > qStart && pEnd <= qEnd)) {
4068         overlap = PETSC_TRUE;
4069         break;
4070       }
4071     }
4072     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);
4073   }
4074   PetscCall(ISCreateStride(PETSC_COMM_SELF, pEnd - pStart, pStart, 1, &stratumIS));
4075   PetscCall(DMLabelSetStratumIS(label, depth, stratumIS));
4076   PetscCall(ISDestroy(&stratumIS));
4077   PetscFunctionReturn(PETSC_SUCCESS);
4078 }
4079 
4080 /*@
4081   DMPlexStratify - Computes the strata for all points in the `DMPLEX`
4082 
4083   Collective
4084 
4085   Input Parameter:
4086 . dm - The `DMPLEX`
4087 
4088   Level: beginner
4089 
4090   Notes:
4091   The strata group all points of the same grade, and this function calculates the strata. This
4092   grade can be seen as the height (or depth) of the point in the DAG.
4093 
4094   The DAG for most topologies is a graded poset (https://en.wikipedia.org/wiki/Graded_poset), and
4095   can be illustrated by a Hasse Diagram (https://en.wikipedia.org/wiki/Hasse_diagram).
4096   Concretely, `DMPlexStratify()` creates a new label named "depth" containing the depth in the DAG of each point. For cell-vertex
4097   meshes, vertices are depth 0 and cells are depth 1. For fully interpolated meshes, depth 0 for vertices, 1 for edges, and so on
4098   until cells have depth equal to the dimension of the mesh. The depth label can be accessed through `DMPlexGetDepthLabel()` or `DMPlexGetDepthStratum()`, or
4099   manually via `DMGetLabel()`.  The height is defined implicitly by height = maxDimension - depth, and can be accessed
4100   via `DMPlexGetHeightStratum()`.  For example, cells have height 0 and faces have height 1.
4101 
4102   The depth of a point is calculated by executing a breadth-first search (BFS) on the DAG. This could produce surprising results
4103   if run on a partially interpolated mesh, meaning one that had some edges and faces, but not others. For example, suppose that
4104   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
4105   to interpolate only that one (e0), so that
4106 .vb
4107   cone(c0) = {e0, v2}
4108   cone(e0) = {v0, v1}
4109 .ve
4110   If `DMPlexStratify()` is run on this mesh, it will give depths
4111 .vb
4112    depth 0 = {v0, v1, v2}
4113    depth 1 = {e0, c0}
4114 .ve
4115   where the triangle has been given depth 1, instead of 2, because it is reachable from vertex v2.
4116 
4117   `DMPlexStratify()` should be called after all calls to `DMPlexSymmetrize()`
4118 
4119 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexComputeCellTypes()`
4120 @*/
4121 PetscErrorCode DMPlexStratify(DM dm)
4122 {
4123   DM_Plex *mesh = (DM_Plex *)dm->data;
4124   DMLabel  label;
4125   PetscInt pStart, pEnd, p;
4126   PetscInt numRoots = 0, numLeaves = 0;
4127 
4128   PetscFunctionBegin;
4129   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4130   PetscCall(PetscLogEventBegin(DMPLEX_Stratify, dm, 0, 0, 0));
4131 
4132   /* Create depth label */
4133   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4134   PetscCall(DMCreateLabel(dm, "depth"));
4135   PetscCall(DMPlexGetDepthLabel(dm, &label));
4136 
4137   {
4138     /* Initialize roots and count leaves */
4139     PetscInt sMin = PETSC_MAX_INT;
4140     PetscInt sMax = PETSC_MIN_INT;
4141     PetscInt coneSize, supportSize;
4142 
4143     for (p = pStart; p < pEnd; ++p) {
4144       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4145       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
4146       if (!coneSize && supportSize) {
4147         sMin = PetscMin(p, sMin);
4148         sMax = PetscMax(p, sMax);
4149         ++numRoots;
4150       } else if (!supportSize && coneSize) {
4151         ++numLeaves;
4152       } else if (!supportSize && !coneSize) {
4153         /* Isolated points */
4154         sMin = PetscMin(p, sMin);
4155         sMax = PetscMax(p, sMax);
4156       }
4157     }
4158     PetscCall(DMPlexCreateDepthStratum(dm, label, 0, sMin, sMax + 1));
4159   }
4160 
4161   if (numRoots + numLeaves == (pEnd - pStart)) {
4162     PetscInt sMin = PETSC_MAX_INT;
4163     PetscInt sMax = PETSC_MIN_INT;
4164     PetscInt coneSize, supportSize;
4165 
4166     for (p = pStart; p < pEnd; ++p) {
4167       PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4168       PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
4169       if (!supportSize && coneSize) {
4170         sMin = PetscMin(p, sMin);
4171         sMax = PetscMax(p, sMax);
4172       }
4173     }
4174     PetscCall(DMPlexCreateDepthStratum(dm, label, 1, sMin, sMax + 1));
4175   } else {
4176     PetscInt level = 0;
4177     PetscInt qStart, qEnd, q;
4178 
4179     PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4180     while (qEnd > qStart) {
4181       PetscInt sMin = PETSC_MAX_INT;
4182       PetscInt sMax = PETSC_MIN_INT;
4183 
4184       for (q = qStart; q < qEnd; ++q) {
4185         const PetscInt *support;
4186         PetscInt        supportSize, s;
4187 
4188         PetscCall(DMPlexGetSupportSize(dm, q, &supportSize));
4189         PetscCall(DMPlexGetSupport(dm, q, &support));
4190         for (s = 0; s < supportSize; ++s) {
4191           sMin = PetscMin(support[s], sMin);
4192           sMax = PetscMax(support[s], sMax);
4193         }
4194       }
4195       PetscCall(DMLabelGetNumValues(label, &level));
4196       PetscCall(DMPlexCreateDepthStratum(dm, label, level, sMin, sMax + 1));
4197       PetscCall(DMLabelGetStratumBounds(label, level, &qStart, &qEnd));
4198     }
4199   }
4200   { /* just in case there is an empty process */
4201     PetscInt numValues, maxValues = 0, v;
4202 
4203     PetscCall(DMLabelGetNumValues(label, &numValues));
4204     PetscCall(MPIU_Allreduce(&numValues, &maxValues, 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
4205     for (v = numValues; v < maxValues; v++) PetscCall(DMLabelAddStratum(label, v));
4206   }
4207   PetscCall(PetscObjectStateGet((PetscObject)label, &mesh->depthState));
4208   PetscCall(PetscLogEventEnd(DMPLEX_Stratify, dm, 0, 0, 0));
4209   PetscFunctionReturn(PETSC_SUCCESS);
4210 }
4211 
4212 PetscErrorCode DMPlexComputeCellType_Internal(DM dm, PetscInt p, PetscInt pdepth, DMPolytopeType *pt)
4213 {
4214   DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4215   PetscInt       dim, depth, pheight, coneSize;
4216 
4217   PetscFunctionBeginHot;
4218   PetscCall(DMGetDimension(dm, &dim));
4219   PetscCall(DMPlexGetDepth(dm, &depth));
4220   PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
4221   pheight = depth - pdepth;
4222   if (depth <= 1) {
4223     switch (pdepth) {
4224     case 0:
4225       ct = DM_POLYTOPE_POINT;
4226       break;
4227     case 1:
4228       switch (coneSize) {
4229       case 2:
4230         ct = DM_POLYTOPE_SEGMENT;
4231         break;
4232       case 3:
4233         ct = DM_POLYTOPE_TRIANGLE;
4234         break;
4235       case 4:
4236         switch (dim) {
4237         case 2:
4238           ct = DM_POLYTOPE_QUADRILATERAL;
4239           break;
4240         case 3:
4241           ct = DM_POLYTOPE_TETRAHEDRON;
4242           break;
4243         default:
4244           break;
4245         }
4246         break;
4247       case 5:
4248         ct = DM_POLYTOPE_PYRAMID;
4249         break;
4250       case 6:
4251         ct = DM_POLYTOPE_TRI_PRISM_TENSOR;
4252         break;
4253       case 8:
4254         ct = DM_POLYTOPE_HEXAHEDRON;
4255         break;
4256       default:
4257         break;
4258       }
4259     }
4260   } else {
4261     if (pdepth == 0) {
4262       ct = DM_POLYTOPE_POINT;
4263     } else if (pheight == 0) {
4264       switch (dim) {
4265       case 1:
4266         switch (coneSize) {
4267         case 2:
4268           ct = DM_POLYTOPE_SEGMENT;
4269           break;
4270         default:
4271           break;
4272         }
4273         break;
4274       case 2:
4275         switch (coneSize) {
4276         case 3:
4277           ct = DM_POLYTOPE_TRIANGLE;
4278           break;
4279         case 4:
4280           ct = DM_POLYTOPE_QUADRILATERAL;
4281           break;
4282         default:
4283           break;
4284         }
4285         break;
4286       case 3:
4287         switch (coneSize) {
4288         case 4:
4289           ct = DM_POLYTOPE_TETRAHEDRON;
4290           break;
4291         case 5: {
4292           const PetscInt *cone;
4293           PetscInt        faceConeSize;
4294 
4295           PetscCall(DMPlexGetCone(dm, p, &cone));
4296           PetscCall(DMPlexGetConeSize(dm, cone[0], &faceConeSize));
4297           switch (faceConeSize) {
4298           case 3:
4299             ct = DM_POLYTOPE_TRI_PRISM_TENSOR;
4300             break;
4301           case 4:
4302             ct = DM_POLYTOPE_PYRAMID;
4303             break;
4304           }
4305         } break;
4306         case 6:
4307           ct = DM_POLYTOPE_HEXAHEDRON;
4308           break;
4309         default:
4310           break;
4311         }
4312         break;
4313       default:
4314         break;
4315       }
4316     } else if (pheight > 0) {
4317       switch (coneSize) {
4318       case 2:
4319         ct = DM_POLYTOPE_SEGMENT;
4320         break;
4321       case 3:
4322         ct = DM_POLYTOPE_TRIANGLE;
4323         break;
4324       case 4:
4325         ct = DM_POLYTOPE_QUADRILATERAL;
4326         break;
4327       default:
4328         break;
4329       }
4330     }
4331   }
4332   *pt = ct;
4333   PetscFunctionReturn(PETSC_SUCCESS);
4334 }
4335 
4336 /*@
4337   DMPlexComputeCellTypes - Infer the polytope type of every cell using its dimension and cone size.
4338 
4339   Collective
4340 
4341   Input Parameter:
4342 . dm - The `DMPLEX`
4343 
4344   Level: developer
4345 
4346   Note:
4347   This function is normally called automatically when a cell type is requested. It creates an
4348   internal `DMLabel` named "celltype" which can be directly accessed using `DMGetLabel()`. A user may disable
4349   automatic creation by creating the label manually, using `DMCreateLabel`(dm, "celltype").
4350 
4351   `DMPlexComputeCellTypes()` should be called after all calls to `DMPlexSymmetrize()` and `DMPlexStratify()`
4352 
4353 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCreate()`, `DMPlexSymmetrize()`, `DMPlexStratify()`, `DMGetLabel()`, `DMCreateLabel()`
4354 @*/
4355 PetscErrorCode DMPlexComputeCellTypes(DM dm)
4356 {
4357   DM_Plex *mesh;
4358   DMLabel  ctLabel;
4359   PetscInt pStart, pEnd, p;
4360 
4361   PetscFunctionBegin;
4362   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4363   mesh = (DM_Plex *)dm->data;
4364   PetscCall(DMCreateLabel(dm, "celltype"));
4365   PetscCall(DMPlexGetCellTypeLabel(dm, &ctLabel));
4366   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
4367   PetscCall(PetscFree(mesh->cellTypes));
4368   PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
4369   for (p = pStart; p < pEnd; ++p) {
4370     DMPolytopeType ct = DM_POLYTOPE_UNKNOWN;
4371     PetscInt       pdepth;
4372 
4373     PetscCall(DMPlexGetPointDepth(dm, p, &pdepth));
4374     PetscCall(DMPlexComputeCellType_Internal(dm, p, pdepth, &ct));
4375     PetscCheck(ct != DM_POLYTOPE_UNKNOWN, PETSC_COMM_SELF, PETSC_ERR_SUP, "Point %" PetscInt_FMT " is screwed up", p);
4376     PetscCall(DMLabelSetValue(ctLabel, p, ct));
4377     mesh->cellTypes[p - pStart].value_as_uint8 = ct;
4378   }
4379   PetscCall(PetscObjectStateGet((PetscObject)ctLabel, &mesh->celltypeState));
4380   PetscCall(PetscObjectViewFromOptions((PetscObject)ctLabel, NULL, "-dm_plex_celltypes_view"));
4381   PetscFunctionReturn(PETSC_SUCCESS);
4382 }
4383 
4384 /*@C
4385   DMPlexGetJoin - Get an array for the join of the set of points
4386 
4387   Not Collective
4388 
4389   Input Parameters:
4390 + dm        - The `DMPLEX` object
4391 . numPoints - The number of input points for the join
4392 - points    - The input points
4393 
4394   Output Parameters:
4395 + numCoveredPoints - The number of points in the join
4396 - coveredPoints    - The points in the join
4397 
4398   Level: intermediate
4399 
4400   Note:
4401   Currently, this is restricted to a single level join
4402 
4403   Fortran Notes:
4404   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4405 
4406 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4407 @*/
4408 PetscErrorCode DMPlexGetJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4409 {
4410   DM_Plex  *mesh = (DM_Plex *)dm->data;
4411   PetscInt *join[2];
4412   PetscInt  joinSize, i = 0;
4413   PetscInt  dof, off, p, c, m;
4414   PetscInt  maxSupportSize;
4415 
4416   PetscFunctionBegin;
4417   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4418   PetscAssertPointer(points, 3);
4419   PetscAssertPointer(numCoveredPoints, 4);
4420   PetscAssertPointer(coveredPoints, 5);
4421   PetscCall(PetscSectionGetMaxDof(mesh->supportSection, &maxSupportSize));
4422   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[0]));
4423   PetscCall(DMGetWorkArray(dm, maxSupportSize, MPIU_INT, &join[1]));
4424   /* Copy in support of first point */
4425   PetscCall(PetscSectionGetDof(mesh->supportSection, points[0], &dof));
4426   PetscCall(PetscSectionGetOffset(mesh->supportSection, points[0], &off));
4427   for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = mesh->supports[off + joinSize];
4428   /* Check each successive support */
4429   for (p = 1; p < numPoints; ++p) {
4430     PetscInt newJoinSize = 0;
4431 
4432     PetscCall(PetscSectionGetDof(mesh->supportSection, points[p], &dof));
4433     PetscCall(PetscSectionGetOffset(mesh->supportSection, points[p], &off));
4434     for (c = 0; c < dof; ++c) {
4435       const PetscInt point = mesh->supports[off + c];
4436 
4437       for (m = 0; m < joinSize; ++m) {
4438         if (point == join[i][m]) {
4439           join[1 - i][newJoinSize++] = point;
4440           break;
4441         }
4442       }
4443     }
4444     joinSize = newJoinSize;
4445     i        = 1 - i;
4446   }
4447   *numCoveredPoints = joinSize;
4448   *coveredPoints    = join[i];
4449   PetscCall(DMRestoreWorkArray(dm, maxSupportSize, MPIU_INT, &join[1 - i]));
4450   PetscFunctionReturn(PETSC_SUCCESS);
4451 }
4452 
4453 /*@C
4454   DMPlexRestoreJoin - Restore an array for the join of the set of points
4455 
4456   Not Collective
4457 
4458   Input Parameters:
4459 + dm        - The `DMPLEX` object
4460 . numPoints - The number of input points for the join
4461 - points    - The input points
4462 
4463   Output Parameters:
4464 + numCoveredPoints - The number of points in the join
4465 - coveredPoints    - The points in the join
4466 
4467   Level: intermediate
4468 
4469   Fortran Notes:
4470   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4471 
4472 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetJoin()`, `DMPlexGetFullJoin()`, `DMPlexGetMeet()`
4473 @*/
4474 PetscErrorCode DMPlexRestoreJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4475 {
4476   PetscFunctionBegin;
4477   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4478   if (points) PetscAssertPointer(points, 3);
4479   if (numCoveredPoints) PetscAssertPointer(numCoveredPoints, 4);
4480   PetscAssertPointer(coveredPoints, 5);
4481   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
4482   if (numCoveredPoints) *numCoveredPoints = 0;
4483   PetscFunctionReturn(PETSC_SUCCESS);
4484 }
4485 
4486 /*@C
4487   DMPlexGetFullJoin - Get an array for the join of the set of points
4488 
4489   Not Collective
4490 
4491   Input Parameters:
4492 + dm        - The `DMPLEX` object
4493 . numPoints - The number of input points for the join
4494 - points    - The input points
4495 
4496   Output Parameters:
4497 + numCoveredPoints - The number of points in the join
4498 - coveredPoints    - The points in the join
4499 
4500   Level: intermediate
4501 
4502   Fortran Notes:
4503   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4504 
4505 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetJoin()`, `DMPlexRestoreJoin()`, `DMPlexGetMeet()`
4506 @*/
4507 PetscErrorCode DMPlexGetFullJoin(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4508 {
4509   PetscInt *offsets, **closures;
4510   PetscInt *join[2];
4511   PetscInt  depth = 0, maxSize, joinSize = 0, i = 0;
4512   PetscInt  p, d, c, m, ms;
4513 
4514   PetscFunctionBegin;
4515   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4516   PetscAssertPointer(points, 3);
4517   PetscAssertPointer(numCoveredPoints, 4);
4518   PetscAssertPointer(coveredPoints, 5);
4519 
4520   PetscCall(DMPlexGetDepth(dm, &depth));
4521   PetscCall(PetscCalloc1(numPoints, &closures));
4522   PetscCall(DMGetWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
4523   PetscCall(DMPlexGetMaxSizes(dm, NULL, &ms));
4524   maxSize = (ms > 1) ? ((PetscPowInt(ms, depth + 1) - 1) / (ms - 1)) : depth + 1;
4525   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[0]));
4526   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &join[1]));
4527 
4528   for (p = 0; p < numPoints; ++p) {
4529     PetscInt closureSize;
4530 
4531     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_FALSE, &closureSize, &closures[p]));
4532 
4533     offsets[p * (depth + 2) + 0] = 0;
4534     for (d = 0; d < depth + 1; ++d) {
4535       PetscInt pStart, pEnd, i;
4536 
4537       PetscCall(DMPlexGetDepthStratum(dm, d, &pStart, &pEnd));
4538       for (i = offsets[p * (depth + 2) + d]; i < closureSize; ++i) {
4539         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
4540           offsets[p * (depth + 2) + d + 1] = i;
4541           break;
4542         }
4543       }
4544       if (i == closureSize) offsets[p * (depth + 2) + d + 1] = i;
4545     }
4546     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);
4547   }
4548   for (d = 0; d < depth + 1; ++d) {
4549     PetscInt dof;
4550 
4551     /* Copy in support of first point */
4552     dof = offsets[d + 1] - offsets[d];
4553     for (joinSize = 0; joinSize < dof; ++joinSize) join[i][joinSize] = closures[0][(offsets[d] + joinSize) * 2];
4554     /* Check each successive cone */
4555     for (p = 1; p < numPoints && joinSize; ++p) {
4556       PetscInt newJoinSize = 0;
4557 
4558       dof = offsets[p * (depth + 2) + d + 1] - offsets[p * (depth + 2) + d];
4559       for (c = 0; c < dof; ++c) {
4560         const PetscInt point = closures[p][(offsets[p * (depth + 2) + d] + c) * 2];
4561 
4562         for (m = 0; m < joinSize; ++m) {
4563           if (point == join[i][m]) {
4564             join[1 - i][newJoinSize++] = point;
4565             break;
4566           }
4567         }
4568       }
4569       joinSize = newJoinSize;
4570       i        = 1 - i;
4571     }
4572     if (joinSize) break;
4573   }
4574   *numCoveredPoints = joinSize;
4575   *coveredPoints    = join[i];
4576   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_FALSE, NULL, &closures[p]));
4577   PetscCall(PetscFree(closures));
4578   PetscCall(DMRestoreWorkArray(dm, numPoints * (depth + 2), MPIU_INT, &offsets));
4579   PetscCall(DMRestoreWorkArray(dm, ms, MPIU_INT, &join[1 - i]));
4580   PetscFunctionReturn(PETSC_SUCCESS);
4581 }
4582 
4583 /*@C
4584   DMPlexGetMeet - Get an array for the meet of the set of points
4585 
4586   Not Collective
4587 
4588   Input Parameters:
4589 + dm        - The `DMPLEX` object
4590 . numPoints - The number of input points for the meet
4591 - points    - The input points
4592 
4593   Output Parameters:
4594 + numCoveringPoints - The number of points in the meet
4595 - coveringPoints    - The points in the meet
4596 
4597   Level: intermediate
4598 
4599   Note:
4600   Currently, this is restricted to a single level meet
4601 
4602   Fortran Notes:
4603   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4604 
4605 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
4606 @*/
4607 PetscErrorCode DMPlexGetMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveringPoints, const PetscInt **coveringPoints)
4608 {
4609   DM_Plex  *mesh = (DM_Plex *)dm->data;
4610   PetscInt *meet[2];
4611   PetscInt  meetSize, i = 0;
4612   PetscInt  dof, off, p, c, m;
4613   PetscInt  maxConeSize;
4614 
4615   PetscFunctionBegin;
4616   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4617   PetscAssertPointer(points, 3);
4618   PetscAssertPointer(numCoveringPoints, 4);
4619   PetscAssertPointer(coveringPoints, 5);
4620   PetscCall(PetscSectionGetMaxDof(mesh->coneSection, &maxConeSize));
4621   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[0]));
4622   PetscCall(DMGetWorkArray(dm, maxConeSize, MPIU_INT, &meet[1]));
4623   /* Copy in cone of first point */
4624   PetscCall(PetscSectionGetDof(mesh->coneSection, points[0], &dof));
4625   PetscCall(PetscSectionGetOffset(mesh->coneSection, points[0], &off));
4626   for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = mesh->cones[off + meetSize];
4627   /* Check each successive cone */
4628   for (p = 1; p < numPoints; ++p) {
4629     PetscInt newMeetSize = 0;
4630 
4631     PetscCall(PetscSectionGetDof(mesh->coneSection, points[p], &dof));
4632     PetscCall(PetscSectionGetOffset(mesh->coneSection, points[p], &off));
4633     for (c = 0; c < dof; ++c) {
4634       const PetscInt point = mesh->cones[off + c];
4635 
4636       for (m = 0; m < meetSize; ++m) {
4637         if (point == meet[i][m]) {
4638           meet[1 - i][newMeetSize++] = point;
4639           break;
4640         }
4641       }
4642     }
4643     meetSize = newMeetSize;
4644     i        = 1 - i;
4645   }
4646   *numCoveringPoints = meetSize;
4647   *coveringPoints    = meet[i];
4648   PetscCall(DMRestoreWorkArray(dm, maxConeSize, MPIU_INT, &meet[1 - i]));
4649   PetscFunctionReturn(PETSC_SUCCESS);
4650 }
4651 
4652 /*@C
4653   DMPlexRestoreMeet - Restore an array for the meet of the set of points
4654 
4655   Not Collective
4656 
4657   Input Parameters:
4658 + dm        - The `DMPLEX` object
4659 . numPoints - The number of input points for the meet
4660 - points    - The input points
4661 
4662   Output Parameters:
4663 + numCoveredPoints - The number of points in the meet
4664 - coveredPoints    - The points in the meet
4665 
4666   Level: intermediate
4667 
4668   Fortran Notes:
4669   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4670 
4671 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetMeet()`, `DMPlexGetFullMeet()`, `DMPlexGetJoin()`
4672 @*/
4673 PetscErrorCode DMPlexRestoreMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4674 {
4675   PetscFunctionBegin;
4676   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4677   if (points) PetscAssertPointer(points, 3);
4678   if (numCoveredPoints) PetscAssertPointer(numCoveredPoints, 4);
4679   PetscAssertPointer(coveredPoints, 5);
4680   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, (void *)coveredPoints));
4681   if (numCoveredPoints) *numCoveredPoints = 0;
4682   PetscFunctionReturn(PETSC_SUCCESS);
4683 }
4684 
4685 /*@C
4686   DMPlexGetFullMeet - Get an array for the meet of the set of points
4687 
4688   Not Collective
4689 
4690   Input Parameters:
4691 + dm        - The `DMPLEX` object
4692 . numPoints - The number of input points for the meet
4693 - points    - The input points
4694 
4695   Output Parameters:
4696 + numCoveredPoints - The number of points in the meet
4697 - coveredPoints    - The points in the meet
4698 
4699   Level: intermediate
4700 
4701   Fortran Notes:
4702   The `numCoveredPoints` argument is not present in the Fortran binding since it is internal to the array.
4703 
4704 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetMeet()`, `DMPlexRestoreMeet()`, `DMPlexGetJoin()`
4705 @*/
4706 PetscErrorCode DMPlexGetFullMeet(DM dm, PetscInt numPoints, const PetscInt points[], PetscInt *numCoveredPoints, const PetscInt **coveredPoints)
4707 {
4708   PetscInt *offsets, **closures;
4709   PetscInt *meet[2];
4710   PetscInt  height = 0, maxSize, meetSize = 0, i = 0;
4711   PetscInt  p, h, c, m, mc;
4712 
4713   PetscFunctionBegin;
4714   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4715   PetscAssertPointer(points, 3);
4716   PetscAssertPointer(numCoveredPoints, 4);
4717   PetscAssertPointer(coveredPoints, 5);
4718 
4719   PetscCall(DMPlexGetDepth(dm, &height));
4720   PetscCall(PetscMalloc1(numPoints, &closures));
4721   PetscCall(DMGetWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
4722   PetscCall(DMPlexGetMaxSizes(dm, &mc, NULL));
4723   maxSize = (mc > 1) ? ((PetscPowInt(mc, height + 1) - 1) / (mc - 1)) : height + 1;
4724   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[0]));
4725   PetscCall(DMGetWorkArray(dm, maxSize, MPIU_INT, &meet[1]));
4726 
4727   for (p = 0; p < numPoints; ++p) {
4728     PetscInt closureSize;
4729 
4730     PetscCall(DMPlexGetTransitiveClosure(dm, points[p], PETSC_TRUE, &closureSize, &closures[p]));
4731 
4732     offsets[p * (height + 2) + 0] = 0;
4733     for (h = 0; h < height + 1; ++h) {
4734       PetscInt pStart, pEnd, i;
4735 
4736       PetscCall(DMPlexGetHeightStratum(dm, h, &pStart, &pEnd));
4737       for (i = offsets[p * (height + 2) + h]; i < closureSize; ++i) {
4738         if ((pStart > closures[p][i * 2]) || (pEnd <= closures[p][i * 2])) {
4739           offsets[p * (height + 2) + h + 1] = i;
4740           break;
4741         }
4742       }
4743       if (i == closureSize) offsets[p * (height + 2) + h + 1] = i;
4744     }
4745     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);
4746   }
4747   for (h = 0; h < height + 1; ++h) {
4748     PetscInt dof;
4749 
4750     /* Copy in cone of first point */
4751     dof = offsets[h + 1] - offsets[h];
4752     for (meetSize = 0; meetSize < dof; ++meetSize) meet[i][meetSize] = closures[0][(offsets[h] + meetSize) * 2];
4753     /* Check each successive cone */
4754     for (p = 1; p < numPoints && meetSize; ++p) {
4755       PetscInt newMeetSize = 0;
4756 
4757       dof = offsets[p * (height + 2) + h + 1] - offsets[p * (height + 2) + h];
4758       for (c = 0; c < dof; ++c) {
4759         const PetscInt point = closures[p][(offsets[p * (height + 2) + h] + c) * 2];
4760 
4761         for (m = 0; m < meetSize; ++m) {
4762           if (point == meet[i][m]) {
4763             meet[1 - i][newMeetSize++] = point;
4764             break;
4765           }
4766         }
4767       }
4768       meetSize = newMeetSize;
4769       i        = 1 - i;
4770     }
4771     if (meetSize) break;
4772   }
4773   *numCoveredPoints = meetSize;
4774   *coveredPoints    = meet[i];
4775   for (p = 0; p < numPoints; ++p) PetscCall(DMPlexRestoreTransitiveClosure(dm, points[p], PETSC_TRUE, NULL, &closures[p]));
4776   PetscCall(PetscFree(closures));
4777   PetscCall(DMRestoreWorkArray(dm, numPoints * (height + 2), MPIU_INT, &offsets));
4778   PetscCall(DMRestoreWorkArray(dm, mc, MPIU_INT, &meet[1 - i]));
4779   PetscFunctionReturn(PETSC_SUCCESS);
4780 }
4781 
4782 /*@C
4783   DMPlexEqual - Determine if two `DM` have the same topology
4784 
4785   Not Collective
4786 
4787   Input Parameters:
4788 + dmA - A `DMPLEX` object
4789 - dmB - A `DMPLEX` object
4790 
4791   Output Parameter:
4792 . equal - `PETSC_TRUE` if the topologies are identical
4793 
4794   Level: intermediate
4795 
4796   Note:
4797   We are not solving graph isomorphism, so we do not permute.
4798 
4799 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCone()`
4800 @*/
4801 PetscErrorCode DMPlexEqual(DM dmA, DM dmB, PetscBool *equal)
4802 {
4803   PetscInt depth, depthB, pStart, pEnd, pStartB, pEndB, p;
4804 
4805   PetscFunctionBegin;
4806   PetscValidHeaderSpecific(dmA, DM_CLASSID, 1);
4807   PetscValidHeaderSpecific(dmB, DM_CLASSID, 2);
4808   PetscAssertPointer(equal, 3);
4809 
4810   *equal = PETSC_FALSE;
4811   PetscCall(DMPlexGetDepth(dmA, &depth));
4812   PetscCall(DMPlexGetDepth(dmB, &depthB));
4813   if (depth != depthB) PetscFunctionReturn(PETSC_SUCCESS);
4814   PetscCall(DMPlexGetChart(dmA, &pStart, &pEnd));
4815   PetscCall(DMPlexGetChart(dmB, &pStartB, &pEndB));
4816   if ((pStart != pStartB) || (pEnd != pEndB)) PetscFunctionReturn(PETSC_SUCCESS);
4817   for (p = pStart; p < pEnd; ++p) {
4818     const PetscInt *cone, *coneB, *ornt, *orntB, *support, *supportB;
4819     PetscInt        coneSize, coneSizeB, c, supportSize, supportSizeB, s;
4820 
4821     PetscCall(DMPlexGetConeSize(dmA, p, &coneSize));
4822     PetscCall(DMPlexGetCone(dmA, p, &cone));
4823     PetscCall(DMPlexGetConeOrientation(dmA, p, &ornt));
4824     PetscCall(DMPlexGetConeSize(dmB, p, &coneSizeB));
4825     PetscCall(DMPlexGetCone(dmB, p, &coneB));
4826     PetscCall(DMPlexGetConeOrientation(dmB, p, &orntB));
4827     if (coneSize != coneSizeB) PetscFunctionReturn(PETSC_SUCCESS);
4828     for (c = 0; c < coneSize; ++c) {
4829       if (cone[c] != coneB[c]) PetscFunctionReturn(PETSC_SUCCESS);
4830       if (ornt[c] != orntB[c]) PetscFunctionReturn(PETSC_SUCCESS);
4831     }
4832     PetscCall(DMPlexGetSupportSize(dmA, p, &supportSize));
4833     PetscCall(DMPlexGetSupport(dmA, p, &support));
4834     PetscCall(DMPlexGetSupportSize(dmB, p, &supportSizeB));
4835     PetscCall(DMPlexGetSupport(dmB, p, &supportB));
4836     if (supportSize != supportSizeB) PetscFunctionReturn(PETSC_SUCCESS);
4837     for (s = 0; s < supportSize; ++s) {
4838       if (support[s] != supportB[s]) PetscFunctionReturn(PETSC_SUCCESS);
4839     }
4840   }
4841   *equal = PETSC_TRUE;
4842   PetscFunctionReturn(PETSC_SUCCESS);
4843 }
4844 
4845 /*@C
4846   DMPlexGetNumFaceVertices - Returns the number of vertices on a face
4847 
4848   Not Collective
4849 
4850   Input Parameters:
4851 + dm         - The `DMPLEX`
4852 . cellDim    - The cell dimension
4853 - numCorners - The number of vertices on a cell
4854 
4855   Output Parameter:
4856 . numFaceVertices - The number of vertices on a face
4857 
4858   Level: developer
4859 
4860   Note:
4861   Of course this can only work for a restricted set of symmetric shapes
4862 
4863 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCone()`
4864 @*/
4865 PetscErrorCode DMPlexGetNumFaceVertices(DM dm, PetscInt cellDim, PetscInt numCorners, PetscInt *numFaceVertices)
4866 {
4867   MPI_Comm comm;
4868 
4869   PetscFunctionBegin;
4870   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
4871   PetscAssertPointer(numFaceVertices, 4);
4872   switch (cellDim) {
4873   case 0:
4874     *numFaceVertices = 0;
4875     break;
4876   case 1:
4877     *numFaceVertices = 1;
4878     break;
4879   case 2:
4880     switch (numCorners) {
4881     case 3:                 /* triangle */
4882       *numFaceVertices = 2; /* Edge has 2 vertices */
4883       break;
4884     case 4:                 /* quadrilateral */
4885       *numFaceVertices = 2; /* Edge has 2 vertices */
4886       break;
4887     case 6:                 /* quadratic triangle, tri and quad cohesive Lagrange cells */
4888       *numFaceVertices = 3; /* Edge has 3 vertices */
4889       break;
4890     case 9:                 /* quadratic quadrilateral, quadratic quad cohesive Lagrange cells */
4891       *numFaceVertices = 3; /* Edge has 3 vertices */
4892       break;
4893     default:
4894       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
4895     }
4896     break;
4897   case 3:
4898     switch (numCorners) {
4899     case 4:                 /* tetradehdron */
4900       *numFaceVertices = 3; /* Face has 3 vertices */
4901       break;
4902     case 6:                 /* tet cohesive cells */
4903       *numFaceVertices = 4; /* Face has 4 vertices */
4904       break;
4905     case 8:                 /* hexahedron */
4906       *numFaceVertices = 4; /* Face has 4 vertices */
4907       break;
4908     case 9:                 /* tet cohesive Lagrange cells */
4909       *numFaceVertices = 6; /* Face has 6 vertices */
4910       break;
4911     case 10:                /* quadratic tetrahedron */
4912       *numFaceVertices = 6; /* Face has 6 vertices */
4913       break;
4914     case 12:                /* hex cohesive Lagrange cells */
4915       *numFaceVertices = 6; /* Face has 6 vertices */
4916       break;
4917     case 18:                /* quadratic tet cohesive Lagrange cells */
4918       *numFaceVertices = 6; /* Face has 6 vertices */
4919       break;
4920     case 27:                /* quadratic hexahedron, quadratic hex cohesive Lagrange cells */
4921       *numFaceVertices = 9; /* Face has 9 vertices */
4922       break;
4923     default:
4924       SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid number of face corners %" PetscInt_FMT " for dimension %" PetscInt_FMT, numCorners, cellDim);
4925     }
4926     break;
4927   default:
4928     SETERRQ(comm, PETSC_ERR_ARG_OUTOFRANGE, "Invalid cell dimension %" PetscInt_FMT, cellDim);
4929   }
4930   PetscFunctionReturn(PETSC_SUCCESS);
4931 }
4932 
4933 /*@
4934   DMPlexGetDepthLabel - Get the `DMLabel` recording the depth of each point
4935 
4936   Not Collective
4937 
4938   Input Parameter:
4939 . dm - The `DMPLEX` object
4940 
4941   Output Parameter:
4942 . depthLabel - The `DMLabel` recording point depth
4943 
4944   Level: developer
4945 
4946 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepth()`, `DMPlexGetHeightStratum()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`,
4947 @*/
4948 PetscErrorCode DMPlexGetDepthLabel(DM dm, DMLabel *depthLabel)
4949 {
4950   PetscFunctionBegin;
4951   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4952   PetscAssertPointer(depthLabel, 2);
4953   *depthLabel = dm->depthLabel;
4954   PetscFunctionReturn(PETSC_SUCCESS);
4955 }
4956 
4957 /*@
4958   DMPlexGetDepth - Get the depth of the DAG representing this mesh
4959 
4960   Not Collective
4961 
4962   Input Parameter:
4963 . dm - The `DMPLEX` object
4964 
4965   Output Parameter:
4966 . depth - The number of strata (breadth first levels) in the DAG
4967 
4968   Level: developer
4969 
4970   Notes:
4971   This returns maximum of point depths over all points, i.e. maximum value of the label returned by `DMPlexGetDepthLabel()`.
4972 
4973   The point depth is described more in detail in `DMPlexGetDepthStratum()`.
4974 
4975   An empty mesh gives -1.
4976 
4977 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepthLabel()`, `DMPlexGetDepthStratum()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`
4978 @*/
4979 PetscErrorCode DMPlexGetDepth(DM dm, PetscInt *depth)
4980 {
4981   DM_Plex *mesh = (DM_Plex *)dm->data;
4982   DMLabel  label;
4983   PetscInt d = 0;
4984 
4985   PetscFunctionBegin;
4986   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
4987   PetscAssertPointer(depth, 2);
4988   if (mesh->tr) {
4989     PetscCall(DMPlexTransformGetDepth(mesh->tr, depth));
4990   } else {
4991     PetscCall(DMPlexGetDepthLabel(dm, &label));
4992     if (label) PetscCall(DMLabelGetNumValues(label, &d));
4993     *depth = d - 1;
4994   }
4995   PetscFunctionReturn(PETSC_SUCCESS);
4996 }
4997 
4998 /*@
4999   DMPlexGetDepthStratum - Get the bounds [`start`, `end`) for all points at a certain depth.
5000 
5001   Not Collective
5002 
5003   Input Parameters:
5004 + dm    - The `DMPLEX` object
5005 - depth - The requested depth
5006 
5007   Output Parameters:
5008 + start - The first point at this `depth`
5009 - end   - One beyond the last point at this `depth`
5010 
5011   Level: developer
5012 
5013   Notes:
5014   Depth indexing is related to topological dimension.  Depth stratum 0 contains the lowest topological dimension points,
5015   often "vertices".  If the mesh is "interpolated" (see `DMPlexInterpolate()`), then depth stratum 1 contains the next
5016   higher dimension, e.g., "edges".
5017 
5018 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetHeightStratum()`, `DMPlexGetCellTypeStratum()`, `DMPlexGetDepth()`, `DMPlexGetDepthLabel()`, `DMPlexGetPointDepth()`, `DMPlexSymmetrize()`, `DMPlexInterpolate()`
5019 @*/
5020 PetscErrorCode DMPlexGetDepthStratum(DM dm, PetscInt depth, PetscInt *start, PetscInt *end)
5021 {
5022   DM_Plex *mesh = (DM_Plex *)dm->data;
5023   DMLabel  label;
5024   PetscInt pStart, pEnd;
5025 
5026   PetscFunctionBegin;
5027   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5028   if (start) {
5029     PetscAssertPointer(start, 3);
5030     *start = 0;
5031   }
5032   if (end) {
5033     PetscAssertPointer(end, 4);
5034     *end = 0;
5035   }
5036   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
5037   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
5038   if (depth < 0) {
5039     if (start) *start = pStart;
5040     if (end) *end = pEnd;
5041     PetscFunctionReturn(PETSC_SUCCESS);
5042   }
5043   if (mesh->tr) {
5044     PetscCall(DMPlexTransformGetDepthStratum(mesh->tr, depth, start, end));
5045   } else {
5046     PetscCall(DMPlexGetDepthLabel(dm, &label));
5047     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named depth was found");
5048     PetscCall(DMLabelGetStratumBounds(label, depth, start, end));
5049   }
5050   PetscFunctionReturn(PETSC_SUCCESS);
5051 }
5052 
5053 /*@
5054   DMPlexGetHeightStratum - Get the bounds [`start`, `end`) for all points at a certain height.
5055 
5056   Not Collective
5057 
5058   Input Parameters:
5059 + dm     - The `DMPLEX` object
5060 - height - The requested height
5061 
5062   Output Parameters:
5063 + start - The first point at this `height`
5064 - end   - One beyond the last point at this `height`
5065 
5066   Level: developer
5067 
5068   Notes:
5069   Height indexing is related to topological codimension.  Height stratum 0 contains the highest topological dimension
5070   points, often called "cells" or "elements".  If the mesh is "interpolated" (see `DMPlexInterpolate()`), then height
5071   stratum 1 contains the boundary of these "cells", often called "faces" or "facets".
5072 
5073 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetDepthStratum()`, `DMPlexGetCellTypeStratum()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
5074 @*/
5075 PetscErrorCode DMPlexGetHeightStratum(DM dm, PetscInt height, PetscInt *start, PetscInt *end)
5076 {
5077   DMLabel  label;
5078   PetscInt depth, pStart, pEnd;
5079 
5080   PetscFunctionBegin;
5081   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5082   if (start) {
5083     PetscAssertPointer(start, 3);
5084     *start = 0;
5085   }
5086   if (end) {
5087     PetscAssertPointer(end, 4);
5088     *end = 0;
5089   }
5090   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
5091   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
5092   if (height < 0) {
5093     if (start) *start = pStart;
5094     if (end) *end = pEnd;
5095     PetscFunctionReturn(PETSC_SUCCESS);
5096   }
5097   PetscCall(DMPlexGetDepthLabel(dm, &label));
5098   if (label) PetscCall(DMLabelGetNumValues(label, &depth));
5099   else PetscCall(DMGetDimension(dm, &depth));
5100   PetscCheck(depth >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Depth not yet computed");
5101   PetscCall(DMPlexGetDepthStratum(dm, depth - 1 - height, start, end));
5102   PetscFunctionReturn(PETSC_SUCCESS);
5103 }
5104 
5105 /*@
5106   DMPlexGetPointDepth - Get the `depth` of a given point
5107 
5108   Not Collective
5109 
5110   Input Parameters:
5111 + dm    - The `DMPLEX` object
5112 - point - The point
5113 
5114   Output Parameter:
5115 . depth - The depth of the `point`
5116 
5117   Level: intermediate
5118 
5119 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointHeight()`
5120 @*/
5121 PetscErrorCode DMPlexGetPointDepth(DM dm, PetscInt point, PetscInt *depth)
5122 {
5123   PetscFunctionBegin;
5124   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5125   PetscAssertPointer(depth, 3);
5126   PetscCall(DMLabelGetValue(dm->depthLabel, point, depth));
5127   PetscFunctionReturn(PETSC_SUCCESS);
5128 }
5129 
5130 /*@
5131   DMPlexGetPointHeight - Get the `height` of a given point
5132 
5133   Not Collective
5134 
5135   Input Parameters:
5136 + dm    - The `DMPLEX` object
5137 - point - The point
5138 
5139   Output Parameter:
5140 . height - The height of the `point`
5141 
5142   Level: intermediate
5143 
5144 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexGetPointDepth()`
5145 @*/
5146 PetscErrorCode DMPlexGetPointHeight(DM dm, PetscInt point, PetscInt *height)
5147 {
5148   PetscInt n, pDepth;
5149 
5150   PetscFunctionBegin;
5151   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5152   PetscAssertPointer(height, 3);
5153   PetscCall(DMLabelGetNumValues(dm->depthLabel, &n));
5154   PetscCall(DMLabelGetValue(dm->depthLabel, point, &pDepth));
5155   *height = n - 1 - pDepth; /* DAG depth is n-1 */
5156   PetscFunctionReturn(PETSC_SUCCESS);
5157 }
5158 
5159 /*@
5160   DMPlexGetCellTypeLabel - Get the `DMLabel` recording the polytope type of each cell
5161 
5162   Not Collective
5163 
5164   Input Parameter:
5165 . dm - The `DMPLEX` object
5166 
5167   Output Parameter:
5168 . celltypeLabel - The `DMLabel` recording cell polytope type
5169 
5170   Level: developer
5171 
5172   Note:
5173   This function will trigger automatica computation of cell types. This can be disabled by calling
5174   `DMCreateLabel`(dm, "celltype") beforehand.
5175 
5176 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellType()`, `DMPlexGetDepthLabel()`, `DMCreateLabel()`
5177 @*/
5178 PetscErrorCode DMPlexGetCellTypeLabel(DM dm, DMLabel *celltypeLabel)
5179 {
5180   PetscFunctionBegin;
5181   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5182   PetscAssertPointer(celltypeLabel, 2);
5183   if (!dm->celltypeLabel) PetscCall(DMPlexComputeCellTypes(dm));
5184   *celltypeLabel = dm->celltypeLabel;
5185   PetscFunctionReturn(PETSC_SUCCESS);
5186 }
5187 
5188 /*@
5189   DMPlexGetCellType - Get the polytope type of a given cell
5190 
5191   Not Collective
5192 
5193   Input Parameters:
5194 + dm   - The `DMPLEX` object
5195 - cell - The cell
5196 
5197   Output Parameter:
5198 . celltype - The polytope type of the cell
5199 
5200   Level: intermediate
5201 
5202 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPolytopeType`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`
5203 @*/
5204 PetscErrorCode DMPlexGetCellType(DM dm, PetscInt cell, DMPolytopeType *celltype)
5205 {
5206   DM_Plex *mesh = (DM_Plex *)dm->data;
5207   DMLabel  label;
5208   PetscInt ct;
5209 
5210   PetscFunctionBegin;
5211   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5212   PetscAssertPointer(celltype, 3);
5213   if (mesh->tr) {
5214     PetscCall(DMPlexTransformGetCellType(mesh->tr, cell, celltype));
5215   } else {
5216     PetscInt pStart, pEnd;
5217 
5218     PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, NULL));
5219     if (!mesh->cellTypes) { /* XXX remove? optimize? */
5220       PetscCall(PetscSectionGetChart(mesh->coneSection, NULL, &pEnd));
5221       PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
5222       PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5223       for (PetscInt p = pStart; p < pEnd; p++) {
5224         PetscCall(DMLabelGetValue(label, p, &ct));
5225         mesh->cellTypes[p - pStart].value_as_uint8 = (DMPolytopeType)ct;
5226       }
5227     }
5228     *celltype = (DMPolytopeType)mesh->cellTypes[cell - pStart].value_as_uint8;
5229     if (PetscDefined(USE_DEBUG)) {
5230       PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5231       PetscCall(DMLabelGetValue(label, cell, &ct));
5232       PetscCheck(ct >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Cell %" PetscInt_FMT " has not been assigned a cell type", cell);
5233       PetscCheck(ct == (PetscInt)*celltype, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid cellType for %" PetscInt_FMT ": %d != %" PetscInt_FMT, cell, (int)*celltype, ct);
5234     }
5235   }
5236   PetscFunctionReturn(PETSC_SUCCESS);
5237 }
5238 
5239 /*@
5240   DMPlexSetCellType - Set the polytope type of a given cell
5241 
5242   Not Collective
5243 
5244   Input Parameters:
5245 + dm       - The `DMPLEX` object
5246 . cell     - The cell
5247 - celltype - The polytope type of the cell
5248 
5249   Level: advanced
5250 
5251   Note:
5252   By default, cell types will be automatically computed using `DMPlexComputeCellTypes()` before this function
5253   is executed. This function will override the computed type. However, if automatic classification will not succeed
5254   and a user wants to manually specify all types, the classification must be disabled by calling
5255   DMCreateLabel(dm, "celltype") before getting or setting any cell types.
5256 
5257 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellTypeLabel()`, `DMPlexGetDepthLabel()`, `DMPlexGetDepth()`, `DMPlexComputeCellTypes()`, `DMCreateLabel()`
5258 @*/
5259 PetscErrorCode DMPlexSetCellType(DM dm, PetscInt cell, DMPolytopeType celltype)
5260 {
5261   DM_Plex *mesh = (DM_Plex *)dm->data;
5262   DMLabel  label;
5263   PetscInt pStart, pEnd;
5264 
5265   PetscFunctionBegin;
5266   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5267   PetscCall(PetscSectionGetChart(mesh->coneSection, &pStart, &pEnd));
5268   PetscCall(DMPlexGetCellTypeLabel(dm, &label));
5269   PetscCall(DMLabelSetValue(label, cell, celltype));
5270   if (!mesh->cellTypes) PetscCall(PetscMalloc1(pEnd - pStart, &mesh->cellTypes));
5271   mesh->cellTypes[cell - pStart].value_as_uint8 = celltype;
5272   PetscFunctionReturn(PETSC_SUCCESS);
5273 }
5274 
5275 PetscErrorCode DMCreateCoordinateDM_Plex(DM dm, DM *cdm)
5276 {
5277   PetscSection section, s;
5278   Mat          m;
5279   PetscInt     maxHeight;
5280   const char  *prefix;
5281 
5282   PetscFunctionBegin;
5283   PetscCall(DMClone(dm, cdm));
5284   PetscCall(PetscObjectGetOptionsPrefix((PetscObject)dm, &prefix));
5285   PetscCall(PetscObjectSetOptionsPrefix((PetscObject)*cdm, prefix));
5286   PetscCall(PetscObjectAppendOptionsPrefix((PetscObject)*cdm, "cdm_"));
5287   PetscCall(DMPlexGetMaxProjectionHeight(dm, &maxHeight));
5288   PetscCall(DMPlexSetMaxProjectionHeight(*cdm, maxHeight));
5289   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
5290   PetscCall(DMSetLocalSection(*cdm, section));
5291   PetscCall(PetscSectionDestroy(&section));
5292   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, &s));
5293   PetscCall(MatCreate(PETSC_COMM_SELF, &m));
5294   PetscCall(DMSetDefaultConstraints(*cdm, s, m, NULL));
5295   PetscCall(PetscSectionDestroy(&s));
5296   PetscCall(MatDestroy(&m));
5297 
5298   PetscCall(DMSetNumFields(*cdm, 1));
5299   PetscCall(DMCreateDS(*cdm));
5300   (*cdm)->cloneOpts = PETSC_TRUE;
5301   if (dm->setfromoptionscalled) PetscCall(DMSetFromOptions(*cdm));
5302   PetscFunctionReturn(PETSC_SUCCESS);
5303 }
5304 
5305 PetscErrorCode DMCreateCoordinateField_Plex(DM dm, DMField *field)
5306 {
5307   Vec coordsLocal, cellCoordsLocal;
5308   DM  coordsDM, cellCoordsDM;
5309 
5310   PetscFunctionBegin;
5311   *field = NULL;
5312   PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
5313   PetscCall(DMGetCoordinateDM(dm, &coordsDM));
5314   PetscCall(DMGetCellCoordinatesLocal(dm, &cellCoordsLocal));
5315   PetscCall(DMGetCellCoordinateDM(dm, &cellCoordsDM));
5316   if (coordsLocal && coordsDM) {
5317     if (cellCoordsLocal && cellCoordsDM) PetscCall(DMFieldCreateDSWithDG(coordsDM, cellCoordsDM, 0, coordsLocal, cellCoordsLocal, field));
5318     else PetscCall(DMFieldCreateDS(coordsDM, 0, coordsLocal, field));
5319   }
5320   PetscFunctionReturn(PETSC_SUCCESS);
5321 }
5322 
5323 /*@C
5324   DMPlexGetConeSection - Return a section which describes the layout of cone data
5325 
5326   Not Collective
5327 
5328   Input Parameter:
5329 . dm - The `DMPLEX` object
5330 
5331   Output Parameter:
5332 . section - The `PetscSection` object
5333 
5334   Level: developer
5335 
5336 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetSupportSection()`, `DMPlexGetCones()`, `DMPlexGetConeOrientations()`, `PetscSection`
5337 @*/
5338 PetscErrorCode DMPlexGetConeSection(DM dm, PetscSection *section)
5339 {
5340   DM_Plex *mesh = (DM_Plex *)dm->data;
5341 
5342   PetscFunctionBegin;
5343   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5344   if (section) *section = mesh->coneSection;
5345   PetscFunctionReturn(PETSC_SUCCESS);
5346 }
5347 
5348 /*@C
5349   DMPlexGetSupportSection - Return a section which describes the layout of support data
5350 
5351   Not Collective
5352 
5353   Input Parameter:
5354 . dm - The `DMPLEX` object
5355 
5356   Output Parameter:
5357 . section - The `PetscSection` object
5358 
5359   Level: developer
5360 
5361 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `PetscSection`
5362 @*/
5363 PetscErrorCode DMPlexGetSupportSection(DM dm, PetscSection *section)
5364 {
5365   DM_Plex *mesh = (DM_Plex *)dm->data;
5366 
5367   PetscFunctionBegin;
5368   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5369   if (section) *section = mesh->supportSection;
5370   PetscFunctionReturn(PETSC_SUCCESS);
5371 }
5372 
5373 /*@C
5374   DMPlexGetCones - Return cone data
5375 
5376   Not Collective
5377 
5378   Input Parameter:
5379 . dm - The `DMPLEX` object
5380 
5381   Output Parameter:
5382 . cones - The cone for each point
5383 
5384   Level: developer
5385 
5386 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`
5387 @*/
5388 PetscErrorCode DMPlexGetCones(DM dm, PetscInt *cones[])
5389 {
5390   DM_Plex *mesh = (DM_Plex *)dm->data;
5391 
5392   PetscFunctionBegin;
5393   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5394   if (cones) *cones = mesh->cones;
5395   PetscFunctionReturn(PETSC_SUCCESS);
5396 }
5397 
5398 /*@C
5399   DMPlexGetConeOrientations - Return cone orientation data
5400 
5401   Not Collective
5402 
5403   Input Parameter:
5404 . dm - The `DMPLEX` object
5405 
5406   Output Parameter:
5407 . coneOrientations - The array of cone orientations for all points
5408 
5409   Level: developer
5410 
5411   Notes:
5412   The `PetscSection` returned by `DMPlexGetConeSection()` partitions coneOrientations into cone orientations of particular points as returned by `DMPlexGetConeOrientation()`.
5413 
5414   The meaning of coneOrientations values is detailed in `DMPlexGetConeOrientation()`.
5415 
5416 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetConeSection()`, `DMPlexGetConeOrientation()`, `PetscSection`
5417 @*/
5418 PetscErrorCode DMPlexGetConeOrientations(DM dm, PetscInt *coneOrientations[])
5419 {
5420   DM_Plex *mesh = (DM_Plex *)dm->data;
5421 
5422   PetscFunctionBegin;
5423   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
5424   if (coneOrientations) *coneOrientations = mesh->coneOrientations;
5425   PetscFunctionReturn(PETSC_SUCCESS);
5426 }
5427 
5428 /******************************** FEM Support **********************************/
5429 
5430 /*
5431  Returns number of components and tensor degree for the field.  For interpolated meshes, line should be a point
5432  representing a line in the section.
5433 */
5434 static PetscErrorCode PetscSectionFieldGetTensorDegree_Private(PetscSection section, PetscInt field, PetscInt line, PetscBool vertexchart, PetscInt *Nc, PetscInt *k)
5435 {
5436   PetscFunctionBeginHot;
5437   PetscCall(PetscSectionGetFieldComponents(section, field, Nc));
5438   if (line < 0) {
5439     *k  = 0;
5440     *Nc = 0;
5441   } else if (vertexchart) { /* If we only have a vertex chart, we must have degree k=1 */
5442     *k = 1;
5443   } else { /* Assume the full interpolated mesh is in the chart; lines in particular */
5444     /* An order k SEM disc has k-1 dofs on an edge */
5445     PetscCall(PetscSectionGetFieldDof(section, line, field, k));
5446     *k = *k / *Nc + 1;
5447   }
5448   PetscFunctionReturn(PETSC_SUCCESS);
5449 }
5450 
5451 /*@
5452 
5453   DMPlexSetClosurePermutationTensor - Create a permutation from the default (BFS) point ordering in the closure, to a
5454   lexicographic ordering over the tensor product cell (i.e., line, quad, hex, etc.), and set this permutation in the
5455   section provided (or the section of the `DM`).
5456 
5457   Input Parameters:
5458 + dm      - The `DM`
5459 . point   - Either a cell (highest dim point) or an edge (dim 1 point), or `PETSC_DETERMINE`
5460 - section - The `PetscSection` to reorder, or `NULL` for the default section
5461 
5462   Example:
5463   A typical interpolated single-quad mesh might order points as
5464 .vb
5465   [c0, v1, v2, v3, v4, e5, e6, e7, e8]
5466 
5467   v4 -- e6 -- v3
5468   |           |
5469   e7    c0    e8
5470   |           |
5471   v1 -- e5 -- v2
5472 .ve
5473 
5474   (There is no significance to the ordering described here.)  The default section for a Q3 quad might typically assign
5475   dofs in the order of points, e.g.,
5476 .vb
5477     c0 -> [0,1,2,3]
5478     v1 -> [4]
5479     ...
5480     e5 -> [8, 9]
5481 .ve
5482 
5483   which corresponds to the dofs
5484 .vb
5485     6   10  11  7
5486     13  2   3   15
5487     12  0   1   14
5488     4   8   9   5
5489 .ve
5490 
5491   The closure in BFS ordering works through height strata (cells, edges, vertices) to produce the ordering
5492 .vb
5493   0 1 2 3 8 9 14 15 11 10 13 12 4 5 7 6
5494 .ve
5495 
5496   After calling DMPlexSetClosurePermutationTensor(), the closure will be ordered lexicographically,
5497 .vb
5498    4 8 9 5 12 0 1 14 13 2 3 15 6 10 11 7
5499 .ve
5500 
5501   Level: developer
5502 
5503   Notes:
5504   The point is used to determine the number of dofs/field on an edge. For SEM, this is related to the polynomial
5505   degree of the basis.
5506 
5507   This is required to run with libCEED.
5508 
5509 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMGetLocalSection()`, `PetscSectionSetClosurePermutation()`, `DMSetGlobalSection()`
5510 @*/
5511 PetscErrorCode DMPlexSetClosurePermutationTensor(DM dm, PetscInt point, PetscSection section)
5512 {
5513   DMLabel   label;
5514   PetscInt  dim, depth = -1, eStart = -1, Nf;
5515   PetscBool vertexchart;
5516 
5517   PetscFunctionBegin;
5518   PetscCall(DMGetDimension(dm, &dim));
5519   if (dim < 1) PetscFunctionReturn(PETSC_SUCCESS);
5520   if (point < 0) {
5521     PetscInt sStart, sEnd;
5522 
5523     PetscCall(DMPlexGetDepthStratum(dm, 1, &sStart, &sEnd));
5524     point = sEnd - sStart ? sStart : point;
5525   }
5526   PetscCall(DMPlexGetDepthLabel(dm, &label));
5527   if (point >= 0) PetscCall(DMLabelGetValue(label, point, &depth));
5528   if (!section) PetscCall(DMGetLocalSection(dm, &section));
5529   if (depth == 1) {
5530     eStart = point;
5531   } else if (depth == dim) {
5532     const PetscInt *cone;
5533 
5534     PetscCall(DMPlexGetCone(dm, point, &cone));
5535     if (dim == 2) eStart = cone[0];
5536     else if (dim == 3) {
5537       const PetscInt *cone2;
5538       PetscCall(DMPlexGetCone(dm, cone[0], &cone2));
5539       eStart = cone2[0];
5540     } 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);
5541   } 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);
5542   { /* Determine whether the chart covers all points or just vertices. */
5543     PetscInt pStart, pEnd, cStart, cEnd;
5544     PetscCall(DMPlexGetDepthStratum(dm, 0, &pStart, &pEnd));
5545     PetscCall(PetscSectionGetChart(section, &cStart, &cEnd));
5546     if (pStart == cStart && pEnd == cEnd) vertexchart = PETSC_TRUE;      /* Only vertices are in the chart */
5547     else if (cStart <= point && point < cEnd) vertexchart = PETSC_FALSE; /* Some interpolated points exist in the chart */
5548     else vertexchart = PETSC_TRUE;                                       /* Some interpolated points are not in chart; assume dofs only at cells and vertices */
5549   }
5550   PetscCall(PetscSectionGetNumFields(section, &Nf));
5551   for (PetscInt d = 1; d <= dim; d++) {
5552     PetscInt  k, f, Nc, c, i, j, size = 0, offset = 0, foffset = 0;
5553     PetscInt *perm;
5554 
5555     for (f = 0; f < Nf; ++f) {
5556       PetscCall(PetscSectionFieldGetTensorDegree_Private(section, f, eStart, vertexchart, &Nc, &k));
5557       size += PetscPowInt(k + 1, d) * Nc;
5558     }
5559     PetscCall(PetscMalloc1(size, &perm));
5560     for (f = 0; f < Nf; ++f) {
5561       switch (d) {
5562       case 1:
5563         PetscCall(PetscSectionFieldGetTensorDegree_Private(section, f, eStart, vertexchart, &Nc, &k));
5564         /*
5565          Original ordering is [ edge of length k-1; vtx0; vtx1 ]
5566          We want              [ vtx0; edge of length k-1; vtx1 ]
5567          */
5568         for (c = 0; c < Nc; c++, offset++) perm[offset] = (k - 1) * Nc + c + foffset;
5569         for (i = 0; i < k - 1; i++)
5570           for (c = 0; c < Nc; c++, offset++) perm[offset] = i * Nc + c + foffset;
5571         for (c = 0; c < Nc; c++, offset++) perm[offset] = k * Nc + c + foffset;
5572         foffset = offset;
5573         break;
5574       case 2:
5575         /* The original quad closure is oriented clockwise, {f, e_b, e_r, e_t, e_l, v_lb, v_rb, v_tr, v_tl} */
5576         PetscCall(PetscSectionFieldGetTensorDegree_Private(section, f, eStart, vertexchart, &Nc, &k));
5577         /* The SEM order is
5578 
5579          v_lb, {e_b}, v_rb,
5580          e^{(k-1)-i}_l, {f^{i*(k-1)}}, e^i_r,
5581          v_lt, reverse {e_t}, v_rt
5582          */
5583         {
5584           const PetscInt of   = 0;
5585           const PetscInt oeb  = of + PetscSqr(k - 1);
5586           const PetscInt oer  = oeb + (k - 1);
5587           const PetscInt oet  = oer + (k - 1);
5588           const PetscInt oel  = oet + (k - 1);
5589           const PetscInt ovlb = oel + (k - 1);
5590           const PetscInt ovrb = ovlb + 1;
5591           const PetscInt ovrt = ovrb + 1;
5592           const PetscInt ovlt = ovrt + 1;
5593           PetscInt       o;
5594 
5595           /* bottom */
5596           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlb * Nc + c + foffset;
5597           for (o = oeb; o < oer; ++o)
5598             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5599           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrb * Nc + c + foffset;
5600           /* middle */
5601           for (i = 0; i < k - 1; ++i) {
5602             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oel + (k - 2) - i) * Nc + c + foffset;
5603             for (o = of + (k - 1) * i; o < of + (k - 1) * (i + 1); ++o)
5604               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5605             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oer + i) * Nc + c + foffset;
5606           }
5607           /* top */
5608           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovlt * Nc + c + foffset;
5609           for (o = oel - 1; o >= oet; --o)
5610             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5611           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovrt * Nc + c + foffset;
5612           foffset = offset;
5613         }
5614         break;
5615       case 3:
5616         /* The original hex closure is
5617 
5618          {c,
5619          f_b, f_t, f_f, f_b, f_r, f_l,
5620          e_bl, e_bb, e_br, e_bf,  e_tf, e_tr, e_tb, e_tl,  e_rf, e_lf, e_lb, e_rb,
5621          v_blf, v_blb, v_brb, v_brf, v_tlf, v_trf, v_trb, v_tlb}
5622          */
5623         PetscCall(PetscSectionFieldGetTensorDegree_Private(section, f, eStart, vertexchart, &Nc, &k));
5624         /* The SEM order is
5625          Bottom Slice
5626          v_blf, {e^{(k-1)-n}_bf}, v_brf,
5627          e^{i}_bl, f^{n*(k-1)+(k-1)-i}_b, e^{(k-1)-i}_br,
5628          v_blb, {e_bb}, v_brb,
5629 
5630          Middle Slice (j)
5631          {e^{(k-1)-j}_lf}, {f^{j*(k-1)+n}_f}, e^j_rf,
5632          f^{i*(k-1)+j}_l, {c^{(j*(k-1) + i)*(k-1)+n}_t}, f^{j*(k-1)+i}_r,
5633          e^j_lb, {f^{j*(k-1)+(k-1)-n}_b}, e^{(k-1)-j}_rb,
5634 
5635          Top Slice
5636          v_tlf, {e_tf}, v_trf,
5637          e^{(k-1)-i}_tl, {f^{i*(k-1)}_t}, e^{i}_tr,
5638          v_tlb, {e^{(k-1)-n}_tb}, v_trb,
5639          */
5640         {
5641           const PetscInt oc    = 0;
5642           const PetscInt ofb   = oc + PetscSqr(k - 1) * (k - 1);
5643           const PetscInt oft   = ofb + PetscSqr(k - 1);
5644           const PetscInt off   = oft + PetscSqr(k - 1);
5645           const PetscInt ofk   = off + PetscSqr(k - 1);
5646           const PetscInt ofr   = ofk + PetscSqr(k - 1);
5647           const PetscInt ofl   = ofr + PetscSqr(k - 1);
5648           const PetscInt oebl  = ofl + PetscSqr(k - 1);
5649           const PetscInt oebb  = oebl + (k - 1);
5650           const PetscInt oebr  = oebb + (k - 1);
5651           const PetscInt oebf  = oebr + (k - 1);
5652           const PetscInt oetf  = oebf + (k - 1);
5653           const PetscInt oetr  = oetf + (k - 1);
5654           const PetscInt oetb  = oetr + (k - 1);
5655           const PetscInt oetl  = oetb + (k - 1);
5656           const PetscInt oerf  = oetl + (k - 1);
5657           const PetscInt oelf  = oerf + (k - 1);
5658           const PetscInt oelb  = oelf + (k - 1);
5659           const PetscInt oerb  = oelb + (k - 1);
5660           const PetscInt ovblf = oerb + (k - 1);
5661           const PetscInt ovblb = ovblf + 1;
5662           const PetscInt ovbrb = ovblb + 1;
5663           const PetscInt ovbrf = ovbrb + 1;
5664           const PetscInt ovtlf = ovbrf + 1;
5665           const PetscInt ovtrf = ovtlf + 1;
5666           const PetscInt ovtrb = ovtrf + 1;
5667           const PetscInt ovtlb = ovtrb + 1;
5668           PetscInt       o, n;
5669 
5670           /* Bottom Slice */
5671           /*   bottom */
5672           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblf * Nc + c + foffset;
5673           for (o = oetf - 1; o >= oebf; --o)
5674             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5675           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrf * Nc + c + foffset;
5676           /*   middle */
5677           for (i = 0; i < k - 1; ++i) {
5678             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebl + i) * Nc + c + foffset;
5679             for (n = 0; n < k - 1; ++n) {
5680               o = ofb + n * (k - 1) + i;
5681               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5682             }
5683             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oebr + (k - 2) - i) * Nc + c + foffset;
5684           }
5685           /*   top */
5686           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovblb * Nc + c + foffset;
5687           for (o = oebb; o < oebr; ++o)
5688             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5689           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovbrb * Nc + c + foffset;
5690 
5691           /* Middle Slice */
5692           for (j = 0; j < k - 1; ++j) {
5693             /*   bottom */
5694             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelf + (k - 2) - j) * Nc + c + foffset;
5695             for (o = off + j * (k - 1); o < off + (j + 1) * (k - 1); ++o)
5696               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5697             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerf + j) * Nc + c + foffset;
5698             /*   middle */
5699             for (i = 0; i < k - 1; ++i) {
5700               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofl + i * (k - 1) + j) * Nc + c + foffset;
5701               for (n = 0; n < k - 1; ++n)
5702                 for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oc + (j * (k - 1) + i) * (k - 1) + n) * Nc + c + foffset;
5703               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (ofr + j * (k - 1) + i) * Nc + c + foffset;
5704             }
5705             /*   top */
5706             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oelb + j) * Nc + c + foffset;
5707             for (o = ofk + j * (k - 1) + (k - 2); o >= ofk + j * (k - 1); --o)
5708               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5709             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oerb + (k - 2) - j) * Nc + c + foffset;
5710           }
5711 
5712           /* Top Slice */
5713           /*   bottom */
5714           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlf * Nc + c + foffset;
5715           for (o = oetf; o < oetr; ++o)
5716             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5717           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrf * Nc + c + foffset;
5718           /*   middle */
5719           for (i = 0; i < k - 1; ++i) {
5720             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetl + (k - 2) - i) * Nc + c + foffset;
5721             for (n = 0; n < k - 1; ++n)
5722               for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oft + i * (k - 1) + n) * Nc + c + foffset;
5723             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = (oetr + i) * Nc + c + foffset;
5724           }
5725           /*   top */
5726           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtlb * Nc + c + foffset;
5727           for (o = oetl - 1; o >= oetb; --o)
5728             for (c = 0; c < Nc; ++c, ++offset) perm[offset] = o * Nc + c + foffset;
5729           for (c = 0; c < Nc; ++c, ++offset) perm[offset] = ovtrb * Nc + c + foffset;
5730 
5731           foffset = offset;
5732         }
5733         break;
5734       default:
5735         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "No spectral ordering for dimension %" PetscInt_FMT, d);
5736       }
5737     }
5738     PetscCheck(offset == size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Number of permutation entries %" PetscInt_FMT " != %" PetscInt_FMT, offset, size);
5739     /* Check permutation */
5740     {
5741       PetscInt *check;
5742 
5743       PetscCall(PetscMalloc1(size, &check));
5744       for (i = 0; i < size; ++i) {
5745         check[i] = -1;
5746         PetscCheck(perm[i] >= 0 && perm[i] < size, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid permutation index p[%" PetscInt_FMT "] = %" PetscInt_FMT, i, perm[i]);
5747       }
5748       for (i = 0; i < size; ++i) check[perm[i]] = i;
5749       for (i = 0; i < size; ++i) PetscCheck(check[i] >= 0, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Missing permutation index %" PetscInt_FMT, i);
5750       PetscCall(PetscFree(check));
5751     }
5752     PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size, PETSC_OWN_POINTER, perm));
5753     if (d == dim) { // Add permutation for localized (in case this is a coordinate DM)
5754       PetscInt *loc_perm;
5755       PetscCall(PetscMalloc1(size * 2, &loc_perm));
5756       for (PetscInt i = 0; i < size; i++) {
5757         loc_perm[i]        = perm[i];
5758         loc_perm[size + i] = size + perm[i];
5759       }
5760       PetscCall(PetscSectionSetClosurePermutation_Internal(section, (PetscObject)dm, d, size * 2, PETSC_OWN_POINTER, loc_perm));
5761     }
5762   }
5763   PetscFunctionReturn(PETSC_SUCCESS);
5764 }
5765 
5766 PetscErrorCode DMPlexGetPointDualSpaceFEM(DM dm, PetscInt point, PetscInt field, PetscDualSpace *dspace)
5767 {
5768   PetscDS  prob;
5769   PetscInt depth, Nf, h;
5770   DMLabel  label;
5771 
5772   PetscFunctionBeginHot;
5773   PetscCall(DMGetDS(dm, &prob));
5774   Nf      = prob->Nf;
5775   label   = dm->depthLabel;
5776   *dspace = NULL;
5777   if (field < Nf) {
5778     PetscObject disc = prob->disc[field];
5779 
5780     if (disc->classid == PETSCFE_CLASSID) {
5781       PetscDualSpace dsp;
5782 
5783       PetscCall(PetscFEGetDualSpace((PetscFE)disc, &dsp));
5784       PetscCall(DMLabelGetNumValues(label, &depth));
5785       PetscCall(DMLabelGetValue(label, point, &h));
5786       h = depth - 1 - h;
5787       if (h) {
5788         PetscCall(PetscDualSpaceGetHeightSubspace(dsp, h, dspace));
5789       } else {
5790         *dspace = dsp;
5791       }
5792     }
5793   }
5794   PetscFunctionReturn(PETSC_SUCCESS);
5795 }
5796 
5797 static inline PetscErrorCode DMPlexVecGetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
5798 {
5799   PetscScalar       *array;
5800   const PetscScalar *vArray;
5801   const PetscInt    *cone, *coneO;
5802   PetscInt           pStart, pEnd, p, numPoints, size = 0, offset = 0;
5803 
5804   PetscFunctionBeginHot;
5805   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
5806   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
5807   PetscCall(DMPlexGetCone(dm, point, &cone));
5808   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
5809   if (!values || !*values) {
5810     if ((point >= pStart) && (point < pEnd)) {
5811       PetscInt dof;
5812 
5813       PetscCall(PetscSectionGetDof(section, point, &dof));
5814       size += dof;
5815     }
5816     for (p = 0; p < numPoints; ++p) {
5817       const PetscInt cp = cone[p];
5818       PetscInt       dof;
5819 
5820       if ((cp < pStart) || (cp >= pEnd)) continue;
5821       PetscCall(PetscSectionGetDof(section, cp, &dof));
5822       size += dof;
5823     }
5824     if (!values) {
5825       if (csize) *csize = size;
5826       PetscFunctionReturn(PETSC_SUCCESS);
5827     }
5828     PetscCall(DMGetWorkArray(dm, size, MPIU_SCALAR, &array));
5829   } else {
5830     array = *values;
5831   }
5832   size = 0;
5833   PetscCall(VecGetArrayRead(v, &vArray));
5834   if ((point >= pStart) && (point < pEnd)) {
5835     PetscInt           dof, off, d;
5836     const PetscScalar *varr;
5837 
5838     PetscCall(PetscSectionGetDof(section, point, &dof));
5839     PetscCall(PetscSectionGetOffset(section, point, &off));
5840     varr = &vArray[off];
5841     for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
5842     size += dof;
5843   }
5844   for (p = 0; p < numPoints; ++p) {
5845     const PetscInt     cp = cone[p];
5846     PetscInt           o  = coneO[p];
5847     PetscInt           dof, off, d;
5848     const PetscScalar *varr;
5849 
5850     if ((cp < pStart) || (cp >= pEnd)) continue;
5851     PetscCall(PetscSectionGetDof(section, cp, &dof));
5852     PetscCall(PetscSectionGetOffset(section, cp, &off));
5853     varr = &vArray[off];
5854     if (o >= 0) {
5855       for (d = 0; d < dof; ++d, ++offset) array[offset] = varr[d];
5856     } else {
5857       for (d = dof - 1; d >= 0; --d, ++offset) array[offset] = varr[d];
5858     }
5859     size += dof;
5860   }
5861   PetscCall(VecRestoreArrayRead(v, &vArray));
5862   if (!*values) {
5863     if (csize) *csize = size;
5864     *values = array;
5865   } else {
5866     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
5867     *csize = size;
5868   }
5869   PetscFunctionReturn(PETSC_SUCCESS);
5870 }
5871 
5872 /* Compress out points not in the section */
5873 static inline PetscErrorCode CompressPoints_Private(PetscSection section, PetscInt *numPoints, PetscInt points[])
5874 {
5875   const PetscInt np = *numPoints;
5876   PetscInt       pStart, pEnd, p, q;
5877 
5878   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
5879   for (p = 0, q = 0; p < np; ++p) {
5880     const PetscInt r = points[p * 2];
5881     if ((r >= pStart) && (r < pEnd)) {
5882       points[q * 2]     = r;
5883       points[q * 2 + 1] = points[p * 2 + 1];
5884       ++q;
5885     }
5886   }
5887   *numPoints = q;
5888   return PETSC_SUCCESS;
5889 }
5890 
5891 /* Compressed closure does not apply closure permutation */
5892 PetscErrorCode DMPlexGetCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt ornt, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
5893 {
5894   const PetscInt *cla = NULL;
5895   PetscInt        np, *pts = NULL;
5896 
5897   PetscFunctionBeginHot;
5898   PetscCall(PetscSectionGetClosureIndex(section, (PetscObject)dm, clSec, clPoints));
5899   if (!ornt && *clPoints) {
5900     PetscInt dof, off;
5901 
5902     PetscCall(PetscSectionGetDof(*clSec, point, &dof));
5903     PetscCall(PetscSectionGetOffset(*clSec, point, &off));
5904     PetscCall(ISGetIndices(*clPoints, &cla));
5905     np  = dof / 2;
5906     pts = (PetscInt *)&cla[off];
5907   } else {
5908     PetscCall(DMPlexGetTransitiveClosure_Internal(dm, point, ornt, PETSC_TRUE, &np, &pts));
5909     PetscCall(CompressPoints_Private(section, &np, pts));
5910   }
5911   *numPoints = np;
5912   *points    = pts;
5913   *clp       = cla;
5914   PetscFunctionReturn(PETSC_SUCCESS);
5915 }
5916 
5917 PetscErrorCode DMPlexRestoreCompressedClosure(DM dm, PetscSection section, PetscInt point, PetscInt *numPoints, PetscInt **points, PetscSection *clSec, IS *clPoints, const PetscInt **clp)
5918 {
5919   PetscFunctionBeginHot;
5920   if (!*clPoints) {
5921     PetscCall(DMPlexRestoreTransitiveClosure(dm, point, PETSC_TRUE, numPoints, points));
5922   } else {
5923     PetscCall(ISRestoreIndices(*clPoints, clp));
5924   }
5925   *numPoints = 0;
5926   *points    = NULL;
5927   *clSec     = NULL;
5928   *clPoints  = NULL;
5929   *clp       = NULL;
5930   PetscFunctionReturn(PETSC_SUCCESS);
5931 }
5932 
5933 static inline PetscErrorCode DMPlexVecGetClosure_Static(DM dm, PetscSection section, PetscInt numPoints, const PetscInt points[], const PetscInt clperm[], const PetscScalar vArray[], PetscInt *size, PetscScalar array[])
5934 {
5935   PetscInt            offset = 0, p;
5936   const PetscInt    **perms  = NULL;
5937   const PetscScalar **flips  = NULL;
5938 
5939   PetscFunctionBeginHot;
5940   *size = 0;
5941   PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
5942   for (p = 0; p < numPoints; p++) {
5943     const PetscInt     point = points[2 * p];
5944     const PetscInt    *perm  = perms ? perms[p] : NULL;
5945     const PetscScalar *flip  = flips ? flips[p] : NULL;
5946     PetscInt           dof, off, d;
5947     const PetscScalar *varr;
5948 
5949     PetscCall(PetscSectionGetDof(section, point, &dof));
5950     PetscCall(PetscSectionGetOffset(section, point, &off));
5951     varr = &vArray[off];
5952     if (clperm) {
5953       if (perm) {
5954         for (d = 0; d < dof; d++) array[clperm[offset + perm[d]]] = varr[d];
5955       } else {
5956         for (d = 0; d < dof; d++) array[clperm[offset + d]] = varr[d];
5957       }
5958       if (flip) {
5959         for (d = 0; d < dof; d++) array[clperm[offset + d]] *= flip[d];
5960       }
5961     } else {
5962       if (perm) {
5963         for (d = 0; d < dof; d++) array[offset + perm[d]] = varr[d];
5964       } else {
5965         for (d = 0; d < dof; d++) array[offset + d] = varr[d];
5966       }
5967       if (flip) {
5968         for (d = 0; d < dof; d++) array[offset + d] *= flip[d];
5969       }
5970     }
5971     offset += dof;
5972   }
5973   PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
5974   *size = offset;
5975   PetscFunctionReturn(PETSC_SUCCESS);
5976 }
5977 
5978 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[])
5979 {
5980   PetscInt offset = 0, f;
5981 
5982   PetscFunctionBeginHot;
5983   *size = 0;
5984   for (f = 0; f < numFields; ++f) {
5985     PetscInt            p;
5986     const PetscInt    **perms = NULL;
5987     const PetscScalar **flips = NULL;
5988 
5989     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
5990     for (p = 0; p < numPoints; p++) {
5991       const PetscInt     point = points[2 * p];
5992       PetscInt           fdof, foff, b;
5993       const PetscScalar *varr;
5994       const PetscInt    *perm = perms ? perms[p] : NULL;
5995       const PetscScalar *flip = flips ? flips[p] : NULL;
5996 
5997       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
5998       PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
5999       varr = &vArray[foff];
6000       if (clperm) {
6001         if (perm) {
6002           for (b = 0; b < fdof; b++) array[clperm[offset + perm[b]]] = varr[b];
6003         } else {
6004           for (b = 0; b < fdof; b++) array[clperm[offset + b]] = varr[b];
6005         }
6006         if (flip) {
6007           for (b = 0; b < fdof; b++) array[clperm[offset + b]] *= flip[b];
6008         }
6009       } else {
6010         if (perm) {
6011           for (b = 0; b < fdof; b++) array[offset + perm[b]] = varr[b];
6012         } else {
6013           for (b = 0; b < fdof; b++) array[offset + b] = varr[b];
6014         }
6015         if (flip) {
6016           for (b = 0; b < fdof; b++) array[offset + b] *= flip[b];
6017         }
6018       }
6019       offset += fdof;
6020     }
6021     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6022   }
6023   *size = offset;
6024   PetscFunctionReturn(PETSC_SUCCESS);
6025 }
6026 
6027 PetscErrorCode DMPlexVecGetOrientedClosure_Internal(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt ornt, PetscInt *csize, PetscScalar *values[])
6028 {
6029   PetscSection    clSection;
6030   IS              clPoints;
6031   PetscInt       *points = NULL;
6032   const PetscInt *clp, *perm;
6033   PetscInt        depth, numFields, numPoints, asize;
6034 
6035   PetscFunctionBeginHot;
6036   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6037   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6038   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6039   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6040   PetscCall(DMPlexGetDepth(dm, &depth));
6041   PetscCall(PetscSectionGetNumFields(section, &numFields));
6042   if (depth == 1 && numFields < 2) {
6043     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6044     PetscFunctionReturn(PETSC_SUCCESS);
6045   }
6046   /* Get points */
6047   PetscCall(DMPlexGetCompressedClosure(dm, section, point, ornt, &numPoints, &points, &clSection, &clPoints, &clp));
6048   /* Get sizes */
6049   asize = 0;
6050   for (PetscInt p = 0; p < numPoints * 2; p += 2) {
6051     PetscInt dof;
6052     PetscCall(PetscSectionGetDof(section, points[p], &dof));
6053     asize += dof;
6054   }
6055   if (values) {
6056     const PetscScalar *vArray;
6057     PetscInt           size;
6058 
6059     if (*values) {
6060       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);
6061     } else PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, values));
6062     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, asize, &perm));
6063     PetscCall(VecGetArrayRead(v, &vArray));
6064     /* Get values */
6065     if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, numPoints, points, numFields, perm, vArray, &size, *values));
6066     else PetscCall(DMPlexVecGetClosure_Static(dm, section, numPoints, points, perm, vArray, &size, *values));
6067     PetscCheck(asize == size, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Section size %" PetscInt_FMT " does not match Vec closure size %" PetscInt_FMT, asize, size);
6068     /* Cleanup array */
6069     PetscCall(VecRestoreArrayRead(v, &vArray));
6070   }
6071   if (csize) *csize = asize;
6072   /* Cleanup points */
6073   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6074   PetscFunctionReturn(PETSC_SUCCESS);
6075 }
6076 
6077 /*@C
6078   DMPlexVecGetClosure - Get an array of the values on the closure of 'point'
6079 
6080   Not collective
6081 
6082   Input Parameters:
6083 + dm      - The `DM`
6084 . section - The section describing the layout in `v`, or `NULL` to use the default section
6085 . v       - The local vector
6086 - point   - The point in the `DM`
6087 
6088   Input/Output Parameters:
6089 + csize  - The size of the input values array, or `NULL`; on output the number of values in the closure
6090 - values - An array to use for the values, or `NULL` to have it allocated automatically;
6091            if the user provided `NULL`, it is a borrowed array and should not be freed
6092 
6093   Level: intermediate
6094 
6095   Notes:
6096   `DMPlexVecGetClosure()`/`DMPlexVecRestoreClosure()` only allocates the values array if it set to `NULL` in the
6097   calling function. This is because `DMPlexVecGetClosure()` is typically called in the inner loop of a `Vec` or `Mat`
6098   assembly function, and a user may already have allocated storage for this operation.
6099 
6100   A typical use could be
6101 .vb
6102    values = NULL;
6103    PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
6104    for (cl = 0; cl < clSize; ++cl) {
6105      <Compute on closure>
6106    }
6107    PetscCall(DMPlexVecRestoreClosure(dm, NULL, v, p, &clSize, &values));
6108 .ve
6109   or
6110 .vb
6111    PetscMalloc1(clMaxSize, &values);
6112    for (p = pStart; p < pEnd; ++p) {
6113      clSize = clMaxSize;
6114      PetscCall(DMPlexVecGetClosure(dm, NULL, v, p, &clSize, &values));
6115      for (cl = 0; cl < clSize; ++cl) {
6116        <Compute on closure>
6117      }
6118    }
6119    PetscFree(values);
6120 .ve
6121 
6122   Fortran Notes:
6123   The `csize` argument is not present in the Fortran binding since it is internal to the array.
6124 
6125 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecRestoreClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6126 @*/
6127 PetscErrorCode DMPlexVecGetClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6128 {
6129   PetscFunctionBeginHot;
6130   PetscCall(DMPlexVecGetOrientedClosure_Internal(dm, section, v, point, 0, csize, values));
6131   PetscFunctionReturn(PETSC_SUCCESS);
6132 }
6133 
6134 PetscErrorCode DMPlexVecGetClosureAtDepth_Internal(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt depth, PetscInt *csize, PetscScalar *values[])
6135 {
6136   DMLabel            depthLabel;
6137   PetscSection       clSection;
6138   IS                 clPoints;
6139   PetscScalar       *array;
6140   const PetscScalar *vArray;
6141   PetscInt          *points = NULL;
6142   const PetscInt    *clp, *perm = NULL;
6143   PetscInt           mdepth, numFields, numPoints, Np = 0, p, clsize, size;
6144 
6145   PetscFunctionBeginHot;
6146   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6147   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6148   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6149   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6150   PetscCall(DMPlexGetDepth(dm, &mdepth));
6151   PetscCall(DMPlexGetDepthLabel(dm, &depthLabel));
6152   PetscCall(PetscSectionGetNumFields(section, &numFields));
6153   if (mdepth == 1 && numFields < 2) {
6154     PetscCall(DMPlexVecGetClosure_Depth1_Static(dm, section, v, point, csize, values));
6155     PetscFunctionReturn(PETSC_SUCCESS);
6156   }
6157   /* Get points */
6158   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
6159   for (clsize = 0, p = 0; p < Np; p++) {
6160     PetscInt dof;
6161     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
6162     clsize += dof;
6163   }
6164   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &perm));
6165   /* Filter points */
6166   for (p = 0; p < numPoints * 2; p += 2) {
6167     PetscInt dep;
6168 
6169     PetscCall(DMLabelGetValue(depthLabel, points[p], &dep));
6170     if (dep != depth) continue;
6171     points[Np * 2 + 0] = points[p];
6172     points[Np * 2 + 1] = points[p + 1];
6173     ++Np;
6174   }
6175   /* Get array */
6176   if (!values || !*values) {
6177     PetscInt asize = 0, dof;
6178 
6179     for (p = 0; p < Np * 2; p += 2) {
6180       PetscCall(PetscSectionGetDof(section, points[p], &dof));
6181       asize += dof;
6182     }
6183     if (!values) {
6184       PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6185       if (csize) *csize = asize;
6186       PetscFunctionReturn(PETSC_SUCCESS);
6187     }
6188     PetscCall(DMGetWorkArray(dm, asize, MPIU_SCALAR, &array));
6189   } else {
6190     array = *values;
6191   }
6192   PetscCall(VecGetArrayRead(v, &vArray));
6193   /* Get values */
6194   if (numFields > 0) PetscCall(DMPlexVecGetClosure_Fields_Static(dm, section, Np, points, numFields, perm, vArray, &size, array));
6195   else PetscCall(DMPlexVecGetClosure_Static(dm, section, Np, points, perm, vArray, &size, array));
6196   /* Cleanup points */
6197   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6198   /* Cleanup array */
6199   PetscCall(VecRestoreArrayRead(v, &vArray));
6200   if (!*values) {
6201     if (csize) *csize = size;
6202     *values = array;
6203   } else {
6204     PetscCheck(size <= *csize, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Size of input array %" PetscInt_FMT " < actual size %" PetscInt_FMT, *csize, size);
6205     *csize = size;
6206   }
6207   PetscFunctionReturn(PETSC_SUCCESS);
6208 }
6209 
6210 /*@C
6211   DMPlexVecRestoreClosure - Restore the array of the values on the closure of 'point'
6212 
6213   Not collective
6214 
6215   Input Parameters:
6216 + dm      - The `DM`
6217 . section - The section describing the layout in `v`, or `NULL` to use the default section
6218 . v       - The local vector
6219 . point   - The point in the `DM`
6220 . csize   - The number of values in the closure, or `NULL`
6221 - values  - The array of values, which is a borrowed array and should not be freed
6222 
6223   Level: intermediate
6224 
6225   Note:
6226   The array values are discarded and not copied back into `v`. In order to copy values back to `v`, use `DMPlexVecSetClosure()`
6227 
6228   Fortran Notes:
6229   The `csize` argument is not present in the Fortran binding since it is internal to the array.
6230 
6231 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`, `DMPlexMatSetClosure()`
6232 @*/
6233 PetscErrorCode DMPlexVecRestoreClosure(DM dm, PetscSection section, Vec v, PetscInt point, PetscInt *csize, PetscScalar *values[])
6234 {
6235   PetscInt size = 0;
6236 
6237   PetscFunctionBegin;
6238   /* Should work without recalculating size */
6239   PetscCall(DMRestoreWorkArray(dm, size, MPIU_SCALAR, (void *)values));
6240   *values = NULL;
6241   PetscFunctionReturn(PETSC_SUCCESS);
6242 }
6243 
6244 static inline void add(PetscScalar *x, PetscScalar y)
6245 {
6246   *x += y;
6247 }
6248 static inline void insert(PetscScalar *x, PetscScalar y)
6249 {
6250   *x = y;
6251 }
6252 
6253 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[])
6254 {
6255   PetscInt        cdof;  /* The number of constraints on this point */
6256   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6257   PetscScalar    *a;
6258   PetscInt        off, cind = 0, k;
6259 
6260   PetscFunctionBegin;
6261   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6262   PetscCall(PetscSectionGetOffset(section, point, &off));
6263   a = &array[off];
6264   if (!cdof || setBC) {
6265     if (clperm) {
6266       if (perm) {
6267         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6268       } else {
6269         for (k = 0; k < dof; ++k) fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6270       }
6271     } else {
6272       if (perm) {
6273         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6274       } else {
6275         for (k = 0; k < dof; ++k) fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6276       }
6277     }
6278   } else {
6279     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6280     if (clperm) {
6281       if (perm) {
6282         for (k = 0; k < dof; ++k) {
6283           if ((cind < cdof) && (k == cdofs[cind])) {
6284             ++cind;
6285             continue;
6286           }
6287           fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6288         }
6289       } else {
6290         for (k = 0; k < dof; ++k) {
6291           if ((cind < cdof) && (k == cdofs[cind])) {
6292             ++cind;
6293             continue;
6294           }
6295           fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6296         }
6297       }
6298     } else {
6299       if (perm) {
6300         for (k = 0; k < dof; ++k) {
6301           if ((cind < cdof) && (k == cdofs[cind])) {
6302             ++cind;
6303             continue;
6304           }
6305           fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6306         }
6307       } else {
6308         for (k = 0; k < dof; ++k) {
6309           if ((cind < cdof) && (k == cdofs[cind])) {
6310             ++cind;
6311             continue;
6312           }
6313           fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6314         }
6315       }
6316     }
6317   }
6318   PetscFunctionReturn(PETSC_SUCCESS);
6319 }
6320 
6321 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[])
6322 {
6323   PetscInt        cdof;  /* The number of constraints on this point */
6324   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6325   PetscScalar    *a;
6326   PetscInt        off, cind = 0, k;
6327 
6328   PetscFunctionBegin;
6329   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6330   PetscCall(PetscSectionGetOffset(section, point, &off));
6331   a = &array[off];
6332   if (cdof) {
6333     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6334     if (clperm) {
6335       if (perm) {
6336         for (k = 0; k < dof; ++k) {
6337           if ((cind < cdof) && (k == cdofs[cind])) {
6338             fuse(&a[k], values[clperm[offset + perm[k]]] * (flip ? flip[perm[k]] : 1.));
6339             cind++;
6340           }
6341         }
6342       } else {
6343         for (k = 0; k < dof; ++k) {
6344           if ((cind < cdof) && (k == cdofs[cind])) {
6345             fuse(&a[k], values[clperm[offset + k]] * (flip ? flip[k] : 1.));
6346             cind++;
6347           }
6348         }
6349       }
6350     } else {
6351       if (perm) {
6352         for (k = 0; k < dof; ++k) {
6353           if ((cind < cdof) && (k == cdofs[cind])) {
6354             fuse(&a[k], values[offset + perm[k]] * (flip ? flip[perm[k]] : 1.));
6355             cind++;
6356           }
6357         }
6358       } else {
6359         for (k = 0; k < dof; ++k) {
6360           if ((cind < cdof) && (k == cdofs[cind])) {
6361             fuse(&a[k], values[offset + k] * (flip ? flip[k] : 1.));
6362             cind++;
6363           }
6364         }
6365       }
6366     }
6367   }
6368   PetscFunctionReturn(PETSC_SUCCESS);
6369 }
6370 
6371 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[])
6372 {
6373   PetscScalar    *a;
6374   PetscInt        fdof, foff, fcdof, foffset = *offset;
6375   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6376   PetscInt        cind = 0, b;
6377 
6378   PetscFunctionBegin;
6379   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6380   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6381   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6382   a = &array[foff];
6383   if (!fcdof || setBC) {
6384     if (clperm) {
6385       if (perm) {
6386         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6387       } else {
6388         for (b = 0; b < fdof; b++) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6389       }
6390     } else {
6391       if (perm) {
6392         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6393       } else {
6394         for (b = 0; b < fdof; b++) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6395       }
6396     }
6397   } else {
6398     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6399     if (clperm) {
6400       if (perm) {
6401         for (b = 0; b < fdof; b++) {
6402           if ((cind < fcdof) && (b == fcdofs[cind])) {
6403             ++cind;
6404             continue;
6405           }
6406           fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6407         }
6408       } else {
6409         for (b = 0; b < fdof; b++) {
6410           if ((cind < fcdof) && (b == fcdofs[cind])) {
6411             ++cind;
6412             continue;
6413           }
6414           fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6415         }
6416       }
6417     } else {
6418       if (perm) {
6419         for (b = 0; b < fdof; b++) {
6420           if ((cind < fcdof) && (b == fcdofs[cind])) {
6421             ++cind;
6422             continue;
6423           }
6424           fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6425         }
6426       } else {
6427         for (b = 0; b < fdof; b++) {
6428           if ((cind < fcdof) && (b == fcdofs[cind])) {
6429             ++cind;
6430             continue;
6431           }
6432           fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6433         }
6434       }
6435     }
6436   }
6437   *offset += fdof;
6438   PetscFunctionReturn(PETSC_SUCCESS);
6439 }
6440 
6441 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[])
6442 {
6443   PetscScalar    *a;
6444   PetscInt        fdof, foff, fcdof, foffset = *offset;
6445   const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
6446   PetscInt        Nc, cind = 0, ncind = 0, b;
6447   PetscBool       ncSet, fcSet;
6448 
6449   PetscFunctionBegin;
6450   PetscCall(PetscSectionGetFieldComponents(section, f, &Nc));
6451   PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6452   PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &fcdof));
6453   PetscCall(PetscSectionGetFieldOffset(section, point, f, &foff));
6454   a = &array[foff];
6455   if (fcdof) {
6456     /* We just override fcdof and fcdofs with Ncc and comps */
6457     PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
6458     if (clperm) {
6459       if (perm) {
6460         if (comps) {
6461           for (b = 0; b < fdof; b++) {
6462             ncSet = fcSet = PETSC_FALSE;
6463             if (b % Nc == comps[ncind]) {
6464               ncind = (ncind + 1) % Ncc;
6465               ncSet = PETSC_TRUE;
6466             }
6467             if ((cind < fcdof) && (b == fcdofs[cind])) {
6468               ++cind;
6469               fcSet = PETSC_TRUE;
6470             }
6471             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6472           }
6473         } else {
6474           for (b = 0; b < fdof; b++) {
6475             if ((cind < fcdof) && (b == fcdofs[cind])) {
6476               fuse(&a[b], values[clperm[foffset + perm[b]]] * (flip ? flip[perm[b]] : 1.));
6477               ++cind;
6478             }
6479           }
6480         }
6481       } else {
6482         if (comps) {
6483           for (b = 0; b < fdof; b++) {
6484             ncSet = fcSet = PETSC_FALSE;
6485             if (b % Nc == comps[ncind]) {
6486               ncind = (ncind + 1) % Ncc;
6487               ncSet = PETSC_TRUE;
6488             }
6489             if ((cind < fcdof) && (b == fcdofs[cind])) {
6490               ++cind;
6491               fcSet = PETSC_TRUE;
6492             }
6493             if (ncSet && fcSet) fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6494           }
6495         } else {
6496           for (b = 0; b < fdof; b++) {
6497             if ((cind < fcdof) && (b == fcdofs[cind])) {
6498               fuse(&a[b], values[clperm[foffset + b]] * (flip ? flip[b] : 1.));
6499               ++cind;
6500             }
6501           }
6502         }
6503       }
6504     } else {
6505       if (perm) {
6506         if (comps) {
6507           for (b = 0; b < fdof; b++) {
6508             ncSet = fcSet = PETSC_FALSE;
6509             if (b % Nc == comps[ncind]) {
6510               ncind = (ncind + 1) % Ncc;
6511               ncSet = PETSC_TRUE;
6512             }
6513             if ((cind < fcdof) && (b == fcdofs[cind])) {
6514               ++cind;
6515               fcSet = PETSC_TRUE;
6516             }
6517             if (ncSet && fcSet) fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6518           }
6519         } else {
6520           for (b = 0; b < fdof; b++) {
6521             if ((cind < fcdof) && (b == fcdofs[cind])) {
6522               fuse(&a[b], values[foffset + perm[b]] * (flip ? flip[perm[b]] : 1.));
6523               ++cind;
6524             }
6525           }
6526         }
6527       } else {
6528         if (comps) {
6529           for (b = 0; b < fdof; b++) {
6530             ncSet = fcSet = PETSC_FALSE;
6531             if (b % Nc == comps[ncind]) {
6532               ncind = (ncind + 1) % Ncc;
6533               ncSet = PETSC_TRUE;
6534             }
6535             if ((cind < fcdof) && (b == fcdofs[cind])) {
6536               ++cind;
6537               fcSet = PETSC_TRUE;
6538             }
6539             if (ncSet && fcSet) fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6540           }
6541         } else {
6542           for (b = 0; b < fdof; b++) {
6543             if ((cind < fcdof) && (b == fcdofs[cind])) {
6544               fuse(&a[b], values[foffset + b] * (flip ? flip[b] : 1.));
6545               ++cind;
6546             }
6547           }
6548         }
6549       }
6550     }
6551   }
6552   *offset += fdof;
6553   PetscFunctionReturn(PETSC_SUCCESS);
6554 }
6555 
6556 static inline PetscErrorCode DMPlexVecSetClosure_Depth1_Static(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6557 {
6558   PetscScalar    *array;
6559   const PetscInt *cone, *coneO;
6560   PetscInt        pStart, pEnd, p, numPoints, off, dof;
6561 
6562   PetscFunctionBeginHot;
6563   PetscCall(PetscSectionGetChart(section, &pStart, &pEnd));
6564   PetscCall(DMPlexGetConeSize(dm, point, &numPoints));
6565   PetscCall(DMPlexGetCone(dm, point, &cone));
6566   PetscCall(DMPlexGetConeOrientation(dm, point, &coneO));
6567   PetscCall(VecGetArray(v, &array));
6568   for (p = 0, off = 0; p <= numPoints; ++p, off += dof) {
6569     const PetscInt cp = !p ? point : cone[p - 1];
6570     const PetscInt o  = !p ? 0 : coneO[p - 1];
6571 
6572     if ((cp < pStart) || (cp >= pEnd)) {
6573       dof = 0;
6574       continue;
6575     }
6576     PetscCall(PetscSectionGetDof(section, cp, &dof));
6577     /* ADD_VALUES */
6578     {
6579       const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6580       PetscScalar    *a;
6581       PetscInt        cdof, coff, cind = 0, k;
6582 
6583       PetscCall(PetscSectionGetConstraintDof(section, cp, &cdof));
6584       PetscCall(PetscSectionGetOffset(section, cp, &coff));
6585       a = &array[coff];
6586       if (!cdof) {
6587         if (o >= 0) {
6588           for (k = 0; k < dof; ++k) a[k] += values[off + k];
6589         } else {
6590           for (k = 0; k < dof; ++k) a[k] += values[off + dof - k - 1];
6591         }
6592       } else {
6593         PetscCall(PetscSectionGetConstraintIndices(section, cp, &cdofs));
6594         if (o >= 0) {
6595           for (k = 0; k < dof; ++k) {
6596             if ((cind < cdof) && (k == cdofs[cind])) {
6597               ++cind;
6598               continue;
6599             }
6600             a[k] += values[off + k];
6601           }
6602         } else {
6603           for (k = 0; k < dof; ++k) {
6604             if ((cind < cdof) && (k == cdofs[cind])) {
6605               ++cind;
6606               continue;
6607             }
6608             a[k] += values[off + dof - k - 1];
6609           }
6610         }
6611       }
6612     }
6613   }
6614   PetscCall(VecRestoreArray(v, &array));
6615   PetscFunctionReturn(PETSC_SUCCESS);
6616 }
6617 
6618 /*@C
6619   DMPlexVecSetClosure - Set an array of the values on the closure of `point`
6620 
6621   Not collective
6622 
6623   Input Parameters:
6624 + dm      - The `DM`
6625 . section - The section describing the layout in `v`, or `NULL` to use the default section
6626 . v       - The local vector
6627 . point   - The point in the `DM`
6628 . values  - The array of values
6629 - mode    - The insert mode. One of `INSERT_ALL_VALUES`, `ADD_ALL_VALUES`, `INSERT_VALUES`, `ADD_VALUES`, `INSERT_BC_VALUES`, and `ADD_BC_VALUES`,
6630          where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions.
6631 
6632   Level: intermediate
6633 
6634 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`
6635 @*/
6636 PetscErrorCode DMPlexVecSetClosure(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6637 {
6638   PetscSection    clSection;
6639   IS              clPoints;
6640   PetscScalar    *array;
6641   PetscInt       *points = NULL;
6642   const PetscInt *clp, *clperm = NULL;
6643   PetscInt        depth, numFields, numPoints, p, clsize;
6644 
6645   PetscFunctionBeginHot;
6646   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6647   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6648   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6649   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6650   PetscCall(DMPlexGetDepth(dm, &depth));
6651   PetscCall(PetscSectionGetNumFields(section, &numFields));
6652   if (depth == 1 && numFields < 2 && mode == ADD_VALUES) {
6653     PetscCall(DMPlexVecSetClosure_Depth1_Static(dm, section, v, point, values, mode));
6654     PetscFunctionReturn(PETSC_SUCCESS);
6655   }
6656   /* Get points */
6657   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
6658   for (clsize = 0, p = 0; p < numPoints; p++) {
6659     PetscInt dof;
6660     PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
6661     clsize += dof;
6662   }
6663   PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
6664   /* Get array */
6665   PetscCall(VecGetArray(v, &array));
6666   /* Get values */
6667   if (numFields > 0) {
6668     PetscInt offset = 0, f;
6669     for (f = 0; f < numFields; ++f) {
6670       const PetscInt    **perms = NULL;
6671       const PetscScalar **flips = NULL;
6672 
6673       PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6674       switch (mode) {
6675       case INSERT_VALUES:
6676         for (p = 0; p < numPoints; p++) {
6677           const PetscInt     point = points[2 * p];
6678           const PetscInt    *perm  = perms ? perms[p] : NULL;
6679           const PetscScalar *flip  = flips ? flips[p] : NULL;
6680           PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, clperm, values, &offset, array));
6681         }
6682         break;
6683       case INSERT_ALL_VALUES:
6684         for (p = 0; p < numPoints; p++) {
6685           const PetscInt     point = points[2 * p];
6686           const PetscInt    *perm  = perms ? perms[p] : NULL;
6687           const PetscScalar *flip  = flips ? flips[p] : NULL;
6688           PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, clperm, values, &offset, array));
6689         }
6690         break;
6691       case INSERT_BC_VALUES:
6692         for (p = 0; p < numPoints; p++) {
6693           const PetscInt     point = points[2 * p];
6694           const PetscInt    *perm  = perms ? perms[p] : NULL;
6695           const PetscScalar *flip  = flips ? flips[p] : NULL;
6696           PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, insert, clperm, values, &offset, array));
6697         }
6698         break;
6699       case ADD_VALUES:
6700         for (p = 0; p < numPoints; p++) {
6701           const PetscInt     point = points[2 * p];
6702           const PetscInt    *perm  = perms ? perms[p] : NULL;
6703           const PetscScalar *flip  = flips ? flips[p] : NULL;
6704           PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, clperm, values, &offset, array));
6705         }
6706         break;
6707       case ADD_ALL_VALUES:
6708         for (p = 0; p < numPoints; p++) {
6709           const PetscInt     point = points[2 * p];
6710           const PetscInt    *perm  = perms ? perms[p] : NULL;
6711           const PetscScalar *flip  = flips ? flips[p] : NULL;
6712           PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, clperm, values, &offset, array));
6713         }
6714         break;
6715       case ADD_BC_VALUES:
6716         for (p = 0; p < numPoints; p++) {
6717           const PetscInt     point = points[2 * p];
6718           const PetscInt    *perm  = perms ? perms[p] : NULL;
6719           const PetscScalar *flip  = flips ? flips[p] : NULL;
6720           PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, -1, NULL, add, clperm, values, &offset, array));
6721         }
6722         break;
6723       default:
6724         SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6725       }
6726       PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6727     }
6728   } else {
6729     PetscInt            dof, off;
6730     const PetscInt    **perms = NULL;
6731     const PetscScalar **flips = NULL;
6732 
6733     PetscCall(PetscSectionGetPointSyms(section, numPoints, points, &perms, &flips));
6734     switch (mode) {
6735     case INSERT_VALUES:
6736       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6737         const PetscInt     point = points[2 * p];
6738         const PetscInt    *perm  = perms ? perms[p] : NULL;
6739         const PetscScalar *flip  = flips ? flips[p] : NULL;
6740         PetscCall(PetscSectionGetDof(section, point, &dof));
6741         PetscCall(updatePoint_private(section, point, dof, insert, PETSC_FALSE, perm, flip, clperm, values, off, array));
6742       }
6743       break;
6744     case INSERT_ALL_VALUES:
6745       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6746         const PetscInt     point = points[2 * p];
6747         const PetscInt    *perm  = perms ? perms[p] : NULL;
6748         const PetscScalar *flip  = flips ? flips[p] : NULL;
6749         PetscCall(PetscSectionGetDof(section, point, &dof));
6750         PetscCall(updatePoint_private(section, point, dof, insert, PETSC_TRUE, perm, flip, clperm, values, off, array));
6751       }
6752       break;
6753     case INSERT_BC_VALUES:
6754       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6755         const PetscInt     point = points[2 * p];
6756         const PetscInt    *perm  = perms ? perms[p] : NULL;
6757         const PetscScalar *flip  = flips ? flips[p] : NULL;
6758         PetscCall(PetscSectionGetDof(section, point, &dof));
6759         PetscCall(updatePointBC_private(section, point, dof, insert, perm, flip, clperm, values, off, array));
6760       }
6761       break;
6762     case ADD_VALUES:
6763       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6764         const PetscInt     point = points[2 * p];
6765         const PetscInt    *perm  = perms ? perms[p] : NULL;
6766         const PetscScalar *flip  = flips ? flips[p] : NULL;
6767         PetscCall(PetscSectionGetDof(section, point, &dof));
6768         PetscCall(updatePoint_private(section, point, dof, add, PETSC_FALSE, perm, flip, clperm, values, off, array));
6769       }
6770       break;
6771     case ADD_ALL_VALUES:
6772       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6773         const PetscInt     point = points[2 * p];
6774         const PetscInt    *perm  = perms ? perms[p] : NULL;
6775         const PetscScalar *flip  = flips ? flips[p] : NULL;
6776         PetscCall(PetscSectionGetDof(section, point, &dof));
6777         PetscCall(updatePoint_private(section, point, dof, add, PETSC_TRUE, perm, flip, clperm, values, off, array));
6778       }
6779       break;
6780     case ADD_BC_VALUES:
6781       for (p = 0, off = 0; p < numPoints; p++, off += dof) {
6782         const PetscInt     point = points[2 * p];
6783         const PetscInt    *perm  = perms ? perms[p] : NULL;
6784         const PetscScalar *flip  = flips ? flips[p] : NULL;
6785         PetscCall(PetscSectionGetDof(section, point, &dof));
6786         PetscCall(updatePointBC_private(section, point, dof, add, perm, flip, clperm, values, off, array));
6787       }
6788       break;
6789     default:
6790       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6791     }
6792     PetscCall(PetscSectionRestorePointSyms(section, numPoints, points, &perms, &flips));
6793   }
6794   /* Cleanup points */
6795   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6796   /* Cleanup array */
6797   PetscCall(VecRestoreArray(v, &array));
6798   PetscFunctionReturn(PETSC_SUCCESS);
6799 }
6800 
6801 PetscErrorCode DMPlexVecSetStar(DM dm, PetscSection section, Vec v, PetscInt point, const PetscScalar values[], InsertMode mode)
6802 {
6803   const PetscInt *supp, *cone;
6804   PetscScalar    *a;
6805   PetscInt        dim, Ns, dof, off, n = 0;
6806 
6807   PetscFunctionBegin;
6808   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6809   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6810   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6811   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6812   if (PetscDefined(USE_DEBUG)) {
6813     PetscInt vStart, vEnd;
6814 
6815     PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
6816     PetscCheck(point >= vStart && point < vEnd, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Point %" PetscInt_FMT " must be a vertex in [%" PetscInt_FMT ", %" PetscInt_FMT "]", point, vStart, vEnd);
6817   }
6818   PetscAssertPointer(values, 5);
6819 
6820   PetscCall(DMGetDimension(dm, &dim));
6821   PetscCall(DMPlexGetSupportSize(dm, point, &Ns));
6822   PetscCall(DMPlexGetSupport(dm, point, &supp));
6823   PetscCheck(Ns == 2 * dim, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Point %" PetscInt_FMT " has support size %" PetscInt_FMT " != %" PetscInt_FMT, point, Ns, 2 * dim);
6824   PetscCall(VecGetArray(v, &a));
6825   PetscCall(PetscSectionGetDof(section, point, &dof));
6826   PetscCall(PetscSectionGetOffset(section, point, &off));
6827   for (PetscInt i = 0; i < dof; ++i) a[off + i] = values[n++];
6828   for (PetscInt d = 0; d < dim; ++d) {
6829     // Left edge
6830     PetscCall(DMPlexGetCone(dm, supp[2 * d + 0], &cone));
6831     PetscCall(PetscSectionGetDof(section, cone[0], &dof));
6832     PetscCall(PetscSectionGetOffset(section, cone[0], &off));
6833     for (PetscInt i = 0; i < dof; ++i) a[off + i] = values[n++];
6834     // Right edge
6835     PetscCall(DMPlexGetCone(dm, supp[2 * d + 1], &cone));
6836     PetscCall(PetscSectionGetDof(section, cone[1], &dof));
6837     PetscCall(PetscSectionGetOffset(section, cone[1], &off));
6838     for (PetscInt i = 0; i < dof; ++i) a[off + i] = values[n++];
6839   }
6840   PetscCall(VecRestoreArray(v, &a));
6841   PetscFunctionReturn(PETSC_SUCCESS);
6842 }
6843 
6844 /* Check whether the given point is in the label. If not, update the offset to skip this point */
6845 static inline PetscErrorCode CheckPoint_Private(DMLabel label, PetscInt labelId, PetscSection section, PetscInt point, PetscInt f, PetscInt *offset, PetscBool *contains)
6846 {
6847   PetscFunctionBegin;
6848   *contains = PETSC_TRUE;
6849   if (label) {
6850     PetscInt fdof;
6851 
6852     PetscCall(DMLabelStratumHasPoint(label, labelId, point, contains));
6853     if (!*contains) {
6854       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6855       *offset += fdof;
6856       PetscFunctionReturn(PETSC_SUCCESS);
6857     }
6858   }
6859   PetscFunctionReturn(PETSC_SUCCESS);
6860 }
6861 
6862 /* Unlike DMPlexVecSetClosure(), this uses plex-native closure permutation, not a user-specified permutation such as DMPlexSetClosurePermutationTensor(). */
6863 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)
6864 {
6865   PetscSection    clSection;
6866   IS              clPoints;
6867   PetscScalar    *array;
6868   PetscInt       *points = NULL;
6869   const PetscInt *clp;
6870   PetscInt        numFields, numPoints, p;
6871   PetscInt        offset = 0, f;
6872 
6873   PetscFunctionBeginHot;
6874   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6875   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6876   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6877   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6878   PetscCall(PetscSectionGetNumFields(section, &numFields));
6879   /* Get points */
6880   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
6881   /* Get array */
6882   PetscCall(VecGetArray(v, &array));
6883   /* Get values */
6884   for (f = 0; f < numFields; ++f) {
6885     const PetscInt    **perms = NULL;
6886     const PetscScalar **flips = NULL;
6887     PetscBool           contains;
6888 
6889     if (!fieldActive[f]) {
6890       for (p = 0; p < numPoints * 2; p += 2) {
6891         PetscInt fdof;
6892         PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
6893         offset += fdof;
6894       }
6895       continue;
6896     }
6897     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6898     switch (mode) {
6899     case INSERT_VALUES:
6900       for (p = 0; p < numPoints; p++) {
6901         const PetscInt     point = points[2 * p];
6902         const PetscInt    *perm  = perms ? perms[p] : NULL;
6903         const PetscScalar *flip  = flips ? flips[p] : NULL;
6904         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6905         if (!contains) continue;
6906         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, NULL, values, &offset, array));
6907       }
6908       break;
6909     case INSERT_ALL_VALUES:
6910       for (p = 0; p < numPoints; p++) {
6911         const PetscInt     point = points[2 * p];
6912         const PetscInt    *perm  = perms ? perms[p] : NULL;
6913         const PetscScalar *flip  = flips ? flips[p] : NULL;
6914         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6915         if (!contains) continue;
6916         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, NULL, values, &offset, array));
6917       }
6918       break;
6919     case INSERT_BC_VALUES:
6920       for (p = 0; p < numPoints; p++) {
6921         const PetscInt     point = points[2 * p];
6922         const PetscInt    *perm  = perms ? perms[p] : NULL;
6923         const PetscScalar *flip  = flips ? flips[p] : NULL;
6924         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6925         if (!contains) continue;
6926         PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, Ncc, comps, insert, NULL, values, &offset, array));
6927       }
6928       break;
6929     case ADD_VALUES:
6930       for (p = 0; p < numPoints; p++) {
6931         const PetscInt     point = points[2 * p];
6932         const PetscInt    *perm  = perms ? perms[p] : NULL;
6933         const PetscScalar *flip  = flips ? flips[p] : NULL;
6934         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6935         if (!contains) continue;
6936         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, NULL, values, &offset, array));
6937       }
6938       break;
6939     case ADD_ALL_VALUES:
6940       for (p = 0; p < numPoints; p++) {
6941         const PetscInt     point = points[2 * p];
6942         const PetscInt    *perm  = perms ? perms[p] : NULL;
6943         const PetscScalar *flip  = flips ? flips[p] : NULL;
6944         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6945         if (!contains) continue;
6946         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, NULL, values, &offset, array));
6947       }
6948       break;
6949     default:
6950       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6951     }
6952     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6953   }
6954   /* Cleanup points */
6955   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6956   /* Cleanup array */
6957   PetscCall(VecRestoreArray(v, &array));
6958   PetscFunctionReturn(PETSC_SUCCESS);
6959 }
6960 
6961 static PetscErrorCode DMPlexPrintMatSetValues(PetscViewer viewer, Mat A, PetscInt point, PetscInt numRIndices, const PetscInt rindices[], PetscInt numCIndices, const PetscInt cindices[], const PetscScalar values[])
6962 {
6963   PetscMPIInt rank;
6964   PetscInt    i, j;
6965 
6966   PetscFunctionBegin;
6967   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
6968   PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat for point %" PetscInt_FMT "\n", rank, point));
6969   for (i = 0; i < numRIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat row indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, rindices[i]));
6970   for (i = 0; i < numCIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat col indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, cindices[i]));
6971   numCIndices = numCIndices ? numCIndices : numRIndices;
6972   if (!values) PetscFunctionReturn(PETSC_SUCCESS);
6973   for (i = 0; i < numRIndices; i++) {
6974     PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]", rank));
6975     for (j = 0; j < numCIndices; j++) {
6976 #if defined(PETSC_USE_COMPLEX)
6977       PetscCall(PetscViewerASCIIPrintf(viewer, " (%g,%g)", (double)PetscRealPart(values[i * numCIndices + j]), (double)PetscImaginaryPart(values[i * numCIndices + j])));
6978 #else
6979       PetscCall(PetscViewerASCIIPrintf(viewer, " %g", (double)values[i * numCIndices + j]));
6980 #endif
6981     }
6982     PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
6983   }
6984   PetscFunctionReturn(PETSC_SUCCESS);
6985 }
6986 
6987 /*
6988   DMPlexGetIndicesPoint_Internal - Add the indices for dofs on a point to an index array
6989 
6990   Input Parameters:
6991 + section - The section for this data layout
6992 . islocal - Is the section (and thus indices being requested) local or global?
6993 . point   - The point contributing dofs with these indices
6994 . off     - The global offset of this point
6995 . loff    - The local offset of each field
6996 . setBC   - The flag determining whether to include indices of boundary values
6997 . perm    - A permutation of the dofs on this point, or NULL
6998 - indperm - A permutation of the entire indices array, or NULL
6999 
7000   Output Parameter:
7001 . indices - Indices for dofs on this point
7002 
7003   Level: developer
7004 
7005   Note: The indices could be local or global, depending on the value of 'off'.
7006 */
7007 PetscErrorCode DMPlexGetIndicesPoint_Internal(PetscSection section, PetscBool islocal, PetscInt point, PetscInt off, PetscInt *loff, PetscBool setBC, const PetscInt perm[], const PetscInt indperm[], PetscInt indices[])
7008 {
7009   PetscInt        dof;   /* The number of unknowns on this point */
7010   PetscInt        cdof;  /* The number of constraints on this point */
7011   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
7012   PetscInt        cind = 0, k;
7013 
7014   PetscFunctionBegin;
7015   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
7016   PetscCall(PetscSectionGetDof(section, point, &dof));
7017   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
7018   if (!cdof || setBC) {
7019     for (k = 0; k < dof; ++k) {
7020       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
7021       const PetscInt ind    = indperm ? indperm[preind] : preind;
7022 
7023       indices[ind] = off + k;
7024     }
7025   } else {
7026     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
7027     for (k = 0; k < dof; ++k) {
7028       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
7029       const PetscInt ind    = indperm ? indperm[preind] : preind;
7030 
7031       if ((cind < cdof) && (k == cdofs[cind])) {
7032         /* Insert check for returning constrained indices */
7033         indices[ind] = -(off + k + 1);
7034         ++cind;
7035       } else {
7036         indices[ind] = off + k - (islocal ? 0 : cind);
7037       }
7038     }
7039   }
7040   *loff += dof;
7041   PetscFunctionReturn(PETSC_SUCCESS);
7042 }
7043 
7044 /*
7045  DMPlexGetIndicesPointFields_Internal - gets section indices for a point in its canonical ordering.
7046 
7047  Input Parameters:
7048 + section - a section (global or local)
7049 - islocal - `PETSC_TRUE` if requesting local indices (i.e., section is local); `PETSC_FALSE` for global
7050 . point - point within section
7051 . off - The offset of this point in the (local or global) indexed space - should match islocal and (usually) the section
7052 . foffs - array of length numFields containing the offset in canonical point ordering (the location in indices) of each field
7053 . setBC - identify constrained (boundary condition) points via involution.
7054 . perms - perms[f][permsoff][:] is a permutation of dofs within each field
7055 . permsoff - offset
7056 - indperm - index permutation
7057 
7058  Output Parameter:
7059 . foffs - each entry is incremented by the number of (unconstrained if setBC=FALSE) dofs in that field
7060 . indices - array to hold indices (as defined by section) of each dof associated with point
7061 
7062  Notes:
7063  If section is local and setBC=true, there is no distinction between constrained and unconstrained dofs.
7064  If section is local and setBC=false, the indices for constrained points are the involution -(i+1) of their position
7065  in the local vector.
7066 
7067  If section is global and setBC=false, the indices for constrained points are negative (and their value is not
7068  significant).  It is invalid to call with a global section and setBC=true.
7069 
7070  Developer Note:
7071  The section is only used for field layout, so islocal is technically a statement about the offset (off).  At some point
7072  in the future, global sections may have fields set, in which case we could pass the global section and obtain the
7073  offset could be obtained from the section instead of passing it explicitly as we do now.
7074 
7075  Example:
7076  Suppose a point contains one field with three components, and for which the unconstrained indices are {10, 11, 12}.
7077  When the middle component is constrained, we get the array {10, -12, 12} for (islocal=TRUE, setBC=FALSE).
7078  Note that -12 is the involution of 11, so the user can involute negative indices to recover local indices.
7079  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.
7080 
7081  Level: developer
7082 */
7083 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[])
7084 {
7085   PetscInt numFields, foff, f;
7086 
7087   PetscFunctionBegin;
7088   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
7089   PetscCall(PetscSectionGetNumFields(section, &numFields));
7090   for (f = 0, foff = 0; f < numFields; ++f) {
7091     PetscInt        fdof, cfdof;
7092     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7093     PetscInt        cind = 0, b;
7094     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7095 
7096     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7097     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7098     if (!cfdof || setBC) {
7099       for (b = 0; b < fdof; ++b) {
7100         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7101         const PetscInt ind    = indperm ? indperm[preind] : preind;
7102 
7103         indices[ind] = off + foff + b;
7104       }
7105     } else {
7106       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7107       for (b = 0; b < fdof; ++b) {
7108         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7109         const PetscInt ind    = indperm ? indperm[preind] : preind;
7110 
7111         if ((cind < cfdof) && (b == fcdofs[cind])) {
7112           indices[ind] = -(off + foff + b + 1);
7113           ++cind;
7114         } else {
7115           indices[ind] = off + foff + b - (islocal ? 0 : cind);
7116         }
7117       }
7118     }
7119     foff += (setBC || islocal ? fdof : (fdof - cfdof));
7120     foffs[f] += fdof;
7121   }
7122   PetscFunctionReturn(PETSC_SUCCESS);
7123 }
7124 
7125 /*
7126   This version believes the globalSection offsets for each field, rather than just the point offset
7127 
7128  . foffs - The offset into 'indices' for each field, since it is segregated by field
7129 
7130  Notes:
7131  The semantics of this function relate to that of setBC=FALSE in DMPlexGetIndicesPointFields_Internal.
7132  Since this function uses global indices, setBC=TRUE would be invalid, so no such argument exists.
7133 */
7134 static PetscErrorCode DMPlexGetIndicesPointFieldsSplit_Internal(PetscSection section, PetscSection globalSection, PetscInt point, PetscInt foffs[], const PetscInt ***perms, PetscInt permsoff, const PetscInt indperm[], PetscInt indices[])
7135 {
7136   PetscInt numFields, foff, f;
7137 
7138   PetscFunctionBegin;
7139   PetscCall(PetscSectionGetNumFields(section, &numFields));
7140   for (f = 0; f < numFields; ++f) {
7141     PetscInt        fdof, cfdof;
7142     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7143     PetscInt        cind = 0, b;
7144     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7145 
7146     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7147     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7148     PetscCall(PetscSectionGetFieldOffset(globalSection, point, f, &foff));
7149     if (!cfdof) {
7150       for (b = 0; b < fdof; ++b) {
7151         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7152         const PetscInt ind    = indperm ? indperm[preind] : preind;
7153 
7154         indices[ind] = foff + b;
7155       }
7156     } else {
7157       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7158       for (b = 0; b < fdof; ++b) {
7159         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7160         const PetscInt ind    = indperm ? indperm[preind] : preind;
7161 
7162         if ((cind < cfdof) && (b == fcdofs[cind])) {
7163           indices[ind] = -(foff + b + 1);
7164           ++cind;
7165         } else {
7166           indices[ind] = foff + b - cind;
7167         }
7168       }
7169     }
7170     foffs[f] += fdof;
7171   }
7172   PetscFunctionReturn(PETSC_SUCCESS);
7173 }
7174 
7175 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)
7176 {
7177   Mat             cMat;
7178   PetscSection    aSec, cSec;
7179   IS              aIS;
7180   PetscInt        aStart = -1, aEnd = -1;
7181   const PetscInt *anchors;
7182   PetscInt        numFields, f, p, q, newP = 0;
7183   PetscInt        newNumPoints = 0, newNumIndices = 0;
7184   PetscInt       *newPoints, *indices, *newIndices;
7185   PetscInt        maxAnchor, maxDof;
7186   PetscInt        newOffsets[32];
7187   PetscInt       *pointMatOffsets[32];
7188   PetscInt       *newPointOffsets[32];
7189   PetscScalar    *pointMat[32];
7190   PetscScalar    *newValues      = NULL, *tmpValues;
7191   PetscBool       anyConstrained = PETSC_FALSE;
7192 
7193   PetscFunctionBegin;
7194   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7195   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7196   PetscCall(PetscSectionGetNumFields(section, &numFields));
7197 
7198   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
7199   /* if there are point-to-point constraints */
7200   if (aSec) {
7201     PetscCall(PetscArrayzero(newOffsets, 32));
7202     PetscCall(ISGetIndices(aIS, &anchors));
7203     PetscCall(PetscSectionGetChart(aSec, &aStart, &aEnd));
7204     /* figure out how many points are going to be in the new element matrix
7205      * (we allow double counting, because it's all just going to be summed
7206      * into the global matrix anyway) */
7207     for (p = 0; p < 2 * numPoints; p += 2) {
7208       PetscInt b    = points[p];
7209       PetscInt bDof = 0, bSecDof;
7210 
7211       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7212       if (!bSecDof) continue;
7213       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7214       if (bDof) {
7215         /* this point is constrained */
7216         /* it is going to be replaced by its anchors */
7217         PetscInt bOff, q;
7218 
7219         anyConstrained = PETSC_TRUE;
7220         newNumPoints += bDof;
7221         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7222         for (q = 0; q < bDof; q++) {
7223           PetscInt a = anchors[bOff + q];
7224           PetscInt aDof;
7225 
7226           PetscCall(PetscSectionGetDof(section, a, &aDof));
7227           newNumIndices += aDof;
7228           for (f = 0; f < numFields; ++f) {
7229             PetscInt fDof;
7230 
7231             PetscCall(PetscSectionGetFieldDof(section, a, f, &fDof));
7232             newOffsets[f + 1] += fDof;
7233           }
7234         }
7235       } else {
7236         /* this point is not constrained */
7237         newNumPoints++;
7238         newNumIndices += bSecDof;
7239         for (f = 0; f < numFields; ++f) {
7240           PetscInt fDof;
7241 
7242           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7243           newOffsets[f + 1] += fDof;
7244         }
7245       }
7246     }
7247   }
7248   if (!anyConstrained) {
7249     if (outNumPoints) *outNumPoints = 0;
7250     if (outNumIndices) *outNumIndices = 0;
7251     if (outPoints) *outPoints = NULL;
7252     if (outValues) *outValues = NULL;
7253     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7254     PetscFunctionReturn(PETSC_SUCCESS);
7255   }
7256 
7257   if (outNumPoints) *outNumPoints = newNumPoints;
7258   if (outNumIndices) *outNumIndices = newNumIndices;
7259 
7260   for (f = 0; f < numFields; ++f) newOffsets[f + 1] += newOffsets[f];
7261 
7262   if (!outPoints && !outValues) {
7263     if (offsets) {
7264       for (f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7265     }
7266     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7267     PetscFunctionReturn(PETSC_SUCCESS);
7268   }
7269 
7270   PetscCheck(!numFields || newOffsets[numFields] == newNumIndices, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, newOffsets[numFields], newNumIndices);
7271 
7272   PetscCall(DMGetDefaultConstraints(dm, &cSec, &cMat, NULL));
7273 
7274   /* workspaces */
7275   if (numFields) {
7276     for (f = 0; f < numFields; f++) {
7277       PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[f]));
7278       PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[f]));
7279     }
7280   } else {
7281     PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[0]));
7282     PetscCall(DMGetWorkArray(dm, numPoints, MPIU_INT, &newPointOffsets[0]));
7283   }
7284 
7285   /* get workspaces for the point-to-point matrices */
7286   if (numFields) {
7287     PetscInt totalOffset, totalMatOffset;
7288 
7289     for (p = 0; p < numPoints; p++) {
7290       PetscInt b    = points[2 * p];
7291       PetscInt bDof = 0, bSecDof;
7292 
7293       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7294       if (!bSecDof) {
7295         for (f = 0; f < numFields; f++) {
7296           newPointOffsets[f][p + 1] = 0;
7297           pointMatOffsets[f][p + 1] = 0;
7298         }
7299         continue;
7300       }
7301       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7302       if (bDof) {
7303         for (f = 0; f < numFields; f++) {
7304           PetscInt fDof, q, bOff, allFDof = 0;
7305 
7306           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7307           PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7308           for (q = 0; q < bDof; q++) {
7309             PetscInt a = anchors[bOff + q];
7310             PetscInt aFDof;
7311 
7312             PetscCall(PetscSectionGetFieldDof(section, a, f, &aFDof));
7313             allFDof += aFDof;
7314           }
7315           newPointOffsets[f][p + 1] = allFDof;
7316           pointMatOffsets[f][p + 1] = fDof * allFDof;
7317         }
7318       } else {
7319         for (f = 0; f < numFields; f++) {
7320           PetscInt fDof;
7321 
7322           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7323           newPointOffsets[f][p + 1] = fDof;
7324           pointMatOffsets[f][p + 1] = 0;
7325         }
7326       }
7327     }
7328     for (f = 0, totalOffset = 0, totalMatOffset = 0; f < numFields; f++) {
7329       newPointOffsets[f][0] = totalOffset;
7330       pointMatOffsets[f][0] = totalMatOffset;
7331       for (p = 0; p < numPoints; p++) {
7332         newPointOffsets[f][p + 1] += newPointOffsets[f][p];
7333         pointMatOffsets[f][p + 1] += pointMatOffsets[f][p];
7334       }
7335       totalOffset    = newPointOffsets[f][numPoints];
7336       totalMatOffset = pointMatOffsets[f][numPoints];
7337       PetscCall(DMGetWorkArray(dm, pointMatOffsets[f][numPoints], MPIU_SCALAR, &pointMat[f]));
7338     }
7339   } else {
7340     for (p = 0; p < numPoints; p++) {
7341       PetscInt b    = points[2 * p];
7342       PetscInt bDof = 0, bSecDof;
7343 
7344       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7345       if (!bSecDof) {
7346         newPointOffsets[0][p + 1] = 0;
7347         pointMatOffsets[0][p + 1] = 0;
7348         continue;
7349       }
7350       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7351       if (bDof) {
7352         PetscInt bOff, q, allDof = 0;
7353 
7354         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7355         for (q = 0; q < bDof; q++) {
7356           PetscInt a = anchors[bOff + q], aDof;
7357 
7358           PetscCall(PetscSectionGetDof(section, a, &aDof));
7359           allDof += aDof;
7360         }
7361         newPointOffsets[0][p + 1] = allDof;
7362         pointMatOffsets[0][p + 1] = bSecDof * allDof;
7363       } else {
7364         newPointOffsets[0][p + 1] = bSecDof;
7365         pointMatOffsets[0][p + 1] = 0;
7366       }
7367     }
7368     newPointOffsets[0][0] = 0;
7369     pointMatOffsets[0][0] = 0;
7370     for (p = 0; p < numPoints; p++) {
7371       newPointOffsets[0][p + 1] += newPointOffsets[0][p];
7372       pointMatOffsets[0][p + 1] += pointMatOffsets[0][p];
7373     }
7374     PetscCall(DMGetWorkArray(dm, pointMatOffsets[0][numPoints], MPIU_SCALAR, &pointMat[0]));
7375   }
7376 
7377   /* output arrays */
7378   PetscCall(DMGetWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7379 
7380   /* get the point-to-point matrices; construct newPoints */
7381   PetscCall(PetscSectionGetMaxDof(aSec, &maxAnchor));
7382   PetscCall(PetscSectionGetMaxDof(section, &maxDof));
7383   PetscCall(DMGetWorkArray(dm, maxDof, MPIU_INT, &indices));
7384   PetscCall(DMGetWorkArray(dm, maxAnchor * maxDof, MPIU_INT, &newIndices));
7385   if (numFields) {
7386     for (p = 0, newP = 0; p < numPoints; p++) {
7387       PetscInt b    = points[2 * p];
7388       PetscInt o    = points[2 * p + 1];
7389       PetscInt bDof = 0, bSecDof;
7390 
7391       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7392       if (!bSecDof) continue;
7393       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7394       if (bDof) {
7395         PetscInt fStart[32], fEnd[32], fAnchorStart[32], fAnchorEnd[32], bOff, q;
7396 
7397         fStart[0] = 0;
7398         fEnd[0]   = 0;
7399         for (f = 0; f < numFields; f++) {
7400           PetscInt fDof;
7401 
7402           PetscCall(PetscSectionGetFieldDof(cSec, b, f, &fDof));
7403           fStart[f + 1] = fStart[f] + fDof;
7404           fEnd[f + 1]   = fStart[f + 1];
7405         }
7406         PetscCall(PetscSectionGetOffset(cSec, b, &bOff));
7407         PetscCall(DMPlexGetIndicesPointFields_Internal(cSec, PETSC_TRUE, b, bOff, fEnd, PETSC_TRUE, perms, p, NULL, indices));
7408 
7409         fAnchorStart[0] = 0;
7410         fAnchorEnd[0]   = 0;
7411         for (f = 0; f < numFields; f++) {
7412           PetscInt fDof = newPointOffsets[f][p + 1] - newPointOffsets[f][p];
7413 
7414           fAnchorStart[f + 1] = fAnchorStart[f] + fDof;
7415           fAnchorEnd[f + 1]   = fAnchorStart[f + 1];
7416         }
7417         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7418         for (q = 0; q < bDof; q++) {
7419           PetscInt a = anchors[bOff + q], aOff;
7420 
7421           /* we take the orientation of ap into account in the order that we constructed the indices above: the newly added points have no orientation */
7422           newPoints[2 * (newP + q)]     = a;
7423           newPoints[2 * (newP + q) + 1] = 0;
7424           PetscCall(PetscSectionGetOffset(section, a, &aOff));
7425           PetscCall(DMPlexGetIndicesPointFields_Internal(section, PETSC_TRUE, a, aOff, fAnchorEnd, PETSC_TRUE, NULL, -1, NULL, newIndices));
7426         }
7427         newP += bDof;
7428 
7429         if (outValues) {
7430           /* get the point-to-point submatrix */
7431           for (f = 0; f < numFields; f++) PetscCall(MatGetValues(cMat, fEnd[f] - fStart[f], indices + fStart[f], fAnchorEnd[f] - fAnchorStart[f], newIndices + fAnchorStart[f], pointMat[f] + pointMatOffsets[f][p]));
7432         }
7433       } else {
7434         newPoints[2 * newP]     = b;
7435         newPoints[2 * newP + 1] = o;
7436         newP++;
7437       }
7438     }
7439   } else {
7440     for (p = 0; p < numPoints; p++) {
7441       PetscInt b    = points[2 * p];
7442       PetscInt o    = points[2 * p + 1];
7443       PetscInt bDof = 0, bSecDof;
7444 
7445       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7446       if (!bSecDof) continue;
7447       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7448       if (bDof) {
7449         PetscInt bEnd = 0, bAnchorEnd = 0, bOff;
7450 
7451         PetscCall(PetscSectionGetOffset(cSec, b, &bOff));
7452         PetscCall(DMPlexGetIndicesPoint_Internal(cSec, PETSC_TRUE, b, bOff, &bEnd, PETSC_TRUE, (perms && perms[0]) ? perms[0][p] : NULL, NULL, indices));
7453 
7454         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7455         for (q = 0; q < bDof; q++) {
7456           PetscInt a = anchors[bOff + q], aOff;
7457 
7458           /* we take the orientation of ap into account in the order that we constructed the indices above: the newly added points have no orientation */
7459 
7460           newPoints[2 * (newP + q)]     = a;
7461           newPoints[2 * (newP + q) + 1] = 0;
7462           PetscCall(PetscSectionGetOffset(section, a, &aOff));
7463           PetscCall(DMPlexGetIndicesPoint_Internal(section, PETSC_TRUE, a, aOff, &bAnchorEnd, PETSC_TRUE, NULL, NULL, newIndices));
7464         }
7465         newP += bDof;
7466 
7467         /* get the point-to-point submatrix */
7468         if (outValues) PetscCall(MatGetValues(cMat, bEnd, indices, bAnchorEnd, newIndices, pointMat[0] + pointMatOffsets[0][p]));
7469       } else {
7470         newPoints[2 * newP]     = b;
7471         newPoints[2 * newP + 1] = o;
7472         newP++;
7473       }
7474     }
7475   }
7476 
7477   if (outValues) {
7478     PetscCall(DMGetWorkArray(dm, newNumIndices * numIndices, MPIU_SCALAR, &tmpValues));
7479     PetscCall(PetscArrayzero(tmpValues, newNumIndices * numIndices));
7480     /* multiply constraints on the right */
7481     if (numFields) {
7482       for (f = 0; f < numFields; f++) {
7483         PetscInt oldOff = offsets[f];
7484 
7485         for (p = 0; p < numPoints; p++) {
7486           PetscInt cStart = newPointOffsets[f][p];
7487           PetscInt b      = points[2 * p];
7488           PetscInt c, r, k;
7489           PetscInt dof;
7490 
7491           PetscCall(PetscSectionGetFieldDof(section, b, f, &dof));
7492           if (!dof) continue;
7493           if (pointMatOffsets[f][p] < pointMatOffsets[f][p + 1]) {
7494             PetscInt           nCols = newPointOffsets[f][p + 1] - cStart;
7495             const PetscScalar *mat   = pointMat[f] + pointMatOffsets[f][p];
7496 
7497             for (r = 0; r < numIndices; r++) {
7498               for (c = 0; c < nCols; c++) {
7499                 for (k = 0; k < dof; k++) tmpValues[r * newNumIndices + cStart + c] += values[r * numIndices + oldOff + k] * mat[k * nCols + c];
7500               }
7501             }
7502           } else {
7503             /* copy this column as is */
7504             for (r = 0; r < numIndices; r++) {
7505               for (c = 0; c < dof; c++) tmpValues[r * newNumIndices + cStart + c] = values[r * numIndices + oldOff + c];
7506             }
7507           }
7508           oldOff += dof;
7509         }
7510       }
7511     } else {
7512       PetscInt oldOff = 0;
7513       for (p = 0; p < numPoints; p++) {
7514         PetscInt cStart = newPointOffsets[0][p];
7515         PetscInt b      = points[2 * p];
7516         PetscInt c, r, k;
7517         PetscInt dof;
7518 
7519         PetscCall(PetscSectionGetDof(section, b, &dof));
7520         if (!dof) continue;
7521         if (pointMatOffsets[0][p] < pointMatOffsets[0][p + 1]) {
7522           PetscInt           nCols = newPointOffsets[0][p + 1] - cStart;
7523           const PetscScalar *mat   = pointMat[0] + pointMatOffsets[0][p];
7524 
7525           for (r = 0; r < numIndices; r++) {
7526             for (c = 0; c < nCols; c++) {
7527               for (k = 0; k < dof; k++) tmpValues[r * newNumIndices + cStart + c] += mat[k * nCols + c] * values[r * numIndices + oldOff + k];
7528             }
7529           }
7530         } else {
7531           /* copy this column as is */
7532           for (r = 0; r < numIndices; r++) {
7533             for (c = 0; c < dof; c++) tmpValues[r * newNumIndices + cStart + c] = values[r * numIndices + oldOff + c];
7534           }
7535         }
7536         oldOff += dof;
7537       }
7538     }
7539 
7540     if (multiplyLeft) {
7541       PetscCall(DMGetWorkArray(dm, newNumIndices * newNumIndices, MPIU_SCALAR, &newValues));
7542       PetscCall(PetscArrayzero(newValues, newNumIndices * newNumIndices));
7543       /* multiply constraints transpose on the left */
7544       if (numFields) {
7545         for (f = 0; f < numFields; f++) {
7546           PetscInt oldOff = offsets[f];
7547 
7548           for (p = 0; p < numPoints; p++) {
7549             PetscInt rStart = newPointOffsets[f][p];
7550             PetscInt b      = points[2 * p];
7551             PetscInt c, r, k;
7552             PetscInt dof;
7553 
7554             PetscCall(PetscSectionGetFieldDof(section, b, f, &dof));
7555             if (pointMatOffsets[f][p] < pointMatOffsets[f][p + 1]) {
7556               PetscInt                          nRows = newPointOffsets[f][p + 1] - rStart;
7557               const PetscScalar *PETSC_RESTRICT mat   = pointMat[f] + pointMatOffsets[f][p];
7558 
7559               for (r = 0; r < nRows; r++) {
7560                 for (c = 0; c < newNumIndices; c++) {
7561                   for (k = 0; k < dof; k++) newValues[(rStart + r) * newNumIndices + c] += mat[k * nRows + r] * tmpValues[(oldOff + k) * newNumIndices + c];
7562                 }
7563               }
7564             } else {
7565               /* copy this row as is */
7566               for (r = 0; r < dof; r++) {
7567                 for (c = 0; c < newNumIndices; c++) newValues[(rStart + r) * newNumIndices + c] = tmpValues[(oldOff + r) * newNumIndices + c];
7568               }
7569             }
7570             oldOff += dof;
7571           }
7572         }
7573       } else {
7574         PetscInt oldOff = 0;
7575 
7576         for (p = 0; p < numPoints; p++) {
7577           PetscInt rStart = newPointOffsets[0][p];
7578           PetscInt b      = points[2 * p];
7579           PetscInt c, r, k;
7580           PetscInt dof;
7581 
7582           PetscCall(PetscSectionGetDof(section, b, &dof));
7583           if (pointMatOffsets[0][p] < pointMatOffsets[0][p + 1]) {
7584             PetscInt                          nRows = newPointOffsets[0][p + 1] - rStart;
7585             const PetscScalar *PETSC_RESTRICT mat   = pointMat[0] + pointMatOffsets[0][p];
7586 
7587             for (r = 0; r < nRows; r++) {
7588               for (c = 0; c < newNumIndices; c++) {
7589                 for (k = 0; k < dof; k++) newValues[(rStart + r) * newNumIndices + c] += mat[k * nRows + r] * tmpValues[(oldOff + k) * newNumIndices + c];
7590               }
7591             }
7592           } else {
7593             /* copy this row as is */
7594             for (r = 0; r < dof; r++) {
7595               for (c = 0; c < newNumIndices; c++) newValues[(rStart + r) * newNumIndices + c] = tmpValues[(oldOff + r) * newNumIndices + c];
7596             }
7597           }
7598           oldOff += dof;
7599         }
7600       }
7601 
7602       PetscCall(DMRestoreWorkArray(dm, newNumIndices * numIndices, MPIU_SCALAR, &tmpValues));
7603     } else {
7604       newValues = tmpValues;
7605     }
7606   }
7607 
7608   /* clean up */
7609   PetscCall(DMRestoreWorkArray(dm, maxDof, MPIU_INT, &indices));
7610   PetscCall(DMRestoreWorkArray(dm, maxAnchor * maxDof, MPIU_INT, &newIndices));
7611 
7612   if (numFields) {
7613     for (f = 0; f < numFields; f++) {
7614       PetscCall(DMRestoreWorkArray(dm, pointMatOffsets[f][numPoints], MPIU_SCALAR, &pointMat[f]));
7615       PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[f]));
7616       PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[f]));
7617     }
7618   } else {
7619     PetscCall(DMRestoreWorkArray(dm, pointMatOffsets[0][numPoints], MPIU_SCALAR, &pointMat[0]));
7620     PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[0]));
7621     PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[0]));
7622   }
7623   PetscCall(ISRestoreIndices(aIS, &anchors));
7624 
7625   /* output */
7626   if (outPoints) {
7627     *outPoints = newPoints;
7628   } else {
7629     PetscCall(DMRestoreWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7630   }
7631   if (outValues) *outValues = newValues;
7632   for (f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7633   PetscFunctionReturn(PETSC_SUCCESS);
7634 }
7635 
7636 /*@C
7637   DMPlexGetClosureIndices - Gets the global dof indices associated with the closure of the given point within the provided sections.
7638 
7639   Not collective
7640 
7641   Input Parameters:
7642 + dm         - The `DM`
7643 . section    - The `PetscSection` describing the points (a local section)
7644 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
7645 . point      - The point defining the closure
7646 - useClPerm  - Use the closure point permutation if available
7647 
7648   Output Parameters:
7649 + numIndices - The number of dof indices in the closure of point with the input sections
7650 . indices    - The dof indices
7651 . outOffsets - Array to write the field offsets into, or `NULL`
7652 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or `NULL`
7653 
7654   Level: advanced
7655 
7656   Notes:
7657   Must call `DMPlexRestoreClosureIndices()` to free allocated memory
7658 
7659   If `idxSection` is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
7660   of those indices is not significant.  If `idxSection` is local, the constrained dofs will yield the involution -(idx+1)
7661   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
7662   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when `idxSection` == section, otherwise global
7663   indices (with the above semantics) are implied.
7664 
7665 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`,
7666           `PetscSection`, `DMGetGlobalSection()`
7667 @*/
7668 PetscErrorCode DMPlexGetClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
7669 {
7670   /* Closure ordering */
7671   PetscSection    clSection;
7672   IS              clPoints;
7673   const PetscInt *clp;
7674   PetscInt       *points;
7675   const PetscInt *clperm = NULL;
7676   /* Dof permutation and sign flips */
7677   const PetscInt    **perms[32] = {NULL};
7678   const PetscScalar **flips[32] = {NULL};
7679   PetscScalar        *valCopy   = NULL;
7680   /* Hanging node constraints */
7681   PetscInt    *pointsC = NULL;
7682   PetscScalar *valuesC = NULL;
7683   PetscInt     NclC, NiC;
7684 
7685   PetscInt *idx;
7686   PetscInt  Nf, Ncl, Ni = 0, offsets[32], p, f;
7687   PetscBool isLocal = (section == idxSection) ? PETSC_TRUE : PETSC_FALSE;
7688 
7689   PetscFunctionBeginHot;
7690   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7691   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7692   PetscValidHeaderSpecific(idxSection, PETSC_SECTION_CLASSID, 3);
7693   if (numIndices) PetscAssertPointer(numIndices, 6);
7694   if (indices) PetscAssertPointer(indices, 7);
7695   if (outOffsets) PetscAssertPointer(outOffsets, 8);
7696   if (values) PetscAssertPointer(values, 9);
7697   PetscCall(PetscSectionGetNumFields(section, &Nf));
7698   PetscCheck(Nf <= 31, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", Nf);
7699   PetscCall(PetscArrayzero(offsets, 32));
7700   /* 1) Get points in closure */
7701   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &Ncl, &points, &clSection, &clPoints, &clp));
7702   if (useClPerm) {
7703     PetscInt depth, clsize;
7704     PetscCall(DMPlexGetPointDepth(dm, point, &depth));
7705     for (clsize = 0, p = 0; p < Ncl; p++) {
7706       PetscInt dof;
7707       PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
7708       clsize += dof;
7709     }
7710     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
7711   }
7712   /* 2) Get number of indices on these points and field offsets from section */
7713   for (p = 0; p < Ncl * 2; p += 2) {
7714     PetscInt dof, fdof;
7715 
7716     PetscCall(PetscSectionGetDof(section, points[p], &dof));
7717     for (f = 0; f < Nf; ++f) {
7718       PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
7719       offsets[f + 1] += fdof;
7720     }
7721     Ni += dof;
7722   }
7723   for (f = 1; f < Nf; ++f) offsets[f + 1] += offsets[f];
7724   PetscCheck(!Nf || offsets[Nf] == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, offsets[Nf], Ni);
7725   /* 3) Get symmetries and sign flips. Apply sign flips to values if passed in (only works for square values matrix) */
7726   for (f = 0; f < PetscMax(1, Nf); ++f) {
7727     if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7728     else PetscCall(PetscSectionGetPointSyms(section, Ncl, points, &perms[f], &flips[f]));
7729     /* may need to apply sign changes to the element matrix */
7730     if (values && flips[f]) {
7731       PetscInt foffset = offsets[f];
7732 
7733       for (p = 0; p < Ncl; ++p) {
7734         PetscInt           pnt  = points[2 * p], fdof;
7735         const PetscScalar *flip = flips[f] ? flips[f][p] : NULL;
7736 
7737         if (!Nf) PetscCall(PetscSectionGetDof(section, pnt, &fdof));
7738         else PetscCall(PetscSectionGetFieldDof(section, pnt, f, &fdof));
7739         if (flip) {
7740           PetscInt i, j, k;
7741 
7742           if (!valCopy) {
7743             PetscCall(DMGetWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
7744             for (j = 0; j < Ni * Ni; ++j) valCopy[j] = (*values)[j];
7745             *values = valCopy;
7746           }
7747           for (i = 0; i < fdof; ++i) {
7748             PetscScalar fval = flip[i];
7749 
7750             for (k = 0; k < Ni; ++k) {
7751               valCopy[Ni * (foffset + i) + k] *= fval;
7752               valCopy[Ni * k + (foffset + i)] *= fval;
7753             }
7754           }
7755         }
7756         foffset += fdof;
7757       }
7758     }
7759   }
7760   /* 4) Apply hanging node constraints. Get new symmetries and replace all storage with constrained storage */
7761   PetscCall(DMPlexAnchorsModifyMat(dm, section, Ncl, Ni, points, perms, values ? *values : NULL, &NclC, &NiC, &pointsC, values ? &valuesC : NULL, offsets, PETSC_TRUE));
7762   if (NclC) {
7763     if (valCopy) PetscCall(DMRestoreWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
7764     for (f = 0; f < PetscMax(1, Nf); ++f) {
7765       if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7766       else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
7767     }
7768     for (f = 0; f < PetscMax(1, Nf); ++f) {
7769       if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, NclC, pointsC, &perms[f], &flips[f]));
7770       else PetscCall(PetscSectionGetPointSyms(section, NclC, pointsC, &perms[f], &flips[f]));
7771     }
7772     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7773     Ncl    = NclC;
7774     Ni     = NiC;
7775     points = pointsC;
7776     if (values) *values = valuesC;
7777   }
7778   /* 5) Calculate indices */
7779   PetscCall(DMGetWorkArray(dm, Ni, MPIU_INT, &idx));
7780   if (Nf) {
7781     PetscInt  idxOff;
7782     PetscBool useFieldOffsets;
7783 
7784     if (outOffsets) {
7785       for (f = 0; f <= Nf; f++) outOffsets[f] = offsets[f];
7786     }
7787     PetscCall(PetscSectionGetUseFieldOffsets(idxSection, &useFieldOffsets));
7788     if (useFieldOffsets) {
7789       for (p = 0; p < Ncl; ++p) {
7790         const PetscInt pnt = points[p * 2];
7791 
7792         PetscCall(DMPlexGetIndicesPointFieldsSplit_Internal(section, idxSection, pnt, offsets, perms, p, clperm, idx));
7793       }
7794     } else {
7795       for (p = 0; p < Ncl; ++p) {
7796         const PetscInt pnt = points[p * 2];
7797 
7798         PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
7799         /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
7800          * not (at the time of this writing) have fields set. They probably should, in which case we would pass the
7801          * global section. */
7802         PetscCall(DMPlexGetIndicesPointFields_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, offsets, PETSC_FALSE, perms, p, clperm, idx));
7803       }
7804     }
7805   } else {
7806     PetscInt off = 0, idxOff;
7807 
7808     for (p = 0; p < Ncl; ++p) {
7809       const PetscInt  pnt  = points[p * 2];
7810       const PetscInt *perm = perms[0] ? perms[0][p] : NULL;
7811 
7812       PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
7813       /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
7814        * not (at the time of this writing) have fields set. They probably should, in which case we would pass the global section. */
7815       PetscCall(DMPlexGetIndicesPoint_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, &off, PETSC_FALSE, perm, clperm, idx));
7816     }
7817   }
7818   /* 6) Cleanup */
7819   for (f = 0; f < PetscMax(1, Nf); ++f) {
7820     if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7821     else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
7822   }
7823   if (NclC) {
7824     PetscCall(DMRestoreWorkArray(dm, NclC * 2, MPIU_INT, &pointsC));
7825   } else {
7826     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7827   }
7828 
7829   if (numIndices) *numIndices = Ni;
7830   if (indices) *indices = idx;
7831   PetscFunctionReturn(PETSC_SUCCESS);
7832 }
7833 
7834 /*@C
7835   DMPlexRestoreClosureIndices - Restores the global dof indices associated with the closure of the given point within the provided sections.
7836 
7837   Not collective
7838 
7839   Input Parameters:
7840 + dm         - The `DM`
7841 . section    - The `PetscSection` describing the points (a local section)
7842 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
7843 . point      - The point defining the closure
7844 - useClPerm  - Use the closure point permutation if available
7845 
7846   Output Parameters:
7847 + numIndices - The number of dof indices in the closure of point with the input sections
7848 . indices    - The dof indices
7849 . outOffsets - Array to write the field offsets into, or `NULL`
7850 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or `NULL`
7851 
7852   Level: advanced
7853 
7854   Notes:
7855   If values were modified, the user is responsible for calling `DMRestoreWorkArray`(dm, 0, `MPIU_SCALAR`, &values).
7856 
7857   If idxSection is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
7858   of those indices is not significant.  If idxSection is local, the constrained dofs will yield the involution -(idx+1)
7859   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
7860   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when idxSection == section, otherwise global
7861   indices (with the above semantics) are implied.
7862 
7863 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`, `DMGetGlobalSection()`
7864 @*/
7865 PetscErrorCode DMPlexRestoreClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
7866 {
7867   PetscFunctionBegin;
7868   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7869   PetscAssertPointer(indices, 7);
7870   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, indices));
7871   PetscFunctionReturn(PETSC_SUCCESS);
7872 }
7873 
7874 /*@C
7875   DMPlexMatSetClosure - Set an array of the values on the closure of 'point'
7876 
7877   Not collective
7878 
7879   Input Parameters:
7880 + dm            - The `DM`
7881 . section       - The section describing the layout in `v`, or `NULL` to use the default section
7882 . globalSection - The section describing the layout in `v`, or `NULL` to use the default global section
7883 . A             - The matrix
7884 . point         - The point in the `DM`
7885 . values        - The array of values
7886 - mode          - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
7887 
7888   Level: intermediate
7889 
7890 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosureGeneral()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
7891 @*/
7892 PetscErrorCode DMPlexMatSetClosure(DM dm, PetscSection section, PetscSection globalSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7893 {
7894   DM_Plex           *mesh = (DM_Plex *)dm->data;
7895   PetscInt          *indices;
7896   PetscInt           numIndices;
7897   const PetscScalar *valuesOrig = values;
7898   PetscErrorCode     ierr;
7899 
7900   PetscFunctionBegin;
7901   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7902   if (!section) PetscCall(DMGetLocalSection(dm, &section));
7903   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7904   if (!globalSection) PetscCall(DMGetGlobalSection(dm, &globalSection));
7905   PetscValidHeaderSpecific(globalSection, PETSC_SECTION_CLASSID, 3);
7906   PetscValidHeaderSpecific(A, MAT_CLASSID, 4);
7907 
7908   PetscCall(DMPlexGetClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
7909 
7910   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndices, indices, 0, NULL, values));
7911   /* TODO: fix this code to not use error codes as handle-able exceptions! */
7912   ierr = MatSetValues(A, numIndices, indices, numIndices, indices, values, mode);
7913   if (ierr) {
7914     PetscMPIInt rank;
7915 
7916     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7917     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
7918     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndices, indices, 0, NULL, values));
7919     PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
7920     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
7921     SETERRQ(PetscObjectComm((PetscObject)dm), ierr, "Not possible to set matrix values");
7922   }
7923   if (mesh->printFEM > 1) {
7924     PetscInt i;
7925     PetscCall(PetscPrintf(PETSC_COMM_SELF, "  Indices:"));
7926     for (i = 0; i < numIndices; ++i) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, indices[i]));
7927     PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
7928   }
7929 
7930   PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
7931   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
7932   PetscFunctionReturn(PETSC_SUCCESS);
7933 }
7934 
7935 /*@C
7936   DMPlexMatSetClosureGeneral - Set an array of the values on the closure of 'point' using a different row and column section
7937 
7938   Not collective
7939 
7940   Input Parameters:
7941 + dmRow            - The `DM` for the row fields
7942 . sectionRow       - The section describing the layout, or `NULL` to use the default section in `dmRow`
7943 . globalSectionRow - The section describing the layout, or `NULL` to use the default global section in `dmRow`
7944 . dmCol            - The `DM` for the column fields
7945 . sectionCol       - The section describing the layout, or `NULL` to use the default section in `dmCol`
7946 . globalSectionCol - The section describing the layout, or `NULL` to use the default global section in `dmCol`
7947 . A                - The matrix
7948 . point            - The point in the `DM`
7949 . values           - The array of values
7950 - mode             - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
7951 
7952   Level: intermediate
7953 
7954 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosure()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
7955 @*/
7956 PetscErrorCode DMPlexMatSetClosureGeneral(DM dmRow, PetscSection sectionRow, PetscSection globalSectionRow, DM dmCol, PetscSection sectionCol, PetscSection globalSectionCol, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7957 {
7958   DM_Plex           *mesh = (DM_Plex *)dmRow->data;
7959   PetscInt          *indicesRow, *indicesCol;
7960   PetscInt           numIndicesRow, numIndicesCol;
7961   const PetscScalar *valuesOrig = values;
7962   PetscErrorCode     ierr;
7963 
7964   PetscFunctionBegin;
7965   PetscValidHeaderSpecific(dmRow, DM_CLASSID, 1);
7966   if (!sectionRow) PetscCall(DMGetLocalSection(dmRow, &sectionRow));
7967   PetscValidHeaderSpecific(sectionRow, PETSC_SECTION_CLASSID, 2);
7968   if (!globalSectionRow) PetscCall(DMGetGlobalSection(dmRow, &globalSectionRow));
7969   PetscValidHeaderSpecific(globalSectionRow, PETSC_SECTION_CLASSID, 3);
7970   PetscValidHeaderSpecific(dmCol, DM_CLASSID, 4);
7971   if (!sectionCol) PetscCall(DMGetLocalSection(dmCol, &sectionCol));
7972   PetscValidHeaderSpecific(sectionCol, PETSC_SECTION_CLASSID, 5);
7973   if (!globalSectionCol) PetscCall(DMGetGlobalSection(dmCol, &globalSectionCol));
7974   PetscValidHeaderSpecific(globalSectionCol, PETSC_SECTION_CLASSID, 6);
7975   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
7976 
7977   PetscCall(DMPlexGetClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&values));
7978   PetscCall(DMPlexGetClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&values));
7979 
7980   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
7981   /* TODO: fix this code to not use error codes as handle-able exceptions! */
7982   ierr = MatSetValues(A, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values, mode);
7983   if (ierr) {
7984     PetscMPIInt rank;
7985 
7986     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7987     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
7988     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
7989     PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&values));
7990     PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesRow, NULL, (PetscScalar **)&values));
7991     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &values));
7992   }
7993 
7994   PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&values));
7995   PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&values));
7996   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &values));
7997   PetscFunctionReturn(PETSC_SUCCESS);
7998 }
7999 
8000 PetscErrorCode DMPlexMatSetClosureRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
8001 {
8002   DM_Plex        *mesh    = (DM_Plex *)dmf->data;
8003   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
8004   PetscInt       *cpoints = NULL;
8005   PetscInt       *findices, *cindices;
8006   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
8007   PetscInt        foffsets[32], coffsets[32];
8008   DMPolytopeType  ct;
8009   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
8010   PetscErrorCode  ierr;
8011 
8012   PetscFunctionBegin;
8013   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
8014   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
8015   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
8016   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
8017   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
8018   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
8019   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
8020   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
8021   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
8022   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
8023   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
8024   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
8025   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
8026   PetscCall(PetscArrayzero(foffsets, 32));
8027   PetscCall(PetscArrayzero(coffsets, 32));
8028   /* Column indices */
8029   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8030   maxFPoints = numCPoints;
8031   /* Compress out points not in the section */
8032   /*   TODO: Squeeze out points with 0 dof as well */
8033   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
8034   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
8035     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
8036       cpoints[q * 2]     = cpoints[p];
8037       cpoints[q * 2 + 1] = cpoints[p + 1];
8038       ++q;
8039     }
8040   }
8041   numCPoints = q;
8042   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
8043     PetscInt fdof;
8044 
8045     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
8046     if (!dof) continue;
8047     for (f = 0; f < numFields; ++f) {
8048       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
8049       coffsets[f + 1] += fdof;
8050     }
8051     numCIndices += dof;
8052   }
8053   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
8054   /* Row indices */
8055   PetscCall(DMPlexGetCellType(dmc, point, &ct));
8056   {
8057     DMPlexTransform tr;
8058     DMPolytopeType *rct;
8059     PetscInt       *rsize, *rcone, *rornt, Nt;
8060 
8061     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8062     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8063     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8064     numSubcells = rsize[Nt - 1];
8065     PetscCall(DMPlexTransformDestroy(&tr));
8066   }
8067   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8068   for (r = 0, q = 0; r < numSubcells; ++r) {
8069     /* TODO Map from coarse to fine cells */
8070     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8071     /* Compress out points not in the section */
8072     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8073     for (p = 0; p < numFPoints * 2; p += 2) {
8074       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8075         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8076         if (!dof) continue;
8077         for (s = 0; s < q; ++s)
8078           if (fpoints[p] == ftotpoints[s * 2]) break;
8079         if (s < q) continue;
8080         ftotpoints[q * 2]     = fpoints[p];
8081         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8082         ++q;
8083       }
8084     }
8085     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8086   }
8087   numFPoints = q;
8088   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8089     PetscInt fdof;
8090 
8091     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8092     if (!dof) continue;
8093     for (f = 0; f < numFields; ++f) {
8094       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8095       foffsets[f + 1] += fdof;
8096     }
8097     numFIndices += dof;
8098   }
8099   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8100 
8101   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8102   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8103   PetscCall(DMGetWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8104   PetscCall(DMGetWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8105   if (numFields) {
8106     const PetscInt **permsF[32] = {NULL};
8107     const PetscInt **permsC[32] = {NULL};
8108 
8109     for (f = 0; f < numFields; f++) {
8110       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8111       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8112     }
8113     for (p = 0; p < numFPoints; p++) {
8114       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8115       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8116     }
8117     for (p = 0; p < numCPoints; p++) {
8118       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8119       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8120     }
8121     for (f = 0; f < numFields; f++) {
8122       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8123       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8124     }
8125   } else {
8126     const PetscInt **permsF = NULL;
8127     const PetscInt **permsC = NULL;
8128 
8129     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8130     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8131     for (p = 0, off = 0; p < numFPoints; p++) {
8132       const PetscInt *perm = permsF ? permsF[p] : NULL;
8133 
8134       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8135       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8136     }
8137     for (p = 0, off = 0; p < numCPoints; p++) {
8138       const PetscInt *perm = permsC ? permsC[p] : NULL;
8139 
8140       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8141       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8142     }
8143     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8144     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8145   }
8146   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8147   /* TODO: flips */
8148   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8149   ierr = MatSetValues(A, numFIndices, findices, numCIndices, cindices, values, mode);
8150   if (ierr) {
8151     PetscMPIInt rank;
8152 
8153     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8154     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8155     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8156     PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8157     PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8158   }
8159   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8160   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8161   PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8162   PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8163   PetscFunctionReturn(PETSC_SUCCESS);
8164 }
8165 
8166 PetscErrorCode DMPlexMatGetClosureIndicesRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, PetscInt point, PetscInt cindices[], PetscInt findices[])
8167 {
8168   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
8169   PetscInt       *cpoints = NULL;
8170   PetscInt        foffsets[32], coffsets[32];
8171   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
8172   DMPolytopeType  ct;
8173   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
8174 
8175   PetscFunctionBegin;
8176   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
8177   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
8178   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
8179   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
8180   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
8181   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
8182   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
8183   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
8184   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
8185   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
8186   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
8187   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
8188   PetscCall(PetscArrayzero(foffsets, 32));
8189   PetscCall(PetscArrayzero(coffsets, 32));
8190   /* Column indices */
8191   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8192   maxFPoints = numCPoints;
8193   /* Compress out points not in the section */
8194   /*   TODO: Squeeze out points with 0 dof as well */
8195   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
8196   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
8197     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
8198       cpoints[q * 2]     = cpoints[p];
8199       cpoints[q * 2 + 1] = cpoints[p + 1];
8200       ++q;
8201     }
8202   }
8203   numCPoints = q;
8204   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
8205     PetscInt fdof;
8206 
8207     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
8208     if (!dof) continue;
8209     for (f = 0; f < numFields; ++f) {
8210       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
8211       coffsets[f + 1] += fdof;
8212     }
8213     numCIndices += dof;
8214   }
8215   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
8216   /* Row indices */
8217   PetscCall(DMPlexGetCellType(dmc, point, &ct));
8218   {
8219     DMPlexTransform tr;
8220     DMPolytopeType *rct;
8221     PetscInt       *rsize, *rcone, *rornt, Nt;
8222 
8223     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8224     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8225     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8226     numSubcells = rsize[Nt - 1];
8227     PetscCall(DMPlexTransformDestroy(&tr));
8228   }
8229   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8230   for (r = 0, q = 0; r < numSubcells; ++r) {
8231     /* TODO Map from coarse to fine cells */
8232     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8233     /* Compress out points not in the section */
8234     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8235     for (p = 0; p < numFPoints * 2; p += 2) {
8236       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8237         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8238         if (!dof) continue;
8239         for (s = 0; s < q; ++s)
8240           if (fpoints[p] == ftotpoints[s * 2]) break;
8241         if (s < q) continue;
8242         ftotpoints[q * 2]     = fpoints[p];
8243         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8244         ++q;
8245       }
8246     }
8247     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8248   }
8249   numFPoints = q;
8250   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8251     PetscInt fdof;
8252 
8253     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8254     if (!dof) continue;
8255     for (f = 0; f < numFields; ++f) {
8256       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8257       foffsets[f + 1] += fdof;
8258     }
8259     numFIndices += dof;
8260   }
8261   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8262 
8263   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8264   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8265   if (numFields) {
8266     const PetscInt **permsF[32] = {NULL};
8267     const PetscInt **permsC[32] = {NULL};
8268 
8269     for (f = 0; f < numFields; f++) {
8270       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8271       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8272     }
8273     for (p = 0; p < numFPoints; p++) {
8274       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8275       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8276     }
8277     for (p = 0; p < numCPoints; p++) {
8278       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8279       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8280     }
8281     for (f = 0; f < numFields; f++) {
8282       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8283       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8284     }
8285   } else {
8286     const PetscInt **permsF = NULL;
8287     const PetscInt **permsC = NULL;
8288 
8289     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8290     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8291     for (p = 0, off = 0; p < numFPoints; p++) {
8292       const PetscInt *perm = permsF ? permsF[p] : NULL;
8293 
8294       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8295       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8296     }
8297     for (p = 0, off = 0; p < numCPoints; p++) {
8298       const PetscInt *perm = permsC ? permsC[p] : NULL;
8299 
8300       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8301       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8302     }
8303     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8304     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8305   }
8306   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8307   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8308   PetscFunctionReturn(PETSC_SUCCESS);
8309 }
8310 
8311 /*@C
8312   DMPlexGetVTKCellHeight - Returns the height in the DAG used to determine which points are cells (normally 0)
8313 
8314   Input Parameter:
8315 . dm - The `DMPLEX` object
8316 
8317   Output Parameter:
8318 . cellHeight - The height of a cell
8319 
8320   Level: developer
8321 
8322 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetVTKCellHeight()`
8323 @*/
8324 PetscErrorCode DMPlexGetVTKCellHeight(DM dm, PetscInt *cellHeight)
8325 {
8326   DM_Plex *mesh = (DM_Plex *)dm->data;
8327 
8328   PetscFunctionBegin;
8329   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8330   PetscAssertPointer(cellHeight, 2);
8331   *cellHeight = mesh->vtkCellHeight;
8332   PetscFunctionReturn(PETSC_SUCCESS);
8333 }
8334 
8335 /*@C
8336   DMPlexSetVTKCellHeight - Sets the height in the DAG used to determine which points are cells (normally 0)
8337 
8338   Input Parameters:
8339 + dm         - The `DMPLEX` object
8340 - cellHeight - The height of a cell
8341 
8342   Level: developer
8343 
8344 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetVTKCellHeight()`
8345 @*/
8346 PetscErrorCode DMPlexSetVTKCellHeight(DM dm, PetscInt cellHeight)
8347 {
8348   DM_Plex *mesh = (DM_Plex *)dm->data;
8349 
8350   PetscFunctionBegin;
8351   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8352   mesh->vtkCellHeight = cellHeight;
8353   PetscFunctionReturn(PETSC_SUCCESS);
8354 }
8355 
8356 /*@
8357   DMPlexGetCellTypeStratum - Get the range of cells of a given celltype
8358 
8359   Input Parameters:
8360 + dm - The `DMPLEX` object
8361 - ct - The `DMPolytopeType` of the cell
8362 
8363   Output Parameters:
8364 + start - The first cell of this type, or `NULL`
8365 - end   - The upper bound on this celltype, or `NULL`
8366 
8367   Level: advanced
8368 
8369 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexConstructGhostCells()`, `DMPlexGetDepthStratum()`, `DMPlexGetHeightStratum()`
8370 @*/
8371 PetscErrorCode DMPlexGetCellTypeStratum(DM dm, DMPolytopeType ct, PetscInt *start, PetscInt *end)
8372 {
8373   DM_Plex *mesh = (DM_Plex *)dm->data;
8374   DMLabel  label;
8375   PetscInt pStart, pEnd;
8376 
8377   PetscFunctionBegin;
8378   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8379   if (start) {
8380     PetscAssertPointer(start, 3);
8381     *start = 0;
8382   }
8383   if (end) {
8384     PetscAssertPointer(end, 4);
8385     *end = 0;
8386   }
8387   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8388   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
8389   if (mesh->tr) {
8390     PetscCall(DMPlexTransformGetCellTypeStratum(mesh->tr, ct, start, end));
8391   } else {
8392     PetscCall(DMPlexGetCellTypeLabel(dm, &label));
8393     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named celltype was found");
8394     PetscCall(DMLabelGetStratumBounds(label, ct, start, end));
8395   }
8396   PetscFunctionReturn(PETSC_SUCCESS);
8397 }
8398 
8399 PetscErrorCode DMPlexCreateNumbering_Plex(DM dm, PetscInt pStart, PetscInt pEnd, PetscInt shift, PetscInt *globalSize, PetscSF sf, IS *numbering)
8400 {
8401   PetscSection section, globalSection;
8402   PetscInt    *numbers, p;
8403 
8404   PetscFunctionBegin;
8405   if (PetscDefined(USE_DEBUG)) PetscCall(DMPlexCheckPointSF(dm, sf, PETSC_TRUE));
8406   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
8407   PetscCall(PetscSectionSetChart(section, pStart, pEnd));
8408   for (p = pStart; p < pEnd; ++p) PetscCall(PetscSectionSetDof(section, p, 1));
8409   PetscCall(PetscSectionSetUp(section));
8410   PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_FALSE, PETSC_FALSE, &globalSection));
8411   PetscCall(PetscMalloc1(pEnd - pStart, &numbers));
8412   for (p = pStart; p < pEnd; ++p) {
8413     PetscCall(PetscSectionGetOffset(globalSection, p, &numbers[p - pStart]));
8414     if (numbers[p - pStart] < 0) numbers[p - pStart] -= shift;
8415     else numbers[p - pStart] += shift;
8416   }
8417   PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), pEnd - pStart, numbers, PETSC_OWN_POINTER, numbering));
8418   if (globalSize) {
8419     PetscLayout layout;
8420     PetscCall(PetscSectionGetPointLayout(PetscObjectComm((PetscObject)dm), globalSection, &layout));
8421     PetscCall(PetscLayoutGetSize(layout, globalSize));
8422     PetscCall(PetscLayoutDestroy(&layout));
8423   }
8424   PetscCall(PetscSectionDestroy(&section));
8425   PetscCall(PetscSectionDestroy(&globalSection));
8426   PetscFunctionReturn(PETSC_SUCCESS);
8427 }
8428 
8429 PetscErrorCode DMPlexCreateCellNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalCellNumbers)
8430 {
8431   PetscInt cellHeight, cStart, cEnd;
8432 
8433   PetscFunctionBegin;
8434   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8435   if (includeHybrid) PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8436   else PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd));
8437   PetscCall(DMPlexCreateNumbering_Plex(dm, cStart, cEnd, 0, NULL, dm->sf, globalCellNumbers));
8438   PetscFunctionReturn(PETSC_SUCCESS);
8439 }
8440 
8441 /*@
8442   DMPlexGetCellNumbering - Get a global cell numbering for all cells on this process
8443 
8444   Input Parameter:
8445 . dm - The `DMPLEX` object
8446 
8447   Output Parameter:
8448 . globalCellNumbers - Global cell numbers for all cells on this process
8449 
8450   Level: developer
8451 
8452 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetVertexNumbering()`
8453 @*/
8454 PetscErrorCode DMPlexGetCellNumbering(DM dm, IS *globalCellNumbers)
8455 {
8456   DM_Plex *mesh = (DM_Plex *)dm->data;
8457 
8458   PetscFunctionBegin;
8459   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8460   if (!mesh->globalCellNumbers) PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_FALSE, &mesh->globalCellNumbers));
8461   *globalCellNumbers = mesh->globalCellNumbers;
8462   PetscFunctionReturn(PETSC_SUCCESS);
8463 }
8464 
8465 PetscErrorCode DMPlexCreateVertexNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalVertexNumbers)
8466 {
8467   PetscInt vStart, vEnd;
8468 
8469   PetscFunctionBegin;
8470   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8471   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8472   PetscCall(DMPlexCreateNumbering_Plex(dm, vStart, vEnd, 0, NULL, dm->sf, globalVertexNumbers));
8473   PetscFunctionReturn(PETSC_SUCCESS);
8474 }
8475 
8476 /*@
8477   DMPlexGetVertexNumbering - Get a global vertex numbering for all vertices on this process
8478 
8479   Input Parameter:
8480 . dm - The `DMPLEX` object
8481 
8482   Output Parameter:
8483 . globalVertexNumbers - Global vertex numbers for all vertices on this process
8484 
8485   Level: developer
8486 
8487 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
8488 @*/
8489 PetscErrorCode DMPlexGetVertexNumbering(DM dm, IS *globalVertexNumbers)
8490 {
8491   DM_Plex *mesh = (DM_Plex *)dm->data;
8492 
8493   PetscFunctionBegin;
8494   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8495   if (!mesh->globalVertexNumbers) PetscCall(DMPlexCreateVertexNumbering_Internal(dm, PETSC_FALSE, &mesh->globalVertexNumbers));
8496   *globalVertexNumbers = mesh->globalVertexNumbers;
8497   PetscFunctionReturn(PETSC_SUCCESS);
8498 }
8499 
8500 /*@
8501   DMPlexCreatePointNumbering - Create a global numbering for all points.
8502 
8503   Collective
8504 
8505   Input Parameter:
8506 . dm - The `DMPLEX` object
8507 
8508   Output Parameter:
8509 . globalPointNumbers - Global numbers for all points on this process
8510 
8511   Level: developer
8512 
8513   Notes:
8514   The point numbering `IS` is parallel, with local portion indexed by local points (see `DMGetLocalSection()`). The global
8515   points are taken as stratified, with each MPI rank owning a contiguous subset of each stratum. In the IS, owned points
8516   will have their non-negative value while points owned by different ranks will be involuted -(idx+1). As an example,
8517   consider a parallel mesh in which the first two elements and first two vertices are owned by rank 0.
8518 
8519   The partitioned mesh is
8520   ```
8521   (2)--0--(3)--1--(4)    (1)--0--(2)
8522   ```
8523   and its global numbering is
8524   ```
8525   (3)--0--(4)--1--(5)--2--(6)
8526   ```
8527   Then the global numbering is provided as
8528   ```
8529   [0] Number of indices in set 5
8530   [0] 0 0
8531   [0] 1 1
8532   [0] 2 3
8533   [0] 3 4
8534   [0] 4 -6
8535   [1] Number of indices in set 3
8536   [1] 0 2
8537   [1] 1 5
8538   [1] 2 6
8539   ```
8540 
8541 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
8542 @*/
8543 PetscErrorCode DMPlexCreatePointNumbering(DM dm, IS *globalPointNumbers)
8544 {
8545   IS        nums[4];
8546   PetscInt  depths[4], gdepths[4], starts[4];
8547   PetscInt  depth, d, shift = 0;
8548   PetscBool empty = PETSC_FALSE;
8549 
8550   PetscFunctionBegin;
8551   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8552   PetscCall(DMPlexGetDepth(dm, &depth));
8553   // For unstratified meshes use dim instead of depth
8554   if (depth < 0) PetscCall(DMGetDimension(dm, &depth));
8555   // If any stratum is empty, we must mark all empty
8556   for (d = 0; d <= depth; ++d) {
8557     PetscInt end;
8558 
8559     depths[d] = depth - d;
8560     PetscCall(DMPlexGetDepthStratum(dm, depths[d], &starts[d], &end));
8561     if (!(starts[d] - end)) empty = PETSC_TRUE;
8562   }
8563   if (empty)
8564     for (d = 0; d <= depth; ++d) {
8565       depths[d] = -1;
8566       starts[d] = -1;
8567     }
8568   else PetscCall(PetscSortIntWithArray(depth + 1, starts, depths));
8569   PetscCall(MPIU_Allreduce(depths, gdepths, depth + 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
8570   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]);
8571   // Note here that 'shift' is collective, so that the numbering is stratified by depth
8572   for (d = 0; d <= depth; ++d) {
8573     PetscInt pStart, pEnd, gsize;
8574 
8575     PetscCall(DMPlexGetDepthStratum(dm, gdepths[d], &pStart, &pEnd));
8576     PetscCall(DMPlexCreateNumbering_Plex(dm, pStart, pEnd, shift, &gsize, dm->sf, &nums[d]));
8577     shift += gsize;
8578   }
8579   PetscCall(ISConcatenate(PETSC_COMM_SELF, depth + 1, nums, globalPointNumbers));
8580   for (d = 0; d <= depth; ++d) PetscCall(ISDestroy(&nums[d]));
8581   PetscFunctionReturn(PETSC_SUCCESS);
8582 }
8583 
8584 /*@
8585   DMPlexCreateRankField - Create a cell field whose value is the rank of the owner
8586 
8587   Input Parameter:
8588 . dm - The `DMPLEX` object
8589 
8590   Output Parameter:
8591 . ranks - The rank field
8592 
8593   Options Database Key:
8594 . -dm_partition_view - Adds the rank field into the `DM` output from `-dm_view` using the same viewer
8595 
8596   Level: intermediate
8597 
8598 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`
8599 @*/
8600 PetscErrorCode DMPlexCreateRankField(DM dm, Vec *ranks)
8601 {
8602   DM             rdm;
8603   PetscFE        fe;
8604   PetscScalar   *r;
8605   PetscMPIInt    rank;
8606   DMPolytopeType ct;
8607   PetscInt       dim, cStart, cEnd, c;
8608   PetscBool      simplex;
8609 
8610   PetscFunctionBeginUser;
8611   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8612   PetscAssertPointer(ranks, 2);
8613   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
8614   PetscCall(DMClone(dm, &rdm));
8615   PetscCall(DMGetDimension(rdm, &dim));
8616   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
8617   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
8618   simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct) + 1 ? PETSC_TRUE : PETSC_FALSE;
8619   PetscCall(PetscFECreateDefault(PETSC_COMM_SELF, dim, 1, simplex, "PETSc___rank_", -1, &fe));
8620   PetscCall(PetscObjectSetName((PetscObject)fe, "rank"));
8621   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
8622   PetscCall(PetscFEDestroy(&fe));
8623   PetscCall(DMCreateDS(rdm));
8624   PetscCall(DMCreateGlobalVector(rdm, ranks));
8625   PetscCall(PetscObjectSetName((PetscObject)*ranks, "partition"));
8626   PetscCall(VecGetArray(*ranks, &r));
8627   for (c = cStart; c < cEnd; ++c) {
8628     PetscScalar *lr;
8629 
8630     PetscCall(DMPlexPointGlobalRef(rdm, c, r, &lr));
8631     if (lr) *lr = rank;
8632   }
8633   PetscCall(VecRestoreArray(*ranks, &r));
8634   PetscCall(DMDestroy(&rdm));
8635   PetscFunctionReturn(PETSC_SUCCESS);
8636 }
8637 
8638 /*@
8639   DMPlexCreateLabelField - Create a cell field whose value is the label value for that cell
8640 
8641   Input Parameters:
8642 + dm    - The `DMPLEX`
8643 - label - The `DMLabel`
8644 
8645   Output Parameter:
8646 . val - The label value field
8647 
8648   Options Database Key:
8649 . -dm_label_view - Adds the label value field into the `DM` output from `-dm_view` using the same viewer
8650 
8651   Level: intermediate
8652 
8653 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`
8654 @*/
8655 PetscErrorCode DMPlexCreateLabelField(DM dm, DMLabel label, Vec *val)
8656 {
8657   DM           rdm;
8658   PetscFE      fe;
8659   PetscScalar *v;
8660   PetscInt     dim, cStart, cEnd, c;
8661 
8662   PetscFunctionBeginUser;
8663   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8664   PetscAssertPointer(label, 2);
8665   PetscAssertPointer(val, 3);
8666   PetscCall(DMClone(dm, &rdm));
8667   PetscCall(DMGetDimension(rdm, &dim));
8668   PetscCall(PetscFECreateDefault(PetscObjectComm((PetscObject)rdm), dim, 1, PETSC_TRUE, "PETSc___label_value_", -1, &fe));
8669   PetscCall(PetscObjectSetName((PetscObject)fe, "label_value"));
8670   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
8671   PetscCall(PetscFEDestroy(&fe));
8672   PetscCall(DMCreateDS(rdm));
8673   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
8674   PetscCall(DMCreateGlobalVector(rdm, val));
8675   PetscCall(PetscObjectSetName((PetscObject)*val, "label_value"));
8676   PetscCall(VecGetArray(*val, &v));
8677   for (c = cStart; c < cEnd; ++c) {
8678     PetscScalar *lv;
8679     PetscInt     cval;
8680 
8681     PetscCall(DMPlexPointGlobalRef(rdm, c, v, &lv));
8682     PetscCall(DMLabelGetValue(label, c, &cval));
8683     *lv = cval;
8684   }
8685   PetscCall(VecRestoreArray(*val, &v));
8686   PetscCall(DMDestroy(&rdm));
8687   PetscFunctionReturn(PETSC_SUCCESS);
8688 }
8689 
8690 /*@
8691   DMPlexCheckSymmetry - Check that the adjacency information in the mesh is symmetric.
8692 
8693   Input Parameter:
8694 . dm - The `DMPLEX` object
8695 
8696   Level: developer
8697 
8698   Notes:
8699   This is a useful diagnostic when creating meshes programmatically.
8700 
8701   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
8702 
8703 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
8704 @*/
8705 PetscErrorCode DMPlexCheckSymmetry(DM dm)
8706 {
8707   PetscSection    coneSection, supportSection;
8708   const PetscInt *cone, *support;
8709   PetscInt        coneSize, c, supportSize, s;
8710   PetscInt        pStart, pEnd, p, pp, csize, ssize;
8711   PetscBool       storagecheck = PETSC_TRUE;
8712 
8713   PetscFunctionBegin;
8714   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8715   PetscCall(DMViewFromOptions(dm, NULL, "-sym_dm_view"));
8716   PetscCall(DMPlexGetConeSection(dm, &coneSection));
8717   PetscCall(DMPlexGetSupportSection(dm, &supportSection));
8718   /* Check that point p is found in the support of its cone points, and vice versa */
8719   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8720   for (p = pStart; p < pEnd; ++p) {
8721     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
8722     PetscCall(DMPlexGetCone(dm, p, &cone));
8723     for (c = 0; c < coneSize; ++c) {
8724       PetscBool dup = PETSC_FALSE;
8725       PetscInt  d;
8726       for (d = c - 1; d >= 0; --d) {
8727         if (cone[c] == cone[d]) {
8728           dup = PETSC_TRUE;
8729           break;
8730         }
8731       }
8732       PetscCall(DMPlexGetSupportSize(dm, cone[c], &supportSize));
8733       PetscCall(DMPlexGetSupport(dm, cone[c], &support));
8734       for (s = 0; s < supportSize; ++s) {
8735         if (support[s] == p) break;
8736       }
8737       if ((s >= supportSize) || (dup && (support[s + 1] != p))) {
8738         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", p));
8739         for (s = 0; s < coneSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[s]));
8740         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8741         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", cone[c]));
8742         for (s = 0; s < supportSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[s]));
8743         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8744         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]);
8745         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in support of cone point %" PetscInt_FMT, p, cone[c]);
8746       }
8747     }
8748     PetscCall(DMPlexGetTreeParent(dm, p, &pp, NULL));
8749     if (p != pp) {
8750       storagecheck = PETSC_FALSE;
8751       continue;
8752     }
8753     PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
8754     PetscCall(DMPlexGetSupport(dm, p, &support));
8755     for (s = 0; s < supportSize; ++s) {
8756       PetscCall(DMPlexGetConeSize(dm, support[s], &coneSize));
8757       PetscCall(DMPlexGetCone(dm, support[s], &cone));
8758       for (c = 0; c < coneSize; ++c) {
8759         PetscCall(DMPlexGetTreeParent(dm, cone[c], &pp, NULL));
8760         if (cone[c] != pp) {
8761           c = 0;
8762           break;
8763         }
8764         if (cone[c] == p) break;
8765       }
8766       if (c >= coneSize) {
8767         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", p));
8768         for (c = 0; c < supportSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[c]));
8769         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8770         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", support[s]));
8771         for (c = 0; c < coneSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[c]));
8772         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8773         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in cone of support point %" PetscInt_FMT, p, support[s]);
8774       }
8775     }
8776   }
8777   if (storagecheck) {
8778     PetscCall(PetscSectionGetStorageSize(coneSection, &csize));
8779     PetscCall(PetscSectionGetStorageSize(supportSection, &ssize));
8780     PetscCheck(csize == ssize, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Total cone size %" PetscInt_FMT " != Total support size %" PetscInt_FMT, csize, ssize);
8781   }
8782   PetscFunctionReturn(PETSC_SUCCESS);
8783 }
8784 
8785 /*
8786   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.
8787 */
8788 static PetscErrorCode DMPlexCellUnsplitVertices_Private(DM dm, PetscInt c, DMPolytopeType ct, PetscInt *unsplit)
8789 {
8790   DMPolytopeType  cct;
8791   PetscInt        ptpoints[4];
8792   const PetscInt *cone, *ccone, *ptcone;
8793   PetscInt        coneSize, cp, cconeSize, ccp, npt = 0, pt;
8794 
8795   PetscFunctionBegin;
8796   *unsplit = 0;
8797   switch (ct) {
8798   case DM_POLYTOPE_POINT_PRISM_TENSOR:
8799     ptpoints[npt++] = c;
8800     break;
8801   case DM_POLYTOPE_SEG_PRISM_TENSOR:
8802     PetscCall(DMPlexGetCone(dm, c, &cone));
8803     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8804     for (cp = 0; cp < coneSize; ++cp) {
8805       PetscCall(DMPlexGetCellType(dm, cone[cp], &cct));
8806       if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) ptpoints[npt++] = cone[cp];
8807     }
8808     break;
8809   case DM_POLYTOPE_TRI_PRISM_TENSOR:
8810   case DM_POLYTOPE_QUAD_PRISM_TENSOR:
8811     PetscCall(DMPlexGetCone(dm, c, &cone));
8812     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8813     for (cp = 0; cp < coneSize; ++cp) {
8814       PetscCall(DMPlexGetCone(dm, cone[cp], &ccone));
8815       PetscCall(DMPlexGetConeSize(dm, cone[cp], &cconeSize));
8816       for (ccp = 0; ccp < cconeSize; ++ccp) {
8817         PetscCall(DMPlexGetCellType(dm, ccone[ccp], &cct));
8818         if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) {
8819           PetscInt p;
8820           for (p = 0; p < npt; ++p)
8821             if (ptpoints[p] == ccone[ccp]) break;
8822           if (p == npt) ptpoints[npt++] = ccone[ccp];
8823         }
8824       }
8825     }
8826     break;
8827   default:
8828     break;
8829   }
8830   for (pt = 0; pt < npt; ++pt) {
8831     PetscCall(DMPlexGetCone(dm, ptpoints[pt], &ptcone));
8832     if (ptcone[0] == ptcone[1]) ++(*unsplit);
8833   }
8834   PetscFunctionReturn(PETSC_SUCCESS);
8835 }
8836 
8837 /*@
8838   DMPlexCheckSkeleton - Check that each cell has the correct number of vertices
8839 
8840   Input Parameters:
8841 + dm         - The `DMPLEX` object
8842 - cellHeight - Normally 0
8843 
8844   Level: developer
8845 
8846   Notes:
8847   This is a useful diagnostic when creating meshes programmatically.
8848   Currently applicable only to homogeneous simplex or tensor meshes.
8849 
8850   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
8851 
8852 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
8853 @*/
8854 PetscErrorCode DMPlexCheckSkeleton(DM dm, PetscInt cellHeight)
8855 {
8856   DMPlexInterpolatedFlag interp;
8857   DMPolytopeType         ct;
8858   PetscInt               vStart, vEnd, cStart, cEnd, c;
8859 
8860   PetscFunctionBegin;
8861   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8862   PetscCall(DMPlexIsInterpolated(dm, &interp));
8863   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8864   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8865   for (c = cStart; c < cEnd; ++c) {
8866     PetscInt *closure = NULL;
8867     PetscInt  coneSize, closureSize, cl, Nv = 0;
8868 
8869     PetscCall(DMPlexGetCellType(dm, c, &ct));
8870     PetscCheck((PetscInt)ct >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell %" PetscInt_FMT " has no cell type", c);
8871     if (ct == DM_POLYTOPE_UNKNOWN) continue;
8872     if (interp == DMPLEX_INTERPOLATED_FULL) {
8873       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8874       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));
8875     }
8876     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8877     for (cl = 0; cl < closureSize * 2; cl += 2) {
8878       const PetscInt p = closure[cl];
8879       if ((p >= vStart) && (p < vEnd)) ++Nv;
8880     }
8881     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8882     /* Special Case: Tensor faces with identified vertices */
8883     if (Nv < DMPolytopeTypeGetNumVertices(ct)) {
8884       PetscInt unsplit;
8885 
8886       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8887       if (Nv + unsplit == DMPolytopeTypeGetNumVertices(ct)) continue;
8888     }
8889     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));
8890   }
8891   PetscFunctionReturn(PETSC_SUCCESS);
8892 }
8893 
8894 /*@
8895   DMPlexCheckFaces - Check that the faces of each cell give a vertex order this is consistent with what we expect from the cell type
8896 
8897   Collective
8898 
8899   Input Parameters:
8900 + dm         - The `DMPLEX` object
8901 - cellHeight - Normally 0
8902 
8903   Level: developer
8904 
8905   Notes:
8906   This is a useful diagnostic when creating meshes programmatically.
8907   This routine is only relevant for meshes that are fully interpolated across all ranks.
8908   It will error out if a partially interpolated mesh is given on some rank.
8909   It will do nothing for locally uninterpolated mesh (as there is nothing to check).
8910 
8911   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
8912 
8913 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMPlexGetVTKCellHeight()`, `DMSetFromOptions()`
8914 @*/
8915 PetscErrorCode DMPlexCheckFaces(DM dm, PetscInt cellHeight)
8916 {
8917   PetscInt               dim, depth, vStart, vEnd, cStart, cEnd, c, h;
8918   DMPlexInterpolatedFlag interpEnum;
8919 
8920   PetscFunctionBegin;
8921   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8922   PetscCall(DMPlexIsInterpolatedCollective(dm, &interpEnum));
8923   if (interpEnum == DMPLEX_INTERPOLATED_NONE) PetscFunctionReturn(PETSC_SUCCESS);
8924   if (interpEnum != DMPLEX_INTERPOLATED_FULL) {
8925     PetscCall(PetscPrintf(PetscObjectComm((PetscObject)dm), "DMPlexCheckFaces() warning: Mesh is only partially interpolated, this is currently not supported"));
8926     PetscFunctionReturn(PETSC_SUCCESS);
8927   }
8928 
8929   PetscCall(DMGetDimension(dm, &dim));
8930   PetscCall(DMPlexGetDepth(dm, &depth));
8931   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8932   for (h = cellHeight; h < PetscMin(depth, dim); ++h) {
8933     PetscCall(DMPlexGetHeightStratum(dm, h, &cStart, &cEnd));
8934     for (c = cStart; c < cEnd; ++c) {
8935       const PetscInt       *cone, *ornt, *faceSizes, *faces;
8936       const DMPolytopeType *faceTypes;
8937       DMPolytopeType        ct;
8938       PetscInt              numFaces, coneSize, f;
8939       PetscInt             *closure = NULL, closureSize, cl, numCorners = 0, fOff = 0, unsplit;
8940 
8941       PetscCall(DMPlexGetCellType(dm, c, &ct));
8942       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8943       if (unsplit) continue;
8944       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8945       PetscCall(DMPlexGetCone(dm, c, &cone));
8946       PetscCall(DMPlexGetConeOrientation(dm, c, &ornt));
8947       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8948       for (cl = 0; cl < closureSize * 2; cl += 2) {
8949         const PetscInt p = closure[cl];
8950         if ((p >= vStart) && (p < vEnd)) closure[numCorners++] = p;
8951       }
8952       PetscCall(DMPlexGetRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
8953       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);
8954       for (f = 0; f < numFaces; ++f) {
8955         DMPolytopeType fct;
8956         PetscInt      *fclosure = NULL, fclosureSize, cl, fnumCorners = 0, v;
8957 
8958         PetscCall(DMPlexGetCellType(dm, cone[f], &fct));
8959         PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[f], ornt[f], PETSC_TRUE, &fclosureSize, &fclosure));
8960         for (cl = 0; cl < fclosureSize * 2; cl += 2) {
8961           const PetscInt p = fclosure[cl];
8962           if ((p >= vStart) && (p < vEnd)) fclosure[fnumCorners++] = p;
8963         }
8964         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]);
8965         for (v = 0; v < fnumCorners; ++v) {
8966           if (fclosure[v] != faces[fOff + v]) {
8967             PetscInt v1;
8968 
8969             PetscCall(PetscPrintf(PETSC_COMM_SELF, "face closure:"));
8970             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, fclosure[v1]));
8971             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\ncell face:"));
8972             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, faces[fOff + v1]));
8973             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8974             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]);
8975           }
8976         }
8977         PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[f], PETSC_TRUE, &fclosureSize, &fclosure));
8978         fOff += faceSizes[f];
8979       }
8980       PetscCall(DMPlexRestoreRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
8981       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8982     }
8983   }
8984   PetscFunctionReturn(PETSC_SUCCESS);
8985 }
8986 
8987 /*@
8988   DMPlexCheckGeometry - Check the geometry of mesh cells
8989 
8990   Input Parameter:
8991 . dm - The `DMPLEX` object
8992 
8993   Level: developer
8994 
8995   Notes:
8996   This is a useful diagnostic when creating meshes programmatically.
8997 
8998   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
8999 
9000 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9001 @*/
9002 PetscErrorCode DMPlexCheckGeometry(DM dm)
9003 {
9004   Vec       coordinates;
9005   PetscReal detJ, J[9], refVol = 1.0;
9006   PetscReal vol;
9007   PetscInt  dim, depth, dE, d, cStart, cEnd, c;
9008 
9009   PetscFunctionBegin;
9010   PetscCall(DMGetDimension(dm, &dim));
9011   PetscCall(DMGetCoordinateDim(dm, &dE));
9012   if (dim != dE) PetscFunctionReturn(PETSC_SUCCESS);
9013   PetscCall(DMPlexGetDepth(dm, &depth));
9014   for (d = 0; d < dim; ++d) refVol *= 2.0;
9015   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
9016   /* Make sure local coordinates are created, because that step is collective */
9017   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
9018   if (!coordinates) PetscFunctionReturn(PETSC_SUCCESS);
9019   for (c = cStart; c < cEnd; ++c) {
9020     DMPolytopeType ct;
9021     PetscInt       unsplit;
9022     PetscBool      ignoreZeroVol = PETSC_FALSE;
9023 
9024     PetscCall(DMPlexGetCellType(dm, c, &ct));
9025     switch (ct) {
9026     case DM_POLYTOPE_SEG_PRISM_TENSOR:
9027     case DM_POLYTOPE_TRI_PRISM_TENSOR:
9028     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9029       ignoreZeroVol = PETSC_TRUE;
9030       break;
9031     default:
9032       break;
9033     }
9034     switch (ct) {
9035     case DM_POLYTOPE_TRI_PRISM:
9036     case DM_POLYTOPE_TRI_PRISM_TENSOR:
9037     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
9038     case DM_POLYTOPE_PYRAMID:
9039       continue;
9040     default:
9041       break;
9042     }
9043     PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9044     if (unsplit) continue;
9045     PetscCall(DMPlexComputeCellGeometryFEM(dm, c, NULL, NULL, J, NULL, &detJ));
9046     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);
9047     PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FEM Volume %g\n", c, (double)(detJ * refVol)));
9048     /* This should work with periodicity since DG coordinates should be used */
9049     if (depth > 1) {
9050       PetscCall(DMPlexComputeCellGeometryFVM(dm, c, &vol, NULL, NULL));
9051       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);
9052       PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FVM Volume %g\n", c, (double)vol));
9053     }
9054   }
9055   PetscFunctionReturn(PETSC_SUCCESS);
9056 }
9057 
9058 /*@
9059   DMPlexCheckPointSF - Check that several necessary conditions are met for the point `PetscSF` of this plex.
9060 
9061   Collective
9062 
9063   Input Parameters:
9064 + dm              - The `DMPLEX` object
9065 . pointSF         - The `PetscSF`, or `NULL` for `PointSF` attached to `DM`
9066 - allowExtraRoots - Flag to allow extra points not present in the `DM`
9067 
9068   Level: developer
9069 
9070   Notes:
9071   This is mainly intended for debugging/testing purposes.
9072 
9073   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9074 
9075   Extra roots can come from priodic cuts, where additional points appear on the boundary
9076 
9077 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMGetPointSF()`, `DMSetFromOptions()`
9078 @*/
9079 PetscErrorCode DMPlexCheckPointSF(DM dm, PetscSF pointSF, PetscBool allowExtraRoots)
9080 {
9081   PetscInt           l, nleaves, nroots, overlap;
9082   const PetscInt    *locals;
9083   const PetscSFNode *remotes;
9084   PetscBool          distributed;
9085   MPI_Comm           comm;
9086   PetscMPIInt        rank;
9087 
9088   PetscFunctionBegin;
9089   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9090   if (pointSF) PetscValidHeaderSpecific(pointSF, PETSCSF_CLASSID, 2);
9091   else pointSF = dm->sf;
9092   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9093   PetscCheck(pointSF, comm, PETSC_ERR_ARG_WRONGSTATE, "DMPlex must have Point SF attached");
9094   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9095   {
9096     PetscMPIInt mpiFlag;
9097 
9098     PetscCallMPI(MPI_Comm_compare(comm, PetscObjectComm((PetscObject)pointSF), &mpiFlag));
9099     PetscCheck(mpiFlag == MPI_CONGRUENT || mpiFlag == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "DM and Point SF have different communicators (flag %d)", mpiFlag);
9100   }
9101   PetscCall(PetscSFGetGraph(pointSF, &nroots, &nleaves, &locals, &remotes));
9102   PetscCall(DMPlexIsDistributed(dm, &distributed));
9103   if (!distributed) {
9104     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);
9105     PetscFunctionReturn(PETSC_SUCCESS);
9106   }
9107   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);
9108   PetscCall(DMPlexGetOverlap(dm, &overlap));
9109 
9110   /* Check SF graph is compatible with DMPlex chart */
9111   {
9112     PetscInt pStart, pEnd, maxLeaf;
9113 
9114     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
9115     PetscCall(PetscSFGetLeafRange(pointSF, NULL, &maxLeaf));
9116     PetscCheck(allowExtraRoots || pEnd - pStart == nroots, PETSC_COMM_SELF, PETSC_ERR_PLIB, "pEnd - pStart = %" PetscInt_FMT " != nroots = %" PetscInt_FMT, pEnd - pStart, nroots);
9117     PetscCheck(maxLeaf < pEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "maxLeaf = %" PetscInt_FMT " >= pEnd = %" PetscInt_FMT, maxLeaf, pEnd);
9118   }
9119 
9120   /* Check Point SF has no local points referenced */
9121   for (l = 0; l < nleaves; l++) {
9122     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);
9123   }
9124 
9125   /* Check there are no cells in interface */
9126   if (!overlap) {
9127     PetscInt cellHeight, cStart, cEnd;
9128 
9129     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9130     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9131     for (l = 0; l < nleaves; ++l) {
9132       const PetscInt point = locals ? locals[l] : l;
9133 
9134       PetscCheck(point < cStart || point >= cEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " which is a cell", point);
9135     }
9136   }
9137 
9138   /* If some point is in interface, then all its cone points must be also in interface (either as leaves or roots) */
9139   {
9140     const PetscInt *rootdegree;
9141 
9142     PetscCall(PetscSFComputeDegreeBegin(pointSF, &rootdegree));
9143     PetscCall(PetscSFComputeDegreeEnd(pointSF, &rootdegree));
9144     for (l = 0; l < nleaves; ++l) {
9145       const PetscInt  point = locals ? locals[l] : l;
9146       const PetscInt *cone;
9147       PetscInt        coneSize, c, idx;
9148 
9149       PetscCall(DMPlexGetConeSize(dm, point, &coneSize));
9150       PetscCall(DMPlexGetCone(dm, point, &cone));
9151       for (c = 0; c < coneSize; ++c) {
9152         if (!rootdegree[cone[c]]) {
9153           if (locals) {
9154             PetscCall(PetscFindInt(cone[c], nleaves, locals, &idx));
9155           } else {
9156             idx = (cone[c] < nleaves) ? cone[c] : -1;
9157           }
9158           PetscCheck(idx >= 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " but not %" PetscInt_FMT " from its cone", point, cone[c]);
9159         }
9160       }
9161     }
9162   }
9163   PetscFunctionReturn(PETSC_SUCCESS);
9164 }
9165 
9166 /*@
9167   DMPlexCheck - Perform various checks of `DMPLEX` sanity
9168 
9169   Input Parameter:
9170 . dm - The `DMPLEX` object
9171 
9172   Level: developer
9173 
9174   Notes:
9175   This is a useful diagnostic when creating meshes programmatically.
9176 
9177   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9178 
9179   Currently does not include `DMPlexCheckCellShape()`.
9180 
9181 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9182 @*/
9183 PetscErrorCode DMPlexCheck(DM dm)
9184 {
9185   PetscInt cellHeight;
9186 
9187   PetscFunctionBegin;
9188   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9189   PetscCall(DMPlexCheckSymmetry(dm));
9190   PetscCall(DMPlexCheckSkeleton(dm, cellHeight));
9191   PetscCall(DMPlexCheckFaces(dm, cellHeight));
9192   PetscCall(DMPlexCheckGeometry(dm));
9193   PetscCall(DMPlexCheckPointSF(dm, NULL, PETSC_FALSE));
9194   PetscCall(DMPlexCheckInterfaceCones(dm));
9195   PetscFunctionReturn(PETSC_SUCCESS);
9196 }
9197 
9198 typedef struct cell_stats {
9199   PetscReal min, max, sum, squaresum;
9200   PetscInt  count;
9201 } cell_stats_t;
9202 
9203 static void MPIAPI cell_stats_reduce(void *a, void *b, int *len, MPI_Datatype *datatype)
9204 {
9205   PetscInt i, N = *len;
9206 
9207   for (i = 0; i < N; i++) {
9208     cell_stats_t *A = (cell_stats_t *)a;
9209     cell_stats_t *B = (cell_stats_t *)b;
9210 
9211     B->min = PetscMin(A->min, B->min);
9212     B->max = PetscMax(A->max, B->max);
9213     B->sum += A->sum;
9214     B->squaresum += A->squaresum;
9215     B->count += A->count;
9216   }
9217 }
9218 
9219 /*@
9220   DMPlexCheckCellShape - Checks the Jacobian of the mapping from reference to real cells and computes some minimal statistics.
9221 
9222   Collective
9223 
9224   Input Parameters:
9225 + dm        - The `DMPLEX` object
9226 . output    - If true, statistics will be displayed on `stdout`
9227 - condLimit - Display all cells above this condition number, or `PETSC_DETERMINE` for no cell output
9228 
9229   Level: developer
9230 
9231   Notes:
9232   This is mainly intended for debugging/testing purposes.
9233 
9234   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9235 
9236 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexComputeOrthogonalQuality()`
9237 @*/
9238 PetscErrorCode DMPlexCheckCellShape(DM dm, PetscBool output, PetscReal condLimit)
9239 {
9240   DM           dmCoarse;
9241   cell_stats_t stats, globalStats;
9242   MPI_Comm     comm = PetscObjectComm((PetscObject)dm);
9243   PetscReal   *J, *invJ, min = 0, max = 0, mean = 0, stdev = 0;
9244   PetscReal    limit = condLimit > 0 ? condLimit : PETSC_MAX_REAL;
9245   PetscInt     cdim, cStart, cEnd, c, eStart, eEnd, count = 0;
9246   PetscMPIInt  rank, size;
9247 
9248   PetscFunctionBegin;
9249   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9250   stats.min = PETSC_MAX_REAL;
9251   stats.max = PETSC_MIN_REAL;
9252   stats.sum = stats.squaresum = 0.;
9253   stats.count                 = 0;
9254 
9255   PetscCallMPI(MPI_Comm_size(comm, &size));
9256   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9257   PetscCall(DMGetCoordinateDim(dm, &cdim));
9258   PetscCall(PetscMalloc2(PetscSqr(cdim), &J, PetscSqr(cdim), &invJ));
9259   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
9260   PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
9261   for (c = cStart; c < cEnd; c++) {
9262     PetscInt  i;
9263     PetscReal frobJ = 0., frobInvJ = 0., cond2, cond, detJ;
9264 
9265     PetscCall(DMPlexComputeCellGeometryAffineFEM(dm, c, NULL, J, invJ, &detJ));
9266     PetscCheck(detJ >= 0.0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Mesh cell %" PetscInt_FMT " is inverted", c);
9267     for (i = 0; i < PetscSqr(cdim); ++i) {
9268       frobJ += J[i] * J[i];
9269       frobInvJ += invJ[i] * invJ[i];
9270     }
9271     cond2 = frobJ * frobInvJ;
9272     cond  = PetscSqrtReal(cond2);
9273 
9274     stats.min = PetscMin(stats.min, cond);
9275     stats.max = PetscMax(stats.max, cond);
9276     stats.sum += cond;
9277     stats.squaresum += cond2;
9278     stats.count++;
9279     if (output && cond > limit) {
9280       PetscSection coordSection;
9281       Vec          coordsLocal;
9282       PetscScalar *coords = NULL;
9283       PetscInt     Nv, d, clSize, cl, *closure = NULL;
9284 
9285       PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
9286       PetscCall(DMGetCoordinateSection(dm, &coordSection));
9287       PetscCall(DMPlexVecGetClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9288       PetscCall(PetscSynchronizedPrintf(comm, "[%d] Cell %" PetscInt_FMT " cond %g\n", rank, c, (double)cond));
9289       for (i = 0; i < Nv / cdim; ++i) {
9290         PetscCall(PetscSynchronizedPrintf(comm, "  Vertex %" PetscInt_FMT ": (", i));
9291         for (d = 0; d < cdim; ++d) {
9292           if (d > 0) PetscCall(PetscSynchronizedPrintf(comm, ", "));
9293           PetscCall(PetscSynchronizedPrintf(comm, "%g", (double)PetscRealPart(coords[i * cdim + d])));
9294         }
9295         PetscCall(PetscSynchronizedPrintf(comm, ")\n"));
9296       }
9297       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9298       for (cl = 0; cl < clSize * 2; cl += 2) {
9299         const PetscInt edge = closure[cl];
9300 
9301         if ((edge >= eStart) && (edge < eEnd)) {
9302           PetscReal len;
9303 
9304           PetscCall(DMPlexComputeCellGeometryFVM(dm, edge, &len, NULL, NULL));
9305           PetscCall(PetscSynchronizedPrintf(comm, "  Edge %" PetscInt_FMT ": length %g\n", edge, (double)len));
9306         }
9307       }
9308       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9309       PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9310     }
9311   }
9312   if (output) PetscCall(PetscSynchronizedFlush(comm, NULL));
9313 
9314   if (size > 1) {
9315     PetscMPIInt  blockLengths[2] = {4, 1};
9316     MPI_Aint     blockOffsets[2] = {offsetof(cell_stats_t, min), offsetof(cell_stats_t, count)};
9317     MPI_Datatype blockTypes[2]   = {MPIU_REAL, MPIU_INT}, statType;
9318     MPI_Op       statReduce;
9319 
9320     PetscCallMPI(MPI_Type_create_struct(2, blockLengths, blockOffsets, blockTypes, &statType));
9321     PetscCallMPI(MPI_Type_commit(&statType));
9322     PetscCallMPI(MPI_Op_create(cell_stats_reduce, PETSC_TRUE, &statReduce));
9323     PetscCallMPI(MPI_Reduce(&stats, &globalStats, 1, statType, statReduce, 0, comm));
9324     PetscCallMPI(MPI_Op_free(&statReduce));
9325     PetscCallMPI(MPI_Type_free(&statType));
9326   } else {
9327     PetscCall(PetscArraycpy(&globalStats, &stats, 1));
9328   }
9329   if (rank == 0) {
9330     count = globalStats.count;
9331     min   = globalStats.min;
9332     max   = globalStats.max;
9333     mean  = globalStats.sum / globalStats.count;
9334     stdev = globalStats.count > 1 ? PetscSqrtReal(PetscMax((globalStats.squaresum - globalStats.count * mean * mean) / (globalStats.count - 1), 0)) : 0.0;
9335   }
9336 
9337   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));
9338   PetscCall(PetscFree2(J, invJ));
9339 
9340   PetscCall(DMGetCoarseDM(dm, &dmCoarse));
9341   if (dmCoarse) {
9342     PetscBool isplex;
9343 
9344     PetscCall(PetscObjectTypeCompare((PetscObject)dmCoarse, DMPLEX, &isplex));
9345     if (isplex) PetscCall(DMPlexCheckCellShape(dmCoarse, output, condLimit));
9346   }
9347   PetscFunctionReturn(PETSC_SUCCESS);
9348 }
9349 
9350 /*@
9351   DMPlexComputeOrthogonalQuality - Compute cell-wise orthogonal quality mesh statistic. Optionally tags all cells with
9352   orthogonal quality below given tolerance.
9353 
9354   Collective
9355 
9356   Input Parameters:
9357 + dm   - The `DMPLEX` object
9358 . fv   - Optional `PetscFV` object for pre-computed cell/face centroid information
9359 - atol - [0, 1] Absolute tolerance for tagging cells.
9360 
9361   Output Parameters:
9362 + OrthQual      - `Vec` containing orthogonal quality per cell
9363 - OrthQualLabel - `DMLabel` tagging cells below atol with `DM_ADAPT_REFINE`
9364 
9365   Options Database Keys:
9366 + -dm_plex_orthogonal_quality_label_view - view OrthQualLabel if label is requested. Currently only `PETSCVIEWERASCII` is supported.
9367 - -dm_plex_orthogonal_quality_vec_view   - view OrthQual vector.
9368 
9369   Level: intermediate
9370 
9371   Notes:
9372   Orthogonal quality is given by the following formula\:
9373 
9374   $ \min \left[ \frac{A_i \cdot f_i}{\|A_i\| \|f_i\|} , \frac{A_i \cdot c_i}{\|A_i\| \|c_i\|} \right]$
9375 
9376   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
9377   is the vector from the current cells centroid to the centroid of its i'th neighbor (which shares a face with the
9378   current cell). This computes the vector similarity between each cell face and its corresponding neighbor centroid by
9379   calculating the cosine of the angle between these vectors.
9380 
9381   Orthogonal quality ranges from 1 (best) to 0 (worst).
9382 
9383   This routine is mainly useful for FVM, however is not restricted to only FVM. The `PetscFV` object is optionally used to check for
9384   pre-computed FVM cell data, but if it is not passed in then this data will be computed.
9385 
9386   Cells are tagged if they have an orthogonal quality less than or equal to the absolute tolerance.
9387 
9388 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCheckCellShape()`, `DMCreateLabel()`, `PetscFV`, `DMLabel`, `Vec`
9389 @*/
9390 PetscErrorCode DMPlexComputeOrthogonalQuality(DM dm, PetscFV fv, PetscReal atol, Vec *OrthQual, DMLabel *OrthQualLabel)
9391 {
9392   PetscInt               nc, cellHeight, cStart, cEnd, cell, cellIter = 0;
9393   PetscInt              *idx;
9394   PetscScalar           *oqVals;
9395   const PetscScalar     *cellGeomArr, *faceGeomArr;
9396   PetscReal             *ci, *fi, *Ai;
9397   MPI_Comm               comm;
9398   Vec                    cellgeom, facegeom;
9399   DM                     dmFace, dmCell;
9400   IS                     glob;
9401   ISLocalToGlobalMapping ltog;
9402   PetscViewer            vwr;
9403 
9404   PetscFunctionBegin;
9405   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9406   if (fv) PetscValidHeaderSpecific(fv, PETSCFV_CLASSID, 2);
9407   PetscAssertPointer(OrthQual, 4);
9408   PetscCheck(atol >= 0.0 && atol <= 1.0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Absolute tolerance %g not in [0,1]", (double)atol);
9409   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9410   PetscCall(DMGetDimension(dm, &nc));
9411   PetscCheck(nc >= 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must have dimension >= 2 (current %" PetscInt_FMT ")", nc);
9412   {
9413     DMPlexInterpolatedFlag interpFlag;
9414 
9415     PetscCall(DMPlexIsInterpolated(dm, &interpFlag));
9416     if (interpFlag != DMPLEX_INTERPOLATED_FULL) {
9417       PetscMPIInt rank;
9418 
9419       PetscCallMPI(MPI_Comm_rank(comm, &rank));
9420       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must be fully interpolated, DM on rank %d is not fully interpolated", rank);
9421     }
9422   }
9423   if (OrthQualLabel) {
9424     PetscAssertPointer(OrthQualLabel, 5);
9425     PetscCall(DMCreateLabel(dm, "Orthogonal_Quality"));
9426     PetscCall(DMGetLabel(dm, "Orthogonal_Quality", OrthQualLabel));
9427   } else {
9428     *OrthQualLabel = NULL;
9429   }
9430   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9431   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9432   PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_TRUE, &glob));
9433   PetscCall(ISLocalToGlobalMappingCreateIS(glob, &ltog));
9434   PetscCall(ISLocalToGlobalMappingSetType(ltog, ISLOCALTOGLOBALMAPPINGHASH));
9435   PetscCall(VecCreate(comm, OrthQual));
9436   PetscCall(VecSetType(*OrthQual, VECSTANDARD));
9437   PetscCall(VecSetSizes(*OrthQual, cEnd - cStart, PETSC_DETERMINE));
9438   PetscCall(VecSetLocalToGlobalMapping(*OrthQual, ltog));
9439   PetscCall(VecSetUp(*OrthQual));
9440   PetscCall(ISDestroy(&glob));
9441   PetscCall(ISLocalToGlobalMappingDestroy(&ltog));
9442   PetscCall(DMPlexGetDataFVM(dm, fv, &cellgeom, &facegeom, NULL));
9443   PetscCall(VecGetArrayRead(cellgeom, &cellGeomArr));
9444   PetscCall(VecGetArrayRead(facegeom, &faceGeomArr));
9445   PetscCall(VecGetDM(cellgeom, &dmCell));
9446   PetscCall(VecGetDM(facegeom, &dmFace));
9447   PetscCall(PetscMalloc5(cEnd - cStart, &idx, cEnd - cStart, &oqVals, nc, &ci, nc, &fi, nc, &Ai));
9448   for (cell = cStart; cell < cEnd; cellIter++, cell++) {
9449     PetscInt         cellneigh, cellneighiter = 0, adjSize = PETSC_DETERMINE;
9450     PetscInt         cellarr[2], *adj = NULL;
9451     PetscScalar     *cArr, *fArr;
9452     PetscReal        minvalc = 1.0, minvalf = 1.0;
9453     PetscFVCellGeom *cg;
9454 
9455     idx[cellIter] = cell - cStart;
9456     cellarr[0]    = cell;
9457     /* Make indexing into cellGeom easier */
9458     PetscCall(DMPlexPointLocalRead(dmCell, cell, cellGeomArr, &cg));
9459     PetscCall(DMPlexGetAdjacency_Internal(dm, cell, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &adjSize, &adj));
9460     /* Technically 1 too big, but easier than fiddling with empty adjacency array */
9461     PetscCall(PetscCalloc2(adjSize, &cArr, adjSize, &fArr));
9462     for (cellneigh = 0; cellneigh < adjSize; cellneighiter++, cellneigh++) {
9463       PetscInt         i;
9464       const PetscInt   neigh  = adj[cellneigh];
9465       PetscReal        normci = 0, normfi = 0, normai = 0;
9466       PetscFVCellGeom *cgneigh;
9467       PetscFVFaceGeom *fg;
9468 
9469       /* Don't count ourselves in the neighbor list */
9470       if (neigh == cell) continue;
9471       PetscCall(DMPlexPointLocalRead(dmCell, neigh, cellGeomArr, &cgneigh));
9472       cellarr[1] = neigh;
9473       {
9474         PetscInt        numcovpts;
9475         const PetscInt *covpts;
9476 
9477         PetscCall(DMPlexGetMeet(dm, 2, cellarr, &numcovpts, &covpts));
9478         PetscCall(DMPlexPointLocalRead(dmFace, covpts[0], faceGeomArr, &fg));
9479         PetscCall(DMPlexRestoreMeet(dm, 2, cellarr, &numcovpts, &covpts));
9480       }
9481 
9482       /* Compute c_i, f_i and their norms */
9483       for (i = 0; i < nc; i++) {
9484         ci[i] = cgneigh->centroid[i] - cg->centroid[i];
9485         fi[i] = fg->centroid[i] - cg->centroid[i];
9486         Ai[i] = fg->normal[i];
9487         normci += PetscPowReal(ci[i], 2);
9488         normfi += PetscPowReal(fi[i], 2);
9489         normai += PetscPowReal(Ai[i], 2);
9490       }
9491       normci = PetscSqrtReal(normci);
9492       normfi = PetscSqrtReal(normfi);
9493       normai = PetscSqrtReal(normai);
9494 
9495       /* Normalize and compute for each face-cell-normal pair */
9496       for (i = 0; i < nc; i++) {
9497         ci[i] = ci[i] / normci;
9498         fi[i] = fi[i] / normfi;
9499         Ai[i] = Ai[i] / normai;
9500         /* PetscAbs because I don't know if normals are guaranteed to point out */
9501         cArr[cellneighiter] += PetscAbs(Ai[i] * ci[i]);
9502         fArr[cellneighiter] += PetscAbs(Ai[i] * fi[i]);
9503       }
9504       if (PetscRealPart(cArr[cellneighiter]) < minvalc) minvalc = PetscRealPart(cArr[cellneighiter]);
9505       if (PetscRealPart(fArr[cellneighiter]) < minvalf) minvalf = PetscRealPart(fArr[cellneighiter]);
9506     }
9507     PetscCall(PetscFree(adj));
9508     PetscCall(PetscFree2(cArr, fArr));
9509     /* Defer to cell if they're equal */
9510     oqVals[cellIter] = PetscMin(minvalf, minvalc);
9511     if (OrthQualLabel) {
9512       if (PetscRealPart(oqVals[cellIter]) <= atol) PetscCall(DMLabelSetValue(*OrthQualLabel, cell, DM_ADAPT_REFINE));
9513     }
9514   }
9515   PetscCall(VecSetValuesLocal(*OrthQual, cEnd - cStart, idx, oqVals, INSERT_VALUES));
9516   PetscCall(VecAssemblyBegin(*OrthQual));
9517   PetscCall(VecAssemblyEnd(*OrthQual));
9518   PetscCall(VecRestoreArrayRead(cellgeom, &cellGeomArr));
9519   PetscCall(VecRestoreArrayRead(facegeom, &faceGeomArr));
9520   PetscCall(PetscOptionsGetViewer(comm, NULL, NULL, "-dm_plex_orthogonal_quality_label_view", &vwr, NULL, NULL));
9521   if (OrthQualLabel) {
9522     if (vwr) PetscCall(DMLabelView(*OrthQualLabel, vwr));
9523   }
9524   PetscCall(PetscFree5(idx, oqVals, ci, fi, Ai));
9525   PetscCall(PetscViewerDestroy(&vwr));
9526   PetscCall(VecViewFromOptions(*OrthQual, NULL, "-dm_plex_orthogonal_quality_vec_view"));
9527   PetscFunctionReturn(PETSC_SUCCESS);
9528 }
9529 
9530 /* this is here instead of DMGetOutputDM because output DM still has constraints in the local indices that affect
9531  * interpolator construction */
9532 static PetscErrorCode DMGetFullDM(DM dm, DM *odm)
9533 {
9534   PetscSection section, newSection, gsection;
9535   PetscSF      sf;
9536   PetscBool    hasConstraints, ghasConstraints;
9537 
9538   PetscFunctionBegin;
9539   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9540   PetscAssertPointer(odm, 2);
9541   PetscCall(DMGetLocalSection(dm, &section));
9542   PetscCall(PetscSectionHasConstraints(section, &hasConstraints));
9543   PetscCall(MPIU_Allreduce(&hasConstraints, &ghasConstraints, 1, MPIU_BOOL, MPI_LOR, PetscObjectComm((PetscObject)dm)));
9544   if (!ghasConstraints) {
9545     PetscCall(PetscObjectReference((PetscObject)dm));
9546     *odm = dm;
9547     PetscFunctionReturn(PETSC_SUCCESS);
9548   }
9549   PetscCall(DMClone(dm, odm));
9550   PetscCall(DMCopyFields(dm, *odm));
9551   PetscCall(DMGetLocalSection(*odm, &newSection));
9552   PetscCall(DMGetPointSF(*odm, &sf));
9553   PetscCall(PetscSectionCreateGlobalSection(newSection, sf, PETSC_TRUE, PETSC_FALSE, &gsection));
9554   PetscCall(DMSetGlobalSection(*odm, gsection));
9555   PetscCall(PetscSectionDestroy(&gsection));
9556   PetscFunctionReturn(PETSC_SUCCESS);
9557 }
9558 
9559 static PetscErrorCode DMCreateAffineInterpolationCorrection_Plex(DM dmc, DM dmf, Vec *shift)
9560 {
9561   DM        dmco, dmfo;
9562   Mat       interpo;
9563   Vec       rscale;
9564   Vec       cglobalo, clocal;
9565   Vec       fglobal, fglobalo, flocal;
9566   PetscBool regular;
9567 
9568   PetscFunctionBegin;
9569   PetscCall(DMGetFullDM(dmc, &dmco));
9570   PetscCall(DMGetFullDM(dmf, &dmfo));
9571   PetscCall(DMSetCoarseDM(dmfo, dmco));
9572   PetscCall(DMPlexGetRegularRefinement(dmf, &regular));
9573   PetscCall(DMPlexSetRegularRefinement(dmfo, regular));
9574   PetscCall(DMCreateInterpolation(dmco, dmfo, &interpo, &rscale));
9575   PetscCall(DMCreateGlobalVector(dmco, &cglobalo));
9576   PetscCall(DMCreateLocalVector(dmc, &clocal));
9577   PetscCall(VecSet(cglobalo, 0.));
9578   PetscCall(VecSet(clocal, 0.));
9579   PetscCall(DMCreateGlobalVector(dmf, &fglobal));
9580   PetscCall(DMCreateGlobalVector(dmfo, &fglobalo));
9581   PetscCall(DMCreateLocalVector(dmf, &flocal));
9582   PetscCall(VecSet(fglobal, 0.));
9583   PetscCall(VecSet(fglobalo, 0.));
9584   PetscCall(VecSet(flocal, 0.));
9585   PetscCall(DMPlexInsertBoundaryValues(dmc, PETSC_TRUE, clocal, 0., NULL, NULL, NULL));
9586   PetscCall(DMLocalToGlobalBegin(dmco, clocal, INSERT_VALUES, cglobalo));
9587   PetscCall(DMLocalToGlobalEnd(dmco, clocal, INSERT_VALUES, cglobalo));
9588   PetscCall(MatMult(interpo, cglobalo, fglobalo));
9589   PetscCall(DMGlobalToLocalBegin(dmfo, fglobalo, INSERT_VALUES, flocal));
9590   PetscCall(DMGlobalToLocalEnd(dmfo, fglobalo, INSERT_VALUES, flocal));
9591   PetscCall(DMLocalToGlobalBegin(dmf, flocal, INSERT_VALUES, fglobal));
9592   PetscCall(DMLocalToGlobalEnd(dmf, flocal, INSERT_VALUES, fglobal));
9593   *shift = fglobal;
9594   PetscCall(VecDestroy(&flocal));
9595   PetscCall(VecDestroy(&fglobalo));
9596   PetscCall(VecDestroy(&clocal));
9597   PetscCall(VecDestroy(&cglobalo));
9598   PetscCall(VecDestroy(&rscale));
9599   PetscCall(MatDestroy(&interpo));
9600   PetscCall(DMDestroy(&dmfo));
9601   PetscCall(DMDestroy(&dmco));
9602   PetscFunctionReturn(PETSC_SUCCESS);
9603 }
9604 
9605 PETSC_INTERN PetscErrorCode DMInterpolateSolution_Plex(DM coarse, DM fine, Mat interp, Vec coarseSol, Vec fineSol)
9606 {
9607   PetscObject shifto;
9608   Vec         shift;
9609 
9610   PetscFunctionBegin;
9611   if (!interp) {
9612     Vec rscale;
9613 
9614     PetscCall(DMCreateInterpolation(coarse, fine, &interp, &rscale));
9615     PetscCall(VecDestroy(&rscale));
9616   } else {
9617     PetscCall(PetscObjectReference((PetscObject)interp));
9618   }
9619   PetscCall(PetscObjectQuery((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", &shifto));
9620   if (!shifto) {
9621     PetscCall(DMCreateAffineInterpolationCorrection_Plex(coarse, fine, &shift));
9622     PetscCall(PetscObjectCompose((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", (PetscObject)shift));
9623     shifto = (PetscObject)shift;
9624     PetscCall(VecDestroy(&shift));
9625   }
9626   shift = (Vec)shifto;
9627   PetscCall(MatInterpolate(interp, coarseSol, fineSol));
9628   PetscCall(VecAXPY(fineSol, 1.0, shift));
9629   PetscCall(MatDestroy(&interp));
9630   PetscFunctionReturn(PETSC_SUCCESS);
9631 }
9632 
9633 /* Pointwise interpolation
9634      Just code FEM for now
9635      u^f = I u^c
9636      sum_k u^f_k phi^f_k = I sum_j u^c_j phi^c_j
9637      u^f_i = sum_j psi^f_i I phi^c_j u^c_j
9638      I_{ij} = psi^f_i phi^c_j
9639 */
9640 PetscErrorCode DMCreateInterpolation_Plex(DM dmCoarse, DM dmFine, Mat *interpolation, Vec *scaling)
9641 {
9642   PetscSection gsc, gsf;
9643   PetscInt     m, n;
9644   void        *ctx;
9645   DM           cdm;
9646   PetscBool    regular, ismatis, isRefined = dmCoarse->data == dmFine->data ? PETSC_FALSE : PETSC_TRUE;
9647 
9648   PetscFunctionBegin;
9649   PetscCall(DMGetGlobalSection(dmFine, &gsf));
9650   PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
9651   PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
9652   PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
9653 
9654   PetscCall(PetscStrcmp(dmCoarse->mattype, MATIS, &ismatis));
9655   PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), interpolation));
9656   PetscCall(MatSetSizes(*interpolation, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
9657   PetscCall(MatSetType(*interpolation, ismatis ? MATAIJ : dmCoarse->mattype));
9658   PetscCall(DMGetApplicationContext(dmFine, &ctx));
9659 
9660   PetscCall(DMGetCoarseDM(dmFine, &cdm));
9661   PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
9662   if (!isRefined || (regular && cdm == dmCoarse)) PetscCall(DMPlexComputeInterpolatorNested(dmCoarse, dmFine, isRefined, *interpolation, ctx));
9663   else PetscCall(DMPlexComputeInterpolatorGeneral(dmCoarse, dmFine, *interpolation, ctx));
9664   PetscCall(MatViewFromOptions(*interpolation, NULL, "-interp_mat_view"));
9665   if (scaling) {
9666     /* Use naive scaling */
9667     PetscCall(DMCreateInterpolationScale(dmCoarse, dmFine, *interpolation, scaling));
9668   }
9669   PetscFunctionReturn(PETSC_SUCCESS);
9670 }
9671 
9672 PetscErrorCode DMCreateInjection_Plex(DM dmCoarse, DM dmFine, Mat *mat)
9673 {
9674   VecScatter ctx;
9675 
9676   PetscFunctionBegin;
9677   PetscCall(DMPlexComputeInjectorFEM(dmCoarse, dmFine, &ctx, NULL));
9678   PetscCall(MatCreateScatter(PetscObjectComm((PetscObject)ctx), ctx, mat));
9679   PetscCall(VecScatterDestroy(&ctx));
9680   PetscFunctionReturn(PETSC_SUCCESS);
9681 }
9682 
9683 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[])
9684 {
9685   const PetscInt Nc = uOff[1] - uOff[0];
9686   PetscInt       c;
9687   for (c = 0; c < Nc; ++c) g0[c * Nc + c] = 1.0;
9688 }
9689 
9690 PetscErrorCode DMCreateMassMatrixLumped_Plex(DM dm, Vec *mass)
9691 {
9692   DM           dmc;
9693   PetscDS      ds;
9694   Vec          ones, locmass;
9695   IS           cellIS;
9696   PetscFormKey key;
9697   PetscInt     depth;
9698 
9699   PetscFunctionBegin;
9700   PetscCall(DMClone(dm, &dmc));
9701   PetscCall(DMCopyDisc(dm, dmc));
9702   PetscCall(DMGetDS(dmc, &ds));
9703   PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
9704   PetscCall(DMCreateGlobalVector(dmc, mass));
9705   PetscCall(DMGetLocalVector(dmc, &ones));
9706   PetscCall(DMGetLocalVector(dmc, &locmass));
9707   PetscCall(DMPlexGetDepth(dmc, &depth));
9708   PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
9709   PetscCall(VecSet(locmass, 0.0));
9710   PetscCall(VecSet(ones, 1.0));
9711   key.label = NULL;
9712   key.value = 0;
9713   key.field = 0;
9714   key.part  = 0;
9715   PetscCall(DMPlexComputeJacobian_Action_Internal(dmc, key, cellIS, 0.0, 0.0, ones, NULL, ones, locmass, NULL));
9716   PetscCall(ISDestroy(&cellIS));
9717   PetscCall(VecSet(*mass, 0.0));
9718   PetscCall(DMLocalToGlobalBegin(dmc, locmass, ADD_VALUES, *mass));
9719   PetscCall(DMLocalToGlobalEnd(dmc, locmass, ADD_VALUES, *mass));
9720   PetscCall(DMRestoreLocalVector(dmc, &ones));
9721   PetscCall(DMRestoreLocalVector(dmc, &locmass));
9722   PetscCall(DMDestroy(&dmc));
9723   PetscFunctionReturn(PETSC_SUCCESS);
9724 }
9725 
9726 PetscErrorCode DMCreateMassMatrix_Plex(DM dmCoarse, DM dmFine, Mat *mass)
9727 {
9728   PetscSection gsc, gsf;
9729   PetscInt     m, n;
9730   void        *ctx;
9731   DM           cdm;
9732   PetscBool    regular;
9733 
9734   PetscFunctionBegin;
9735   if (dmFine == dmCoarse) {
9736     DM            dmc;
9737     PetscDS       ds;
9738     PetscWeakForm wf;
9739     Vec           u;
9740     IS            cellIS;
9741     PetscFormKey  key;
9742     PetscInt      depth;
9743 
9744     PetscCall(DMClone(dmFine, &dmc));
9745     PetscCall(DMCopyDisc(dmFine, dmc));
9746     PetscCall(DMGetDS(dmc, &ds));
9747     PetscCall(PetscDSGetWeakForm(ds, &wf));
9748     PetscCall(PetscWeakFormClear(wf));
9749     PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
9750     PetscCall(DMCreateMatrix(dmc, mass));
9751     PetscCall(DMGetLocalVector(dmc, &u));
9752     PetscCall(DMPlexGetDepth(dmc, &depth));
9753     PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
9754     PetscCall(MatZeroEntries(*mass));
9755     key.label = NULL;
9756     key.value = 0;
9757     key.field = 0;
9758     key.part  = 0;
9759     PetscCall(DMPlexComputeJacobian_Internal(dmc, key, cellIS, 0.0, 0.0, u, NULL, *mass, *mass, NULL));
9760     PetscCall(ISDestroy(&cellIS));
9761     PetscCall(DMRestoreLocalVector(dmc, &u));
9762     PetscCall(DMDestroy(&dmc));
9763   } else {
9764     PetscCall(DMGetGlobalSection(dmFine, &gsf));
9765     PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
9766     PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
9767     PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
9768 
9769     PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), mass));
9770     PetscCall(MatSetSizes(*mass, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
9771     PetscCall(MatSetType(*mass, dmCoarse->mattype));
9772     PetscCall(DMGetApplicationContext(dmFine, &ctx));
9773 
9774     PetscCall(DMGetCoarseDM(dmFine, &cdm));
9775     PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
9776     if (regular && cdm == dmCoarse) PetscCall(DMPlexComputeMassMatrixNested(dmCoarse, dmFine, *mass, ctx));
9777     else PetscCall(DMPlexComputeMassMatrixGeneral(dmCoarse, dmFine, *mass, ctx));
9778   }
9779   PetscCall(MatViewFromOptions(*mass, NULL, "-mass_mat_view"));
9780   PetscFunctionReturn(PETSC_SUCCESS);
9781 }
9782 
9783 /*@
9784   DMPlexGetRegularRefinement - Get the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
9785 
9786   Input Parameter:
9787 . dm - The `DMPLEX` object
9788 
9789   Output Parameter:
9790 . regular - The flag
9791 
9792   Level: intermediate
9793 
9794 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetRegularRefinement()`
9795 @*/
9796 PetscErrorCode DMPlexGetRegularRefinement(DM dm, PetscBool *regular)
9797 {
9798   PetscFunctionBegin;
9799   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9800   PetscAssertPointer(regular, 2);
9801   *regular = ((DM_Plex *)dm->data)->regularRefinement;
9802   PetscFunctionReturn(PETSC_SUCCESS);
9803 }
9804 
9805 /*@
9806   DMPlexSetRegularRefinement - Set the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
9807 
9808   Input Parameters:
9809 + dm      - The `DMPLEX` object
9810 - regular - The flag
9811 
9812   Level: intermediate
9813 
9814 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetRegularRefinement()`
9815 @*/
9816 PetscErrorCode DMPlexSetRegularRefinement(DM dm, PetscBool regular)
9817 {
9818   PetscFunctionBegin;
9819   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9820   ((DM_Plex *)dm->data)->regularRefinement = regular;
9821   PetscFunctionReturn(PETSC_SUCCESS);
9822 }
9823 
9824 /*@
9825   DMPlexGetAnchors - Get the layout of the anchor (point-to-point) constraints.  Typically, the user will not have to
9826   call DMPlexGetAnchors() directly: if there are anchors, then `DMPlexGetAnchors()` is called during `DMGetDefaultConstraints()`.
9827 
9828   Not Collective
9829 
9830   Input Parameter:
9831 . dm - The `DMPLEX` object
9832 
9833   Output Parameters:
9834 + anchorSection - If not `NULL`, set to the section describing which points anchor the constrained points.
9835 - anchorIS      - If not `NULL`, set to the list of anchors indexed by `anchorSection`
9836 
9837   Level: intermediate
9838 
9839 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`, `IS`, `PetscSection`
9840 @*/
9841 PetscErrorCode DMPlexGetAnchors(DM dm, PetscSection *anchorSection, IS *anchorIS)
9842 {
9843   DM_Plex *plex = (DM_Plex *)dm->data;
9844 
9845   PetscFunctionBegin;
9846   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9847   if (!plex->anchorSection && !plex->anchorIS && plex->createanchors) PetscCall((*plex->createanchors)(dm));
9848   if (anchorSection) *anchorSection = plex->anchorSection;
9849   if (anchorIS) *anchorIS = plex->anchorIS;
9850   PetscFunctionReturn(PETSC_SUCCESS);
9851 }
9852 
9853 /*@
9854   DMPlexSetAnchors - Set the layout of the local anchor (point-to-point) constraints.
9855 
9856   Collective
9857 
9858   Input Parameters:
9859 + dm            - The `DMPLEX` object
9860 . anchorSection - The section that describes the mapping from constrained points to the anchor points listed in anchorIS.
9861                   Must have a local communicator (`PETSC_COMM_SELF` or derivative).
9862 - anchorIS      - The list of all anchor points.  Must have a local communicator (`PETSC_COMM_SELF` or derivative).
9863 
9864   Level: intermediate
9865 
9866   Notes:
9867   Unlike boundary conditions, when a point's degrees of freedom in a section are constrained to
9868   an outside value, the anchor constraints set a point's degrees of freedom to be a linear
9869   combination of other points' degrees of freedom.
9870 
9871   After specifying the layout of constraints with `DMPlexSetAnchors()`, one specifies the constraints by calling
9872   `DMGetDefaultConstraints()` and filling in the entries in the constraint matrix.
9873 
9874   The reference counts of `anchorSection` and `anchorIS` are incremented.
9875 
9876 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`
9877 @*/
9878 PetscErrorCode DMPlexSetAnchors(DM dm, PetscSection anchorSection, IS anchorIS)
9879 {
9880   DM_Plex    *plex = (DM_Plex *)dm->data;
9881   PetscMPIInt result;
9882 
9883   PetscFunctionBegin;
9884   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9885   if (anchorSection) {
9886     PetscValidHeaderSpecific(anchorSection, PETSC_SECTION_CLASSID, 2);
9887     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorSection), &result));
9888     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor section must have local communicator");
9889   }
9890   if (anchorIS) {
9891     PetscValidHeaderSpecific(anchorIS, IS_CLASSID, 3);
9892     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorIS), &result));
9893     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor IS must have local communicator");
9894   }
9895 
9896   PetscCall(PetscObjectReference((PetscObject)anchorSection));
9897   PetscCall(PetscSectionDestroy(&plex->anchorSection));
9898   plex->anchorSection = anchorSection;
9899 
9900   PetscCall(PetscObjectReference((PetscObject)anchorIS));
9901   PetscCall(ISDestroy(&plex->anchorIS));
9902   plex->anchorIS = anchorIS;
9903 
9904   if (PetscUnlikelyDebug(anchorIS && anchorSection)) {
9905     PetscInt        size, a, pStart, pEnd;
9906     const PetscInt *anchors;
9907 
9908     PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
9909     PetscCall(ISGetLocalSize(anchorIS, &size));
9910     PetscCall(ISGetIndices(anchorIS, &anchors));
9911     for (a = 0; a < size; a++) {
9912       PetscInt p;
9913 
9914       p = anchors[a];
9915       if (p >= pStart && p < pEnd) {
9916         PetscInt dof;
9917 
9918         PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
9919         if (dof) {
9920           PetscCall(ISRestoreIndices(anchorIS, &anchors));
9921           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Point %" PetscInt_FMT " cannot be constrained and an anchor", p);
9922         }
9923       }
9924     }
9925     PetscCall(ISRestoreIndices(anchorIS, &anchors));
9926   }
9927   /* reset the generic constraints */
9928   PetscCall(DMSetDefaultConstraints(dm, NULL, NULL, NULL));
9929   PetscFunctionReturn(PETSC_SUCCESS);
9930 }
9931 
9932 static PetscErrorCode DMPlexCreateConstraintSection_Anchors(DM dm, PetscSection section, PetscSection *cSec)
9933 {
9934   PetscSection anchorSection;
9935   PetscInt     pStart, pEnd, sStart, sEnd, p, dof, numFields, f;
9936 
9937   PetscFunctionBegin;
9938   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9939   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
9940   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, cSec));
9941   PetscCall(PetscSectionGetNumFields(section, &numFields));
9942   if (numFields) {
9943     PetscInt f;
9944     PetscCall(PetscSectionSetNumFields(*cSec, numFields));
9945 
9946     for (f = 0; f < numFields; f++) {
9947       PetscInt numComp;
9948 
9949       PetscCall(PetscSectionGetFieldComponents(section, f, &numComp));
9950       PetscCall(PetscSectionSetFieldComponents(*cSec, f, numComp));
9951     }
9952   }
9953   PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
9954   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
9955   pStart = PetscMax(pStart, sStart);
9956   pEnd   = PetscMin(pEnd, sEnd);
9957   pEnd   = PetscMax(pStart, pEnd);
9958   PetscCall(PetscSectionSetChart(*cSec, pStart, pEnd));
9959   for (p = pStart; p < pEnd; p++) {
9960     PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
9961     if (dof) {
9962       PetscCall(PetscSectionGetDof(section, p, &dof));
9963       PetscCall(PetscSectionSetDof(*cSec, p, dof));
9964       for (f = 0; f < numFields; f++) {
9965         PetscCall(PetscSectionGetFieldDof(section, p, f, &dof));
9966         PetscCall(PetscSectionSetFieldDof(*cSec, p, f, dof));
9967       }
9968     }
9969   }
9970   PetscCall(PetscSectionSetUp(*cSec));
9971   PetscCall(PetscObjectSetName((PetscObject)*cSec, "Constraint Section"));
9972   PetscFunctionReturn(PETSC_SUCCESS);
9973 }
9974 
9975 static PetscErrorCode DMPlexCreateConstraintMatrix_Anchors(DM dm, PetscSection section, PetscSection cSec, Mat *cMat)
9976 {
9977   PetscSection    aSec;
9978   PetscInt        pStart, pEnd, p, sStart, sEnd, dof, aDof, aOff, off, nnz, annz, m, n, q, a, offset, *i, *j;
9979   const PetscInt *anchors;
9980   PetscInt        numFields, f;
9981   IS              aIS;
9982   MatType         mtype;
9983   PetscBool       iscuda, iskokkos;
9984 
9985   PetscFunctionBegin;
9986   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9987   PetscCall(PetscSectionGetStorageSize(cSec, &m));
9988   PetscCall(PetscSectionGetStorageSize(section, &n));
9989   PetscCall(MatCreate(PETSC_COMM_SELF, cMat));
9990   PetscCall(MatSetSizes(*cMat, m, n, m, n));
9991   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJCUSPARSE, &iscuda));
9992   if (!iscuda) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJCUSPARSE, &iscuda));
9993   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJKOKKOS, &iskokkos));
9994   if (!iskokkos) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJKOKKOS, &iskokkos));
9995   if (iscuda) mtype = MATSEQAIJCUSPARSE;
9996   else if (iskokkos) mtype = MATSEQAIJKOKKOS;
9997   else mtype = MATSEQAIJ;
9998   PetscCall(MatSetType(*cMat, mtype));
9999   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
10000   PetscCall(ISGetIndices(aIS, &anchors));
10001   /* cSec will be a subset of aSec and section */
10002   PetscCall(PetscSectionGetChart(cSec, &pStart, &pEnd));
10003   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
10004   PetscCall(PetscMalloc1(m + 1, &i));
10005   i[0] = 0;
10006   PetscCall(PetscSectionGetNumFields(section, &numFields));
10007   for (p = pStart; p < pEnd; p++) {
10008     PetscInt rDof, rOff, r;
10009 
10010     PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10011     if (!rDof) continue;
10012     PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10013     if (numFields) {
10014       for (f = 0; f < numFields; f++) {
10015         annz = 0;
10016         for (r = 0; r < rDof; r++) {
10017           a = anchors[rOff + r];
10018           if (a < sStart || a >= sEnd) continue;
10019           PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
10020           annz += aDof;
10021         }
10022         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
10023         PetscCall(PetscSectionGetFieldOffset(cSec, p, f, &off));
10024         for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
10025       }
10026     } else {
10027       annz = 0;
10028       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10029       for (q = 0; q < dof; q++) {
10030         a = anchors[rOff + q];
10031         if (a < sStart || a >= sEnd) continue;
10032         PetscCall(PetscSectionGetDof(section, a, &aDof));
10033         annz += aDof;
10034       }
10035       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10036       PetscCall(PetscSectionGetOffset(cSec, p, &off));
10037       for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
10038     }
10039   }
10040   nnz = i[m];
10041   PetscCall(PetscMalloc1(nnz, &j));
10042   offset = 0;
10043   for (p = pStart; p < pEnd; p++) {
10044     if (numFields) {
10045       for (f = 0; f < numFields; f++) {
10046         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
10047         for (q = 0; q < dof; q++) {
10048           PetscInt rDof, rOff, r;
10049           PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10050           PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10051           for (r = 0; r < rDof; r++) {
10052             PetscInt s;
10053 
10054             a = anchors[rOff + r];
10055             if (a < sStart || a >= sEnd) continue;
10056             PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
10057             PetscCall(PetscSectionGetFieldOffset(section, a, f, &aOff));
10058             for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
10059           }
10060         }
10061       }
10062     } else {
10063       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10064       for (q = 0; q < dof; q++) {
10065         PetscInt rDof, rOff, r;
10066         PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10067         PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10068         for (r = 0; r < rDof; r++) {
10069           PetscInt s;
10070 
10071           a = anchors[rOff + r];
10072           if (a < sStart || a >= sEnd) continue;
10073           PetscCall(PetscSectionGetDof(section, a, &aDof));
10074           PetscCall(PetscSectionGetOffset(section, a, &aOff));
10075           for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
10076         }
10077       }
10078     }
10079   }
10080   PetscCall(MatSeqAIJSetPreallocationCSR(*cMat, i, j, NULL));
10081   PetscCall(PetscFree(i));
10082   PetscCall(PetscFree(j));
10083   PetscCall(ISRestoreIndices(aIS, &anchors));
10084   PetscFunctionReturn(PETSC_SUCCESS);
10085 }
10086 
10087 PetscErrorCode DMCreateDefaultConstraints_Plex(DM dm)
10088 {
10089   DM_Plex     *plex = (DM_Plex *)dm->data;
10090   PetscSection anchorSection, section, cSec;
10091   Mat          cMat;
10092 
10093   PetscFunctionBegin;
10094   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10095   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
10096   if (anchorSection) {
10097     PetscInt Nf;
10098 
10099     PetscCall(DMGetLocalSection(dm, &section));
10100     PetscCall(DMPlexCreateConstraintSection_Anchors(dm, section, &cSec));
10101     PetscCall(DMPlexCreateConstraintMatrix_Anchors(dm, section, cSec, &cMat));
10102     PetscCall(DMGetNumFields(dm, &Nf));
10103     if (Nf && plex->computeanchormatrix) PetscCall((*plex->computeanchormatrix)(dm, section, cSec, cMat));
10104     PetscCall(DMSetDefaultConstraints(dm, cSec, cMat, NULL));
10105     PetscCall(PetscSectionDestroy(&cSec));
10106     PetscCall(MatDestroy(&cMat));
10107   }
10108   PetscFunctionReturn(PETSC_SUCCESS);
10109 }
10110 
10111 PetscErrorCode DMCreateSubDomainDM_Plex(DM dm, DMLabel label, PetscInt value, IS *is, DM *subdm)
10112 {
10113   IS           subis;
10114   PetscSection section, subsection;
10115 
10116   PetscFunctionBegin;
10117   PetscCall(DMGetLocalSection(dm, &section));
10118   PetscCheck(section, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set default section for DM before splitting subdomain");
10119   PetscCheck(subdm, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set output subDM for splitting subdomain");
10120   /* Create subdomain */
10121   PetscCall(DMPlexFilter(dm, label, value, subdm));
10122   /* Create submodel */
10123   PetscCall(DMPlexGetSubpointIS(*subdm, &subis));
10124   PetscCall(PetscSectionCreateSubmeshSection(section, subis, &subsection));
10125   PetscCall(DMSetLocalSection(*subdm, subsection));
10126   PetscCall(PetscSectionDestroy(&subsection));
10127   PetscCall(DMCopyDisc(dm, *subdm));
10128   /* Create map from submodel to global model */
10129   if (is) {
10130     PetscSection    sectionGlobal, subsectionGlobal;
10131     IS              spIS;
10132     const PetscInt *spmap;
10133     PetscInt       *subIndices;
10134     PetscInt        subSize = 0, subOff = 0, pStart, pEnd, p;
10135     PetscInt        Nf, f, bs = -1, bsLocal[2], bsMinMax[2];
10136 
10137     PetscCall(DMPlexGetSubpointIS(*subdm, &spIS));
10138     PetscCall(ISGetIndices(spIS, &spmap));
10139     PetscCall(PetscSectionGetNumFields(section, &Nf));
10140     PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
10141     PetscCall(DMGetGlobalSection(*subdm, &subsectionGlobal));
10142     PetscCall(PetscSectionGetChart(subsection, &pStart, &pEnd));
10143     for (p = pStart; p < pEnd; ++p) {
10144       PetscInt gdof, pSubSize = 0;
10145 
10146       PetscCall(PetscSectionGetDof(sectionGlobal, p, &gdof));
10147       if (gdof > 0) {
10148         for (f = 0; f < Nf; ++f) {
10149           PetscInt fdof, fcdof;
10150 
10151           PetscCall(PetscSectionGetFieldDof(subsection, p, f, &fdof));
10152           PetscCall(PetscSectionGetFieldConstraintDof(subsection, p, f, &fcdof));
10153           pSubSize += fdof - fcdof;
10154         }
10155         subSize += pSubSize;
10156         if (pSubSize) {
10157           if (bs < 0) {
10158             bs = pSubSize;
10159           } else if (bs != pSubSize) {
10160             /* Layout does not admit a pointwise block size */
10161             bs = 1;
10162           }
10163         }
10164       }
10165     }
10166     /* Must have same blocksize on all procs (some might have no points) */
10167     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs;
10168     bsLocal[1] = bs;
10169     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
10170     if (bsMinMax[0] != bsMinMax[1]) {
10171       bs = 1;
10172     } else {
10173       bs = bsMinMax[0];
10174     }
10175     PetscCall(PetscMalloc1(subSize, &subIndices));
10176     for (p = pStart; p < pEnd; ++p) {
10177       PetscInt gdof, goff;
10178 
10179       PetscCall(PetscSectionGetDof(subsectionGlobal, p, &gdof));
10180       if (gdof > 0) {
10181         const PetscInt point = spmap[p];
10182 
10183         PetscCall(PetscSectionGetOffset(sectionGlobal, point, &goff));
10184         for (f = 0; f < Nf; ++f) {
10185           PetscInt fdof, fcdof, fc, f2, poff = 0;
10186 
10187           /* Can get rid of this loop by storing field information in the global section */
10188           for (f2 = 0; f2 < f; ++f2) {
10189             PetscCall(PetscSectionGetFieldDof(section, p, f2, &fdof));
10190             PetscCall(PetscSectionGetFieldConstraintDof(section, p, f2, &fcdof));
10191             poff += fdof - fcdof;
10192           }
10193           PetscCall(PetscSectionGetFieldDof(section, p, f, &fdof));
10194           PetscCall(PetscSectionGetFieldConstraintDof(section, p, f, &fcdof));
10195           for (fc = 0; fc < fdof - fcdof; ++fc, ++subOff) subIndices[subOff] = goff + poff + fc;
10196         }
10197       }
10198     }
10199     PetscCall(ISRestoreIndices(spIS, &spmap));
10200     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), subSize, subIndices, PETSC_OWN_POINTER, is));
10201     if (bs > 1) {
10202       /* We need to check that the block size does not come from non-contiguous fields */
10203       PetscInt i, j, set = 1;
10204       for (i = 0; i < subSize; i += bs) {
10205         for (j = 0; j < bs; ++j) {
10206           if (subIndices[i + j] != subIndices[i] + j) {
10207             set = 0;
10208             break;
10209           }
10210         }
10211       }
10212       if (set) PetscCall(ISSetBlockSize(*is, bs));
10213     }
10214     /* Attach nullspace */
10215     for (f = 0; f < Nf; ++f) {
10216       (*subdm)->nullspaceConstructors[f] = dm->nullspaceConstructors[f];
10217       if ((*subdm)->nullspaceConstructors[f]) break;
10218     }
10219     if (f < Nf) {
10220       MatNullSpace nullSpace;
10221       PetscCall((*(*subdm)->nullspaceConstructors[f])(*subdm, f, f, &nullSpace));
10222 
10223       PetscCall(PetscObjectCompose((PetscObject)*is, "nullspace", (PetscObject)nullSpace));
10224       PetscCall(MatNullSpaceDestroy(&nullSpace));
10225     }
10226   }
10227   PetscFunctionReturn(PETSC_SUCCESS);
10228 }
10229 
10230 /*@
10231   DMPlexMonitorThroughput - Report the cell throughput of FE integration
10232 
10233   Input Parameters:
10234 + dm    - The `DM`
10235 - dummy - unused argument
10236 
10237   Options Database Key:
10238 . -dm_plex_monitor_throughput - Activate the monitor
10239 
10240   Level: developer
10241 
10242 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexCreate()`
10243 @*/
10244 PetscErrorCode DMPlexMonitorThroughput(DM dm, void *dummy)
10245 {
10246   PetscLogHandler default_handler;
10247 
10248   PetscFunctionBegin;
10249   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10250   PetscCall(PetscLogGetDefaultHandler(&default_handler));
10251   if (default_handler) {
10252     PetscLogEvent      event;
10253     PetscEventPerfInfo eventInfo;
10254     PetscReal          cellRate, flopRate;
10255     PetscInt           cStart, cEnd, Nf, N;
10256     const char        *name;
10257 
10258     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
10259     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
10260     PetscCall(DMGetNumFields(dm, &Nf));
10261     PetscCall(PetscLogEventGetId("DMPlexResidualFE", &event));
10262     PetscCall(PetscLogEventGetPerfInfo(PETSC_DEFAULT, event, &eventInfo));
10263     N        = (cEnd - cStart) * Nf * eventInfo.count;
10264     flopRate = eventInfo.flops / eventInfo.time;
10265     cellRate = N / eventInfo.time;
10266     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)));
10267   } else {
10268     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.");
10269   }
10270   PetscFunctionReturn(PETSC_SUCCESS);
10271 }
10272