xref: /petsc/src/dm/impls/plex/plex.c (revision cf5559fda63577c063f2295093a6cc1d073921a1)
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 /* Check whether the given point is in the label. If not, update the offset to skip this point */
6802 static inline PetscErrorCode CheckPoint_Private(DMLabel label, PetscInt labelId, PetscSection section, PetscInt point, PetscInt f, PetscInt *offset, PetscBool *contains)
6803 {
6804   PetscFunctionBegin;
6805   *contains = PETSC_TRUE;
6806   if (label) {
6807     PetscInt fdof;
6808 
6809     PetscCall(DMLabelStratumHasPoint(label, labelId, point, contains));
6810     if (!*contains) {
6811       PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
6812       *offset += fdof;
6813       PetscFunctionReturn(PETSC_SUCCESS);
6814     }
6815   }
6816   PetscFunctionReturn(PETSC_SUCCESS);
6817 }
6818 
6819 /* Unlike DMPlexVecSetClosure(), this uses plex-native closure permutation, not a user-specified permutation such as DMPlexSetClosurePermutationTensor(). */
6820 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)
6821 {
6822   PetscSection    clSection;
6823   IS              clPoints;
6824   PetscScalar    *array;
6825   PetscInt       *points = NULL;
6826   const PetscInt *clp;
6827   PetscInt        numFields, numPoints, p;
6828   PetscInt        offset = 0, f;
6829 
6830   PetscFunctionBeginHot;
6831   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
6832   if (!section) PetscCall(DMGetLocalSection(dm, &section));
6833   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
6834   PetscValidHeaderSpecific(v, VEC_CLASSID, 3);
6835   PetscCall(PetscSectionGetNumFields(section, &numFields));
6836   /* Get points */
6837   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &numPoints, &points, &clSection, &clPoints, &clp));
6838   /* Get array */
6839   PetscCall(VecGetArray(v, &array));
6840   /* Get values */
6841   for (f = 0; f < numFields; ++f) {
6842     const PetscInt    **perms = NULL;
6843     const PetscScalar **flips = NULL;
6844     PetscBool           contains;
6845 
6846     if (!fieldActive[f]) {
6847       for (p = 0; p < numPoints * 2; p += 2) {
6848         PetscInt fdof;
6849         PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
6850         offset += fdof;
6851       }
6852       continue;
6853     }
6854     PetscCall(PetscSectionGetFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6855     switch (mode) {
6856     case INSERT_VALUES:
6857       for (p = 0; p < numPoints; p++) {
6858         const PetscInt     point = points[2 * p];
6859         const PetscInt    *perm  = perms ? perms[p] : NULL;
6860         const PetscScalar *flip  = flips ? flips[p] : NULL;
6861         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6862         if (!contains) continue;
6863         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_FALSE, NULL, values, &offset, array));
6864       }
6865       break;
6866     case INSERT_ALL_VALUES:
6867       for (p = 0; p < numPoints; p++) {
6868         const PetscInt     point = points[2 * p];
6869         const PetscInt    *perm  = perms ? perms[p] : NULL;
6870         const PetscScalar *flip  = flips ? flips[p] : NULL;
6871         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6872         if (!contains) continue;
6873         PetscCall(updatePointFields_private(section, point, perm, flip, f, insert, PETSC_TRUE, NULL, values, &offset, array));
6874       }
6875       break;
6876     case INSERT_BC_VALUES:
6877       for (p = 0; p < numPoints; p++) {
6878         const PetscInt     point = points[2 * p];
6879         const PetscInt    *perm  = perms ? perms[p] : NULL;
6880         const PetscScalar *flip  = flips ? flips[p] : NULL;
6881         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6882         if (!contains) continue;
6883         PetscCall(updatePointFieldsBC_private(section, point, perm, flip, f, Ncc, comps, insert, NULL, values, &offset, array));
6884       }
6885       break;
6886     case ADD_VALUES:
6887       for (p = 0; p < numPoints; p++) {
6888         const PetscInt     point = points[2 * p];
6889         const PetscInt    *perm  = perms ? perms[p] : NULL;
6890         const PetscScalar *flip  = flips ? flips[p] : NULL;
6891         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6892         if (!contains) continue;
6893         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_FALSE, NULL, values, &offset, array));
6894       }
6895       break;
6896     case ADD_ALL_VALUES:
6897       for (p = 0; p < numPoints; p++) {
6898         const PetscInt     point = points[2 * p];
6899         const PetscInt    *perm  = perms ? perms[p] : NULL;
6900         const PetscScalar *flip  = flips ? flips[p] : NULL;
6901         PetscCall(CheckPoint_Private(label, labelId, section, point, f, &offset, &contains));
6902         if (!contains) continue;
6903         PetscCall(updatePointFields_private(section, point, perm, flip, f, add, PETSC_TRUE, NULL, values, &offset, array));
6904       }
6905       break;
6906     default:
6907       SETERRQ(PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Invalid insert mode %d", mode);
6908     }
6909     PetscCall(PetscSectionRestoreFieldPointSyms(section, f, numPoints, points, &perms, &flips));
6910   }
6911   /* Cleanup points */
6912   PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &numPoints, &points, &clSection, &clPoints, &clp));
6913   /* Cleanup array */
6914   PetscCall(VecRestoreArray(v, &array));
6915   PetscFunctionReturn(PETSC_SUCCESS);
6916 }
6917 
6918 static PetscErrorCode DMPlexPrintMatSetValues(PetscViewer viewer, Mat A, PetscInt point, PetscInt numRIndices, const PetscInt rindices[], PetscInt numCIndices, const PetscInt cindices[], const PetscScalar values[])
6919 {
6920   PetscMPIInt rank;
6921   PetscInt    i, j;
6922 
6923   PetscFunctionBegin;
6924   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
6925   PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat for point %" PetscInt_FMT "\n", rank, point));
6926   for (i = 0; i < numRIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat row indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, rindices[i]));
6927   for (i = 0; i < numCIndices; i++) PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]mat col indices[%" PetscInt_FMT "] = %" PetscInt_FMT "\n", rank, i, cindices[i]));
6928   numCIndices = numCIndices ? numCIndices : numRIndices;
6929   if (!values) PetscFunctionReturn(PETSC_SUCCESS);
6930   for (i = 0; i < numRIndices; i++) {
6931     PetscCall(PetscViewerASCIIPrintf(viewer, "[%d]", rank));
6932     for (j = 0; j < numCIndices; j++) {
6933 #if defined(PETSC_USE_COMPLEX)
6934       PetscCall(PetscViewerASCIIPrintf(viewer, " (%g,%g)", (double)PetscRealPart(values[i * numCIndices + j]), (double)PetscImaginaryPart(values[i * numCIndices + j])));
6935 #else
6936       PetscCall(PetscViewerASCIIPrintf(viewer, " %g", (double)values[i * numCIndices + j]));
6937 #endif
6938     }
6939     PetscCall(PetscViewerASCIIPrintf(viewer, "\n"));
6940   }
6941   PetscFunctionReturn(PETSC_SUCCESS);
6942 }
6943 
6944 /*
6945   DMPlexGetIndicesPoint_Internal - Add the indices for dofs on a point to an index array
6946 
6947   Input Parameters:
6948 + section - The section for this data layout
6949 . islocal - Is the section (and thus indices being requested) local or global?
6950 . point   - The point contributing dofs with these indices
6951 . off     - The global offset of this point
6952 . loff    - The local offset of each field
6953 . setBC   - The flag determining whether to include indices of boundary values
6954 . perm    - A permutation of the dofs on this point, or NULL
6955 - indperm - A permutation of the entire indices array, or NULL
6956 
6957   Output Parameter:
6958 . indices - Indices for dofs on this point
6959 
6960   Level: developer
6961 
6962   Note: The indices could be local or global, depending on the value of 'off'.
6963 */
6964 PetscErrorCode DMPlexGetIndicesPoint_Internal(PetscSection section, PetscBool islocal, PetscInt point, PetscInt off, PetscInt *loff, PetscBool setBC, const PetscInt perm[], const PetscInt indperm[], PetscInt indices[])
6965 {
6966   PetscInt        dof;   /* The number of unknowns on this point */
6967   PetscInt        cdof;  /* The number of constraints on this point */
6968   const PetscInt *cdofs; /* The indices of the constrained dofs on this point */
6969   PetscInt        cind = 0, k;
6970 
6971   PetscFunctionBegin;
6972   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
6973   PetscCall(PetscSectionGetDof(section, point, &dof));
6974   PetscCall(PetscSectionGetConstraintDof(section, point, &cdof));
6975   if (!cdof || setBC) {
6976     for (k = 0; k < dof; ++k) {
6977       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
6978       const PetscInt ind    = indperm ? indperm[preind] : preind;
6979 
6980       indices[ind] = off + k;
6981     }
6982   } else {
6983     PetscCall(PetscSectionGetConstraintIndices(section, point, &cdofs));
6984     for (k = 0; k < dof; ++k) {
6985       const PetscInt preind = perm ? *loff + perm[k] : *loff + k;
6986       const PetscInt ind    = indperm ? indperm[preind] : preind;
6987 
6988       if ((cind < cdof) && (k == cdofs[cind])) {
6989         /* Insert check for returning constrained indices */
6990         indices[ind] = -(off + k + 1);
6991         ++cind;
6992       } else {
6993         indices[ind] = off + k - (islocal ? 0 : cind);
6994       }
6995     }
6996   }
6997   *loff += dof;
6998   PetscFunctionReturn(PETSC_SUCCESS);
6999 }
7000 
7001 /*
7002  DMPlexGetIndicesPointFields_Internal - gets section indices for a point in its canonical ordering.
7003 
7004  Input Parameters:
7005 + section - a section (global or local)
7006 - islocal - `PETSC_TRUE` if requesting local indices (i.e., section is local); `PETSC_FALSE` for global
7007 . point - point within section
7008 . off - The offset of this point in the (local or global) indexed space - should match islocal and (usually) the section
7009 . foffs - array of length numFields containing the offset in canonical point ordering (the location in indices) of each field
7010 . setBC - identify constrained (boundary condition) points via involution.
7011 . perms - perms[f][permsoff][:] is a permutation of dofs within each field
7012 . permsoff - offset
7013 - indperm - index permutation
7014 
7015  Output Parameter:
7016 . foffs - each entry is incremented by the number of (unconstrained if setBC=FALSE) dofs in that field
7017 . indices - array to hold indices (as defined by section) of each dof associated with point
7018 
7019  Notes:
7020  If section is local and setBC=true, there is no distinction between constrained and unconstrained dofs.
7021  If section is local and setBC=false, the indices for constrained points are the involution -(i+1) of their position
7022  in the local vector.
7023 
7024  If section is global and setBC=false, the indices for constrained points are negative (and their value is not
7025  significant).  It is invalid to call with a global section and setBC=true.
7026 
7027  Developer Note:
7028  The section is only used for field layout, so islocal is technically a statement about the offset (off).  At some point
7029  in the future, global sections may have fields set, in which case we could pass the global section and obtain the
7030  offset could be obtained from the section instead of passing it explicitly as we do now.
7031 
7032  Example:
7033  Suppose a point contains one field with three components, and for which the unconstrained indices are {10, 11, 12}.
7034  When the middle component is constrained, we get the array {10, -12, 12} for (islocal=TRUE, setBC=FALSE).
7035  Note that -12 is the involution of 11, so the user can involute negative indices to recover local indices.
7036  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.
7037 
7038  Level: developer
7039 */
7040 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[])
7041 {
7042   PetscInt numFields, foff, f;
7043 
7044   PetscFunctionBegin;
7045   PetscCheck(islocal || !setBC, PetscObjectComm((PetscObject)section), PETSC_ERR_ARG_INCOMP, "setBC incompatible with global indices; use a local section or disable setBC");
7046   PetscCall(PetscSectionGetNumFields(section, &numFields));
7047   for (f = 0, foff = 0; f < numFields; ++f) {
7048     PetscInt        fdof, cfdof;
7049     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7050     PetscInt        cind = 0, b;
7051     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7052 
7053     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7054     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7055     if (!cfdof || setBC) {
7056       for (b = 0; b < fdof; ++b) {
7057         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7058         const PetscInt ind    = indperm ? indperm[preind] : preind;
7059 
7060         indices[ind] = off + foff + b;
7061       }
7062     } else {
7063       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7064       for (b = 0; b < fdof; ++b) {
7065         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7066         const PetscInt ind    = indperm ? indperm[preind] : preind;
7067 
7068         if ((cind < cfdof) && (b == fcdofs[cind])) {
7069           indices[ind] = -(off + foff + b + 1);
7070           ++cind;
7071         } else {
7072           indices[ind] = off + foff + b - (islocal ? 0 : cind);
7073         }
7074       }
7075     }
7076     foff += (setBC || islocal ? fdof : (fdof - cfdof));
7077     foffs[f] += fdof;
7078   }
7079   PetscFunctionReturn(PETSC_SUCCESS);
7080 }
7081 
7082 /*
7083   This version believes the globalSection offsets for each field, rather than just the point offset
7084 
7085  . foffs - The offset into 'indices' for each field, since it is segregated by field
7086 
7087  Notes:
7088  The semantics of this function relate to that of setBC=FALSE in DMPlexGetIndicesPointFields_Internal.
7089  Since this function uses global indices, setBC=TRUE would be invalid, so no such argument exists.
7090 */
7091 static PetscErrorCode DMPlexGetIndicesPointFieldsSplit_Internal(PetscSection section, PetscSection globalSection, PetscInt point, PetscInt foffs[], const PetscInt ***perms, PetscInt permsoff, const PetscInt indperm[], PetscInt indices[])
7092 {
7093   PetscInt numFields, foff, f;
7094 
7095   PetscFunctionBegin;
7096   PetscCall(PetscSectionGetNumFields(section, &numFields));
7097   for (f = 0; f < numFields; ++f) {
7098     PetscInt        fdof, cfdof;
7099     const PetscInt *fcdofs; /* The indices of the constrained dofs for field f on this point */
7100     PetscInt        cind = 0, b;
7101     const PetscInt *perm = (perms && perms[f]) ? perms[f][permsoff] : NULL;
7102 
7103     PetscCall(PetscSectionGetFieldDof(section, point, f, &fdof));
7104     PetscCall(PetscSectionGetFieldConstraintDof(section, point, f, &cfdof));
7105     PetscCall(PetscSectionGetFieldOffset(globalSection, point, f, &foff));
7106     if (!cfdof) {
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         indices[ind] = foff + b;
7112       }
7113     } else {
7114       PetscCall(PetscSectionGetFieldConstraintIndices(section, point, f, &fcdofs));
7115       for (b = 0; b < fdof; ++b) {
7116         const PetscInt preind = perm ? foffs[f] + perm[b] : foffs[f] + b;
7117         const PetscInt ind    = indperm ? indperm[preind] : preind;
7118 
7119         if ((cind < cfdof) && (b == fcdofs[cind])) {
7120           indices[ind] = -(foff + b + 1);
7121           ++cind;
7122         } else {
7123           indices[ind] = foff + b - cind;
7124         }
7125       }
7126     }
7127     foffs[f] += fdof;
7128   }
7129   PetscFunctionReturn(PETSC_SUCCESS);
7130 }
7131 
7132 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)
7133 {
7134   Mat             cMat;
7135   PetscSection    aSec, cSec;
7136   IS              aIS;
7137   PetscInt        aStart = -1, aEnd = -1;
7138   const PetscInt *anchors;
7139   PetscInt        numFields, f, p, q, newP = 0;
7140   PetscInt        newNumPoints = 0, newNumIndices = 0;
7141   PetscInt       *newPoints, *indices, *newIndices;
7142   PetscInt        maxAnchor, maxDof;
7143   PetscInt        newOffsets[32];
7144   PetscInt       *pointMatOffsets[32];
7145   PetscInt       *newPointOffsets[32];
7146   PetscScalar    *pointMat[32];
7147   PetscScalar    *newValues      = NULL, *tmpValues;
7148   PetscBool       anyConstrained = PETSC_FALSE;
7149 
7150   PetscFunctionBegin;
7151   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7152   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7153   PetscCall(PetscSectionGetNumFields(section, &numFields));
7154 
7155   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
7156   /* if there are point-to-point constraints */
7157   if (aSec) {
7158     PetscCall(PetscArrayzero(newOffsets, 32));
7159     PetscCall(ISGetIndices(aIS, &anchors));
7160     PetscCall(PetscSectionGetChart(aSec, &aStart, &aEnd));
7161     /* figure out how many points are going to be in the new element matrix
7162      * (we allow double counting, because it's all just going to be summed
7163      * into the global matrix anyway) */
7164     for (p = 0; p < 2 * numPoints; p += 2) {
7165       PetscInt b    = points[p];
7166       PetscInt bDof = 0, bSecDof;
7167 
7168       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7169       if (!bSecDof) continue;
7170       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7171       if (bDof) {
7172         /* this point is constrained */
7173         /* it is going to be replaced by its anchors */
7174         PetscInt bOff, q;
7175 
7176         anyConstrained = PETSC_TRUE;
7177         newNumPoints += bDof;
7178         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7179         for (q = 0; q < bDof; q++) {
7180           PetscInt a = anchors[bOff + q];
7181           PetscInt aDof;
7182 
7183           PetscCall(PetscSectionGetDof(section, a, &aDof));
7184           newNumIndices += aDof;
7185           for (f = 0; f < numFields; ++f) {
7186             PetscInt fDof;
7187 
7188             PetscCall(PetscSectionGetFieldDof(section, a, f, &fDof));
7189             newOffsets[f + 1] += fDof;
7190           }
7191         }
7192       } else {
7193         /* this point is not constrained */
7194         newNumPoints++;
7195         newNumIndices += bSecDof;
7196         for (f = 0; f < numFields; ++f) {
7197           PetscInt fDof;
7198 
7199           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7200           newOffsets[f + 1] += fDof;
7201         }
7202       }
7203     }
7204   }
7205   if (!anyConstrained) {
7206     if (outNumPoints) *outNumPoints = 0;
7207     if (outNumIndices) *outNumIndices = 0;
7208     if (outPoints) *outPoints = NULL;
7209     if (outValues) *outValues = NULL;
7210     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7211     PetscFunctionReturn(PETSC_SUCCESS);
7212   }
7213 
7214   if (outNumPoints) *outNumPoints = newNumPoints;
7215   if (outNumIndices) *outNumIndices = newNumIndices;
7216 
7217   for (f = 0; f < numFields; ++f) newOffsets[f + 1] += newOffsets[f];
7218 
7219   if (!outPoints && !outValues) {
7220     if (offsets) {
7221       for (f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7222     }
7223     if (aSec) PetscCall(ISRestoreIndices(aIS, &anchors));
7224     PetscFunctionReturn(PETSC_SUCCESS);
7225   }
7226 
7227   PetscCheck(!numFields || newOffsets[numFields] == newNumIndices, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, newOffsets[numFields], newNumIndices);
7228 
7229   PetscCall(DMGetDefaultConstraints(dm, &cSec, &cMat, NULL));
7230 
7231   /* workspaces */
7232   if (numFields) {
7233     for (f = 0; f < numFields; f++) {
7234       PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[f]));
7235       PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[f]));
7236     }
7237   } else {
7238     PetscCall(DMGetWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[0]));
7239     PetscCall(DMGetWorkArray(dm, numPoints, MPIU_INT, &newPointOffsets[0]));
7240   }
7241 
7242   /* get workspaces for the point-to-point matrices */
7243   if (numFields) {
7244     PetscInt totalOffset, totalMatOffset;
7245 
7246     for (p = 0; p < numPoints; p++) {
7247       PetscInt b    = points[2 * p];
7248       PetscInt bDof = 0, bSecDof;
7249 
7250       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7251       if (!bSecDof) {
7252         for (f = 0; f < numFields; f++) {
7253           newPointOffsets[f][p + 1] = 0;
7254           pointMatOffsets[f][p + 1] = 0;
7255         }
7256         continue;
7257       }
7258       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7259       if (bDof) {
7260         for (f = 0; f < numFields; f++) {
7261           PetscInt fDof, q, bOff, allFDof = 0;
7262 
7263           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7264           PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7265           for (q = 0; q < bDof; q++) {
7266             PetscInt a = anchors[bOff + q];
7267             PetscInt aFDof;
7268 
7269             PetscCall(PetscSectionGetFieldDof(section, a, f, &aFDof));
7270             allFDof += aFDof;
7271           }
7272           newPointOffsets[f][p + 1] = allFDof;
7273           pointMatOffsets[f][p + 1] = fDof * allFDof;
7274         }
7275       } else {
7276         for (f = 0; f < numFields; f++) {
7277           PetscInt fDof;
7278 
7279           PetscCall(PetscSectionGetFieldDof(section, b, f, &fDof));
7280           newPointOffsets[f][p + 1] = fDof;
7281           pointMatOffsets[f][p + 1] = 0;
7282         }
7283       }
7284     }
7285     for (f = 0, totalOffset = 0, totalMatOffset = 0; f < numFields; f++) {
7286       newPointOffsets[f][0] = totalOffset;
7287       pointMatOffsets[f][0] = totalMatOffset;
7288       for (p = 0; p < numPoints; p++) {
7289         newPointOffsets[f][p + 1] += newPointOffsets[f][p];
7290         pointMatOffsets[f][p + 1] += pointMatOffsets[f][p];
7291       }
7292       totalOffset    = newPointOffsets[f][numPoints];
7293       totalMatOffset = pointMatOffsets[f][numPoints];
7294       PetscCall(DMGetWorkArray(dm, pointMatOffsets[f][numPoints], MPIU_SCALAR, &pointMat[f]));
7295     }
7296   } else {
7297     for (p = 0; p < numPoints; p++) {
7298       PetscInt b    = points[2 * p];
7299       PetscInt bDof = 0, bSecDof;
7300 
7301       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7302       if (!bSecDof) {
7303         newPointOffsets[0][p + 1] = 0;
7304         pointMatOffsets[0][p + 1] = 0;
7305         continue;
7306       }
7307       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7308       if (bDof) {
7309         PetscInt bOff, q, allDof = 0;
7310 
7311         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7312         for (q = 0; q < bDof; q++) {
7313           PetscInt a = anchors[bOff + q], aDof;
7314 
7315           PetscCall(PetscSectionGetDof(section, a, &aDof));
7316           allDof += aDof;
7317         }
7318         newPointOffsets[0][p + 1] = allDof;
7319         pointMatOffsets[0][p + 1] = bSecDof * allDof;
7320       } else {
7321         newPointOffsets[0][p + 1] = bSecDof;
7322         pointMatOffsets[0][p + 1] = 0;
7323       }
7324     }
7325     newPointOffsets[0][0] = 0;
7326     pointMatOffsets[0][0] = 0;
7327     for (p = 0; p < numPoints; p++) {
7328       newPointOffsets[0][p + 1] += newPointOffsets[0][p];
7329       pointMatOffsets[0][p + 1] += pointMatOffsets[0][p];
7330     }
7331     PetscCall(DMGetWorkArray(dm, pointMatOffsets[0][numPoints], MPIU_SCALAR, &pointMat[0]));
7332   }
7333 
7334   /* output arrays */
7335   PetscCall(DMGetWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7336 
7337   /* get the point-to-point matrices; construct newPoints */
7338   PetscCall(PetscSectionGetMaxDof(aSec, &maxAnchor));
7339   PetscCall(PetscSectionGetMaxDof(section, &maxDof));
7340   PetscCall(DMGetWorkArray(dm, maxDof, MPIU_INT, &indices));
7341   PetscCall(DMGetWorkArray(dm, maxAnchor * maxDof, MPIU_INT, &newIndices));
7342   if (numFields) {
7343     for (p = 0, newP = 0; p < numPoints; p++) {
7344       PetscInt b    = points[2 * p];
7345       PetscInt o    = points[2 * p + 1];
7346       PetscInt bDof = 0, bSecDof;
7347 
7348       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7349       if (!bSecDof) continue;
7350       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7351       if (bDof) {
7352         PetscInt fStart[32], fEnd[32], fAnchorStart[32], fAnchorEnd[32], bOff, q;
7353 
7354         fStart[0] = 0;
7355         fEnd[0]   = 0;
7356         for (f = 0; f < numFields; f++) {
7357           PetscInt fDof;
7358 
7359           PetscCall(PetscSectionGetFieldDof(cSec, b, f, &fDof));
7360           fStart[f + 1] = fStart[f] + fDof;
7361           fEnd[f + 1]   = fStart[f + 1];
7362         }
7363         PetscCall(PetscSectionGetOffset(cSec, b, &bOff));
7364         PetscCall(DMPlexGetIndicesPointFields_Internal(cSec, PETSC_TRUE, b, bOff, fEnd, PETSC_TRUE, perms, p, NULL, indices));
7365 
7366         fAnchorStart[0] = 0;
7367         fAnchorEnd[0]   = 0;
7368         for (f = 0; f < numFields; f++) {
7369           PetscInt fDof = newPointOffsets[f][p + 1] - newPointOffsets[f][p];
7370 
7371           fAnchorStart[f + 1] = fAnchorStart[f] + fDof;
7372           fAnchorEnd[f + 1]   = fAnchorStart[f + 1];
7373         }
7374         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7375         for (q = 0; q < bDof; q++) {
7376           PetscInt a = anchors[bOff + q], aOff;
7377 
7378           /* we take the orientation of ap into account in the order that we constructed the indices above: the newly added points have no orientation */
7379           newPoints[2 * (newP + q)]     = a;
7380           newPoints[2 * (newP + q) + 1] = 0;
7381           PetscCall(PetscSectionGetOffset(section, a, &aOff));
7382           PetscCall(DMPlexGetIndicesPointFields_Internal(section, PETSC_TRUE, a, aOff, fAnchorEnd, PETSC_TRUE, NULL, -1, NULL, newIndices));
7383         }
7384         newP += bDof;
7385 
7386         if (outValues) {
7387           /* get the point-to-point submatrix */
7388           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]));
7389         }
7390       } else {
7391         newPoints[2 * newP]     = b;
7392         newPoints[2 * newP + 1] = o;
7393         newP++;
7394       }
7395     }
7396   } else {
7397     for (p = 0; p < numPoints; p++) {
7398       PetscInt b    = points[2 * p];
7399       PetscInt o    = points[2 * p + 1];
7400       PetscInt bDof = 0, bSecDof;
7401 
7402       PetscCall(PetscSectionGetDof(section, b, &bSecDof));
7403       if (!bSecDof) continue;
7404       if (b >= aStart && b < aEnd) PetscCall(PetscSectionGetDof(aSec, b, &bDof));
7405       if (bDof) {
7406         PetscInt bEnd = 0, bAnchorEnd = 0, bOff;
7407 
7408         PetscCall(PetscSectionGetOffset(cSec, b, &bOff));
7409         PetscCall(DMPlexGetIndicesPoint_Internal(cSec, PETSC_TRUE, b, bOff, &bEnd, PETSC_TRUE, (perms && perms[0]) ? perms[0][p] : NULL, NULL, indices));
7410 
7411         PetscCall(PetscSectionGetOffset(aSec, b, &bOff));
7412         for (q = 0; q < bDof; q++) {
7413           PetscInt a = anchors[bOff + q], aOff;
7414 
7415           /* we take the orientation of ap into account in the order that we constructed the indices above: the newly added points have no orientation */
7416 
7417           newPoints[2 * (newP + q)]     = a;
7418           newPoints[2 * (newP + q) + 1] = 0;
7419           PetscCall(PetscSectionGetOffset(section, a, &aOff));
7420           PetscCall(DMPlexGetIndicesPoint_Internal(section, PETSC_TRUE, a, aOff, &bAnchorEnd, PETSC_TRUE, NULL, NULL, newIndices));
7421         }
7422         newP += bDof;
7423 
7424         /* get the point-to-point submatrix */
7425         if (outValues) PetscCall(MatGetValues(cMat, bEnd, indices, bAnchorEnd, newIndices, pointMat[0] + pointMatOffsets[0][p]));
7426       } else {
7427         newPoints[2 * newP]     = b;
7428         newPoints[2 * newP + 1] = o;
7429         newP++;
7430       }
7431     }
7432   }
7433 
7434   if (outValues) {
7435     PetscCall(DMGetWorkArray(dm, newNumIndices * numIndices, MPIU_SCALAR, &tmpValues));
7436     PetscCall(PetscArrayzero(tmpValues, newNumIndices * numIndices));
7437     /* multiply constraints on the right */
7438     if (numFields) {
7439       for (f = 0; f < numFields; f++) {
7440         PetscInt oldOff = offsets[f];
7441 
7442         for (p = 0; p < numPoints; p++) {
7443           PetscInt cStart = newPointOffsets[f][p];
7444           PetscInt b      = points[2 * p];
7445           PetscInt c, r, k;
7446           PetscInt dof;
7447 
7448           PetscCall(PetscSectionGetFieldDof(section, b, f, &dof));
7449           if (!dof) continue;
7450           if (pointMatOffsets[f][p] < pointMatOffsets[f][p + 1]) {
7451             PetscInt           nCols = newPointOffsets[f][p + 1] - cStart;
7452             const PetscScalar *mat   = pointMat[f] + pointMatOffsets[f][p];
7453 
7454             for (r = 0; r < numIndices; r++) {
7455               for (c = 0; c < nCols; c++) {
7456                 for (k = 0; k < dof; k++) tmpValues[r * newNumIndices + cStart + c] += values[r * numIndices + oldOff + k] * mat[k * nCols + c];
7457               }
7458             }
7459           } else {
7460             /* copy this column as is */
7461             for (r = 0; r < numIndices; r++) {
7462               for (c = 0; c < dof; c++) tmpValues[r * newNumIndices + cStart + c] = values[r * numIndices + oldOff + c];
7463             }
7464           }
7465           oldOff += dof;
7466         }
7467       }
7468     } else {
7469       PetscInt oldOff = 0;
7470       for (p = 0; p < numPoints; p++) {
7471         PetscInt cStart = newPointOffsets[0][p];
7472         PetscInt b      = points[2 * p];
7473         PetscInt c, r, k;
7474         PetscInt dof;
7475 
7476         PetscCall(PetscSectionGetDof(section, b, &dof));
7477         if (!dof) continue;
7478         if (pointMatOffsets[0][p] < pointMatOffsets[0][p + 1]) {
7479           PetscInt           nCols = newPointOffsets[0][p + 1] - cStart;
7480           const PetscScalar *mat   = pointMat[0] + pointMatOffsets[0][p];
7481 
7482           for (r = 0; r < numIndices; r++) {
7483             for (c = 0; c < nCols; c++) {
7484               for (k = 0; k < dof; k++) tmpValues[r * newNumIndices + cStart + c] += mat[k * nCols + c] * values[r * numIndices + oldOff + k];
7485             }
7486           }
7487         } else {
7488           /* copy this column as is */
7489           for (r = 0; r < numIndices; r++) {
7490             for (c = 0; c < dof; c++) tmpValues[r * newNumIndices + cStart + c] = values[r * numIndices + oldOff + c];
7491           }
7492         }
7493         oldOff += dof;
7494       }
7495     }
7496 
7497     if (multiplyLeft) {
7498       PetscCall(DMGetWorkArray(dm, newNumIndices * newNumIndices, MPIU_SCALAR, &newValues));
7499       PetscCall(PetscArrayzero(newValues, newNumIndices * newNumIndices));
7500       /* multiply constraints transpose on the left */
7501       if (numFields) {
7502         for (f = 0; f < numFields; f++) {
7503           PetscInt oldOff = offsets[f];
7504 
7505           for (p = 0; p < numPoints; p++) {
7506             PetscInt rStart = newPointOffsets[f][p];
7507             PetscInt b      = points[2 * p];
7508             PetscInt c, r, k;
7509             PetscInt dof;
7510 
7511             PetscCall(PetscSectionGetFieldDof(section, b, f, &dof));
7512             if (pointMatOffsets[f][p] < pointMatOffsets[f][p + 1]) {
7513               PetscInt                          nRows = newPointOffsets[f][p + 1] - rStart;
7514               const PetscScalar *PETSC_RESTRICT mat   = pointMat[f] + pointMatOffsets[f][p];
7515 
7516               for (r = 0; r < nRows; r++) {
7517                 for (c = 0; c < newNumIndices; c++) {
7518                   for (k = 0; k < dof; k++) newValues[(rStart + r) * newNumIndices + c] += mat[k * nRows + r] * tmpValues[(oldOff + k) * newNumIndices + c];
7519                 }
7520               }
7521             } else {
7522               /* copy this row as is */
7523               for (r = 0; r < dof; r++) {
7524                 for (c = 0; c < newNumIndices; c++) newValues[(rStart + r) * newNumIndices + c] = tmpValues[(oldOff + r) * newNumIndices + c];
7525               }
7526             }
7527             oldOff += dof;
7528           }
7529         }
7530       } else {
7531         PetscInt oldOff = 0;
7532 
7533         for (p = 0; p < numPoints; p++) {
7534           PetscInt rStart = newPointOffsets[0][p];
7535           PetscInt b      = points[2 * p];
7536           PetscInt c, r, k;
7537           PetscInt dof;
7538 
7539           PetscCall(PetscSectionGetDof(section, b, &dof));
7540           if (pointMatOffsets[0][p] < pointMatOffsets[0][p + 1]) {
7541             PetscInt                          nRows = newPointOffsets[0][p + 1] - rStart;
7542             const PetscScalar *PETSC_RESTRICT mat   = pointMat[0] + pointMatOffsets[0][p];
7543 
7544             for (r = 0; r < nRows; r++) {
7545               for (c = 0; c < newNumIndices; c++) {
7546                 for (k = 0; k < dof; k++) newValues[(rStart + r) * newNumIndices + c] += mat[k * nRows + r] * tmpValues[(oldOff + k) * newNumIndices + c];
7547               }
7548             }
7549           } else {
7550             /* copy this row as is */
7551             for (r = 0; r < dof; r++) {
7552               for (c = 0; c < newNumIndices; c++) newValues[(rStart + r) * newNumIndices + c] = tmpValues[(oldOff + r) * newNumIndices + c];
7553             }
7554           }
7555           oldOff += dof;
7556         }
7557       }
7558 
7559       PetscCall(DMRestoreWorkArray(dm, newNumIndices * numIndices, MPIU_SCALAR, &tmpValues));
7560     } else {
7561       newValues = tmpValues;
7562     }
7563   }
7564 
7565   /* clean up */
7566   PetscCall(DMRestoreWorkArray(dm, maxDof, MPIU_INT, &indices));
7567   PetscCall(DMRestoreWorkArray(dm, maxAnchor * maxDof, MPIU_INT, &newIndices));
7568 
7569   if (numFields) {
7570     for (f = 0; f < numFields; f++) {
7571       PetscCall(DMRestoreWorkArray(dm, pointMatOffsets[f][numPoints], MPIU_SCALAR, &pointMat[f]));
7572       PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[f]));
7573       PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[f]));
7574     }
7575   } else {
7576     PetscCall(DMRestoreWorkArray(dm, pointMatOffsets[0][numPoints], MPIU_SCALAR, &pointMat[0]));
7577     PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &pointMatOffsets[0]));
7578     PetscCall(DMRestoreWorkArray(dm, numPoints + 1, MPIU_INT, &newPointOffsets[0]));
7579   }
7580   PetscCall(ISRestoreIndices(aIS, &anchors));
7581 
7582   /* output */
7583   if (outPoints) {
7584     *outPoints = newPoints;
7585   } else {
7586     PetscCall(DMRestoreWorkArray(dm, 2 * newNumPoints, MPIU_INT, &newPoints));
7587   }
7588   if (outValues) *outValues = newValues;
7589   for (f = 0; f <= numFields; f++) offsets[f] = newOffsets[f];
7590   PetscFunctionReturn(PETSC_SUCCESS);
7591 }
7592 
7593 /*@C
7594   DMPlexGetClosureIndices - Gets the global dof indices associated with the closure of the given point within the provided sections.
7595 
7596   Not collective
7597 
7598   Input Parameters:
7599 + dm         - The `DM`
7600 . section    - The `PetscSection` describing the points (a local section)
7601 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
7602 . point      - The point defining the closure
7603 - useClPerm  - Use the closure point permutation if available
7604 
7605   Output Parameters:
7606 + numIndices - The number of dof indices in the closure of point with the input sections
7607 . indices    - The dof indices
7608 . outOffsets - Array to write the field offsets into, or `NULL`
7609 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or `NULL`
7610 
7611   Level: advanced
7612 
7613   Notes:
7614   Must call `DMPlexRestoreClosureIndices()` to free allocated memory
7615 
7616   If `idxSection` is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
7617   of those indices is not significant.  If `idxSection` is local, the constrained dofs will yield the involution -(idx+1)
7618   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
7619   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when `idxSection` == section, otherwise global
7620   indices (with the above semantics) are implied.
7621 
7622 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexRestoreClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`,
7623           `PetscSection`, `DMGetGlobalSection()`
7624 @*/
7625 PetscErrorCode DMPlexGetClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
7626 {
7627   /* Closure ordering */
7628   PetscSection    clSection;
7629   IS              clPoints;
7630   const PetscInt *clp;
7631   PetscInt       *points;
7632   const PetscInt *clperm = NULL;
7633   /* Dof permutation and sign flips */
7634   const PetscInt    **perms[32] = {NULL};
7635   const PetscScalar **flips[32] = {NULL};
7636   PetscScalar        *valCopy   = NULL;
7637   /* Hanging node constraints */
7638   PetscInt    *pointsC = NULL;
7639   PetscScalar *valuesC = NULL;
7640   PetscInt     NclC, NiC;
7641 
7642   PetscInt *idx;
7643   PetscInt  Nf, Ncl, Ni = 0, offsets[32], p, f;
7644   PetscBool isLocal = (section == idxSection) ? PETSC_TRUE : PETSC_FALSE;
7645 
7646   PetscFunctionBeginHot;
7647   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7648   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7649   PetscValidHeaderSpecific(idxSection, PETSC_SECTION_CLASSID, 3);
7650   if (numIndices) PetscAssertPointer(numIndices, 6);
7651   if (indices) PetscAssertPointer(indices, 7);
7652   if (outOffsets) PetscAssertPointer(outOffsets, 8);
7653   if (values) PetscAssertPointer(values, 9);
7654   PetscCall(PetscSectionGetNumFields(section, &Nf));
7655   PetscCheck(Nf <= 31, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", Nf);
7656   PetscCall(PetscArrayzero(offsets, 32));
7657   /* 1) Get points in closure */
7658   PetscCall(DMPlexGetCompressedClosure(dm, section, point, 0, &Ncl, &points, &clSection, &clPoints, &clp));
7659   if (useClPerm) {
7660     PetscInt depth, clsize;
7661     PetscCall(DMPlexGetPointDepth(dm, point, &depth));
7662     for (clsize = 0, p = 0; p < Ncl; p++) {
7663       PetscInt dof;
7664       PetscCall(PetscSectionGetDof(section, points[2 * p], &dof));
7665       clsize += dof;
7666     }
7667     PetscCall(PetscSectionGetClosureInversePermutation_Internal(section, (PetscObject)dm, depth, clsize, &clperm));
7668   }
7669   /* 2) Get number of indices on these points and field offsets from section */
7670   for (p = 0; p < Ncl * 2; p += 2) {
7671     PetscInt dof, fdof;
7672 
7673     PetscCall(PetscSectionGetDof(section, points[p], &dof));
7674     for (f = 0; f < Nf; ++f) {
7675       PetscCall(PetscSectionGetFieldDof(section, points[p], f, &fdof));
7676       offsets[f + 1] += fdof;
7677     }
7678     Ni += dof;
7679   }
7680   for (f = 1; f < Nf; ++f) offsets[f + 1] += offsets[f];
7681   PetscCheck(!Nf || offsets[Nf] == Ni, PetscObjectComm((PetscObject)dm), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, offsets[Nf], Ni);
7682   /* 3) Get symmetries and sign flips. Apply sign flips to values if passed in (only works for square values matrix) */
7683   for (f = 0; f < PetscMax(1, Nf); ++f) {
7684     if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7685     else PetscCall(PetscSectionGetPointSyms(section, Ncl, points, &perms[f], &flips[f]));
7686     /* may need to apply sign changes to the element matrix */
7687     if (values && flips[f]) {
7688       PetscInt foffset = offsets[f];
7689 
7690       for (p = 0; p < Ncl; ++p) {
7691         PetscInt           pnt  = points[2 * p], fdof;
7692         const PetscScalar *flip = flips[f] ? flips[f][p] : NULL;
7693 
7694         if (!Nf) PetscCall(PetscSectionGetDof(section, pnt, &fdof));
7695         else PetscCall(PetscSectionGetFieldDof(section, pnt, f, &fdof));
7696         if (flip) {
7697           PetscInt i, j, k;
7698 
7699           if (!valCopy) {
7700             PetscCall(DMGetWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
7701             for (j = 0; j < Ni * Ni; ++j) valCopy[j] = (*values)[j];
7702             *values = valCopy;
7703           }
7704           for (i = 0; i < fdof; ++i) {
7705             PetscScalar fval = flip[i];
7706 
7707             for (k = 0; k < Ni; ++k) {
7708               valCopy[Ni * (foffset + i) + k] *= fval;
7709               valCopy[Ni * k + (foffset + i)] *= fval;
7710             }
7711           }
7712         }
7713         foffset += fdof;
7714       }
7715     }
7716   }
7717   /* 4) Apply hanging node constraints. Get new symmetries and replace all storage with constrained storage */
7718   PetscCall(DMPlexAnchorsModifyMat(dm, section, Ncl, Ni, points, perms, values ? *values : NULL, &NclC, &NiC, &pointsC, values ? &valuesC : NULL, offsets, PETSC_TRUE));
7719   if (NclC) {
7720     if (valCopy) PetscCall(DMRestoreWorkArray(dm, Ni * Ni, MPIU_SCALAR, &valCopy));
7721     for (f = 0; f < PetscMax(1, Nf); ++f) {
7722       if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7723       else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
7724     }
7725     for (f = 0; f < PetscMax(1, Nf); ++f) {
7726       if (Nf) PetscCall(PetscSectionGetFieldPointSyms(section, f, NclC, pointsC, &perms[f], &flips[f]));
7727       else PetscCall(PetscSectionGetPointSyms(section, NclC, pointsC, &perms[f], &flips[f]));
7728     }
7729     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7730     Ncl    = NclC;
7731     Ni     = NiC;
7732     points = pointsC;
7733     if (values) *values = valuesC;
7734   }
7735   /* 5) Calculate indices */
7736   PetscCall(DMGetWorkArray(dm, Ni, MPIU_INT, &idx));
7737   if (Nf) {
7738     PetscInt  idxOff;
7739     PetscBool useFieldOffsets;
7740 
7741     if (outOffsets) {
7742       for (f = 0; f <= Nf; f++) outOffsets[f] = offsets[f];
7743     }
7744     PetscCall(PetscSectionGetUseFieldOffsets(idxSection, &useFieldOffsets));
7745     if (useFieldOffsets) {
7746       for (p = 0; p < Ncl; ++p) {
7747         const PetscInt pnt = points[p * 2];
7748 
7749         PetscCall(DMPlexGetIndicesPointFieldsSplit_Internal(section, idxSection, pnt, offsets, perms, p, clperm, idx));
7750       }
7751     } else {
7752       for (p = 0; p < Ncl; ++p) {
7753         const PetscInt pnt = points[p * 2];
7754 
7755         PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
7756         /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
7757          * not (at the time of this writing) have fields set. They probably should, in which case we would pass the
7758          * global section. */
7759         PetscCall(DMPlexGetIndicesPointFields_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, offsets, PETSC_FALSE, perms, p, clperm, idx));
7760       }
7761     }
7762   } else {
7763     PetscInt off = 0, idxOff;
7764 
7765     for (p = 0; p < Ncl; ++p) {
7766       const PetscInt  pnt  = points[p * 2];
7767       const PetscInt *perm = perms[0] ? perms[0][p] : NULL;
7768 
7769       PetscCall(PetscSectionGetOffset(idxSection, pnt, &idxOff));
7770       /* Note that we pass a local section even though we're using global offsets.  This is because global sections do
7771        * not (at the time of this writing) have fields set. They probably should, in which case we would pass the global section. */
7772       PetscCall(DMPlexGetIndicesPoint_Internal(section, isLocal, pnt, idxOff < 0 ? -(idxOff + 1) : idxOff, &off, PETSC_FALSE, perm, clperm, idx));
7773     }
7774   }
7775   /* 6) Cleanup */
7776   for (f = 0; f < PetscMax(1, Nf); ++f) {
7777     if (Nf) PetscCall(PetscSectionRestoreFieldPointSyms(section, f, Ncl, points, &perms[f], &flips[f]));
7778     else PetscCall(PetscSectionRestorePointSyms(section, Ncl, points, &perms[f], &flips[f]));
7779   }
7780   if (NclC) {
7781     PetscCall(DMRestoreWorkArray(dm, NclC * 2, MPIU_INT, &pointsC));
7782   } else {
7783     PetscCall(DMPlexRestoreCompressedClosure(dm, section, point, &Ncl, &points, &clSection, &clPoints, &clp));
7784   }
7785 
7786   if (numIndices) *numIndices = Ni;
7787   if (indices) *indices = idx;
7788   PetscFunctionReturn(PETSC_SUCCESS);
7789 }
7790 
7791 /*@C
7792   DMPlexRestoreClosureIndices - Restores the global dof indices associated with the closure of the given point within the provided sections.
7793 
7794   Not collective
7795 
7796   Input Parameters:
7797 + dm         - The `DM`
7798 . section    - The `PetscSection` describing the points (a local section)
7799 . idxSection - The `PetscSection` from which to obtain indices (may be local or global)
7800 . point      - The point defining the closure
7801 - useClPerm  - Use the closure point permutation if available
7802 
7803   Output Parameters:
7804 + numIndices - The number of dof indices in the closure of point with the input sections
7805 . indices    - The dof indices
7806 . outOffsets - Array to write the field offsets into, or `NULL`
7807 - values     - The input values, which may be modified if sign flips are induced by the point symmetries, or `NULL`
7808 
7809   Level: advanced
7810 
7811   Notes:
7812   If values were modified, the user is responsible for calling `DMRestoreWorkArray`(dm, 0, `MPIU_SCALAR`, &values).
7813 
7814   If idxSection is global, any constrained dofs (see `DMAddBoundary()`, for example) will get negative indices.  The value
7815   of those indices is not significant.  If idxSection is local, the constrained dofs will yield the involution -(idx+1)
7816   of their index in a local vector.  A caller who does not wish to distinguish those points may recover the nonnegative
7817   indices via involution, -(-(idx+1)+1)==idx.  Local indices are provided when idxSection == section, otherwise global
7818   indices (with the above semantics) are implied.
7819 
7820 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetClosureIndices()`, `DMPlexVecGetClosure()`, `DMPlexMatSetClosure()`, `DMGetLocalSection()`, `DMGetGlobalSection()`
7821 @*/
7822 PetscErrorCode DMPlexRestoreClosureIndices(DM dm, PetscSection section, PetscSection idxSection, PetscInt point, PetscBool useClPerm, PetscInt *numIndices, PetscInt *indices[], PetscInt outOffsets[], PetscScalar *values[])
7823 {
7824   PetscFunctionBegin;
7825   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7826   PetscAssertPointer(indices, 7);
7827   PetscCall(DMRestoreWorkArray(dm, 0, MPIU_INT, indices));
7828   PetscFunctionReturn(PETSC_SUCCESS);
7829 }
7830 
7831 /*@C
7832   DMPlexMatSetClosure - Set an array of the values on the closure of 'point'
7833 
7834   Not collective
7835 
7836   Input Parameters:
7837 + dm            - The `DM`
7838 . section       - The section describing the layout in `v`, or `NULL` to use the default section
7839 . globalSection - The section describing the layout in `v`, or `NULL` to use the default global section
7840 . A             - The matrix
7841 . point         - The point in the `DM`
7842 . values        - The array of values
7843 - mode          - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
7844 
7845   Level: intermediate
7846 
7847 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosureGeneral()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
7848 @*/
7849 PetscErrorCode DMPlexMatSetClosure(DM dm, PetscSection section, PetscSection globalSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7850 {
7851   DM_Plex           *mesh = (DM_Plex *)dm->data;
7852   PetscInt          *indices;
7853   PetscInt           numIndices;
7854   const PetscScalar *valuesOrig = values;
7855   PetscErrorCode     ierr;
7856 
7857   PetscFunctionBegin;
7858   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
7859   if (!section) PetscCall(DMGetLocalSection(dm, &section));
7860   PetscValidHeaderSpecific(section, PETSC_SECTION_CLASSID, 2);
7861   if (!globalSection) PetscCall(DMGetGlobalSection(dm, &globalSection));
7862   PetscValidHeaderSpecific(globalSection, PETSC_SECTION_CLASSID, 3);
7863   PetscValidHeaderSpecific(A, MAT_CLASSID, 4);
7864 
7865   PetscCall(DMPlexGetClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
7866 
7867   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndices, indices, 0, NULL, values));
7868   /* TODO: fix this code to not use error codes as handle-able exceptions! */
7869   ierr = MatSetValues(A, numIndices, indices, numIndices, indices, values, mode);
7870   if (ierr) {
7871     PetscMPIInt rank;
7872 
7873     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7874     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
7875     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndices, indices, 0, NULL, values));
7876     PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
7877     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
7878     SETERRQ(PetscObjectComm((PetscObject)dm), ierr, "Not possible to set matrix values");
7879   }
7880   if (mesh->printFEM > 1) {
7881     PetscInt i;
7882     PetscCall(PetscPrintf(PETSC_COMM_SELF, "  Indices:"));
7883     for (i = 0; i < numIndices; ++i) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, indices[i]));
7884     PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
7885   }
7886 
7887   PetscCall(DMPlexRestoreClosureIndices(dm, section, globalSection, point, PETSC_TRUE, &numIndices, &indices, NULL, (PetscScalar **)&values));
7888   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dm, 0, MPIU_SCALAR, &values));
7889   PetscFunctionReturn(PETSC_SUCCESS);
7890 }
7891 
7892 /*@C
7893   DMPlexMatSetClosureGeneral - Set an array of the values on the closure of 'point' using a different row and column section
7894 
7895   Not collective
7896 
7897   Input Parameters:
7898 + dmRow            - The `DM` for the row fields
7899 . sectionRow       - The section describing the layout, or `NULL` to use the default section in `dmRow`
7900 . globalSectionRow - The section describing the layout, or `NULL` to use the default global section in `dmRow`
7901 . dmCol            - The `DM` for the column fields
7902 . sectionCol       - The section describing the layout, or `NULL` to use the default section in `dmCol`
7903 . globalSectionCol - The section describing the layout, or `NULL` to use the default global section in `dmCol`
7904 . A                - The matrix
7905 . point            - The point in the `DM`
7906 . values           - The array of values
7907 - mode             - The insert mode, where `INSERT_ALL_VALUES` and `ADD_ALL_VALUES` also overwrite boundary conditions
7908 
7909   Level: intermediate
7910 
7911 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexMatSetClosure()`, `DMPlexVecGetClosure()`, `DMPlexVecSetClosure()`
7912 @*/
7913 PetscErrorCode DMPlexMatSetClosureGeneral(DM dmRow, PetscSection sectionRow, PetscSection globalSectionRow, DM dmCol, PetscSection sectionCol, PetscSection globalSectionCol, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7914 {
7915   DM_Plex           *mesh = (DM_Plex *)dmRow->data;
7916   PetscInt          *indicesRow, *indicesCol;
7917   PetscInt           numIndicesRow, numIndicesCol;
7918   const PetscScalar *valuesOrig = values;
7919   PetscErrorCode     ierr;
7920 
7921   PetscFunctionBegin;
7922   PetscValidHeaderSpecific(dmRow, DM_CLASSID, 1);
7923   if (!sectionRow) PetscCall(DMGetLocalSection(dmRow, &sectionRow));
7924   PetscValidHeaderSpecific(sectionRow, PETSC_SECTION_CLASSID, 2);
7925   if (!globalSectionRow) PetscCall(DMGetGlobalSection(dmRow, &globalSectionRow));
7926   PetscValidHeaderSpecific(globalSectionRow, PETSC_SECTION_CLASSID, 3);
7927   PetscValidHeaderSpecific(dmCol, DM_CLASSID, 4);
7928   if (!sectionCol) PetscCall(DMGetLocalSection(dmCol, &sectionCol));
7929   PetscValidHeaderSpecific(sectionCol, PETSC_SECTION_CLASSID, 5);
7930   if (!globalSectionCol) PetscCall(DMGetGlobalSection(dmCol, &globalSectionCol));
7931   PetscValidHeaderSpecific(globalSectionCol, PETSC_SECTION_CLASSID, 6);
7932   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
7933 
7934   PetscCall(DMPlexGetClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&values));
7935   PetscCall(DMPlexGetClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&values));
7936 
7937   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
7938   /* TODO: fix this code to not use error codes as handle-able exceptions! */
7939   ierr = MatSetValues(A, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values, mode);
7940   if (ierr) {
7941     PetscMPIInt rank;
7942 
7943     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
7944     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
7945     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numIndicesRow, indicesRow, numIndicesCol, indicesCol, values));
7946     PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&values));
7947     PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesRow, NULL, (PetscScalar **)&values));
7948     if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &values));
7949   }
7950 
7951   PetscCall(DMPlexRestoreClosureIndices(dmRow, sectionRow, globalSectionRow, point, PETSC_TRUE, &numIndicesRow, &indicesRow, NULL, (PetscScalar **)&values));
7952   PetscCall(DMPlexRestoreClosureIndices(dmCol, sectionCol, globalSectionCol, point, PETSC_TRUE, &numIndicesCol, &indicesCol, NULL, (PetscScalar **)&values));
7953   if (values != valuesOrig) PetscCall(DMRestoreWorkArray(dmRow, 0, MPIU_SCALAR, &values));
7954   PetscFunctionReturn(PETSC_SUCCESS);
7955 }
7956 
7957 PetscErrorCode DMPlexMatSetClosureRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, Mat A, PetscInt point, const PetscScalar values[], InsertMode mode)
7958 {
7959   DM_Plex        *mesh    = (DM_Plex *)dmf->data;
7960   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
7961   PetscInt       *cpoints = NULL;
7962   PetscInt       *findices, *cindices;
7963   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
7964   PetscInt        foffsets[32], coffsets[32];
7965   DMPolytopeType  ct;
7966   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
7967   PetscErrorCode  ierr;
7968 
7969   PetscFunctionBegin;
7970   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
7971   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
7972   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
7973   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
7974   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
7975   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
7976   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
7977   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
7978   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
7979   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
7980   PetscValidHeaderSpecific(A, MAT_CLASSID, 7);
7981   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
7982   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
7983   PetscCall(PetscArrayzero(foffsets, 32));
7984   PetscCall(PetscArrayzero(coffsets, 32));
7985   /* Column indices */
7986   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
7987   maxFPoints = numCPoints;
7988   /* Compress out points not in the section */
7989   /*   TODO: Squeeze out points with 0 dof as well */
7990   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
7991   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
7992     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
7993       cpoints[q * 2]     = cpoints[p];
7994       cpoints[q * 2 + 1] = cpoints[p + 1];
7995       ++q;
7996     }
7997   }
7998   numCPoints = q;
7999   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
8000     PetscInt fdof;
8001 
8002     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
8003     if (!dof) continue;
8004     for (f = 0; f < numFields; ++f) {
8005       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
8006       coffsets[f + 1] += fdof;
8007     }
8008     numCIndices += dof;
8009   }
8010   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
8011   /* Row indices */
8012   PetscCall(DMPlexGetCellType(dmc, point, &ct));
8013   {
8014     DMPlexTransform tr;
8015     DMPolytopeType *rct;
8016     PetscInt       *rsize, *rcone, *rornt, Nt;
8017 
8018     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8019     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8020     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8021     numSubcells = rsize[Nt - 1];
8022     PetscCall(DMPlexTransformDestroy(&tr));
8023   }
8024   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8025   for (r = 0, q = 0; r < numSubcells; ++r) {
8026     /* TODO Map from coarse to fine cells */
8027     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8028     /* Compress out points not in the section */
8029     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8030     for (p = 0; p < numFPoints * 2; p += 2) {
8031       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8032         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8033         if (!dof) continue;
8034         for (s = 0; s < q; ++s)
8035           if (fpoints[p] == ftotpoints[s * 2]) break;
8036         if (s < q) continue;
8037         ftotpoints[q * 2]     = fpoints[p];
8038         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8039         ++q;
8040       }
8041     }
8042     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8043   }
8044   numFPoints = q;
8045   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8046     PetscInt fdof;
8047 
8048     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8049     if (!dof) continue;
8050     for (f = 0; f < numFields; ++f) {
8051       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8052       foffsets[f + 1] += fdof;
8053     }
8054     numFIndices += dof;
8055   }
8056   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8057 
8058   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8059   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8060   PetscCall(DMGetWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8061   PetscCall(DMGetWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8062   if (numFields) {
8063     const PetscInt **permsF[32] = {NULL};
8064     const PetscInt **permsC[32] = {NULL};
8065 
8066     for (f = 0; f < numFields; f++) {
8067       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8068       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8069     }
8070     for (p = 0; p < numFPoints; p++) {
8071       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8072       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8073     }
8074     for (p = 0; p < numCPoints; p++) {
8075       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8076       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8077     }
8078     for (f = 0; f < numFields; f++) {
8079       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8080       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8081     }
8082   } else {
8083     const PetscInt **permsF = NULL;
8084     const PetscInt **permsC = NULL;
8085 
8086     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8087     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8088     for (p = 0, off = 0; p < numFPoints; p++) {
8089       const PetscInt *perm = permsF ? permsF[p] : NULL;
8090 
8091       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8092       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8093     }
8094     for (p = 0, off = 0; p < numCPoints; p++) {
8095       const PetscInt *perm = permsC ? permsC[p] : NULL;
8096 
8097       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8098       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8099     }
8100     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8101     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8102   }
8103   if (mesh->printSetValues) PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDOUT_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8104   /* TODO: flips */
8105   /* TODO: fix this code to not use error codes as handle-able exceptions! */
8106   ierr = MatSetValues(A, numFIndices, findices, numCIndices, cindices, values, mode);
8107   if (ierr) {
8108     PetscMPIInt rank;
8109 
8110     PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)A), &rank));
8111     PetscCall((*PetscErrorPrintf)("[%d]ERROR in DMPlexMatSetClosure\n", rank));
8112     PetscCall(DMPlexPrintMatSetValues(PETSC_VIEWER_STDERR_SELF, A, point, numFIndices, findices, numCIndices, cindices, values));
8113     PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8114     PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8115   }
8116   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8117   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8118   PetscCall(DMRestoreWorkArray(dmf, numFIndices, MPIU_INT, &findices));
8119   PetscCall(DMRestoreWorkArray(dmc, numCIndices, MPIU_INT, &cindices));
8120   PetscFunctionReturn(PETSC_SUCCESS);
8121 }
8122 
8123 PetscErrorCode DMPlexMatGetClosureIndicesRefined(DM dmf, PetscSection fsection, PetscSection globalFSection, DM dmc, PetscSection csection, PetscSection globalCSection, PetscInt point, PetscInt cindices[], PetscInt findices[])
8124 {
8125   PetscInt       *fpoints = NULL, *ftotpoints = NULL;
8126   PetscInt       *cpoints = NULL;
8127   PetscInt        foffsets[32], coffsets[32];
8128   const PetscInt *fclperm = NULL, *cclperm = NULL; /* Closure permutations cannot work here */
8129   DMPolytopeType  ct;
8130   PetscInt        numFields, numSubcells, maxFPoints, numFPoints, numCPoints, numFIndices, numCIndices, dof, off, globalOff, pStart, pEnd, p, q, r, s, f;
8131 
8132   PetscFunctionBegin;
8133   PetscValidHeaderSpecific(dmf, DM_CLASSID, 1);
8134   PetscValidHeaderSpecific(dmc, DM_CLASSID, 4);
8135   if (!fsection) PetscCall(DMGetLocalSection(dmf, &fsection));
8136   PetscValidHeaderSpecific(fsection, PETSC_SECTION_CLASSID, 2);
8137   if (!csection) PetscCall(DMGetLocalSection(dmc, &csection));
8138   PetscValidHeaderSpecific(csection, PETSC_SECTION_CLASSID, 5);
8139   if (!globalFSection) PetscCall(DMGetGlobalSection(dmf, &globalFSection));
8140   PetscValidHeaderSpecific(globalFSection, PETSC_SECTION_CLASSID, 3);
8141   if (!globalCSection) PetscCall(DMGetGlobalSection(dmc, &globalCSection));
8142   PetscValidHeaderSpecific(globalCSection, PETSC_SECTION_CLASSID, 6);
8143   PetscCall(PetscSectionGetNumFields(fsection, &numFields));
8144   PetscCheck(numFields <= 31, PetscObjectComm((PetscObject)dmf), PETSC_ERR_ARG_OUTOFRANGE, "Number of fields %" PetscInt_FMT " limited to 31", numFields);
8145   PetscCall(PetscArrayzero(foffsets, 32));
8146   PetscCall(PetscArrayzero(coffsets, 32));
8147   /* Column indices */
8148   PetscCall(DMPlexGetTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8149   maxFPoints = numCPoints;
8150   /* Compress out points not in the section */
8151   /*   TODO: Squeeze out points with 0 dof as well */
8152   PetscCall(PetscSectionGetChart(csection, &pStart, &pEnd));
8153   for (p = 0, q = 0; p < numCPoints * 2; p += 2) {
8154     if ((cpoints[p] >= pStart) && (cpoints[p] < pEnd)) {
8155       cpoints[q * 2]     = cpoints[p];
8156       cpoints[q * 2 + 1] = cpoints[p + 1];
8157       ++q;
8158     }
8159   }
8160   numCPoints = q;
8161   for (p = 0, numCIndices = 0; p < numCPoints * 2; p += 2) {
8162     PetscInt fdof;
8163 
8164     PetscCall(PetscSectionGetDof(csection, cpoints[p], &dof));
8165     if (!dof) continue;
8166     for (f = 0; f < numFields; ++f) {
8167       PetscCall(PetscSectionGetFieldDof(csection, cpoints[p], f, &fdof));
8168       coffsets[f + 1] += fdof;
8169     }
8170     numCIndices += dof;
8171   }
8172   for (f = 1; f < numFields; ++f) coffsets[f + 1] += coffsets[f];
8173   /* Row indices */
8174   PetscCall(DMPlexGetCellType(dmc, point, &ct));
8175   {
8176     DMPlexTransform tr;
8177     DMPolytopeType *rct;
8178     PetscInt       *rsize, *rcone, *rornt, Nt;
8179 
8180     PetscCall(DMPlexTransformCreate(PETSC_COMM_SELF, &tr));
8181     PetscCall(DMPlexTransformSetType(tr, DMPLEXREFINEREGULAR));
8182     PetscCall(DMPlexTransformCellTransform(tr, ct, point, NULL, &Nt, &rct, &rsize, &rcone, &rornt));
8183     numSubcells = rsize[Nt - 1];
8184     PetscCall(DMPlexTransformDestroy(&tr));
8185   }
8186   PetscCall(DMGetWorkArray(dmf, maxFPoints * 2 * numSubcells, MPIU_INT, &ftotpoints));
8187   for (r = 0, q = 0; r < numSubcells; ++r) {
8188     /* TODO Map from coarse to fine cells */
8189     PetscCall(DMPlexGetTransitiveClosure(dmf, point * numSubcells + r, PETSC_TRUE, &numFPoints, &fpoints));
8190     /* Compress out points not in the section */
8191     PetscCall(PetscSectionGetChart(fsection, &pStart, &pEnd));
8192     for (p = 0; p < numFPoints * 2; p += 2) {
8193       if ((fpoints[p] >= pStart) && (fpoints[p] < pEnd)) {
8194         PetscCall(PetscSectionGetDof(fsection, fpoints[p], &dof));
8195         if (!dof) continue;
8196         for (s = 0; s < q; ++s)
8197           if (fpoints[p] == ftotpoints[s * 2]) break;
8198         if (s < q) continue;
8199         ftotpoints[q * 2]     = fpoints[p];
8200         ftotpoints[q * 2 + 1] = fpoints[p + 1];
8201         ++q;
8202       }
8203     }
8204     PetscCall(DMPlexRestoreTransitiveClosure(dmf, point, PETSC_TRUE, &numFPoints, &fpoints));
8205   }
8206   numFPoints = q;
8207   for (p = 0, numFIndices = 0; p < numFPoints * 2; p += 2) {
8208     PetscInt fdof;
8209 
8210     PetscCall(PetscSectionGetDof(fsection, ftotpoints[p], &dof));
8211     if (!dof) continue;
8212     for (f = 0; f < numFields; ++f) {
8213       PetscCall(PetscSectionGetFieldDof(fsection, ftotpoints[p], f, &fdof));
8214       foffsets[f + 1] += fdof;
8215     }
8216     numFIndices += dof;
8217   }
8218   for (f = 1; f < numFields; ++f) foffsets[f + 1] += foffsets[f];
8219 
8220   PetscCheck(!numFields || foffsets[numFields] == numFIndices, PetscObjectComm((PetscObject)dmf), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, foffsets[numFields], numFIndices);
8221   PetscCheck(!numFields || coffsets[numFields] == numCIndices, PetscObjectComm((PetscObject)dmc), PETSC_ERR_PLIB, "Invalid size for closure %" PetscInt_FMT " should be %" PetscInt_FMT, coffsets[numFields], numCIndices);
8222   if (numFields) {
8223     const PetscInt **permsF[32] = {NULL};
8224     const PetscInt **permsC[32] = {NULL};
8225 
8226     for (f = 0; f < numFields; f++) {
8227       PetscCall(PetscSectionGetFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8228       PetscCall(PetscSectionGetFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8229     }
8230     for (p = 0; p < numFPoints; p++) {
8231       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8232       PetscCall(DMPlexGetIndicesPointFields_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, foffsets, PETSC_FALSE, permsF, p, fclperm, findices));
8233     }
8234     for (p = 0; p < numCPoints; p++) {
8235       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8236       PetscCall(DMPlexGetIndicesPointFields_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, coffsets, PETSC_FALSE, permsC, p, cclperm, cindices));
8237     }
8238     for (f = 0; f < numFields; f++) {
8239       PetscCall(PetscSectionRestoreFieldPointSyms(fsection, f, numFPoints, ftotpoints, &permsF[f], NULL));
8240       PetscCall(PetscSectionRestoreFieldPointSyms(csection, f, numCPoints, cpoints, &permsC[f], NULL));
8241     }
8242   } else {
8243     const PetscInt **permsF = NULL;
8244     const PetscInt **permsC = NULL;
8245 
8246     PetscCall(PetscSectionGetPointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8247     PetscCall(PetscSectionGetPointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8248     for (p = 0, off = 0; p < numFPoints; p++) {
8249       const PetscInt *perm = permsF ? permsF[p] : NULL;
8250 
8251       PetscCall(PetscSectionGetOffset(globalFSection, ftotpoints[2 * p], &globalOff));
8252       PetscCall(DMPlexGetIndicesPoint_Internal(fsection, PETSC_FALSE, ftotpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, fclperm, findices));
8253     }
8254     for (p = 0, off = 0; p < numCPoints; p++) {
8255       const PetscInt *perm = permsC ? permsC[p] : NULL;
8256 
8257       PetscCall(PetscSectionGetOffset(globalCSection, cpoints[2 * p], &globalOff));
8258       PetscCall(DMPlexGetIndicesPoint_Internal(csection, PETSC_FALSE, cpoints[2 * p], globalOff < 0 ? -(globalOff + 1) : globalOff, &off, PETSC_FALSE, perm, cclperm, cindices));
8259     }
8260     PetscCall(PetscSectionRestorePointSyms(fsection, numFPoints, ftotpoints, &permsF, NULL));
8261     PetscCall(PetscSectionRestorePointSyms(csection, numCPoints, cpoints, &permsC, NULL));
8262   }
8263   PetscCall(DMRestoreWorkArray(dmf, numCPoints * 2 * 4, MPIU_INT, &ftotpoints));
8264   PetscCall(DMPlexRestoreTransitiveClosure(dmc, point, PETSC_TRUE, &numCPoints, &cpoints));
8265   PetscFunctionReturn(PETSC_SUCCESS);
8266 }
8267 
8268 /*@C
8269   DMPlexGetVTKCellHeight - Returns the height in the DAG used to determine which points are cells (normally 0)
8270 
8271   Input Parameter:
8272 . dm - The `DMPLEX` object
8273 
8274   Output Parameter:
8275 . cellHeight - The height of a cell
8276 
8277   Level: developer
8278 
8279 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetVTKCellHeight()`
8280 @*/
8281 PetscErrorCode DMPlexGetVTKCellHeight(DM dm, PetscInt *cellHeight)
8282 {
8283   DM_Plex *mesh = (DM_Plex *)dm->data;
8284 
8285   PetscFunctionBegin;
8286   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8287   PetscAssertPointer(cellHeight, 2);
8288   *cellHeight = mesh->vtkCellHeight;
8289   PetscFunctionReturn(PETSC_SUCCESS);
8290 }
8291 
8292 /*@C
8293   DMPlexSetVTKCellHeight - Sets the height in the DAG used to determine which points are cells (normally 0)
8294 
8295   Input Parameters:
8296 + dm         - The `DMPLEX` object
8297 - cellHeight - The height of a cell
8298 
8299   Level: developer
8300 
8301 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetVTKCellHeight()`
8302 @*/
8303 PetscErrorCode DMPlexSetVTKCellHeight(DM dm, PetscInt cellHeight)
8304 {
8305   DM_Plex *mesh = (DM_Plex *)dm->data;
8306 
8307   PetscFunctionBegin;
8308   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8309   mesh->vtkCellHeight = cellHeight;
8310   PetscFunctionReturn(PETSC_SUCCESS);
8311 }
8312 
8313 /*@
8314   DMPlexGetCellTypeStratum - Get the range of cells of a given celltype
8315 
8316   Input Parameters:
8317 + dm - The `DMPLEX` object
8318 - ct - The `DMPolytopeType` of the cell
8319 
8320   Output Parameters:
8321 + start - The first cell of this type, or `NULL`
8322 - end   - The upper bound on this celltype, or `NULL`
8323 
8324   Level: advanced
8325 
8326 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexConstructGhostCells()`, `DMPlexGetDepthStratum()`, `DMPlexGetHeightStratum()`
8327 @*/
8328 PetscErrorCode DMPlexGetCellTypeStratum(DM dm, DMPolytopeType ct, PetscInt *start, PetscInt *end)
8329 {
8330   DM_Plex *mesh = (DM_Plex *)dm->data;
8331   DMLabel  label;
8332   PetscInt pStart, pEnd;
8333 
8334   PetscFunctionBegin;
8335   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8336   if (start) {
8337     PetscAssertPointer(start, 3);
8338     *start = 0;
8339   }
8340   if (end) {
8341     PetscAssertPointer(end, 4);
8342     *end = 0;
8343   }
8344   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8345   if (pStart == pEnd) PetscFunctionReturn(PETSC_SUCCESS);
8346   if (mesh->tr) {
8347     PetscCall(DMPlexTransformGetCellTypeStratum(mesh->tr, ct, start, end));
8348   } else {
8349     PetscCall(DMPlexGetCellTypeLabel(dm, &label));
8350     PetscCheck(label, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "No label named celltype was found");
8351     PetscCall(DMLabelGetStratumBounds(label, ct, start, end));
8352   }
8353   PetscFunctionReturn(PETSC_SUCCESS);
8354 }
8355 
8356 PetscErrorCode DMPlexCreateNumbering_Plex(DM dm, PetscInt pStart, PetscInt pEnd, PetscInt shift, PetscInt *globalSize, PetscSF sf, IS *numbering)
8357 {
8358   PetscSection section, globalSection;
8359   PetscInt    *numbers, p;
8360 
8361   PetscFunctionBegin;
8362   if (PetscDefined(USE_DEBUG)) PetscCall(DMPlexCheckPointSF(dm, sf, PETSC_TRUE));
8363   PetscCall(PetscSectionCreate(PetscObjectComm((PetscObject)dm), &section));
8364   PetscCall(PetscSectionSetChart(section, pStart, pEnd));
8365   for (p = pStart; p < pEnd; ++p) PetscCall(PetscSectionSetDof(section, p, 1));
8366   PetscCall(PetscSectionSetUp(section));
8367   PetscCall(PetscSectionCreateGlobalSection(section, sf, PETSC_FALSE, PETSC_FALSE, &globalSection));
8368   PetscCall(PetscMalloc1(pEnd - pStart, &numbers));
8369   for (p = pStart; p < pEnd; ++p) {
8370     PetscCall(PetscSectionGetOffset(globalSection, p, &numbers[p - pStart]));
8371     if (numbers[p - pStart] < 0) numbers[p - pStart] -= shift;
8372     else numbers[p - pStart] += shift;
8373   }
8374   PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), pEnd - pStart, numbers, PETSC_OWN_POINTER, numbering));
8375   if (globalSize) {
8376     PetscLayout layout;
8377     PetscCall(PetscSectionGetPointLayout(PetscObjectComm((PetscObject)dm), globalSection, &layout));
8378     PetscCall(PetscLayoutGetSize(layout, globalSize));
8379     PetscCall(PetscLayoutDestroy(&layout));
8380   }
8381   PetscCall(PetscSectionDestroy(&section));
8382   PetscCall(PetscSectionDestroy(&globalSection));
8383   PetscFunctionReturn(PETSC_SUCCESS);
8384 }
8385 
8386 PetscErrorCode DMPlexCreateCellNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalCellNumbers)
8387 {
8388   PetscInt cellHeight, cStart, cEnd;
8389 
8390   PetscFunctionBegin;
8391   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
8392   if (includeHybrid) PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8393   else PetscCall(DMPlexGetSimplexOrBoxCells(dm, cellHeight, &cStart, &cEnd));
8394   PetscCall(DMPlexCreateNumbering_Plex(dm, cStart, cEnd, 0, NULL, dm->sf, globalCellNumbers));
8395   PetscFunctionReturn(PETSC_SUCCESS);
8396 }
8397 
8398 /*@
8399   DMPlexGetCellNumbering - Get a global cell numbering for all cells on this process
8400 
8401   Input Parameter:
8402 . dm - The `DMPLEX` object
8403 
8404   Output Parameter:
8405 . globalCellNumbers - Global cell numbers for all cells on this process
8406 
8407   Level: developer
8408 
8409 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetVertexNumbering()`
8410 @*/
8411 PetscErrorCode DMPlexGetCellNumbering(DM dm, IS *globalCellNumbers)
8412 {
8413   DM_Plex *mesh = (DM_Plex *)dm->data;
8414 
8415   PetscFunctionBegin;
8416   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8417   if (!mesh->globalCellNumbers) PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_FALSE, &mesh->globalCellNumbers));
8418   *globalCellNumbers = mesh->globalCellNumbers;
8419   PetscFunctionReturn(PETSC_SUCCESS);
8420 }
8421 
8422 PetscErrorCode DMPlexCreateVertexNumbering_Internal(DM dm, PetscBool includeHybrid, IS *globalVertexNumbers)
8423 {
8424   PetscInt vStart, vEnd;
8425 
8426   PetscFunctionBegin;
8427   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8428   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8429   PetscCall(DMPlexCreateNumbering_Plex(dm, vStart, vEnd, 0, NULL, dm->sf, globalVertexNumbers));
8430   PetscFunctionReturn(PETSC_SUCCESS);
8431 }
8432 
8433 /*@
8434   DMPlexGetVertexNumbering - Get a global vertex numbering for all vertices on this process
8435 
8436   Input Parameter:
8437 . dm - The `DMPLEX` object
8438 
8439   Output Parameter:
8440 . globalVertexNumbers - Global vertex numbers for all vertices on this process
8441 
8442   Level: developer
8443 
8444 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
8445 @*/
8446 PetscErrorCode DMPlexGetVertexNumbering(DM dm, IS *globalVertexNumbers)
8447 {
8448   DM_Plex *mesh = (DM_Plex *)dm->data;
8449 
8450   PetscFunctionBegin;
8451   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8452   if (!mesh->globalVertexNumbers) PetscCall(DMPlexCreateVertexNumbering_Internal(dm, PETSC_FALSE, &mesh->globalVertexNumbers));
8453   *globalVertexNumbers = mesh->globalVertexNumbers;
8454   PetscFunctionReturn(PETSC_SUCCESS);
8455 }
8456 
8457 /*@
8458   DMPlexCreatePointNumbering - Create a global numbering for all points.
8459 
8460   Collective
8461 
8462   Input Parameter:
8463 . dm - The `DMPLEX` object
8464 
8465   Output Parameter:
8466 . globalPointNumbers - Global numbers for all points on this process
8467 
8468   Level: developer
8469 
8470   Notes:
8471   The point numbering `IS` is parallel, with local portion indexed by local points (see `DMGetLocalSection()`). The global
8472   points are taken as stratified, with each MPI rank owning a contiguous subset of each stratum. In the IS, owned points
8473   will have their non-negative value while points owned by different ranks will be involuted -(idx+1). As an example,
8474   consider a parallel mesh in which the first two elements and first two vertices are owned by rank 0.
8475 
8476   The partitioned mesh is
8477   ```
8478   (2)--0--(3)--1--(4)    (1)--0--(2)
8479   ```
8480   and its global numbering is
8481   ```
8482   (3)--0--(4)--1--(5)--2--(6)
8483   ```
8484   Then the global numbering is provided as
8485   ```
8486   [0] Number of indices in set 5
8487   [0] 0 0
8488   [0] 1 1
8489   [0] 2 3
8490   [0] 3 4
8491   [0] 4 -6
8492   [1] Number of indices in set 3
8493   [1] 0 2
8494   [1] 1 5
8495   [1] 2 6
8496   ```
8497 
8498 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetCellNumbering()`
8499 @*/
8500 PetscErrorCode DMPlexCreatePointNumbering(DM dm, IS *globalPointNumbers)
8501 {
8502   IS        nums[4];
8503   PetscInt  depths[4], gdepths[4], starts[4];
8504   PetscInt  depth, d, shift = 0;
8505   PetscBool empty = PETSC_FALSE;
8506 
8507   PetscFunctionBegin;
8508   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8509   PetscCall(DMPlexGetDepth(dm, &depth));
8510   // For unstratified meshes use dim instead of depth
8511   if (depth < 0) PetscCall(DMGetDimension(dm, &depth));
8512   // If any stratum is empty, we must mark all empty
8513   for (d = 0; d <= depth; ++d) {
8514     PetscInt end;
8515 
8516     depths[d] = depth - d;
8517     PetscCall(DMPlexGetDepthStratum(dm, depths[d], &starts[d], &end));
8518     if (!(starts[d] - end)) empty = PETSC_TRUE;
8519   }
8520   if (empty)
8521     for (d = 0; d <= depth; ++d) {
8522       depths[d] = -1;
8523       starts[d] = -1;
8524     }
8525   else PetscCall(PetscSortIntWithArray(depth + 1, starts, depths));
8526   PetscCall(MPIU_Allreduce(depths, gdepths, depth + 1, MPIU_INT, MPI_MAX, PetscObjectComm((PetscObject)dm)));
8527   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]);
8528   // Note here that 'shift' is collective, so that the numbering is stratified by depth
8529   for (d = 0; d <= depth; ++d) {
8530     PetscInt pStart, pEnd, gsize;
8531 
8532     PetscCall(DMPlexGetDepthStratum(dm, gdepths[d], &pStart, &pEnd));
8533     PetscCall(DMPlexCreateNumbering_Plex(dm, pStart, pEnd, shift, &gsize, dm->sf, &nums[d]));
8534     shift += gsize;
8535   }
8536   PetscCall(ISConcatenate(PETSC_COMM_SELF, depth + 1, nums, globalPointNumbers));
8537   for (d = 0; d <= depth; ++d) PetscCall(ISDestroy(&nums[d]));
8538   PetscFunctionReturn(PETSC_SUCCESS);
8539 }
8540 
8541 /*@
8542   DMPlexCreateRankField - Create a cell field whose value is the rank of the owner
8543 
8544   Input Parameter:
8545 . dm - The `DMPLEX` object
8546 
8547   Output Parameter:
8548 . ranks - The rank field
8549 
8550   Options Database Key:
8551 . -dm_partition_view - Adds the rank field into the `DM` output from `-dm_view` using the same viewer
8552 
8553   Level: intermediate
8554 
8555 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`
8556 @*/
8557 PetscErrorCode DMPlexCreateRankField(DM dm, Vec *ranks)
8558 {
8559   DM             rdm;
8560   PetscFE        fe;
8561   PetscScalar   *r;
8562   PetscMPIInt    rank;
8563   DMPolytopeType ct;
8564   PetscInt       dim, cStart, cEnd, c;
8565   PetscBool      simplex;
8566 
8567   PetscFunctionBeginUser;
8568   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8569   PetscAssertPointer(ranks, 2);
8570   PetscCallMPI(MPI_Comm_rank(PetscObjectComm((PetscObject)dm), &rank));
8571   PetscCall(DMClone(dm, &rdm));
8572   PetscCall(DMGetDimension(rdm, &dim));
8573   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
8574   PetscCall(DMPlexGetCellType(dm, cStart, &ct));
8575   simplex = DMPolytopeTypeGetNumVertices(ct) == DMPolytopeTypeGetDim(ct) + 1 ? PETSC_TRUE : PETSC_FALSE;
8576   PetscCall(PetscFECreateDefault(PETSC_COMM_SELF, dim, 1, simplex, "PETSc___rank_", -1, &fe));
8577   PetscCall(PetscObjectSetName((PetscObject)fe, "rank"));
8578   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
8579   PetscCall(PetscFEDestroy(&fe));
8580   PetscCall(DMCreateDS(rdm));
8581   PetscCall(DMCreateGlobalVector(rdm, ranks));
8582   PetscCall(PetscObjectSetName((PetscObject)*ranks, "partition"));
8583   PetscCall(VecGetArray(*ranks, &r));
8584   for (c = cStart; c < cEnd; ++c) {
8585     PetscScalar *lr;
8586 
8587     PetscCall(DMPlexPointGlobalRef(rdm, c, r, &lr));
8588     if (lr) *lr = rank;
8589   }
8590   PetscCall(VecRestoreArray(*ranks, &r));
8591   PetscCall(DMDestroy(&rdm));
8592   PetscFunctionReturn(PETSC_SUCCESS);
8593 }
8594 
8595 /*@
8596   DMPlexCreateLabelField - Create a cell field whose value is the label value for that cell
8597 
8598   Input Parameters:
8599 + dm    - The `DMPLEX`
8600 - label - The `DMLabel`
8601 
8602   Output Parameter:
8603 . val - The label value field
8604 
8605   Options Database Key:
8606 . -dm_label_view - Adds the label value field into the `DM` output from `-dm_view` using the same viewer
8607 
8608   Level: intermediate
8609 
8610 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMView()`
8611 @*/
8612 PetscErrorCode DMPlexCreateLabelField(DM dm, DMLabel label, Vec *val)
8613 {
8614   DM           rdm;
8615   PetscFE      fe;
8616   PetscScalar *v;
8617   PetscInt     dim, cStart, cEnd, c;
8618 
8619   PetscFunctionBeginUser;
8620   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8621   PetscAssertPointer(label, 2);
8622   PetscAssertPointer(val, 3);
8623   PetscCall(DMClone(dm, &rdm));
8624   PetscCall(DMGetDimension(rdm, &dim));
8625   PetscCall(PetscFECreateDefault(PetscObjectComm((PetscObject)rdm), dim, 1, PETSC_TRUE, "PETSc___label_value_", -1, &fe));
8626   PetscCall(PetscObjectSetName((PetscObject)fe, "label_value"));
8627   PetscCall(DMSetField(rdm, 0, NULL, (PetscObject)fe));
8628   PetscCall(PetscFEDestroy(&fe));
8629   PetscCall(DMCreateDS(rdm));
8630   PetscCall(DMPlexGetHeightStratum(rdm, 0, &cStart, &cEnd));
8631   PetscCall(DMCreateGlobalVector(rdm, val));
8632   PetscCall(PetscObjectSetName((PetscObject)*val, "label_value"));
8633   PetscCall(VecGetArray(*val, &v));
8634   for (c = cStart; c < cEnd; ++c) {
8635     PetscScalar *lv;
8636     PetscInt     cval;
8637 
8638     PetscCall(DMPlexPointGlobalRef(rdm, c, v, &lv));
8639     PetscCall(DMLabelGetValue(label, c, &cval));
8640     *lv = cval;
8641   }
8642   PetscCall(VecRestoreArray(*val, &v));
8643   PetscCall(DMDestroy(&rdm));
8644   PetscFunctionReturn(PETSC_SUCCESS);
8645 }
8646 
8647 /*@
8648   DMPlexCheckSymmetry - Check that the adjacency information in the mesh is symmetric.
8649 
8650   Input Parameter:
8651 . dm - The `DMPLEX` object
8652 
8653   Level: developer
8654 
8655   Notes:
8656   This is a useful diagnostic when creating meshes programmatically.
8657 
8658   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
8659 
8660 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
8661 @*/
8662 PetscErrorCode DMPlexCheckSymmetry(DM dm)
8663 {
8664   PetscSection    coneSection, supportSection;
8665   const PetscInt *cone, *support;
8666   PetscInt        coneSize, c, supportSize, s;
8667   PetscInt        pStart, pEnd, p, pp, csize, ssize;
8668   PetscBool       storagecheck = PETSC_TRUE;
8669 
8670   PetscFunctionBegin;
8671   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8672   PetscCall(DMViewFromOptions(dm, NULL, "-sym_dm_view"));
8673   PetscCall(DMPlexGetConeSection(dm, &coneSection));
8674   PetscCall(DMPlexGetSupportSection(dm, &supportSection));
8675   /* Check that point p is found in the support of its cone points, and vice versa */
8676   PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
8677   for (p = pStart; p < pEnd; ++p) {
8678     PetscCall(DMPlexGetConeSize(dm, p, &coneSize));
8679     PetscCall(DMPlexGetCone(dm, p, &cone));
8680     for (c = 0; c < coneSize; ++c) {
8681       PetscBool dup = PETSC_FALSE;
8682       PetscInt  d;
8683       for (d = c - 1; d >= 0; --d) {
8684         if (cone[c] == cone[d]) {
8685           dup = PETSC_TRUE;
8686           break;
8687         }
8688       }
8689       PetscCall(DMPlexGetSupportSize(dm, cone[c], &supportSize));
8690       PetscCall(DMPlexGetSupport(dm, cone[c], &support));
8691       for (s = 0; s < supportSize; ++s) {
8692         if (support[s] == p) break;
8693       }
8694       if ((s >= supportSize) || (dup && (support[s + 1] != p))) {
8695         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", p));
8696         for (s = 0; s < coneSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[s]));
8697         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8698         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", cone[c]));
8699         for (s = 0; s < supportSize; ++s) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[s]));
8700         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8701         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]);
8702         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in support of cone point %" PetscInt_FMT, p, cone[c]);
8703       }
8704     }
8705     PetscCall(DMPlexGetTreeParent(dm, p, &pp, NULL));
8706     if (p != pp) {
8707       storagecheck = PETSC_FALSE;
8708       continue;
8709     }
8710     PetscCall(DMPlexGetSupportSize(dm, p, &supportSize));
8711     PetscCall(DMPlexGetSupport(dm, p, &support));
8712     for (s = 0; s < supportSize; ++s) {
8713       PetscCall(DMPlexGetConeSize(dm, support[s], &coneSize));
8714       PetscCall(DMPlexGetCone(dm, support[s], &cone));
8715       for (c = 0; c < coneSize; ++c) {
8716         PetscCall(DMPlexGetTreeParent(dm, cone[c], &pp, NULL));
8717         if (cone[c] != pp) {
8718           c = 0;
8719           break;
8720         }
8721         if (cone[c] == p) break;
8722       }
8723       if (c >= coneSize) {
8724         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " support: ", p));
8725         for (c = 0; c < supportSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", support[c]));
8726         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8727         PetscCall(PetscPrintf(PETSC_COMM_SELF, "p: %" PetscInt_FMT " cone: ", support[s]));
8728         for (c = 0; c < coneSize; ++c) PetscCall(PetscPrintf(PETSC_COMM_SELF, "%" PetscInt_FMT ", ", cone[c]));
8729         PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8730         SETERRQ(PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point %" PetscInt_FMT " not found in cone of support point %" PetscInt_FMT, p, support[s]);
8731       }
8732     }
8733   }
8734   if (storagecheck) {
8735     PetscCall(PetscSectionGetStorageSize(coneSection, &csize));
8736     PetscCall(PetscSectionGetStorageSize(supportSection, &ssize));
8737     PetscCheck(csize == ssize, PETSC_COMM_SELF, PETSC_ERR_ARG_SIZ, "Total cone size %" PetscInt_FMT " != Total support size %" PetscInt_FMT, csize, ssize);
8738   }
8739   PetscFunctionReturn(PETSC_SUCCESS);
8740 }
8741 
8742 /*
8743   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.
8744 */
8745 static PetscErrorCode DMPlexCellUnsplitVertices_Private(DM dm, PetscInt c, DMPolytopeType ct, PetscInt *unsplit)
8746 {
8747   DMPolytopeType  cct;
8748   PetscInt        ptpoints[4];
8749   const PetscInt *cone, *ccone, *ptcone;
8750   PetscInt        coneSize, cp, cconeSize, ccp, npt = 0, pt;
8751 
8752   PetscFunctionBegin;
8753   *unsplit = 0;
8754   switch (ct) {
8755   case DM_POLYTOPE_POINT_PRISM_TENSOR:
8756     ptpoints[npt++] = c;
8757     break;
8758   case DM_POLYTOPE_SEG_PRISM_TENSOR:
8759     PetscCall(DMPlexGetCone(dm, c, &cone));
8760     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8761     for (cp = 0; cp < coneSize; ++cp) {
8762       PetscCall(DMPlexGetCellType(dm, cone[cp], &cct));
8763       if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) ptpoints[npt++] = cone[cp];
8764     }
8765     break;
8766   case DM_POLYTOPE_TRI_PRISM_TENSOR:
8767   case DM_POLYTOPE_QUAD_PRISM_TENSOR:
8768     PetscCall(DMPlexGetCone(dm, c, &cone));
8769     PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8770     for (cp = 0; cp < coneSize; ++cp) {
8771       PetscCall(DMPlexGetCone(dm, cone[cp], &ccone));
8772       PetscCall(DMPlexGetConeSize(dm, cone[cp], &cconeSize));
8773       for (ccp = 0; ccp < cconeSize; ++ccp) {
8774         PetscCall(DMPlexGetCellType(dm, ccone[ccp], &cct));
8775         if (cct == DM_POLYTOPE_POINT_PRISM_TENSOR) {
8776           PetscInt p;
8777           for (p = 0; p < npt; ++p)
8778             if (ptpoints[p] == ccone[ccp]) break;
8779           if (p == npt) ptpoints[npt++] = ccone[ccp];
8780         }
8781       }
8782     }
8783     break;
8784   default:
8785     break;
8786   }
8787   for (pt = 0; pt < npt; ++pt) {
8788     PetscCall(DMPlexGetCone(dm, ptpoints[pt], &ptcone));
8789     if (ptcone[0] == ptcone[1]) ++(*unsplit);
8790   }
8791   PetscFunctionReturn(PETSC_SUCCESS);
8792 }
8793 
8794 /*@
8795   DMPlexCheckSkeleton - Check that each cell has the correct number of vertices
8796 
8797   Input Parameters:
8798 + dm         - The `DMPLEX` object
8799 - cellHeight - Normally 0
8800 
8801   Level: developer
8802 
8803   Notes:
8804   This is a useful diagnostic when creating meshes programmatically.
8805   Currently applicable only to homogeneous simplex or tensor meshes.
8806 
8807   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
8808 
8809 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
8810 @*/
8811 PetscErrorCode DMPlexCheckSkeleton(DM dm, PetscInt cellHeight)
8812 {
8813   DMPlexInterpolatedFlag interp;
8814   DMPolytopeType         ct;
8815   PetscInt               vStart, vEnd, cStart, cEnd, c;
8816 
8817   PetscFunctionBegin;
8818   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8819   PetscCall(DMPlexIsInterpolated(dm, &interp));
8820   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
8821   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8822   for (c = cStart; c < cEnd; ++c) {
8823     PetscInt *closure = NULL;
8824     PetscInt  coneSize, closureSize, cl, Nv = 0;
8825 
8826     PetscCall(DMPlexGetCellType(dm, c, &ct));
8827     PetscCheck((PetscInt)ct >= 0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Cell %" PetscInt_FMT " has no cell type", c);
8828     if (ct == DM_POLYTOPE_UNKNOWN) continue;
8829     if (interp == DMPLEX_INTERPOLATED_FULL) {
8830       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8831       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));
8832     }
8833     PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8834     for (cl = 0; cl < closureSize * 2; cl += 2) {
8835       const PetscInt p = closure[cl];
8836       if ((p >= vStart) && (p < vEnd)) ++Nv;
8837     }
8838     PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8839     /* Special Case: Tensor faces with identified vertices */
8840     if (Nv < DMPolytopeTypeGetNumVertices(ct)) {
8841       PetscInt unsplit;
8842 
8843       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8844       if (Nv + unsplit == DMPolytopeTypeGetNumVertices(ct)) continue;
8845     }
8846     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));
8847   }
8848   PetscFunctionReturn(PETSC_SUCCESS);
8849 }
8850 
8851 /*@
8852   DMPlexCheckFaces - Check that the faces of each cell give a vertex order this is consistent with what we expect from the cell type
8853 
8854   Collective
8855 
8856   Input Parameters:
8857 + dm         - The `DMPLEX` object
8858 - cellHeight - Normally 0
8859 
8860   Level: developer
8861 
8862   Notes:
8863   This is a useful diagnostic when creating meshes programmatically.
8864   This routine is only relevant for meshes that are fully interpolated across all ranks.
8865   It will error out if a partially interpolated mesh is given on some rank.
8866   It will do nothing for locally uninterpolated mesh (as there is nothing to check).
8867 
8868   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
8869 
8870 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMPlexGetVTKCellHeight()`, `DMSetFromOptions()`
8871 @*/
8872 PetscErrorCode DMPlexCheckFaces(DM dm, PetscInt cellHeight)
8873 {
8874   PetscInt               dim, depth, vStart, vEnd, cStart, cEnd, c, h;
8875   DMPlexInterpolatedFlag interpEnum;
8876 
8877   PetscFunctionBegin;
8878   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
8879   PetscCall(DMPlexIsInterpolatedCollective(dm, &interpEnum));
8880   if (interpEnum == DMPLEX_INTERPOLATED_NONE) PetscFunctionReturn(PETSC_SUCCESS);
8881   if (interpEnum != DMPLEX_INTERPOLATED_FULL) {
8882     PetscCall(PetscPrintf(PetscObjectComm((PetscObject)dm), "DMPlexCheckFaces() warning: Mesh is only partially interpolated, this is currently not supported"));
8883     PetscFunctionReturn(PETSC_SUCCESS);
8884   }
8885 
8886   PetscCall(DMGetDimension(dm, &dim));
8887   PetscCall(DMPlexGetDepth(dm, &depth));
8888   PetscCall(DMPlexGetDepthStratum(dm, 0, &vStart, &vEnd));
8889   for (h = cellHeight; h < PetscMin(depth, dim); ++h) {
8890     PetscCall(DMPlexGetHeightStratum(dm, h, &cStart, &cEnd));
8891     for (c = cStart; c < cEnd; ++c) {
8892       const PetscInt       *cone, *ornt, *faceSizes, *faces;
8893       const DMPolytopeType *faceTypes;
8894       DMPolytopeType        ct;
8895       PetscInt              numFaces, coneSize, f;
8896       PetscInt             *closure = NULL, closureSize, cl, numCorners = 0, fOff = 0, unsplit;
8897 
8898       PetscCall(DMPlexGetCellType(dm, c, &ct));
8899       PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
8900       if (unsplit) continue;
8901       PetscCall(DMPlexGetConeSize(dm, c, &coneSize));
8902       PetscCall(DMPlexGetCone(dm, c, &cone));
8903       PetscCall(DMPlexGetConeOrientation(dm, c, &ornt));
8904       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8905       for (cl = 0; cl < closureSize * 2; cl += 2) {
8906         const PetscInt p = closure[cl];
8907         if ((p >= vStart) && (p < vEnd)) closure[numCorners++] = p;
8908       }
8909       PetscCall(DMPlexGetRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
8910       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);
8911       for (f = 0; f < numFaces; ++f) {
8912         DMPolytopeType fct;
8913         PetscInt      *fclosure = NULL, fclosureSize, cl, fnumCorners = 0, v;
8914 
8915         PetscCall(DMPlexGetCellType(dm, cone[f], &fct));
8916         PetscCall(DMPlexGetTransitiveClosure_Internal(dm, cone[f], ornt[f], PETSC_TRUE, &fclosureSize, &fclosure));
8917         for (cl = 0; cl < fclosureSize * 2; cl += 2) {
8918           const PetscInt p = fclosure[cl];
8919           if ((p >= vStart) && (p < vEnd)) fclosure[fnumCorners++] = p;
8920         }
8921         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]);
8922         for (v = 0; v < fnumCorners; ++v) {
8923           if (fclosure[v] != faces[fOff + v]) {
8924             PetscInt v1;
8925 
8926             PetscCall(PetscPrintf(PETSC_COMM_SELF, "face closure:"));
8927             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, fclosure[v1]));
8928             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\ncell face:"));
8929             for (v1 = 0; v1 < fnumCorners; ++v1) PetscCall(PetscPrintf(PETSC_COMM_SELF, " %" PetscInt_FMT, faces[fOff + v1]));
8930             PetscCall(PetscPrintf(PETSC_COMM_SELF, "\n"));
8931             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]);
8932           }
8933         }
8934         PetscCall(DMPlexRestoreTransitiveClosure(dm, cone[f], PETSC_TRUE, &fclosureSize, &fclosure));
8935         fOff += faceSizes[f];
8936       }
8937       PetscCall(DMPlexRestoreRawFaces_Internal(dm, ct, closure, &numFaces, &faceTypes, &faceSizes, &faces));
8938       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &closureSize, &closure));
8939     }
8940   }
8941   PetscFunctionReturn(PETSC_SUCCESS);
8942 }
8943 
8944 /*@
8945   DMPlexCheckGeometry - Check the geometry of mesh cells
8946 
8947   Input Parameter:
8948 . dm - The `DMPLEX` object
8949 
8950   Level: developer
8951 
8952   Notes:
8953   This is a useful diagnostic when creating meshes programmatically.
8954 
8955   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
8956 
8957 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
8958 @*/
8959 PetscErrorCode DMPlexCheckGeometry(DM dm)
8960 {
8961   Vec       coordinates;
8962   PetscReal detJ, J[9], refVol = 1.0;
8963   PetscReal vol;
8964   PetscInt  dim, depth, dE, d, cStart, cEnd, c;
8965 
8966   PetscFunctionBegin;
8967   PetscCall(DMGetDimension(dm, &dim));
8968   PetscCall(DMGetCoordinateDim(dm, &dE));
8969   if (dim != dE) PetscFunctionReturn(PETSC_SUCCESS);
8970   PetscCall(DMPlexGetDepth(dm, &depth));
8971   for (d = 0; d < dim; ++d) refVol *= 2.0;
8972   PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
8973   /* Make sure local coordinates are created, because that step is collective */
8974   PetscCall(DMGetCoordinatesLocal(dm, &coordinates));
8975   if (!coordinates) PetscFunctionReturn(PETSC_SUCCESS);
8976   for (c = cStart; c < cEnd; ++c) {
8977     DMPolytopeType ct;
8978     PetscInt       unsplit;
8979     PetscBool      ignoreZeroVol = PETSC_FALSE;
8980 
8981     PetscCall(DMPlexGetCellType(dm, c, &ct));
8982     switch (ct) {
8983     case DM_POLYTOPE_SEG_PRISM_TENSOR:
8984     case DM_POLYTOPE_TRI_PRISM_TENSOR:
8985     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
8986       ignoreZeroVol = PETSC_TRUE;
8987       break;
8988     default:
8989       break;
8990     }
8991     switch (ct) {
8992     case DM_POLYTOPE_TRI_PRISM:
8993     case DM_POLYTOPE_TRI_PRISM_TENSOR:
8994     case DM_POLYTOPE_QUAD_PRISM_TENSOR:
8995     case DM_POLYTOPE_PYRAMID:
8996       continue;
8997     default:
8998       break;
8999     }
9000     PetscCall(DMPlexCellUnsplitVertices_Private(dm, c, ct, &unsplit));
9001     if (unsplit) continue;
9002     PetscCall(DMPlexComputeCellGeometryFEM(dm, c, NULL, NULL, J, NULL, &detJ));
9003     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);
9004     PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FEM Volume %g\n", c, (double)(detJ * refVol)));
9005     /* This should work with periodicity since DG coordinates should be used */
9006     if (depth > 1) {
9007       PetscCall(DMPlexComputeCellGeometryFVM(dm, c, &vol, NULL, NULL));
9008       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);
9009       PetscCall(PetscInfo(dm, "Cell %" PetscInt_FMT " FVM Volume %g\n", c, (double)vol));
9010     }
9011   }
9012   PetscFunctionReturn(PETSC_SUCCESS);
9013 }
9014 
9015 /*@
9016   DMPlexCheckPointSF - Check that several necessary conditions are met for the point `PetscSF` of this plex.
9017 
9018   Collective
9019 
9020   Input Parameters:
9021 + dm              - The `DMPLEX` object
9022 . pointSF         - The `PetscSF`, or `NULL` for `PointSF` attached to `DM`
9023 - allowExtraRoots - Flag to allow extra points not present in the `DM`
9024 
9025   Level: developer
9026 
9027   Notes:
9028   This is mainly intended for debugging/testing purposes.
9029 
9030   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9031 
9032   Extra roots can come from priodic cuts, where additional points appear on the boundary
9033 
9034 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMGetPointSF()`, `DMSetFromOptions()`
9035 @*/
9036 PetscErrorCode DMPlexCheckPointSF(DM dm, PetscSF pointSF, PetscBool allowExtraRoots)
9037 {
9038   PetscInt           l, nleaves, nroots, overlap;
9039   const PetscInt    *locals;
9040   const PetscSFNode *remotes;
9041   PetscBool          distributed;
9042   MPI_Comm           comm;
9043   PetscMPIInt        rank;
9044 
9045   PetscFunctionBegin;
9046   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9047   if (pointSF) PetscValidHeaderSpecific(pointSF, PETSCSF_CLASSID, 2);
9048   else pointSF = dm->sf;
9049   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9050   PetscCheck(pointSF, comm, PETSC_ERR_ARG_WRONGSTATE, "DMPlex must have Point SF attached");
9051   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9052   {
9053     PetscMPIInt mpiFlag;
9054 
9055     PetscCallMPI(MPI_Comm_compare(comm, PetscObjectComm((PetscObject)pointSF), &mpiFlag));
9056     PetscCheck(mpiFlag == MPI_CONGRUENT || mpiFlag == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "DM and Point SF have different communicators (flag %d)", mpiFlag);
9057   }
9058   PetscCall(PetscSFGetGraph(pointSF, &nroots, &nleaves, &locals, &remotes));
9059   PetscCall(DMPlexIsDistributed(dm, &distributed));
9060   if (!distributed) {
9061     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);
9062     PetscFunctionReturn(PETSC_SUCCESS);
9063   }
9064   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);
9065   PetscCall(DMPlexGetOverlap(dm, &overlap));
9066 
9067   /* Check SF graph is compatible with DMPlex chart */
9068   {
9069     PetscInt pStart, pEnd, maxLeaf;
9070 
9071     PetscCall(DMPlexGetChart(dm, &pStart, &pEnd));
9072     PetscCall(PetscSFGetLeafRange(pointSF, NULL, &maxLeaf));
9073     PetscCheck(allowExtraRoots || pEnd - pStart == nroots, PETSC_COMM_SELF, PETSC_ERR_PLIB, "pEnd - pStart = %" PetscInt_FMT " != nroots = %" PetscInt_FMT, pEnd - pStart, nroots);
9074     PetscCheck(maxLeaf < pEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "maxLeaf = %" PetscInt_FMT " >= pEnd = %" PetscInt_FMT, maxLeaf, pEnd);
9075   }
9076 
9077   /* Check Point SF has no local points referenced */
9078   for (l = 0; l < nleaves; l++) {
9079     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);
9080   }
9081 
9082   /* Check there are no cells in interface */
9083   if (!overlap) {
9084     PetscInt cellHeight, cStart, cEnd;
9085 
9086     PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9087     PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9088     for (l = 0; l < nleaves; ++l) {
9089       const PetscInt point = locals ? locals[l] : l;
9090 
9091       PetscCheck(point < cStart || point >= cEnd, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " which is a cell", point);
9092     }
9093   }
9094 
9095   /* If some point is in interface, then all its cone points must be also in interface (either as leaves or roots) */
9096   {
9097     const PetscInt *rootdegree;
9098 
9099     PetscCall(PetscSFComputeDegreeBegin(pointSF, &rootdegree));
9100     PetscCall(PetscSFComputeDegreeEnd(pointSF, &rootdegree));
9101     for (l = 0; l < nleaves; ++l) {
9102       const PetscInt  point = locals ? locals[l] : l;
9103       const PetscInt *cone;
9104       PetscInt        coneSize, c, idx;
9105 
9106       PetscCall(DMPlexGetConeSize(dm, point, &coneSize));
9107       PetscCall(DMPlexGetCone(dm, point, &cone));
9108       for (c = 0; c < coneSize; ++c) {
9109         if (!rootdegree[cone[c]]) {
9110           if (locals) {
9111             PetscCall(PetscFindInt(cone[c], nleaves, locals, &idx));
9112           } else {
9113             idx = (cone[c] < nleaves) ? cone[c] : -1;
9114           }
9115           PetscCheck(idx >= 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Point SF contains %" PetscInt_FMT " but not %" PetscInt_FMT " from its cone", point, cone[c]);
9116         }
9117       }
9118     }
9119   }
9120   PetscFunctionReturn(PETSC_SUCCESS);
9121 }
9122 
9123 /*@
9124   DMPlexCheck - Perform various checks of `DMPLEX` sanity
9125 
9126   Input Parameter:
9127 . dm - The `DMPLEX` object
9128 
9129   Level: developer
9130 
9131   Notes:
9132   This is a useful diagnostic when creating meshes programmatically.
9133 
9134   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9135 
9136   Currently does not include `DMPlexCheckCellShape()`.
9137 
9138 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMCreate()`, `DMSetFromOptions()`
9139 @*/
9140 PetscErrorCode DMPlexCheck(DM dm)
9141 {
9142   PetscInt cellHeight;
9143 
9144   PetscFunctionBegin;
9145   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9146   PetscCall(DMPlexCheckSymmetry(dm));
9147   PetscCall(DMPlexCheckSkeleton(dm, cellHeight));
9148   PetscCall(DMPlexCheckFaces(dm, cellHeight));
9149   PetscCall(DMPlexCheckGeometry(dm));
9150   PetscCall(DMPlexCheckPointSF(dm, NULL, PETSC_FALSE));
9151   PetscCall(DMPlexCheckInterfaceCones(dm));
9152   PetscFunctionReturn(PETSC_SUCCESS);
9153 }
9154 
9155 typedef struct cell_stats {
9156   PetscReal min, max, sum, squaresum;
9157   PetscInt  count;
9158 } cell_stats_t;
9159 
9160 static void MPIAPI cell_stats_reduce(void *a, void *b, int *len, MPI_Datatype *datatype)
9161 {
9162   PetscInt i, N = *len;
9163 
9164   for (i = 0; i < N; i++) {
9165     cell_stats_t *A = (cell_stats_t *)a;
9166     cell_stats_t *B = (cell_stats_t *)b;
9167 
9168     B->min = PetscMin(A->min, B->min);
9169     B->max = PetscMax(A->max, B->max);
9170     B->sum += A->sum;
9171     B->squaresum += A->squaresum;
9172     B->count += A->count;
9173   }
9174 }
9175 
9176 /*@
9177   DMPlexCheckCellShape - Checks the Jacobian of the mapping from reference to real cells and computes some minimal statistics.
9178 
9179   Collective
9180 
9181   Input Parameters:
9182 + dm        - The `DMPLEX` object
9183 . output    - If true, statistics will be displayed on `stdout`
9184 - condLimit - Display all cells above this condition number, or `PETSC_DETERMINE` for no cell output
9185 
9186   Level: developer
9187 
9188   Notes:
9189   This is mainly intended for debugging/testing purposes.
9190 
9191   For the complete list of DMPlexCheck* functions, see `DMSetFromOptions()`.
9192 
9193 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexComputeOrthogonalQuality()`
9194 @*/
9195 PetscErrorCode DMPlexCheckCellShape(DM dm, PetscBool output, PetscReal condLimit)
9196 {
9197   DM           dmCoarse;
9198   cell_stats_t stats, globalStats;
9199   MPI_Comm     comm = PetscObjectComm((PetscObject)dm);
9200   PetscReal   *J, *invJ, min = 0, max = 0, mean = 0, stdev = 0;
9201   PetscReal    limit = condLimit > 0 ? condLimit : PETSC_MAX_REAL;
9202   PetscInt     cdim, cStart, cEnd, c, eStart, eEnd, count = 0;
9203   PetscMPIInt  rank, size;
9204 
9205   PetscFunctionBegin;
9206   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9207   stats.min = PETSC_MAX_REAL;
9208   stats.max = PETSC_MIN_REAL;
9209   stats.sum = stats.squaresum = 0.;
9210   stats.count                 = 0;
9211 
9212   PetscCallMPI(MPI_Comm_size(comm, &size));
9213   PetscCallMPI(MPI_Comm_rank(comm, &rank));
9214   PetscCall(DMGetCoordinateDim(dm, &cdim));
9215   PetscCall(PetscMalloc2(PetscSqr(cdim), &J, PetscSqr(cdim), &invJ));
9216   PetscCall(DMPlexGetSimplexOrBoxCells(dm, 0, &cStart, &cEnd));
9217   PetscCall(DMPlexGetDepthStratum(dm, 1, &eStart, &eEnd));
9218   for (c = cStart; c < cEnd; c++) {
9219     PetscInt  i;
9220     PetscReal frobJ = 0., frobInvJ = 0., cond2, cond, detJ;
9221 
9222     PetscCall(DMPlexComputeCellGeometryAffineFEM(dm, c, NULL, J, invJ, &detJ));
9223     PetscCheck(detJ >= 0.0, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONG, "Mesh cell %" PetscInt_FMT " is inverted", c);
9224     for (i = 0; i < PetscSqr(cdim); ++i) {
9225       frobJ += J[i] * J[i];
9226       frobInvJ += invJ[i] * invJ[i];
9227     }
9228     cond2 = frobJ * frobInvJ;
9229     cond  = PetscSqrtReal(cond2);
9230 
9231     stats.min = PetscMin(stats.min, cond);
9232     stats.max = PetscMax(stats.max, cond);
9233     stats.sum += cond;
9234     stats.squaresum += cond2;
9235     stats.count++;
9236     if (output && cond > limit) {
9237       PetscSection coordSection;
9238       Vec          coordsLocal;
9239       PetscScalar *coords = NULL;
9240       PetscInt     Nv, d, clSize, cl, *closure = NULL;
9241 
9242       PetscCall(DMGetCoordinatesLocal(dm, &coordsLocal));
9243       PetscCall(DMGetCoordinateSection(dm, &coordSection));
9244       PetscCall(DMPlexVecGetClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9245       PetscCall(PetscSynchronizedPrintf(comm, "[%d] Cell %" PetscInt_FMT " cond %g\n", rank, c, (double)cond));
9246       for (i = 0; i < Nv / cdim; ++i) {
9247         PetscCall(PetscSynchronizedPrintf(comm, "  Vertex %" PetscInt_FMT ": (", i));
9248         for (d = 0; d < cdim; ++d) {
9249           if (d > 0) PetscCall(PetscSynchronizedPrintf(comm, ", "));
9250           PetscCall(PetscSynchronizedPrintf(comm, "%g", (double)PetscRealPart(coords[i * cdim + d])));
9251         }
9252         PetscCall(PetscSynchronizedPrintf(comm, ")\n"));
9253       }
9254       PetscCall(DMPlexGetTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9255       for (cl = 0; cl < clSize * 2; cl += 2) {
9256         const PetscInt edge = closure[cl];
9257 
9258         if ((edge >= eStart) && (edge < eEnd)) {
9259           PetscReal len;
9260 
9261           PetscCall(DMPlexComputeCellGeometryFVM(dm, edge, &len, NULL, NULL));
9262           PetscCall(PetscSynchronizedPrintf(comm, "  Edge %" PetscInt_FMT ": length %g\n", edge, (double)len));
9263         }
9264       }
9265       PetscCall(DMPlexRestoreTransitiveClosure(dm, c, PETSC_TRUE, &clSize, &closure));
9266       PetscCall(DMPlexVecRestoreClosure(dm, coordSection, coordsLocal, c, &Nv, &coords));
9267     }
9268   }
9269   if (output) PetscCall(PetscSynchronizedFlush(comm, NULL));
9270 
9271   if (size > 1) {
9272     PetscMPIInt  blockLengths[2] = {4, 1};
9273     MPI_Aint     blockOffsets[2] = {offsetof(cell_stats_t, min), offsetof(cell_stats_t, count)};
9274     MPI_Datatype blockTypes[2]   = {MPIU_REAL, MPIU_INT}, statType;
9275     MPI_Op       statReduce;
9276 
9277     PetscCallMPI(MPI_Type_create_struct(2, blockLengths, blockOffsets, blockTypes, &statType));
9278     PetscCallMPI(MPI_Type_commit(&statType));
9279     PetscCallMPI(MPI_Op_create(cell_stats_reduce, PETSC_TRUE, &statReduce));
9280     PetscCallMPI(MPI_Reduce(&stats, &globalStats, 1, statType, statReduce, 0, comm));
9281     PetscCallMPI(MPI_Op_free(&statReduce));
9282     PetscCallMPI(MPI_Type_free(&statType));
9283   } else {
9284     PetscCall(PetscArraycpy(&globalStats, &stats, 1));
9285   }
9286   if (rank == 0) {
9287     count = globalStats.count;
9288     min   = globalStats.min;
9289     max   = globalStats.max;
9290     mean  = globalStats.sum / globalStats.count;
9291     stdev = globalStats.count > 1 ? PetscSqrtReal(PetscMax((globalStats.squaresum - globalStats.count * mean * mean) / (globalStats.count - 1), 0)) : 0.0;
9292   }
9293 
9294   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));
9295   PetscCall(PetscFree2(J, invJ));
9296 
9297   PetscCall(DMGetCoarseDM(dm, &dmCoarse));
9298   if (dmCoarse) {
9299     PetscBool isplex;
9300 
9301     PetscCall(PetscObjectTypeCompare((PetscObject)dmCoarse, DMPLEX, &isplex));
9302     if (isplex) PetscCall(DMPlexCheckCellShape(dmCoarse, output, condLimit));
9303   }
9304   PetscFunctionReturn(PETSC_SUCCESS);
9305 }
9306 
9307 /*@
9308   DMPlexComputeOrthogonalQuality - Compute cell-wise orthogonal quality mesh statistic. Optionally tags all cells with
9309   orthogonal quality below given tolerance.
9310 
9311   Collective
9312 
9313   Input Parameters:
9314 + dm   - The `DMPLEX` object
9315 . fv   - Optional `PetscFV` object for pre-computed cell/face centroid information
9316 - atol - [0, 1] Absolute tolerance for tagging cells.
9317 
9318   Output Parameters:
9319 + OrthQual      - `Vec` containing orthogonal quality per cell
9320 - OrthQualLabel - `DMLabel` tagging cells below atol with `DM_ADAPT_REFINE`
9321 
9322   Options Database Keys:
9323 + -dm_plex_orthogonal_quality_label_view - view OrthQualLabel if label is requested. Currently only `PETSCVIEWERASCII` is supported.
9324 - -dm_plex_orthogonal_quality_vec_view   - view OrthQual vector.
9325 
9326   Level: intermediate
9327 
9328   Notes:
9329   Orthogonal quality is given by the following formula\:
9330 
9331   $ \min \left[ \frac{A_i \cdot f_i}{\|A_i\| \|f_i\|} , \frac{A_i \cdot c_i}{\|A_i\| \|c_i\|} \right]$
9332 
9333   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
9334   is the vector from the current cells centroid to the centroid of its i'th neighbor (which shares a face with the
9335   current cell). This computes the vector similarity between each cell face and its corresponding neighbor centroid by
9336   calculating the cosine of the angle between these vectors.
9337 
9338   Orthogonal quality ranges from 1 (best) to 0 (worst).
9339 
9340   This routine is mainly useful for FVM, however is not restricted to only FVM. The `PetscFV` object is optionally used to check for
9341   pre-computed FVM cell data, but if it is not passed in then this data will be computed.
9342 
9343   Cells are tagged if they have an orthogonal quality less than or equal to the absolute tolerance.
9344 
9345 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexCheckCellShape()`, `DMCreateLabel()`, `PetscFV`, `DMLabel`, `Vec`
9346 @*/
9347 PetscErrorCode DMPlexComputeOrthogonalQuality(DM dm, PetscFV fv, PetscReal atol, Vec *OrthQual, DMLabel *OrthQualLabel)
9348 {
9349   PetscInt               nc, cellHeight, cStart, cEnd, cell, cellIter = 0;
9350   PetscInt              *idx;
9351   PetscScalar           *oqVals;
9352   const PetscScalar     *cellGeomArr, *faceGeomArr;
9353   PetscReal             *ci, *fi, *Ai;
9354   MPI_Comm               comm;
9355   Vec                    cellgeom, facegeom;
9356   DM                     dmFace, dmCell;
9357   IS                     glob;
9358   ISLocalToGlobalMapping ltog;
9359   PetscViewer            vwr;
9360 
9361   PetscFunctionBegin;
9362   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9363   if (fv) PetscValidHeaderSpecific(fv, PETSCFV_CLASSID, 2);
9364   PetscAssertPointer(OrthQual, 4);
9365   PetscCheck(atol >= 0.0 && atol <= 1.0, PETSC_COMM_SELF, PETSC_ERR_ARG_OUTOFRANGE, "Absolute tolerance %g not in [0,1]", (double)atol);
9366   PetscCall(PetscObjectGetComm((PetscObject)dm, &comm));
9367   PetscCall(DMGetDimension(dm, &nc));
9368   PetscCheck(nc >= 2, PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must have dimension >= 2 (current %" PetscInt_FMT ")", nc);
9369   {
9370     DMPlexInterpolatedFlag interpFlag;
9371 
9372     PetscCall(DMPlexIsInterpolated(dm, &interpFlag));
9373     if (interpFlag != DMPLEX_INTERPOLATED_FULL) {
9374       PetscMPIInt rank;
9375 
9376       PetscCallMPI(MPI_Comm_rank(comm, &rank));
9377       SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_WRONGSTATE, "DM must be fully interpolated, DM on rank %d is not fully interpolated", rank);
9378     }
9379   }
9380   if (OrthQualLabel) {
9381     PetscAssertPointer(OrthQualLabel, 5);
9382     PetscCall(DMCreateLabel(dm, "Orthogonal_Quality"));
9383     PetscCall(DMGetLabel(dm, "Orthogonal_Quality", OrthQualLabel));
9384   } else {
9385     *OrthQualLabel = NULL;
9386   }
9387   PetscCall(DMPlexGetVTKCellHeight(dm, &cellHeight));
9388   PetscCall(DMPlexGetHeightStratum(dm, cellHeight, &cStart, &cEnd));
9389   PetscCall(DMPlexCreateCellNumbering_Internal(dm, PETSC_TRUE, &glob));
9390   PetscCall(ISLocalToGlobalMappingCreateIS(glob, &ltog));
9391   PetscCall(ISLocalToGlobalMappingSetType(ltog, ISLOCALTOGLOBALMAPPINGHASH));
9392   PetscCall(VecCreate(comm, OrthQual));
9393   PetscCall(VecSetType(*OrthQual, VECSTANDARD));
9394   PetscCall(VecSetSizes(*OrthQual, cEnd - cStart, PETSC_DETERMINE));
9395   PetscCall(VecSetLocalToGlobalMapping(*OrthQual, ltog));
9396   PetscCall(VecSetUp(*OrthQual));
9397   PetscCall(ISDestroy(&glob));
9398   PetscCall(ISLocalToGlobalMappingDestroy(&ltog));
9399   PetscCall(DMPlexGetDataFVM(dm, fv, &cellgeom, &facegeom, NULL));
9400   PetscCall(VecGetArrayRead(cellgeom, &cellGeomArr));
9401   PetscCall(VecGetArrayRead(facegeom, &faceGeomArr));
9402   PetscCall(VecGetDM(cellgeom, &dmCell));
9403   PetscCall(VecGetDM(facegeom, &dmFace));
9404   PetscCall(PetscMalloc5(cEnd - cStart, &idx, cEnd - cStart, &oqVals, nc, &ci, nc, &fi, nc, &Ai));
9405   for (cell = cStart; cell < cEnd; cellIter++, cell++) {
9406     PetscInt         cellneigh, cellneighiter = 0, adjSize = PETSC_DETERMINE;
9407     PetscInt         cellarr[2], *adj = NULL;
9408     PetscScalar     *cArr, *fArr;
9409     PetscReal        minvalc = 1.0, minvalf = 1.0;
9410     PetscFVCellGeom *cg;
9411 
9412     idx[cellIter] = cell - cStart;
9413     cellarr[0]    = cell;
9414     /* Make indexing into cellGeom easier */
9415     PetscCall(DMPlexPointLocalRead(dmCell, cell, cellGeomArr, &cg));
9416     PetscCall(DMPlexGetAdjacency_Internal(dm, cell, PETSC_TRUE, PETSC_FALSE, PETSC_FALSE, &adjSize, &adj));
9417     /* Technically 1 too big, but easier than fiddling with empty adjacency array */
9418     PetscCall(PetscCalloc2(adjSize, &cArr, adjSize, &fArr));
9419     for (cellneigh = 0; cellneigh < adjSize; cellneighiter++, cellneigh++) {
9420       PetscInt         i;
9421       const PetscInt   neigh  = adj[cellneigh];
9422       PetscReal        normci = 0, normfi = 0, normai = 0;
9423       PetscFVCellGeom *cgneigh;
9424       PetscFVFaceGeom *fg;
9425 
9426       /* Don't count ourselves in the neighbor list */
9427       if (neigh == cell) continue;
9428       PetscCall(DMPlexPointLocalRead(dmCell, neigh, cellGeomArr, &cgneigh));
9429       cellarr[1] = neigh;
9430       {
9431         PetscInt        numcovpts;
9432         const PetscInt *covpts;
9433 
9434         PetscCall(DMPlexGetMeet(dm, 2, cellarr, &numcovpts, &covpts));
9435         PetscCall(DMPlexPointLocalRead(dmFace, covpts[0], faceGeomArr, &fg));
9436         PetscCall(DMPlexRestoreMeet(dm, 2, cellarr, &numcovpts, &covpts));
9437       }
9438 
9439       /* Compute c_i, f_i and their norms */
9440       for (i = 0; i < nc; i++) {
9441         ci[i] = cgneigh->centroid[i] - cg->centroid[i];
9442         fi[i] = fg->centroid[i] - cg->centroid[i];
9443         Ai[i] = fg->normal[i];
9444         normci += PetscPowReal(ci[i], 2);
9445         normfi += PetscPowReal(fi[i], 2);
9446         normai += PetscPowReal(Ai[i], 2);
9447       }
9448       normci = PetscSqrtReal(normci);
9449       normfi = PetscSqrtReal(normfi);
9450       normai = PetscSqrtReal(normai);
9451 
9452       /* Normalize and compute for each face-cell-normal pair */
9453       for (i = 0; i < nc; i++) {
9454         ci[i] = ci[i] / normci;
9455         fi[i] = fi[i] / normfi;
9456         Ai[i] = Ai[i] / normai;
9457         /* PetscAbs because I don't know if normals are guaranteed to point out */
9458         cArr[cellneighiter] += PetscAbs(Ai[i] * ci[i]);
9459         fArr[cellneighiter] += PetscAbs(Ai[i] * fi[i]);
9460       }
9461       if (PetscRealPart(cArr[cellneighiter]) < minvalc) minvalc = PetscRealPart(cArr[cellneighiter]);
9462       if (PetscRealPart(fArr[cellneighiter]) < minvalf) minvalf = PetscRealPart(fArr[cellneighiter]);
9463     }
9464     PetscCall(PetscFree(adj));
9465     PetscCall(PetscFree2(cArr, fArr));
9466     /* Defer to cell if they're equal */
9467     oqVals[cellIter] = PetscMin(minvalf, minvalc);
9468     if (OrthQualLabel) {
9469       if (PetscRealPart(oqVals[cellIter]) <= atol) PetscCall(DMLabelSetValue(*OrthQualLabel, cell, DM_ADAPT_REFINE));
9470     }
9471   }
9472   PetscCall(VecSetValuesLocal(*OrthQual, cEnd - cStart, idx, oqVals, INSERT_VALUES));
9473   PetscCall(VecAssemblyBegin(*OrthQual));
9474   PetscCall(VecAssemblyEnd(*OrthQual));
9475   PetscCall(VecRestoreArrayRead(cellgeom, &cellGeomArr));
9476   PetscCall(VecRestoreArrayRead(facegeom, &faceGeomArr));
9477   PetscCall(PetscOptionsGetViewer(comm, NULL, NULL, "-dm_plex_orthogonal_quality_label_view", &vwr, NULL, NULL));
9478   if (OrthQualLabel) {
9479     if (vwr) PetscCall(DMLabelView(*OrthQualLabel, vwr));
9480   }
9481   PetscCall(PetscFree5(idx, oqVals, ci, fi, Ai));
9482   PetscCall(PetscViewerDestroy(&vwr));
9483   PetscCall(VecViewFromOptions(*OrthQual, NULL, "-dm_plex_orthogonal_quality_vec_view"));
9484   PetscFunctionReturn(PETSC_SUCCESS);
9485 }
9486 
9487 /* this is here instead of DMGetOutputDM because output DM still has constraints in the local indices that affect
9488  * interpolator construction */
9489 static PetscErrorCode DMGetFullDM(DM dm, DM *odm)
9490 {
9491   PetscSection section, newSection, gsection;
9492   PetscSF      sf;
9493   PetscBool    hasConstraints, ghasConstraints;
9494 
9495   PetscFunctionBegin;
9496   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9497   PetscAssertPointer(odm, 2);
9498   PetscCall(DMGetLocalSection(dm, &section));
9499   PetscCall(PetscSectionHasConstraints(section, &hasConstraints));
9500   PetscCall(MPIU_Allreduce(&hasConstraints, &ghasConstraints, 1, MPIU_BOOL, MPI_LOR, PetscObjectComm((PetscObject)dm)));
9501   if (!ghasConstraints) {
9502     PetscCall(PetscObjectReference((PetscObject)dm));
9503     *odm = dm;
9504     PetscFunctionReturn(PETSC_SUCCESS);
9505   }
9506   PetscCall(DMClone(dm, odm));
9507   PetscCall(DMCopyFields(dm, *odm));
9508   PetscCall(DMGetLocalSection(*odm, &newSection));
9509   PetscCall(DMGetPointSF(*odm, &sf));
9510   PetscCall(PetscSectionCreateGlobalSection(newSection, sf, PETSC_TRUE, PETSC_FALSE, &gsection));
9511   PetscCall(DMSetGlobalSection(*odm, gsection));
9512   PetscCall(PetscSectionDestroy(&gsection));
9513   PetscFunctionReturn(PETSC_SUCCESS);
9514 }
9515 
9516 static PetscErrorCode DMCreateAffineInterpolationCorrection_Plex(DM dmc, DM dmf, Vec *shift)
9517 {
9518   DM        dmco, dmfo;
9519   Mat       interpo;
9520   Vec       rscale;
9521   Vec       cglobalo, clocal;
9522   Vec       fglobal, fglobalo, flocal;
9523   PetscBool regular;
9524 
9525   PetscFunctionBegin;
9526   PetscCall(DMGetFullDM(dmc, &dmco));
9527   PetscCall(DMGetFullDM(dmf, &dmfo));
9528   PetscCall(DMSetCoarseDM(dmfo, dmco));
9529   PetscCall(DMPlexGetRegularRefinement(dmf, &regular));
9530   PetscCall(DMPlexSetRegularRefinement(dmfo, regular));
9531   PetscCall(DMCreateInterpolation(dmco, dmfo, &interpo, &rscale));
9532   PetscCall(DMCreateGlobalVector(dmco, &cglobalo));
9533   PetscCall(DMCreateLocalVector(dmc, &clocal));
9534   PetscCall(VecSet(cglobalo, 0.));
9535   PetscCall(VecSet(clocal, 0.));
9536   PetscCall(DMCreateGlobalVector(dmf, &fglobal));
9537   PetscCall(DMCreateGlobalVector(dmfo, &fglobalo));
9538   PetscCall(DMCreateLocalVector(dmf, &flocal));
9539   PetscCall(VecSet(fglobal, 0.));
9540   PetscCall(VecSet(fglobalo, 0.));
9541   PetscCall(VecSet(flocal, 0.));
9542   PetscCall(DMPlexInsertBoundaryValues(dmc, PETSC_TRUE, clocal, 0., NULL, NULL, NULL));
9543   PetscCall(DMLocalToGlobalBegin(dmco, clocal, INSERT_VALUES, cglobalo));
9544   PetscCall(DMLocalToGlobalEnd(dmco, clocal, INSERT_VALUES, cglobalo));
9545   PetscCall(MatMult(interpo, cglobalo, fglobalo));
9546   PetscCall(DMGlobalToLocalBegin(dmfo, fglobalo, INSERT_VALUES, flocal));
9547   PetscCall(DMGlobalToLocalEnd(dmfo, fglobalo, INSERT_VALUES, flocal));
9548   PetscCall(DMLocalToGlobalBegin(dmf, flocal, INSERT_VALUES, fglobal));
9549   PetscCall(DMLocalToGlobalEnd(dmf, flocal, INSERT_VALUES, fglobal));
9550   *shift = fglobal;
9551   PetscCall(VecDestroy(&flocal));
9552   PetscCall(VecDestroy(&fglobalo));
9553   PetscCall(VecDestroy(&clocal));
9554   PetscCall(VecDestroy(&cglobalo));
9555   PetscCall(VecDestroy(&rscale));
9556   PetscCall(MatDestroy(&interpo));
9557   PetscCall(DMDestroy(&dmfo));
9558   PetscCall(DMDestroy(&dmco));
9559   PetscFunctionReturn(PETSC_SUCCESS);
9560 }
9561 
9562 PETSC_INTERN PetscErrorCode DMInterpolateSolution_Plex(DM coarse, DM fine, Mat interp, Vec coarseSol, Vec fineSol)
9563 {
9564   PetscObject shifto;
9565   Vec         shift;
9566 
9567   PetscFunctionBegin;
9568   if (!interp) {
9569     Vec rscale;
9570 
9571     PetscCall(DMCreateInterpolation(coarse, fine, &interp, &rscale));
9572     PetscCall(VecDestroy(&rscale));
9573   } else {
9574     PetscCall(PetscObjectReference((PetscObject)interp));
9575   }
9576   PetscCall(PetscObjectQuery((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", &shifto));
9577   if (!shifto) {
9578     PetscCall(DMCreateAffineInterpolationCorrection_Plex(coarse, fine, &shift));
9579     PetscCall(PetscObjectCompose((PetscObject)interp, "_DMInterpolateSolution_Plex_Vec", (PetscObject)shift));
9580     shifto = (PetscObject)shift;
9581     PetscCall(VecDestroy(&shift));
9582   }
9583   shift = (Vec)shifto;
9584   PetscCall(MatInterpolate(interp, coarseSol, fineSol));
9585   PetscCall(VecAXPY(fineSol, 1.0, shift));
9586   PetscCall(MatDestroy(&interp));
9587   PetscFunctionReturn(PETSC_SUCCESS);
9588 }
9589 
9590 /* Pointwise interpolation
9591      Just code FEM for now
9592      u^f = I u^c
9593      sum_k u^f_k phi^f_k = I sum_j u^c_j phi^c_j
9594      u^f_i = sum_j psi^f_i I phi^c_j u^c_j
9595      I_{ij} = psi^f_i phi^c_j
9596 */
9597 PetscErrorCode DMCreateInterpolation_Plex(DM dmCoarse, DM dmFine, Mat *interpolation, Vec *scaling)
9598 {
9599   PetscSection gsc, gsf;
9600   PetscInt     m, n;
9601   void        *ctx;
9602   DM           cdm;
9603   PetscBool    regular, ismatis, isRefined = dmCoarse->data == dmFine->data ? PETSC_FALSE : PETSC_TRUE;
9604 
9605   PetscFunctionBegin;
9606   PetscCall(DMGetGlobalSection(dmFine, &gsf));
9607   PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
9608   PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
9609   PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
9610 
9611   PetscCall(PetscStrcmp(dmCoarse->mattype, MATIS, &ismatis));
9612   PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), interpolation));
9613   PetscCall(MatSetSizes(*interpolation, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
9614   PetscCall(MatSetType(*interpolation, ismatis ? MATAIJ : dmCoarse->mattype));
9615   PetscCall(DMGetApplicationContext(dmFine, &ctx));
9616 
9617   PetscCall(DMGetCoarseDM(dmFine, &cdm));
9618   PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
9619   if (!isRefined || (regular && cdm == dmCoarse)) PetscCall(DMPlexComputeInterpolatorNested(dmCoarse, dmFine, isRefined, *interpolation, ctx));
9620   else PetscCall(DMPlexComputeInterpolatorGeneral(dmCoarse, dmFine, *interpolation, ctx));
9621   PetscCall(MatViewFromOptions(*interpolation, NULL, "-interp_mat_view"));
9622   if (scaling) {
9623     /* Use naive scaling */
9624     PetscCall(DMCreateInterpolationScale(dmCoarse, dmFine, *interpolation, scaling));
9625   }
9626   PetscFunctionReturn(PETSC_SUCCESS);
9627 }
9628 
9629 PetscErrorCode DMCreateInjection_Plex(DM dmCoarse, DM dmFine, Mat *mat)
9630 {
9631   VecScatter ctx;
9632 
9633   PetscFunctionBegin;
9634   PetscCall(DMPlexComputeInjectorFEM(dmCoarse, dmFine, &ctx, NULL));
9635   PetscCall(MatCreateScatter(PetscObjectComm((PetscObject)ctx), ctx, mat));
9636   PetscCall(VecScatterDestroy(&ctx));
9637   PetscFunctionReturn(PETSC_SUCCESS);
9638 }
9639 
9640 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[])
9641 {
9642   const PetscInt Nc = uOff[1] - uOff[0];
9643   PetscInt       c;
9644   for (c = 0; c < Nc; ++c) g0[c * Nc + c] = 1.0;
9645 }
9646 
9647 PetscErrorCode DMCreateMassMatrixLumped_Plex(DM dm, Vec *mass)
9648 {
9649   DM           dmc;
9650   PetscDS      ds;
9651   Vec          ones, locmass;
9652   IS           cellIS;
9653   PetscFormKey key;
9654   PetscInt     depth;
9655 
9656   PetscFunctionBegin;
9657   PetscCall(DMClone(dm, &dmc));
9658   PetscCall(DMCopyDisc(dm, dmc));
9659   PetscCall(DMGetDS(dmc, &ds));
9660   PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
9661   PetscCall(DMCreateGlobalVector(dmc, mass));
9662   PetscCall(DMGetLocalVector(dmc, &ones));
9663   PetscCall(DMGetLocalVector(dmc, &locmass));
9664   PetscCall(DMPlexGetDepth(dmc, &depth));
9665   PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
9666   PetscCall(VecSet(locmass, 0.0));
9667   PetscCall(VecSet(ones, 1.0));
9668   key.label = NULL;
9669   key.value = 0;
9670   key.field = 0;
9671   key.part  = 0;
9672   PetscCall(DMPlexComputeJacobian_Action_Internal(dmc, key, cellIS, 0.0, 0.0, ones, NULL, ones, locmass, NULL));
9673   PetscCall(ISDestroy(&cellIS));
9674   PetscCall(VecSet(*mass, 0.0));
9675   PetscCall(DMLocalToGlobalBegin(dmc, locmass, ADD_VALUES, *mass));
9676   PetscCall(DMLocalToGlobalEnd(dmc, locmass, ADD_VALUES, *mass));
9677   PetscCall(DMRestoreLocalVector(dmc, &ones));
9678   PetscCall(DMRestoreLocalVector(dmc, &locmass));
9679   PetscCall(DMDestroy(&dmc));
9680   PetscFunctionReturn(PETSC_SUCCESS);
9681 }
9682 
9683 PetscErrorCode DMCreateMassMatrix_Plex(DM dmCoarse, DM dmFine, Mat *mass)
9684 {
9685   PetscSection gsc, gsf;
9686   PetscInt     m, n;
9687   void        *ctx;
9688   DM           cdm;
9689   PetscBool    regular;
9690 
9691   PetscFunctionBegin;
9692   if (dmFine == dmCoarse) {
9693     DM            dmc;
9694     PetscDS       ds;
9695     PetscWeakForm wf;
9696     Vec           u;
9697     IS            cellIS;
9698     PetscFormKey  key;
9699     PetscInt      depth;
9700 
9701     PetscCall(DMClone(dmFine, &dmc));
9702     PetscCall(DMCopyDisc(dmFine, dmc));
9703     PetscCall(DMGetDS(dmc, &ds));
9704     PetscCall(PetscDSGetWeakForm(ds, &wf));
9705     PetscCall(PetscWeakFormClear(wf));
9706     PetscCall(PetscDSSetJacobian(ds, 0, 0, g0_identity_private, NULL, NULL, NULL));
9707     PetscCall(DMCreateMatrix(dmc, mass));
9708     PetscCall(DMGetLocalVector(dmc, &u));
9709     PetscCall(DMPlexGetDepth(dmc, &depth));
9710     PetscCall(DMGetStratumIS(dmc, "depth", depth, &cellIS));
9711     PetscCall(MatZeroEntries(*mass));
9712     key.label = NULL;
9713     key.value = 0;
9714     key.field = 0;
9715     key.part  = 0;
9716     PetscCall(DMPlexComputeJacobian_Internal(dmc, key, cellIS, 0.0, 0.0, u, NULL, *mass, *mass, NULL));
9717     PetscCall(ISDestroy(&cellIS));
9718     PetscCall(DMRestoreLocalVector(dmc, &u));
9719     PetscCall(DMDestroy(&dmc));
9720   } else {
9721     PetscCall(DMGetGlobalSection(dmFine, &gsf));
9722     PetscCall(PetscSectionGetConstrainedStorageSize(gsf, &m));
9723     PetscCall(DMGetGlobalSection(dmCoarse, &gsc));
9724     PetscCall(PetscSectionGetConstrainedStorageSize(gsc, &n));
9725 
9726     PetscCall(MatCreate(PetscObjectComm((PetscObject)dmCoarse), mass));
9727     PetscCall(MatSetSizes(*mass, m, n, PETSC_DETERMINE, PETSC_DETERMINE));
9728     PetscCall(MatSetType(*mass, dmCoarse->mattype));
9729     PetscCall(DMGetApplicationContext(dmFine, &ctx));
9730 
9731     PetscCall(DMGetCoarseDM(dmFine, &cdm));
9732     PetscCall(DMPlexGetRegularRefinement(dmFine, &regular));
9733     if (regular && cdm == dmCoarse) PetscCall(DMPlexComputeMassMatrixNested(dmCoarse, dmFine, *mass, ctx));
9734     else PetscCall(DMPlexComputeMassMatrixGeneral(dmCoarse, dmFine, *mass, ctx));
9735   }
9736   PetscCall(MatViewFromOptions(*mass, NULL, "-mass_mat_view"));
9737   PetscFunctionReturn(PETSC_SUCCESS);
9738 }
9739 
9740 /*@
9741   DMPlexGetRegularRefinement - Get the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
9742 
9743   Input Parameter:
9744 . dm - The `DMPLEX` object
9745 
9746   Output Parameter:
9747 . regular - The flag
9748 
9749   Level: intermediate
9750 
9751 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetRegularRefinement()`
9752 @*/
9753 PetscErrorCode DMPlexGetRegularRefinement(DM dm, PetscBool *regular)
9754 {
9755   PetscFunctionBegin;
9756   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9757   PetscAssertPointer(regular, 2);
9758   *regular = ((DM_Plex *)dm->data)->regularRefinement;
9759   PetscFunctionReturn(PETSC_SUCCESS);
9760 }
9761 
9762 /*@
9763   DMPlexSetRegularRefinement - Set the flag indicating that this mesh was obtained by regular refinement from its coarse mesh
9764 
9765   Input Parameters:
9766 + dm      - The `DMPLEX` object
9767 - regular - The flag
9768 
9769   Level: intermediate
9770 
9771 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetRegularRefinement()`
9772 @*/
9773 PetscErrorCode DMPlexSetRegularRefinement(DM dm, PetscBool regular)
9774 {
9775   PetscFunctionBegin;
9776   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9777   ((DM_Plex *)dm->data)->regularRefinement = regular;
9778   PetscFunctionReturn(PETSC_SUCCESS);
9779 }
9780 
9781 /*@
9782   DMPlexGetAnchors - Get the layout of the anchor (point-to-point) constraints.  Typically, the user will not have to
9783   call DMPlexGetAnchors() directly: if there are anchors, then `DMPlexGetAnchors()` is called during `DMGetDefaultConstraints()`.
9784 
9785   Not Collective
9786 
9787   Input Parameter:
9788 . dm - The `DMPLEX` object
9789 
9790   Output Parameters:
9791 + anchorSection - If not `NULL`, set to the section describing which points anchor the constrained points.
9792 - anchorIS      - If not `NULL`, set to the list of anchors indexed by `anchorSection`
9793 
9794   Level: intermediate
9795 
9796 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexSetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`, `IS`, `PetscSection`
9797 @*/
9798 PetscErrorCode DMPlexGetAnchors(DM dm, PetscSection *anchorSection, IS *anchorIS)
9799 {
9800   DM_Plex *plex = (DM_Plex *)dm->data;
9801 
9802   PetscFunctionBegin;
9803   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9804   if (!plex->anchorSection && !plex->anchorIS && plex->createanchors) PetscCall((*plex->createanchors)(dm));
9805   if (anchorSection) *anchorSection = plex->anchorSection;
9806   if (anchorIS) *anchorIS = plex->anchorIS;
9807   PetscFunctionReturn(PETSC_SUCCESS);
9808 }
9809 
9810 /*@
9811   DMPlexSetAnchors - Set the layout of the local anchor (point-to-point) constraints.
9812 
9813   Collective
9814 
9815   Input Parameters:
9816 + dm            - The `DMPLEX` object
9817 . anchorSection - The section that describes the mapping from constrained points to the anchor points listed in anchorIS.
9818                   Must have a local communicator (`PETSC_COMM_SELF` or derivative).
9819 - anchorIS      - The list of all anchor points.  Must have a local communicator (`PETSC_COMM_SELF` or derivative).
9820 
9821   Level: intermediate
9822 
9823   Notes:
9824   Unlike boundary conditions, when a point's degrees of freedom in a section are constrained to
9825   an outside value, the anchor constraints set a point's degrees of freedom to be a linear
9826   combination of other points' degrees of freedom.
9827 
9828   After specifying the layout of constraints with `DMPlexSetAnchors()`, one specifies the constraints by calling
9829   `DMGetDefaultConstraints()` and filling in the entries in the constraint matrix.
9830 
9831   The reference counts of `anchorSection` and `anchorIS` are incremented.
9832 
9833 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMPlexGetAnchors()`, `DMGetDefaultConstraints()`, `DMSetDefaultConstraints()`
9834 @*/
9835 PetscErrorCode DMPlexSetAnchors(DM dm, PetscSection anchorSection, IS anchorIS)
9836 {
9837   DM_Plex    *plex = (DM_Plex *)dm->data;
9838   PetscMPIInt result;
9839 
9840   PetscFunctionBegin;
9841   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9842   if (anchorSection) {
9843     PetscValidHeaderSpecific(anchorSection, PETSC_SECTION_CLASSID, 2);
9844     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorSection), &result));
9845     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor section must have local communicator");
9846   }
9847   if (anchorIS) {
9848     PetscValidHeaderSpecific(anchorIS, IS_CLASSID, 3);
9849     PetscCallMPI(MPI_Comm_compare(PETSC_COMM_SELF, PetscObjectComm((PetscObject)anchorIS), &result));
9850     PetscCheck(result == MPI_CONGRUENT || result == MPI_IDENT, PETSC_COMM_SELF, PETSC_ERR_ARG_NOTSAMECOMM, "anchor IS must have local communicator");
9851   }
9852 
9853   PetscCall(PetscObjectReference((PetscObject)anchorSection));
9854   PetscCall(PetscSectionDestroy(&plex->anchorSection));
9855   plex->anchorSection = anchorSection;
9856 
9857   PetscCall(PetscObjectReference((PetscObject)anchorIS));
9858   PetscCall(ISDestroy(&plex->anchorIS));
9859   plex->anchorIS = anchorIS;
9860 
9861   if (PetscUnlikelyDebug(anchorIS && anchorSection)) {
9862     PetscInt        size, a, pStart, pEnd;
9863     const PetscInt *anchors;
9864 
9865     PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
9866     PetscCall(ISGetLocalSize(anchorIS, &size));
9867     PetscCall(ISGetIndices(anchorIS, &anchors));
9868     for (a = 0; a < size; a++) {
9869       PetscInt p;
9870 
9871       p = anchors[a];
9872       if (p >= pStart && p < pEnd) {
9873         PetscInt dof;
9874 
9875         PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
9876         if (dof) {
9877           PetscCall(ISRestoreIndices(anchorIS, &anchors));
9878           SETERRQ(PETSC_COMM_SELF, PETSC_ERR_ARG_INCOMP, "Point %" PetscInt_FMT " cannot be constrained and an anchor", p);
9879         }
9880       }
9881     }
9882     PetscCall(ISRestoreIndices(anchorIS, &anchors));
9883   }
9884   /* reset the generic constraints */
9885   PetscCall(DMSetDefaultConstraints(dm, NULL, NULL, NULL));
9886   PetscFunctionReturn(PETSC_SUCCESS);
9887 }
9888 
9889 static PetscErrorCode DMPlexCreateConstraintSection_Anchors(DM dm, PetscSection section, PetscSection *cSec)
9890 {
9891   PetscSection anchorSection;
9892   PetscInt     pStart, pEnd, sStart, sEnd, p, dof, numFields, f;
9893 
9894   PetscFunctionBegin;
9895   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9896   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
9897   PetscCall(PetscSectionCreate(PETSC_COMM_SELF, cSec));
9898   PetscCall(PetscSectionGetNumFields(section, &numFields));
9899   if (numFields) {
9900     PetscInt f;
9901     PetscCall(PetscSectionSetNumFields(*cSec, numFields));
9902 
9903     for (f = 0; f < numFields; f++) {
9904       PetscInt numComp;
9905 
9906       PetscCall(PetscSectionGetFieldComponents(section, f, &numComp));
9907       PetscCall(PetscSectionSetFieldComponents(*cSec, f, numComp));
9908     }
9909   }
9910   PetscCall(PetscSectionGetChart(anchorSection, &pStart, &pEnd));
9911   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
9912   pStart = PetscMax(pStart, sStart);
9913   pEnd   = PetscMin(pEnd, sEnd);
9914   pEnd   = PetscMax(pStart, pEnd);
9915   PetscCall(PetscSectionSetChart(*cSec, pStart, pEnd));
9916   for (p = pStart; p < pEnd; p++) {
9917     PetscCall(PetscSectionGetDof(anchorSection, p, &dof));
9918     if (dof) {
9919       PetscCall(PetscSectionGetDof(section, p, &dof));
9920       PetscCall(PetscSectionSetDof(*cSec, p, dof));
9921       for (f = 0; f < numFields; f++) {
9922         PetscCall(PetscSectionGetFieldDof(section, p, f, &dof));
9923         PetscCall(PetscSectionSetFieldDof(*cSec, p, f, dof));
9924       }
9925     }
9926   }
9927   PetscCall(PetscSectionSetUp(*cSec));
9928   PetscCall(PetscObjectSetName((PetscObject)*cSec, "Constraint Section"));
9929   PetscFunctionReturn(PETSC_SUCCESS);
9930 }
9931 
9932 static PetscErrorCode DMPlexCreateConstraintMatrix_Anchors(DM dm, PetscSection section, PetscSection cSec, Mat *cMat)
9933 {
9934   PetscSection    aSec;
9935   PetscInt        pStart, pEnd, p, sStart, sEnd, dof, aDof, aOff, off, nnz, annz, m, n, q, a, offset, *i, *j;
9936   const PetscInt *anchors;
9937   PetscInt        numFields, f;
9938   IS              aIS;
9939   MatType         mtype;
9940   PetscBool       iscuda, iskokkos;
9941 
9942   PetscFunctionBegin;
9943   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
9944   PetscCall(PetscSectionGetStorageSize(cSec, &m));
9945   PetscCall(PetscSectionGetStorageSize(section, &n));
9946   PetscCall(MatCreate(PETSC_COMM_SELF, cMat));
9947   PetscCall(MatSetSizes(*cMat, m, n, m, n));
9948   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJCUSPARSE, &iscuda));
9949   if (!iscuda) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJCUSPARSE, &iscuda));
9950   PetscCall(PetscStrcmp(dm->mattype, MATSEQAIJKOKKOS, &iskokkos));
9951   if (!iskokkos) PetscCall(PetscStrcmp(dm->mattype, MATMPIAIJKOKKOS, &iskokkos));
9952   if (iscuda) mtype = MATSEQAIJCUSPARSE;
9953   else if (iskokkos) mtype = MATSEQAIJKOKKOS;
9954   else mtype = MATSEQAIJ;
9955   PetscCall(MatSetType(*cMat, mtype));
9956   PetscCall(DMPlexGetAnchors(dm, &aSec, &aIS));
9957   PetscCall(ISGetIndices(aIS, &anchors));
9958   /* cSec will be a subset of aSec and section */
9959   PetscCall(PetscSectionGetChart(cSec, &pStart, &pEnd));
9960   PetscCall(PetscSectionGetChart(section, &sStart, &sEnd));
9961   PetscCall(PetscMalloc1(m + 1, &i));
9962   i[0] = 0;
9963   PetscCall(PetscSectionGetNumFields(section, &numFields));
9964   for (p = pStart; p < pEnd; p++) {
9965     PetscInt rDof, rOff, r;
9966 
9967     PetscCall(PetscSectionGetDof(aSec, p, &rDof));
9968     if (!rDof) continue;
9969     PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
9970     if (numFields) {
9971       for (f = 0; f < numFields; f++) {
9972         annz = 0;
9973         for (r = 0; r < rDof; r++) {
9974           a = anchors[rOff + r];
9975           if (a < sStart || a >= sEnd) continue;
9976           PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
9977           annz += aDof;
9978         }
9979         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
9980         PetscCall(PetscSectionGetFieldOffset(cSec, p, f, &off));
9981         for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
9982       }
9983     } else {
9984       annz = 0;
9985       PetscCall(PetscSectionGetDof(cSec, p, &dof));
9986       for (q = 0; q < dof; q++) {
9987         a = anchors[rOff + q];
9988         if (a < sStart || a >= sEnd) continue;
9989         PetscCall(PetscSectionGetDof(section, a, &aDof));
9990         annz += aDof;
9991       }
9992       PetscCall(PetscSectionGetDof(cSec, p, &dof));
9993       PetscCall(PetscSectionGetOffset(cSec, p, &off));
9994       for (q = 0; q < dof; q++) i[off + q + 1] = i[off + q] + annz;
9995     }
9996   }
9997   nnz = i[m];
9998   PetscCall(PetscMalloc1(nnz, &j));
9999   offset = 0;
10000   for (p = pStart; p < pEnd; p++) {
10001     if (numFields) {
10002       for (f = 0; f < numFields; f++) {
10003         PetscCall(PetscSectionGetFieldDof(cSec, p, f, &dof));
10004         for (q = 0; q < dof; q++) {
10005           PetscInt rDof, rOff, r;
10006           PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10007           PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10008           for (r = 0; r < rDof; r++) {
10009             PetscInt s;
10010 
10011             a = anchors[rOff + r];
10012             if (a < sStart || a >= sEnd) continue;
10013             PetscCall(PetscSectionGetFieldDof(section, a, f, &aDof));
10014             PetscCall(PetscSectionGetFieldOffset(section, a, f, &aOff));
10015             for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
10016           }
10017         }
10018       }
10019     } else {
10020       PetscCall(PetscSectionGetDof(cSec, p, &dof));
10021       for (q = 0; q < dof; q++) {
10022         PetscInt rDof, rOff, r;
10023         PetscCall(PetscSectionGetDof(aSec, p, &rDof));
10024         PetscCall(PetscSectionGetOffset(aSec, p, &rOff));
10025         for (r = 0; r < rDof; r++) {
10026           PetscInt s;
10027 
10028           a = anchors[rOff + r];
10029           if (a < sStart || a >= sEnd) continue;
10030           PetscCall(PetscSectionGetDof(section, a, &aDof));
10031           PetscCall(PetscSectionGetOffset(section, a, &aOff));
10032           for (s = 0; s < aDof; s++) j[offset++] = aOff + s;
10033         }
10034       }
10035     }
10036   }
10037   PetscCall(MatSeqAIJSetPreallocationCSR(*cMat, i, j, NULL));
10038   PetscCall(PetscFree(i));
10039   PetscCall(PetscFree(j));
10040   PetscCall(ISRestoreIndices(aIS, &anchors));
10041   PetscFunctionReturn(PETSC_SUCCESS);
10042 }
10043 
10044 PetscErrorCode DMCreateDefaultConstraints_Plex(DM dm)
10045 {
10046   DM_Plex     *plex = (DM_Plex *)dm->data;
10047   PetscSection anchorSection, section, cSec;
10048   Mat          cMat;
10049 
10050   PetscFunctionBegin;
10051   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10052   PetscCall(DMPlexGetAnchors(dm, &anchorSection, NULL));
10053   if (anchorSection) {
10054     PetscInt Nf;
10055 
10056     PetscCall(DMGetLocalSection(dm, &section));
10057     PetscCall(DMPlexCreateConstraintSection_Anchors(dm, section, &cSec));
10058     PetscCall(DMPlexCreateConstraintMatrix_Anchors(dm, section, cSec, &cMat));
10059     PetscCall(DMGetNumFields(dm, &Nf));
10060     if (Nf && plex->computeanchormatrix) PetscCall((*plex->computeanchormatrix)(dm, section, cSec, cMat));
10061     PetscCall(DMSetDefaultConstraints(dm, cSec, cMat, NULL));
10062     PetscCall(PetscSectionDestroy(&cSec));
10063     PetscCall(MatDestroy(&cMat));
10064   }
10065   PetscFunctionReturn(PETSC_SUCCESS);
10066 }
10067 
10068 PetscErrorCode DMCreateSubDomainDM_Plex(DM dm, DMLabel label, PetscInt value, IS *is, DM *subdm)
10069 {
10070   IS           subis;
10071   PetscSection section, subsection;
10072 
10073   PetscFunctionBegin;
10074   PetscCall(DMGetLocalSection(dm, &section));
10075   PetscCheck(section, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set default section for DM before splitting subdomain");
10076   PetscCheck(subdm, PetscObjectComm((PetscObject)dm), PETSC_ERR_ARG_WRONG, "Must set output subDM for splitting subdomain");
10077   /* Create subdomain */
10078   PetscCall(DMPlexFilter(dm, label, value, subdm));
10079   /* Create submodel */
10080   PetscCall(DMPlexGetSubpointIS(*subdm, &subis));
10081   PetscCall(PetscSectionCreateSubmeshSection(section, subis, &subsection));
10082   PetscCall(DMSetLocalSection(*subdm, subsection));
10083   PetscCall(PetscSectionDestroy(&subsection));
10084   PetscCall(DMCopyDisc(dm, *subdm));
10085   /* Create map from submodel to global model */
10086   if (is) {
10087     PetscSection    sectionGlobal, subsectionGlobal;
10088     IS              spIS;
10089     const PetscInt *spmap;
10090     PetscInt       *subIndices;
10091     PetscInt        subSize = 0, subOff = 0, pStart, pEnd, p;
10092     PetscInt        Nf, f, bs = -1, bsLocal[2], bsMinMax[2];
10093 
10094     PetscCall(DMPlexGetSubpointIS(*subdm, &spIS));
10095     PetscCall(ISGetIndices(spIS, &spmap));
10096     PetscCall(PetscSectionGetNumFields(section, &Nf));
10097     PetscCall(DMGetGlobalSection(dm, &sectionGlobal));
10098     PetscCall(DMGetGlobalSection(*subdm, &subsectionGlobal));
10099     PetscCall(PetscSectionGetChart(subsection, &pStart, &pEnd));
10100     for (p = pStart; p < pEnd; ++p) {
10101       PetscInt gdof, pSubSize = 0;
10102 
10103       PetscCall(PetscSectionGetDof(sectionGlobal, p, &gdof));
10104       if (gdof > 0) {
10105         for (f = 0; f < Nf; ++f) {
10106           PetscInt fdof, fcdof;
10107 
10108           PetscCall(PetscSectionGetFieldDof(subsection, p, f, &fdof));
10109           PetscCall(PetscSectionGetFieldConstraintDof(subsection, p, f, &fcdof));
10110           pSubSize += fdof - fcdof;
10111         }
10112         subSize += pSubSize;
10113         if (pSubSize) {
10114           if (bs < 0) {
10115             bs = pSubSize;
10116           } else if (bs != pSubSize) {
10117             /* Layout does not admit a pointwise block size */
10118             bs = 1;
10119           }
10120         }
10121       }
10122     }
10123     /* Must have same blocksize on all procs (some might have no points) */
10124     bsLocal[0] = bs < 0 ? PETSC_MAX_INT : bs;
10125     bsLocal[1] = bs;
10126     PetscCall(PetscGlobalMinMaxInt(PetscObjectComm((PetscObject)dm), bsLocal, bsMinMax));
10127     if (bsMinMax[0] != bsMinMax[1]) {
10128       bs = 1;
10129     } else {
10130       bs = bsMinMax[0];
10131     }
10132     PetscCall(PetscMalloc1(subSize, &subIndices));
10133     for (p = pStart; p < pEnd; ++p) {
10134       PetscInt gdof, goff;
10135 
10136       PetscCall(PetscSectionGetDof(subsectionGlobal, p, &gdof));
10137       if (gdof > 0) {
10138         const PetscInt point = spmap[p];
10139 
10140         PetscCall(PetscSectionGetOffset(sectionGlobal, point, &goff));
10141         for (f = 0; f < Nf; ++f) {
10142           PetscInt fdof, fcdof, fc, f2, poff = 0;
10143 
10144           /* Can get rid of this loop by storing field information in the global section */
10145           for (f2 = 0; f2 < f; ++f2) {
10146             PetscCall(PetscSectionGetFieldDof(section, p, f2, &fdof));
10147             PetscCall(PetscSectionGetFieldConstraintDof(section, p, f2, &fcdof));
10148             poff += fdof - fcdof;
10149           }
10150           PetscCall(PetscSectionGetFieldDof(section, p, f, &fdof));
10151           PetscCall(PetscSectionGetFieldConstraintDof(section, p, f, &fcdof));
10152           for (fc = 0; fc < fdof - fcdof; ++fc, ++subOff) subIndices[subOff] = goff + poff + fc;
10153         }
10154       }
10155     }
10156     PetscCall(ISRestoreIndices(spIS, &spmap));
10157     PetscCall(ISCreateGeneral(PetscObjectComm((PetscObject)dm), subSize, subIndices, PETSC_OWN_POINTER, is));
10158     if (bs > 1) {
10159       /* We need to check that the block size does not come from non-contiguous fields */
10160       PetscInt i, j, set = 1;
10161       for (i = 0; i < subSize; i += bs) {
10162         for (j = 0; j < bs; ++j) {
10163           if (subIndices[i + j] != subIndices[i] + j) {
10164             set = 0;
10165             break;
10166           }
10167         }
10168       }
10169       if (set) PetscCall(ISSetBlockSize(*is, bs));
10170     }
10171     /* Attach nullspace */
10172     for (f = 0; f < Nf; ++f) {
10173       (*subdm)->nullspaceConstructors[f] = dm->nullspaceConstructors[f];
10174       if ((*subdm)->nullspaceConstructors[f]) break;
10175     }
10176     if (f < Nf) {
10177       MatNullSpace nullSpace;
10178       PetscCall((*(*subdm)->nullspaceConstructors[f])(*subdm, f, f, &nullSpace));
10179 
10180       PetscCall(PetscObjectCompose((PetscObject)*is, "nullspace", (PetscObject)nullSpace));
10181       PetscCall(MatNullSpaceDestroy(&nullSpace));
10182     }
10183   }
10184   PetscFunctionReturn(PETSC_SUCCESS);
10185 }
10186 
10187 /*@
10188   DMPlexMonitorThroughput - Report the cell throughput of FE integration
10189 
10190   Input Parameters:
10191 + dm    - The `DM`
10192 - dummy - unused argument
10193 
10194   Options Database Key:
10195 . -dm_plex_monitor_throughput - Activate the monitor
10196 
10197   Level: developer
10198 
10199 .seealso: [](ch_unstructured), `DM`, `DMPLEX`, `DMSetFromOptions()`, `DMPlexCreate()`
10200 @*/
10201 PetscErrorCode DMPlexMonitorThroughput(DM dm, void *dummy)
10202 {
10203   PetscLogHandler default_handler;
10204 
10205   PetscFunctionBegin;
10206   PetscValidHeaderSpecific(dm, DM_CLASSID, 1);
10207   PetscCall(PetscLogGetDefaultHandler(&default_handler));
10208   if (default_handler) {
10209     PetscLogEvent      event;
10210     PetscEventPerfInfo eventInfo;
10211     PetscReal          cellRate, flopRate;
10212     PetscInt           cStart, cEnd, Nf, N;
10213     const char        *name;
10214 
10215     PetscCall(PetscObjectGetName((PetscObject)dm, &name));
10216     PetscCall(DMPlexGetHeightStratum(dm, 0, &cStart, &cEnd));
10217     PetscCall(DMGetNumFields(dm, &Nf));
10218     PetscCall(PetscLogEventGetId("DMPlexResidualFE", &event));
10219     PetscCall(PetscLogEventGetPerfInfo(PETSC_DEFAULT, event, &eventInfo));
10220     N        = (cEnd - cStart) * Nf * eventInfo.count;
10221     flopRate = eventInfo.flops / eventInfo.time;
10222     cellRate = N / eventInfo.time;
10223     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)));
10224   } else {
10225     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.");
10226   }
10227   PetscFunctionReturn(PETSC_SUCCESS);
10228 }
10229